基于区域的图像分割-----------区域生长
1. 区域生长
该方法基于同一物体内部各像素在相似特性上的共性来进行像素群落的聚合。自起始区域出发(例如采用小邻域作为起始基础或直接取每个单独像素作为起始单元)。通过整合与当前已有的区域内具有相同特性属性的相邻像素或其他子区域能够整合到当前已有的区域内,并由此持续扩大该区域。直到无法再整合任何像素或其他独立子区域能够加入为止。在该区域内所包含各像素之间的相似特性度量可综合考虑其平均亮度值、纹理特征以及颜色特征等因素。
区域增长方法是一种广泛应用的方法,在无需利用先验知识时能够达到最佳性能水平,并可应用于复杂程度较高的图像处理中如自然景物图像的分割。然而,在实际应用中该方法基于迭代的过程运行,并导致较高的空间和时间成本。
该方法属于串行式的区域分割图像分割技术。该算法从选定像素或多个像素开始逐步扩展并收集相关联的像元群组,并在满足特定条件时停止扩展过程。其效果主要取决于以下三个关键因素:一是初始点(种子点)的选择;二是像元合并的标准;三是停止准则的具体定义。即通过从选定像素或多个起始点开始逐步扩展的方式,在满足一定条件后完成整个图像域的划分,并最终实现目标物的识别和提取过程。
区域生长的原理
区域生长的核心概念是通过收集具有相似特征的像素来形成特定区域。具体操作中,对于每一个需要分割的区域,首先确定一个起始点作为种子像素,并将其纳入该区域。随后,在每一步骤中将与种子像素具有相同或相似特性的周边像素纳入该区域,并以这些新加入的像素作为新的起始点继续这一过程。直至无法再找到符合条件的新像素为止。
以图1为例展示了基于已知种子点进行区域生长的操作流程。其中,在图1(a)中展示的是待分割图像,在其基础上设定两个具有不同灰度 shade 的起始像素点作为种子源码块,并启动区域增长算法。在本研究中所采用的关键判别标准是基于灰度差异程度来确定是否包含目标像素点——即若某考察 pixel 与其所属 seed pixel 的灰度差绝对值小于某一预设阈值 T,则将该 pixel 包含至 seed pixel 所关联的空间区域内。通过这一设定,在实验中发现当取阈值 T 为 3 时,在实验样本上能够较好地将图像划分为两个独立且清晰的空间块;而当取阈值 T 为 6 时,则导致整个图像被归并至单一空间块——由此可以看出选取合适的阈值对于分割效果至关重要
古老的图像分割技术称为区域增长法(Region Growing),最初由Levine及其团队提出了一种经典的二维图像分割算法。通常有两种基本实现途径:一种策略是在实际应用中通常采用以下两种策略之一:一种策略是预先确定目标物体内部的一个小块作为种子区域(seed point),然后在此基础上不断扩展其周围的像素点集合直至形成完整的目标物体对应的完整区域集合;另一种策略则是将整个图像分解为多个高度一致的小块或子块(如区域内像素灰度值相同),随后通过特定规则对这些小块进行逐级合并最终完成整个图像的划分过程(典型的如T. C. Pong等人提出的基于小面facet模型的区域增长算法)。其主要缺陷在于容易导致过细的划分结果
区域生长实现的步骤如下:
1. 对图像顺序扫描!找到第1个还没有归属的像素, 设该像素为(x0, y0);
2. 取中心点坐标为 (xₐ,yₐ),遍历其四个相邻像素点 (ξ,η),若当前象素点 (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)nWidthnHeight);
// 种子点
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)
&& 绝对值(pUnchInput在位置 yy 乘以 保存 宽度 加 xx 处 减去 pUnchInput 在 位置 nCurrY 乘以 保存 宽度 加 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 ;
}
该代码的效果不是很好,大概是选择的生长点不是很好吧。
