基于OpenCV对复杂线段拟合并求其端点

霍夫变换

在图像处理和计算机视觉领域中,我们对于直线检测最常用的就是霍夫变换。霍夫变换是从黑白图像中检测直线和线段。OpenCV支持三种不同的霍夫变换。

  1. 标准霍夫变换 (SHT)

  2. 多尺度霍夫变换(MSHT)

  3. 累积概率霍夫变换(PPHT)

    在opencv中,我们可以用HoughLines函数来调用标准霍夫变换和多尺度霍夫变换。而HoughLinesP函数用于调用累积概率霍夫变换,累计霍夫变换的执行效率很高,所以相较于另外两个函数,我们更倾向于使用HoughLiensP函数进行直线检测。检测原理这里就不再多提,下面是我利用绘图,随手画的一条直线,我们开始进行测试。
    在这里插入图片描述

	Mat srcimg = imread("test.bmp");
	Mat temp, gray;
	Canny(srcimg, temp, 0 ,255, 3);
	cvtColor(temp, gray, COLOR_GRAY2BGR);
	vector<Vec4i>lines;
	HoughLinesP(temp, lines, 1, CV_PI / 180, 25, 20.0, 0);
	for (size_t i = 0; i < lines.size(); i++)
	{
		Vec4i l = lines[i];
		line(srcimg, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(186, 88, 255), 1, CV_AA);
	}
	imshow("dst", srcimg);
	imwrite("L1.bmp", srcimg);
	waitKey();

在这里插入图片描述
大家可以看到,图片上的线段看似是一个完整并笔直的线段。但仔细观察发现可能是线段过粗的原因,其被划分为四节小线段。如果线段较为复杂其检测出来的线段也会更多。这时我们虽然利用霍夫变换检测出来了线段,却无法求其两侧端点及线段长度。

线段拟合

在更多的场合下霍夫变换的精度不高,所测量出来的参数并不能满足我们的需求。这时,我们需要利用线段拟合来解决问题。
线段拟合的方法很多,这里我们这接用一元线性回归。
下面来看一下OpenCV 提供的直线拟合函数。函数原型如下:
void fitLine( InputArray points, OutputArray line, int distType, double param, double reps, double aeps );

distType 指定拟合函数的类型,可以取 CV_DIST_L2、CV_DIST_L1、CV_DIST_L12、CV_DIST_FAIR、CV_DIST_WELSCH、CV_DIST_HUBER。
param 就是 CV_DIST_FAIR、CV_DIST_WELSCH、CV_DIST_HUBER 公式中的C。如果取 0,则程序自动选取合适的值。
reps 表示直线到原点距离的精度,建议取 0.01。
aeps 表示直线角度的精度,建议取 0.01。
下来开始基于上面那张测试图进行线段拟合。

	Point MaxP, MinP, TempP;
	Mat src_img, roi_img, dst_img;
	int temp_x1; int temp_x2; int temp_y1; int temp_y2;
	int initx1, inity1,initx2,inity2;
	vector<cv::Point> fit1;
	src_img = imread("132.bmp", IMREAD_GRAYSCALE);
	roi_img = src_img;
	//阈值化图像	
	Mat binary_img, morhp_img;
	threshold(roi_img, binary_img, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
	imshow("二值化结果", binary_img); 
	//进行开运算操作	
	Mat kernel = getStructuringElement(MORPH_RECT, Size(30, 1), Point(-1, -1)); 
	//定义一个宽20、高1的一个矩形结构元
	kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
	//定义一个宽3、高3的一个矩形结构元	
	//作膨胀操作,图像中的直线增强
	dilate(binary_img, binary_img, kernel);
	imshow("膨胀操作结果", binary_img);
	morphologyEx(binary_img, morhp_img, MORPH_OPEN, kernel, Point(-1, -1)); 
	//作开运算,只保留图像中的直线	
	imshow("开运算操作结果", morhp_img); 
	// 进行膨胀操作	
	// 霍夫直线检测
	vector<Vec4i> lines;
	HoughLinesP(morhp_img, lines, 1, CV_PI / 180, 25,50, 0);	
	Mat result_img = roi_img.clone();	
	cvtColor(result_img, result_img, COLOR_GRAY2BGR);     
	//灰度图转为彩色图    
	for (size_t t = 0; t < lines.size(); ++t)
	{
		Vec4i ln = lines[t];
		//line(result_img, Point(ln[0], ln[1]), Point(ln[2], ln[3]), Scalar(0, 0, 255), 2, 8, 0);
		int x1 = Point(ln[0], ln[1]).x;
		int y1 = Point(ln[0], ln[1]).y;
		int x2 = Point(ln[2], ln[3]).x;
		int y2 = Point(ln[2], ln[3]).y;
		temp_x1 = x1;  temp_x2 = x2;  temp_y1 = y1;  temp_y2 = y2;
		fit1.push_back(cv::Point(x1, y1));
		fit1.push_back(cv::Point(x2, y2));
		if (t > 0)
		{//根据情况进行点过滤,将合适的点导入到点集中
			if ((temp_x1-x1<10)&&(temp_x2-x2<10)&&(temp_y2-y2<10)&&(temp_y1-y1<10))
			{
				fit1.push_back(cv::Point(x1, y1));
				fit1.push_back(cv::Point(x2, y2));
			}			
		}	
	
	}
	//进行线段拟合
	cv::Vec4f line_para1; 
	cv::fitLine(fit1, line_para1, cv::DIST_L2, 0, 1e-2, 1e-2);
	for (int k = 0; k < fit1.size(); k++)
	{
		MaxP = fit1[0]; MinP = fit1[0];
		cv::circle(result_img, fit1[k], 5, cv::Scalar(0, 0, 255), 2, 8, 0);
		for (int j = 0; j < fit1.size() - 1; j++)
		{		
			if (fit1[j + 1].x > MaxP.x)
			{
				MaxP = fit1[j + 1];
			}
			
			if (fit1[j + 1].x < MinP.x)
			{
				MinP = fit1[j + 1];
			}			
		}

	}
	//printf("x1 = %d,y1 = %d\n", MaxP.x, MaxP.y);
	//printf("x2 = %d,y2 = %d\n", MinP.x, MinP.y);
	//获取点斜式的点和斜率
	cv::Point point0;
	point0.x = line_para1[2];
	point0.y = line_para1[3];
	double k = line_para1[1] / line_para1[0];

//	计算直线的端点(y = k(x - x0) + y0)
	cv::Point point1, point2;
	point1.x =MinP.x;
	point1.y = k * (MinP.x - point0.x) + point0.y;
	point2.x = MaxP.x;
	point2.y = k * (MaxP.x - point0.x) + point0.y;
	printf("x1 = %d,y1 = %d\n", point1.x ,point1.y);
	printf("x2 = %d,y2 = %d\n",	point2.x ,point2.y);
	//计算线段长度
    cv::line(result_img, point1, point2, cv::Scalar(0, 255, 0), 2, 8, 0);
	double Distance = sqrt((point1.x - point2.x) * (point1.x - point2.x) + (point1.y - point2.y) * (point1.y - point2.y));
	cout << "Length For Line" << Distance << endl;
	imshow("dst", result_img);
	imwrite("Ldst.bmp", result_img);
	waitKey();

在这里插入图片描述在这里插入图片描述

就此,我们到得了我们想要的参数。(线段的两端点及线段长度)其实也有很多其他方法可以使用,例如线段细化,不过其精度有待考究。
如有不足,还请指正。转载请标明出处。希望能对各位有所帮助。

  • 10
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要在 OpenCV 中寻找多条线段端点,首先需要先通过 HoughLinesP 函数检测出线段。该函数会返回每条线段的起点和终点坐标。然后我们可以将所有线段的起点和终点保存到一个数组中。 接下来,我们可以遍历这个数组,并将每个点与其他点进行比较,找出相邻的点。如果两个点之间的距离小于某个阈值,则它们是相邻的点。我们可以将相邻的点存储到一个列表中。 最后,我们可以根据相邻的点的数量来判断哪些点是端点。如果一个点有很少的相邻点,则它很可能是一个端点。 以下是一个示例代码,用于寻找多条线段端点: ```python import cv2 import numpy as np # 读取图像 img = cv2.imread('lines.png', cv2.IMREAD_GRAYSCALE) # 边缘检测 edges = cv2.Canny(img, 50, 150) # 检测线段 lines = cv2.HoughLinesP(edges, 1, np.pi/180, 50, minLineLength=50, maxLineGap=10) # 保存线段端点 points = [] for line in lines: x1, y1, x2, y2 = line[0] points.append((x1, y1)) points.append((x2, y2)) # 找出相邻的点 adjacent_points = [] for i, point1 in enumerate(points): for j, point2 in enumerate(points): if i != j: distance = np.sqrt((point1[0]-point2[0])**2 + (point1[1]-point2[1])**2) if distance < 10: adjacent_points.append((i, j)) # 统计相邻点的数量 counts = np.zeros(len(points)) for pair in adjacent_points: counts[pair[0]] += 1 counts[pair[1]] += 1 # 找出端点 endpoints = [] for i, count in enumerate(counts): if count < 2: endpoints.append(points[i]) # 显示结果 for point in endpoints: cv2.circle(img, point, 5, (0, 255, 0), -1) cv2.imshow('image', img) cv2.waitKey(0) cv2.destroyAllWindows() ``` 在这个示例代码中,我们首先使用 Canny 边缘检测算法对图像进行边缘检测。然后使用 HoughLinesP 函数检测线段。 接下来,我们将每个线段的起点和终点保存到一个数组中,并找出相邻的点。 最后,我们统计每个点的相邻点数量,并找出端点。在这个示例中,我们将距离阈值设置为 10。如果两个点之间的距离小于 10 像素,则它们是相邻的点。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值