OpenCV ----像素距离与连通域

一. 图像像素距离变换

1.常用距离的三种定义:

(1)欧式距离-----DIST_L2

​ 两像素点的直线距离,利用勾股定理可计算出两点的欧式距离
d = ( x 1 − x 2 ) 2 + ( y 1 − y 2 ) 2 2 d = \sqrt[2]{(x_1-x_2)^2 +(y_1-y_2)^2 } d=2(x1x2)2+(y1y2)2

(2)街区距离-----DIST_L1

​ 两像素点X和Y方向距离之和,计算公式为
d = ∣ x 1 − x 2 ∣ + ∣ y 1 − y 2 ∣ d = |x_1-x_2| +|y_1-y_2| d=x1x2+y1y2

(3)棋盘距离------DIST_C

​ 棋盘距离为两像素点移动到同一行或同一列的最大距离,即两点X方向和Y方向上的最大距离

2. distanceTransform() ------ 距离转换函数
(1) 函数原型
void distanceTransform(InputArray src, 
                            OutputArray dst, 
                            int distanceType, 
                            int maskSize, 
                            int dstType=CV_32F )
     
 //distanceTransform()函数有两个原型,此处为较常用的

src:准备作距离变换的原图像。

dst:经过距离变换后的输出图像。

distanceType:距离类型,标志见上

maskSize :计算距离时使用的mask矩阵大小,参数可以为3或5。

dstType: 输出图像(矩阵)的数据类型,可以是CV_8U 或 CV_32F。当选择CV_8U时,distanceType的类型只能为DIST_L1。

distanceTransform()函数计算的是非零像素点到0像素点的距离

(2)运用演示
//计算简易5*5矩阵  1,1,1,1,1
//               1,1,1,1,1
//               1,1,0,1,1
//               1,1,1,1,1
//               1,1,1,1,1  的三种距离


void QuickDemo::distance_demo(Mat& img)
{
	Mat a = (Mat_<uchar>(5, 5) 
		<< 1, 1, 1, 1, 1 ,
		 1, 1, 1, 1, 1,
		 1, 1, 0, 1, 1 ,
		 1, 1, 1, 1, 1 ,
		 1, 1, 1, 1, 1);

	Mat dis_L1, dis_L2, dis_C;


	distanceTransform(a, dis_L1, DIST_L1, 3, CV_8U);
	cout << "街区距离" << endl << dis_L1 << endl;


	distanceTransform(a, dis_L2, DIST_L2, 3, CV_8U);
	cout << "欧式距离" << endl << dis_L2 << endl;


	distanceTransform(a, dis_C, DIST_C, 3, CV_8U);
	cout << "棋盘距离" << endl << dis_L1 << endl;
}

由于distanceTransform()函数计算的是非零像素点到0像素点的距离,得到下列输出

在这里插入图片描述

二. 图像连通域

(1)定义

图像的连通域是指图像中具有相同像素值并且位置相邻的像素组成的区域。提取图像中不同的连通域是图像处理中较为常用的方法,例如在车牌识别、文字识别、目标检测等领域对感兴趣区域分割与识别。一般情况下,一个连通域内只包含一个像素值,因此为了防止像素值波动对提取不同连通域的影响,连通域分析常处理的是二值化后的图像

(2)邻域

​ opencv中有以下两种形式领域:

4-邻域:必须在水平和垂直方向上相邻,相邻的两个像素坐标必须只有一位不同而且只能相差1个像素

8-邻域: 九宫格形式,相邻的两个像素坐标必须只有一位不同而且只能相差1个像素

在这里插入图片描述

(3)标记连通域函数-----connectedComponents()
int connectedComponents(InputArray image, OutputArray labels,
                          int connectivity, int ltype, int ccltype);

image: 输入二值图像

labels: 输出图像

connectivity: 邻域种类,4或8

ltype: 输出图深度,支持CV_32S 和CV_16S

代码演示:


int main()
{
     Mat img,M,dst;
     img = imread("D:/OpenCV/photo/R.png",1);
	 //对图像进行距离变换
	 if (img.empty())
	 {
		 cout << "请确认图像文件名称是否正确" << endl;
		 return -1;
	 }
	 cv::Mat rice, riceBW;

	 //将图像转成二值图像,用于统计连通域
	 cv::cvtColor(img, rice, cv::COLOR_BGR2GRAY);
	 cv::threshold(rice, riceBW, 50, 255, cv::THRESH_BINARY);

	 //生成随机颜色,用于区分不同连通域
	 cv::RNG rng(10086);
	 cv::Mat out;
	 int number = connectedComponents(riceBW, out, 8, CV_16U);  //统计图像中连通域的个数
	 vector<cv::Vec3b> colors;
	 for (int i = 0; i < number; i++)
	 {
		 //使用均匀分布的随机数确定颜色
		 cv::Vec3b vec3 = cv::Vec3b(rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256));
		 colors.push_back(vec3);
	 }

	 //以不同颜色标记出不同的连通域
	 cv::Mat result = cv::Mat::zeros(rice.size(), img.type());
	 int w = result.cols;
	 int h = result.rows;
	 for (int row = 0; row < h; row++)
	 {
		 for (int col = 0; col < w; col++)
		 {
			 int label = out.at<uint16_t>(row, col);
			 if (label == 0)  //背景的黑色不改变
			 {
				 continue;
			 }
			 result.at<cv::Vec3b>(row, col) = colors[label];
		 }
	 }

	 //显示结果
	 cv::imshow("原图", img);
	 cv::imshow("标记后的图像", result);

	 cv::waitKey(0);
	 return 0;
}

在这里插入图片描述

(3)connectedComponents()函数

connectedComponents()函数除了输出连通区域标签图像外,还会输出每个连通区域的一些统计信息,如像素数目、外接矩形等。该函数的原型如下:

int connectedComponentsWithStats(InputArray image,
                                 OutputArray labels,
                                 OutputArray stats,
                                 OutputArray centroids,
                                 int connectivity = 8,
                                 int ltype = CV_32S);

参数 stats 为输出的统计信息,参数 centroids 为输出的连通区域质心。

代码演示:

void QuickDemo::connectedComponentsWithstats_demo(Mat& img)
{
	if (img.empty())
	{
		cout << "请确认图像文件名称是否正确" << endl;
		return;
	}

	GaussianBlur(img, img, Size(7, 7),3);
	Mat rice, riceBW;
	cout << img.type() << endl;
	cvtColor(img, rice, COLOR_BGR2GRAY);
	threshold(rice, riceBW, 50, 255, THRESH_BINARY);

	RNG rng(10086);
	Mat out;
	Mat satas, cen;
	int number = connectedComponentsWithStats(riceBW, out, satas, cen, 8, CV_16U);
	vector<Vec3b> colors;
	for (int i = 0; i < number; i++)
	{

		cv::Vec3b vec3 = cv::Vec3b(rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256));
		colors.push_back(vec3);
	}


	cv::Mat result = cv::Mat::zeros(rice.size(), img.type());
	//int w = result.cols;
	//int h = result.rows;

	for (int i = 1; i < number; ++i)
	{
		int center_x = cen.at<double>(i, 0);
		int center_y = cen.at<double>(i, 1);
		int x = satas.at<int>(i, CC_STAT_LEFT);
		int w = satas.at<int>(i, CC_STAT_WIDTH);
		int y = satas.at<int>(i, CC_STAT_TOP);
		int h = satas.at<int>(i, CC_STAT_HEIGHT);
		Rect rect(x, y, w, h);
		rectangle(img, rect, Scalar(255, 255, 255), 1, 8);
	}


	//cv::imshow("原图", img);
	cv::imshow("标记后的图像", img);

}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值