区域生长算法及其实现
背景
前面我们已经介绍了
最大熵分割法OTSU算法 他们都有各自的优缺点,通常都不是单独使用这些算法,需要和其它算法来结合使用,前面两类算法都是单独对图像的灰度信息进行处理,不包含图像的空间信息,而区域生长算法则包含了图像的空间信息。
优点:
比较灵活,可以根据项目需要灵活的选择所需要的生长的规则,分割的效果比较好,通常可以较好的分割出种子点周围的区域。
缺点:
当要分割的区域不联通,被分割成一个个小的封闭区域时,会导致出来的只有种子点附近的一个小区域,分割不理想,区域生长算法容易受到噪声等的影响,同时由于该算法是迭代算法,算法的时间开销大。
算法原理:
步骤:
1、在图像中选取一个种子点(x,y)
2、根据生长规则,判断种子点周围8个点哪几个点可以作为下次生长的点,判断完后,将当前的种子标记为已使用。
3、判断下一生长点
4、直到不再有生长点。
哪么生长规则如何设定呢?大概有以下几种判断规则:灰度、梯度幅值等,例如:判断种子点与下一生长点之间的灰度差,如何小于某值,则将其加入种子点中。
算法流程:
1、将初始种子点存入vector
2、弹出1个种子点,根据生长规则判断周围8个点哪个符合规则,符合则加入到vector中
3、判断vector的size是不是为0,为0则结束,不为0则继续第二步
Opencv实现:
void Region_Growing(Mat input, Mat& output, Point2i fristseed, int value)
{
vector<Point2i>allseed; //保存种子
allseed.push_back(fristseed); //压入第一个种子
int direction[8][2] = { { -1, -1 }, { 0, -1 }, { 1, -1 }, { 1, 0 }, { 1, 1 }, { 0, 1 }, { -1, 1 }, { -1, 0 } };//种子选取的顺序
output = Mat::zeros(input.size(), CV_8UC1); //创建一个黑图
output.at<uchar>(fristseed.y, fristseed.x) = 255; //第一个种子点设置为
int cerseedvalue = 0; //初始种子点的值
int nextseedvalue = 0; //与种子点相比较的下一点的值
Point2i comparseed; //与种子点相比较的下一点
cerseedvalue = input.at<uchar>(fristseed.y, fristseed.x); // 种子点的灰度值
while (!allseed.empty())
{
fristseed = allseed.back(); //最后一个种子点的
allseed.pop_back(); //弹出最后一个种子点
for (int i = 0; i < 8; ++i)
{
comparseed.x = direction[i][0] + fristseed.x;
comparseed.y = direction[i][1] + fristseed.y;
if (comparseed.x<0 || comparseed.x>(input.cols-1) || comparseed.y<0 || comparseed.y>(input.rows-1))
continue;
if (output.at<uchar>(comparseed.y, comparseed.x) == 0) //判断有没有被使用过
{
nextseedvalue = input.at<uchar>(comparseed.y, comparseed.x); //生长点的值
if (abs(nextseedvalue - cerseedvalue) < value)//生长规则
{
allseed.push_back(comparseed);
output.at<uchar>(comparseed.y, comparseed.x) =255;
}
}
}
}
}
米粒 | 米粒分割图 |