基于OpenCV Mat 的图像连通区域标记处理

前言

OpenCV好像有自带的求连通域的函数,奈何自己用的版本里没有这个功能,只好自己写orz
求连通域,大概有两种方法:

Two-Pass(两遍扫描法)

Seed Filling(种子填充法)

两种算法原理可以到这位博主的博客看看。

实现过程

本文应该是采用了第二种方法,种子填充法。

  1. 参数 Mat & input 说明:这是一个二值图像,值为0或255,0(黑色)表示背景区域,255(白色)表示物料区域。
  2. 函数 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)
  1. int labelnum;
    标签值。测试时为了便于区分,初始为1,每次递增10
  2. input.at(i, j) = labelnum + 50;
    加50,也是为了使灰度值更大一点,所以并不重要。
  3. 其他的代码中注释应该够了。

代码实现

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()**连通域求解函数。我恨!
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Chauncyxu/article/details/118167553