OpenCV:区域生长法实现
1、基本思想:
区域生长法的基本思想是:将具有相似性质的像素点合并到一起;
在每一个区域内首先要指定一个种子点作为生长的起点;
然后将种子点周围邻域的像素点与种子点进行比较,
对具有相似性质的点合并之后继续向外生长,
直到没有满足条件的像素被包括进来为止;
这样一个区域的生长就完成了。
下图是:发在PAMI上的LSD直线检测算法中的关键一步就是找line support regions.这个区域的查找就是利用区域生长法则,生长的条件就是梯度的方向角度;
图中第一张图是原始图像,第二张是计算梯度角度,第三张是根据梯度角度区域生长的结果,相同颜色就是一个区域生长结果。

2、关键点在于:
①给定种子点(种子点如何选取)
种子点的选取很多时候都采用人工交互的方法实现,
也有用其他方式的,比如寻找物体并提取物体内部点作为种子点。
②确定在生长过程中能将相邻像素包括进来的准则(相似性质准则)
灰度图像的差值;
彩色图像的颜色等等。
都是关于像素与像素间的关系描述。
③生长的停止条件:
四连通与八连通

3、区域生长流程:
①给出种子点,这里一次给出一个种子点
②给出相似性准则,这里采用灰度差;
③将种子点与其八邻域像素点进行相似性比较;相似点则作为下次生长的种子点;
④停止生长:八邻域内没有合并的像素点时,即没有新的种子点时,停止。
如图所示:

4、示例程序:
OpenCV没有自带区域生长函数;
为了说明区域是如何生长的,
#include <opencv2\opencv.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\features2d\features2d.hpp>
#include <opencv2\core\core.hpp>
using namespace std;
using namespace cv;
void RegionGrowing(Mat srcImg,Mat& dstImg,Point pt,int thre)
{
Point ptGrowing;//生长点像素坐标
int nGrowLabel=0;//是否被标记
int startPtValue=0;//生长起始点灰度值
int currPtValue=0;//当前生长点灰度值
//8邻域
int mDir[8][2]={{-1,-1},{0,-1},{1,-1},{-1,0},{1,0},{-1,1},{0,1},{1,1}};
vector<Point> growPtVec;//生长点堆栈
growPtVec.push_back(pt);//将初始生长点压入堆栈
Mat markImg=Mat::zeros(srcImg.size(),CV_8UC1);//标记点
unsigned char *pData = (unsigned char *)(markImg.data+pt.y*markImg.step);
pData[pt.x]=255;//标记初始生长点
startPtValue = ((unsigned char*)(srcImg.data+pt.y*srcImg.step))[pt.x];
while (!growPtVec.empty())
{
Point currPt = growPtVec.back();//返回当前vector最末一个元素
growPtVec.pop_back();//弹出最后压入的数据
for (int i=0;i<8;i++)
{
ptGrowing.x = currPt.x+mDir[i][0];
ptGrowing.y = currPt.y+mDir[i][1];
//判断是否是边缘点
if (ptGrowing.x<0||ptGrowing.y<0||(ptGrowing.x>srcImg.cols-1)||(ptGrowing.y>srcImg.rows))
continue;//继续执行下一次循环
//判断是否已被标记
nGrowLabel = ((unsigned char*)(markImg.data+ptGrowing.y*markImg.step))[ptGrowing.x];
if (nGrowLabel==0)//没有被标记
{
currPtValue = ((unsigned char*)(srcImg.data+ptGrowing.y*srcImg.step))[ptGrowing.x];
if (abs(currPtValue-startPtValue)<=thre)
{
((unsigned char*)(markImg.data+ptGrowing.y*markImg.step))[ptGrowing.x]=255;
growPtVec.push_back(ptGrowing);
}
}
}
}
markImg.copyTo(dstImg);
}
int main()
{
Mat srcImg = imread("1.jpg",0);
if (srcImg.empty())
printf("image read error");
Mat srcImg1=srcImg.clone();
Mat outImg1,outImg2;
RegionGrowing(srcImg1,outImg1,Point(241,258),10);
RegionGrowing(srcImg1,outImg2,Point(302,118),80);
add(outImg1,outImg2,outImg1);
imshow("p1p2",outImg1);
Mat resultImg;
srcImg.copyTo(resultImg,~outImg1);
imshow("outImg",resultImg);
waitKey(0);
return 0;
}


5、算法效果:
这里首先人为选取一个种子点,
在其八邻域内,由相似性准则进行判断并更新种子点;
当不再产生新的种子点时,截止;
当区分多目标是,可以认为选取多个种子点,然后将每个种子点分割区域合并;
即可分割出多目标。

6、给出一个比较好区域生长法的程序:
//仅根据纹理分割图像,纹理特征是四维集合;
void segmentTexture(Mat texture)
{
Mat regions(texture.rows, texture.cols, CV_8UC1, 0.0);
Mat mark(texture.rows, texture.cols, CV_8UC1, 0.0);
int min_regions = 10;
RNG rng(25);
Point seedPoint, now;
Vec4b curr, next, seed;
queue<Point> Q;
vector<pair<pair<Point, Vec3b>, int> > Points;
int pixelCount=0;//pixel covered in a segment
for(int i =250; i>10; i -= 250/min_regions)
{
rng(12345);
do
{
seedPoint.x = rng.uniform(0, texture.cols);
seedPoint.y = rng.uniform(0, texture.rows);
seed = texture.at<Vec4b>(seedPoint.y, seedPoint.x);
}
while(mark.at<uchar>(seedPoint.y, seedPoint.x) !=0 );
mark.at<uchar>(seedPoint.y, seedPoint.x) = i;
Q.push(seedPoint);
while(!Q.empty())
{
now = Q.front();
Q.pop();
curr=texture.at<Vec4b>(now.y, now.x);
for(int p=-1; p<=1; p++)
{
for(int q=-1; q<=1; q++)
{
if(0<=now.x+p && now.x+p<texture.cols && 0<=now.y+q && now.y+q<texture.rows)
{
next=texture.at<Vec4b>(now.y+q, now.x+p);
if(mark.at<uchar>(now.y+q, now.x+p)==0 &&
(textureDifference(next, seed)==1 || farTextureDifference(next, curr)==2))
{
Q.push(Point(now.x+p, now.y+q));
mark.at<uchar>(now.y+q, now.x+p)=i;
//segm.at<Vec3b>(now.y, now.x)=image.at<Vec3b>(seedPoint.y, seedPoint.x);
}
}
}
}
}
}
///namedWindow("texture segment",2);
///imshow("texture segment",mark);
imwrite("textureSegment.bmp",mark);
}