1. 区域生长
区域增长方法是根据同一物体区域内象素的相似性质来聚集象素点的方法,从初始区域(如小邻域或甚至于每个象素)开始,将相邻的具有同样性质的象素或其它区域归并到目前的区域中从而逐步增长区域,直至没有可以归并的点或其它小区域为止。区域内象素的相似性度量可以包括平均灰度值、纹理、颜色等信息。
区域增长方法是一种比较普遍的方法,在没有先验知识可以利用时,可以取得最佳的性能,可以用来分割比较复杂的图象,如自然景物。但是,区域增长方法是一种迭代的方法,空间和时间开销都比较大。
区域生长是一种串行区域分割的图像分割方法。区域生长是指从某个像素出发,按照一定的准则,逐步加入邻近像素,当满足一定的条件时,区域生长终止。区域生长的好坏决定于1.初始点(种子点)的选取。2.生长准则。3.终止条件。区域生长是从某个或者某些像素点出发,最后得到整个区域,进而实现目标的提取。
区域生长的原理
区域生长的基本思想是将具有相似性质的像素集合起来构成区域。具体先对每个需要分割的区域找一个种子像素作为生长起点,然后将种子像素和周围邻域中与种子像素有相同或相似性质的像素(根据某种事先确定的生长或相似准则来判定)合并到种子像素所在的区域中。将这些新像素当作新的种子继续上面的过程,直到没有满足条件的像素可被包括进来。这样一个区域就生长成了。
图1给出已知种子点进行区域生长的一个示例。图1(a)给出需要分割的图像,设已知两个种子像素(标为深浅不同的灰色方块),现要进行区域生长。设这里采用的判定准则是:如果考虑的像素与种子像素灰度值差的绝对值小于某个门限T,则将该像素包括进种子像素所在的区域。图1(b)给出了T=3时的区域生长结果,整幅图被较好地分成2个区域;图1(c)给出了T=1时的区域生长结果,有些像素无法判定;图1(c)给出了T=6时的区域生长的结果,整幅图都被分在一个区域中了。由此可见门限的选择是很重要的。
区域生长是一种古老的图像分割方法,最早的区域生长图像分割方法是由Levine等人提出的。该方法一般有两种方式,一种是先给定图像中要分割的目标物体内的一个小块或者说种子区域(seed point),再在种子区域基础上不断将其周围的像素点以一定的规则加入其中,达到最终将代表该物体的所有像素点结合成一个区域的目的;另一种是先将图像分割成很多的一致性较强,如区域内像素灰度值相同的小区域,再按一定的规则将小区域融合成大区域,达到分割图像的目的,典型的区域生长法如T. C. Pong等人提出的基于小面(facet)模型的区域生长法,区域生长法固有的缺点是往往会造成过度分割,即将图像分割成过多的区域
区域生长实现的步骤如下:
1. 对图像顺序扫描!找到第1个还没有归属的像素, 设该像素为(x0, y0);
2. 以(x0, y0)为中心, 考虑(x0, y0)的4邻域像素(x, y)如果(x0, y0)满足生长准则, 将(x, y)与(x0, y0)合并(在同一区域内), 同时将(x, y)压入堆栈;
3. 从堆栈中取出一个像素, 把它当作(x0, y0)返回到步骤2;
4. 当堆栈为空时!返回到步骤1;
5. 重复步骤1 - 4直到图像中的每个点都有归属时。生长结束。
代码:
/*************************************************************************
*
* /函数名称:
* RegionGrow()
*
* /输入参数:
* CDib * pDib - 指向CDib类的指针,含有原始图象信息
* unsigned char * pUnRegion - 指向区域生长结果的指针
*
* /返回值:
* 无
*
* /说明:
* pUnRegion指针指向的数据区存储了区域生长的结果,其中(逻辑)表示
* 对应象素为生长区域,表示为非生长区域
* 区域生长一般包含三个比较重要的问题:
* 1. 种子点的选取
* 2. 生长准则
* 3. 终止条件
* 可以认为,这三个问题需要具体分析,而且每个问题解决的好坏直接关系到
* 区域生长的结果。
* 本函数的种子点选取为图像的中心,生长准则是相邻象素的象素值小于
* nThreshold, ()终止条件是一直进行到再没有满足生长准则需要的象素时为止
*
*************************************************************************
*/
// 在这个代码中,它认为这张图片就是一个区域,选取了中间点为种子点。
void RegionGrow(CDib * pDib, unsigned char * pUnRegion, int nThreshold)
{
static int nDx[]={-1,0,1,0};
static int nDy[]={0,1,0,-1};
nThreshold = 20;
// 遍历图象的纵坐标
// int y;
// 遍历图象的横坐标
// int x;
// 图象的长宽大小
CSize sizeImage = pDib->GetDimensions();
int nWidth = sizeImage.cx ;
int nHeight = sizeImage.cy ;
// 图像在计算机在存储中的实际大小
CSize sizeImageSave = pDib->GetDibSaveDim();
// 图像在内存中每一行象素占用的实际空间
int nSaveWidth = sizeImageSave.cx;
// 初始化
memset(pUnRegion,0,sizeof(unsigned char)*nWidth*nHeight);
// 种子点
int nSeedX, nSeedY;
// 设置种子点为图像的中心
nSeedX = nWidth /2 ;
nSeedY = nHeight/2 ;
// 定义堆栈,存储坐标
int * pnGrowQueX ;
int * pnGrowQueY ;
// 分配空间
pnGrowQueX = new int [nWidth*nHeight];
pnGrowQueY = new int [nWidth*nHeight];
// 图像数据的指针
unsigned char * pUnchInput =(unsigned char * )pDib->m_lpImage;
// 定义堆栈的起点和终点
// 当nStart=nEnd, 表示堆栈中只有一个点
int nStart ;
int nEnd ;
//初始化
nStart = 0 ;
nEnd = 0 ;
// 把种子点的坐标压入栈
pnGrowQueX[nEnd] = nSeedX;
pnGrowQueY[nEnd] = nSeedY;
// 当前正在处理的象素
int nCurrX ;
int nCurrY ;
// 循环控制变量
int k ;
// 图象的横纵坐标,用来对当前象素的邻域进行遍历
int xx;
int yy;
while (nStart<=nEnd)
{
// 当前种子点的坐标
nCurrX = pnGrowQueX[nStart];
nCurrY = pnGrowQueY[nStart];
// 对当前点的邻域进行遍历
for (k=0; k<4; k++)
{
// 4邻域象素的坐标
xx = nCurrX + nDx[k];
yy = nCurrY + nDy[k];
// 判断象素(xx,yy) 是否在图像内部
// 判断象素(xx,yy) 是否已经处理过
// pUnRegion[yy*nWidth+xx]==0 表示还没有处理
// 生长条件:判断象素(xx,yy)和当前象素(nCurrX,nCurrY) 象素值差的绝对值
if ( (xx < nWidth) && (xx>=0) && (yy<nHeight) && (yy>=0)
&& (pUnRegion[yy*nWidth+xx]==0)
&& abs(pUnchInput[yy*nSaveWidth+xx] - pUnchInput[nCurrY*nSaveWidth+nCurrX])<nThreshold )
{
// 堆栈的尾部指针后移一位
nEnd++;
// 象素(xx,yy) 压入栈
pnGrowQueX[nEnd] = xx;
pnGrowQueY[nEnd] = yy;
// 把象素(xx,yy)设置成逻辑()
// 同时也表明该象素处理过
pUnRegion[yy*nWidth+xx] = 255 ;
}
}
nStart++;
}
// 释放内存
delete []pnGrowQueX;
delete []pnGrowQueY;
pnGrowQueX = NULL ;
pnGrowQueY = NULL ;
}
该代码的效果不是很好,大概是选择的生长点不是很好吧。