前言
OpenCV好像有自带的求连通域的函数,奈何自己用的版本里没有这个功能,只好自己写orz
求连通域,大概有两种方法:
Two-Pass(两遍扫描法)
Seed Filling(种子填充法)
两种算法原理可以到这位博主的博客看看。
实现过程
本文应该是采用了第二种方法,种子填充法。
- 参数 Mat & input 说明:这是一个二值图像,值为0或255,0(黑色)表示背景区域,255(白色)表示物料区域。
- 函数 labelmark 使用两层for循环遍历 Mat input 矩阵;
内层循环中两个串联的 if 语句块:
// 第一个防止 fill_4 函数递归次数太大,内存不够。
if((input.at<unsigned char>(i, j) != 255 && input.at<unsigned char>(i, j) != 0))
//(2)第二个就是实现四邻域的递归判断
if (input.at<unsigned char>(i,j)==255)
- int labelnum;
标签值。测试时为了便于区分,初始为1,每次递增10 - input.at(i, j) = labelnum + 50;
加50,也是为了使灰度值更大一点,所以并不重要。 - 其他的代码中注释应该够了。
代码实现
void fill_4(int x, int y, int count, Mat& input, int & jishu) {
//cout << "x = "<< x << ", y = " << y <<", count = "<< count << endl;
jishu++;
if (x-1<=0 || x+1 >=319 || y-1<=0 ||y+1>=319) // 没有考虑四边,偷懒先把四边就设置为0了。
{
return;
}
if (input.at<unsigned char>(x-1, y) == 255) {
input.at<unsigned char>(x-1, y) = count + 50;
if (jishu == 2000)
{
return;
}
fill_4(x-1, y, count, input, jishu);
}
if (input.at<unsigned char>(x, y-1) == 255) {
input.at<unsigned char>(x, y-1) = count + 50;
if (jishu == 2000)
{
return;
}
fill_4(x, y-1, count, input, jishu);
}
if (input.at<unsigned char>(x+1, y) == 255) {
input.at<unsigned char>(x + 1, y) = count + 50;
if (jishu == 2000)
{
return;
}
fill_4(x+1, y, count, input, jishu);
}
if (input.at<unsigned char>(x, y+1) == 255) {
input.at<unsigned char>(x, y+1) = count + 50;
if (jishu == 2000)
{
return;
}
fill_4(x, y+1, count, input, jishu);
}
return;
}
int labelmark(Mat& input) {
// 用四邻域,八邻域斜角情况较复杂
/*
按行遍历元素:
如果值=0,则跳过;
如果值=255,则判断其上、右两个邻域是否
*/
int temp = 0;
int jishu = 0; // 记录递归次数,本函数在本机上,测试的最大可递归次数为2089,本函数中设置为2000,jishu==2000则结束递归。
int labelnum = 1;
for (int i = 1; i < input.rows-1; i++)
{
for (int j = 1; j < input.cols-1; j++)
{
// 这里是找到递归超限时添加的代码,没删,记录一下提醒自己
//cout << "这是for循环: " << i << ", " << j << endl;
//if (labelnum == 17 && input.at<unsigned char>(i, j) == 255) {
// // cout << "这是for循环: " << i << ", " << j << endl;
// //input.at<unsigned char>(i, j) = 0;
// cout << jishu << endl;
// jishu++;
// continue;
//}
if (input.at<unsigned char>(i, j) != 255 && input.at<unsigned char>(i, j) != 0) // 已经被标记过的
{
// 判断该像素四邻域是否存在值=255的,如果有,则将label变为当前像素值,再次进行递归
if (input.at<unsigned char>(i-1, j) == 255
|| input.at<unsigned char>(i, j - 1) == 255
|| input.at<unsigned char>(i + 1, j) == 255
|| input.at<unsigned char>(i, j + 1) == 255)
{
temp = input.at<unsigned char>(i, j)-50; // -50别忘了
fill_4(i, j, temp, input, jishu);
jishu = 0;
}
}
if (input.at<unsigned char>(i,j)==255)
{
input.at<unsigned char>(i, j) = labelnum + 50;
fill_4(i, j, labelnum, input, jishu);
labelnum+=10;
jishu = 0;
break;
}
}
}
return (labelnum - 1) / 10 + 1;
}
结果
反正我正确了……
实验用的图片暂时不能放出来,万一论文用到了呢(想美事吧orz)
补充
这是我用的库,2的,3版自带有**connectedComponents()**连通域求解函数。我恨!