opencv形态学应用之连通域提取

  • Post author:
  • Post category:其他


提取连通域实际上是标记连通域的过程,其算法如下:

初始点:Bo=某个连通分量中的某点(这里通过遍历图像,知道像素值为255,将此点作为种子点)

循环:


(用3X3d的结构元素对种子点进行膨胀,然后用原图像对膨胀结果取交集)

结束条件:知道膨胀的图像不发生变化

在这里可以用一个模板来存储连通分量,其位置对应原图像的位置。提取完后可以修改原图像的像素值,即一个标签。

实现过程如下:

#include<iostream>
#include<opencv2\opencv.hpp>
using namespace std;
using namespace cv;

/*******************************************
功能:标注连通分量
参数:src-输入图像
      nConn-取值4、8,表示4连通或8连通
********************************************/
void LabelConnRgn(Mat& src, int nConn)
{
	int se[3][3] = { { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 } };//8连通
	if (nConn == 4)
	{
		se[0][0] = -1;
		se[0][2] = -1;
		se[2][0] = -1;
		se[2][2] = -1;
	}
	int nConnRgn = 254;//连通分量的标记号
	Mat dst = Mat::zeros(src.size(), src.type());
	for (int i = 0; i < src.rows; i++)
	{
		
		uchar* srcData = src.ptr<uchar>(i);
		uchar* dstData = dst.ptr<uchar>(i);
		for (int j = 0; j < src.cols; j++)
		{
			if (srcData[j] == 255)
			{ 
				dstData[j] = 255;
				while (true)
				{
					Mat temp;
					dst.copyTo(temp);
					dilate(dst, dst, se);
					dst= dst&src;
					//如果和上一次处理后的图像相同,说明连通区域已经提取完毕
					if (equalImg(dst, temp))
					{
						break;
					}
				}
				//标注刚刚找到的连通区域
				for (int k = 0; k < src.rows; k++)
				{
					uchar* srcData = src.ptr<uchar>(k);
					uchar* dstData = dst.ptr<uchar>(k);
					for (int l = 0; l < src.cols; l++)
					{
						if (dstData[l] == 255)
						{
							//标记原图像上的连通区域
							srcData[l] = nConnRgn;
						}
					}
				}
				nConnRgn-=50;//连通区域编号加1(此处为了显示的方便实际为:nConnRgn--;)
				if (nConnRgn <=0)
				{
					cout << "连通区域大于254个连通区域" << endl;
					i = src.rows;//强制跳出外循环
					break;
				}
			}
		}
	}
}

int main()
{
	Mat src= imread("test1.jpg", 0);
	imshow("原始图像", src);
	LabelConnRgn(src, 4);
	imshow("连通区域",src);
	waitKey(0);
	return 0;
}

原始图像:


提取后的图像:




版权声明:本文为wxplol原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。