opencv c++ 霍夫直线检测

本文介绍如何使用霍夫变换在图像中检测直线,包括从图像边缘提取到转换为极坐标的过程,并对比了OpenCV中HoughLines与HoughLinesP两种方法的不同。

目的:在进行图像边缘提取后,将数据从平面坐标转换到极坐标空间,即完成了直线的信息提取。

1、原理

        平面坐标系:通过之间的斜率k截距b来确定一条直线。y = kx+b

        极坐标系:通过半径r角度θ来确定一条直线。r = xcosθ + y sinθ

注:在图像处理中,定义的平面坐标原点位于左上角,横轴为x,右方向为正方向,纵轴为y,下方向为正方向

        对于直线上任意一点(x_{0}, y_{0}),都有:

        

        对于任意点(x_{0}, y_{0}),经过它的线有无数:

        

        将其通过公式转换为极坐标的形式,这些线就变成了θ-r坐标系上的一系列点:

        

         当引入其他点后,通过极坐标的曲线交点就能确定这些点所处的直线的r、θ信息。:

        

        注:在实际算法中,会设定一个阈值T,当相交于同一点的点确定的曲线数量大于T时,才将这条被确定直线纳入统计,反之则排除它。

参考:(36条消息) 霍夫直线检测原理详解_秃头小苏的博客-优快云博客_霍夫直线

2、API

 OpenCV: Feature Detection

这两个计算直线的API的输出均为 vector<Vec3f>形式,分别储存距离、角度、累加值(用于判断该直线是否纳入统计)。 


void cv::HoughLines	(	InputArray 	image,
                        OutputArray 	lines,
                        double 	rho,
                        double 	theta,
                        int 	threshold,
                        double 	srn = 0,
                        double 	stn = 0,
                        double 	min_theta = 0,
                        double 	max_theta = CV_PI 
                        )	

void cv::HoughLinesP	(	InputArray 	image,
                            OutputArray 	lines,
                            double 	rho,
                            double 	theta,
                            int 	threshold,
                            double 	minLineLength = 0,
                            double 	maxLineGap = 0 
                            )	

image——二值化后的图像。
lines——输出的直线信息, vector<Vec3f>/vector<Vec4i> 形式,分别储存距离、角度、累加值(用于判断该直线是否纳入统计)
rho——距离步长
theta ——角度步长
threshold ——累加值阈值,只统计交点累计格式大于该值的直线。
srn    ——用于多阶霍夫变换, it is a divisor for the distance resolution rho . The coarse accumulator distance resolution is rho and the accurate accumulator resolution is rho/srn . If both srn=0 and stn=0 , the classical Hough transform is used. Otherwise, both these parameters should be positive.
stn     ——用于多阶霍夫变换, it is a divisor for the distance resolution theta.
min_theta ——最小角度阈值
max_theta ——最大角度阈值。Must fall between min_theta and CV_PI. 

minLineLength    Minimum line length. Line segments shorter than that are rejected.
maxLineGap    ——当直线有间断时,认为互相间断的两条直线为同一直线的允许最大像素间隔数。

 HoughLines    与 HoughLinesP    区别:

HoughLines    输出 vector<Vec3f>,HoughLinesP    输出vector<Vec4i>,直线两个端点坐标,顺序为x1,y1, x2, y2。

void QuickDemo::hough_line_detect(Mat& image)
{
	GaussianBlur(image, image, Size(3, 3), 0);
	Mat gray;
	cvtColor(image, gray, COLOR_BGR2GRAY);
	Mat binary;
	threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);
	namedWindow("THRESH_OTSU", WINDOW_FREERATIO);
	imshow("THRESH_OTSU", binary);

	vector<vector<Point>> contours;
	vector<Vec4i> hierachy;
	findContours(binary, contours, hierachy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point());

	//霍夫直线检测
	vector<Vec3f> lines;
	HoughLines(binary, lines, 1, CV_PI / 180.0, 300, 0, 0);

	//绘制检测出的直线
	Point ptz1, ptz2;
	for (size_t i = 0; i < lines.size(); ++i) {
		float rth = lines[i][0];//距离
		float theta = lines[i][1];//角度
		float accu = lines[i][2];//累加值
		//cout << "rth:" << rth << ",theta:" << theta << ",accu:" << accu << endl;

		double a = cos(theta);
		double b = sin(theta);
		double x0 = a * rth, y0 = b * rth;

		ptz1.x = cvRound(x0 + 1000 * (-b));
		ptz1.y = cvRound(y0 + 1000 * (a));
		ptz2.x = cvRound(x0 - 1000 * (-b));
		ptz2.y = cvRound(y0 - 1000 * (a));
		//line(image, ptz1, ptz2, Scalar(255, 0, 0), 2, 8);
		//根据角度给线不同的颜色
		int angle = round((theta / CV_PI) * 180);
		if (rth > 0) {
			line(image, ptz1, ptz2, Scalar(255, 0, 0), 2, 8);
			if(angle == 90)
				line(image, ptz1, ptz2, Scalar(0, 255, 255), 2, 8);
			if(angle < 1)
				line(image, ptz1, ptz2, Scalar(0, 255, 255), 2, 8);
		}
		else
			line(image, ptz1, ptz2, Scalar(0, 255, 0), 2, 8);
	}
	namedWindow("hough line", WINDOW_FREERATIO);
	imshow("hough line", image);
}

void QuickDemo::hough_P_line_detect(Mat& image)
{
	Mat gray, binary;
	cvtColor(image, gray, COLOR_BGR2GRAY);
	Canny(gray, binary, 80, 160, 3, false);
	namedWindow("Canny", WINDOW_FREERATIO);
	imshow("Canny", binary);

	vector<Vec4i> lines;
	HoughLinesP(binary, lines, 1, CV_PI / 180.0, 80, 30, 10);
	Mat result = Mat::zeros(image.size(), image.type());
	for (int i = 0; i < lines.size(); ++i) {
		line(result, Point(lines[i][0], lines[i][1]), Point(lines[i][2], lines[i][3]), Scalar(0, 255, 0),2, 8);

	}
	namedWindow("hough P", WINDOW_FREERATIO);
	imshow("hough P", result);

}

 

<think>好的,用户想用OpenCVC++实现霍夫变换检测半圆。首先,我需要回忆一下霍夫变换的原理和应用。霍夫变换通常用于检测直线、圆等形状,但半圆的检测可能需要一些调整。因为OpenCV自带的霍夫圆变换主要是检测完整圆,所以可能需要修改参数或者采用其他方法。 用户提供的引用中有关于霍夫圆变换的示例,比如引用[1]中的硬币检测使用了HoughCircles函数。但半圆检测可能需要不同的参数或者自定义的方法。比如,标准圆检测需要圆心和半径,而半圆可能只覆盖180度,所以可能需要调整角度范围。 接下来,我需要考虑如何修改现有的霍夫圆变换代码来适应半圆的检测。可能的方法包括:调整霍夫梯度法的参数,限制检测的角度范围,或者结合边缘检测的结果进行后处理。例如,在HoughCircles函数中,param1和param2参数控制边缘检测和高阈值,可能需要调整这些参数以适应半圆的边缘。 另外,引用[4]提到霍夫变换用于直线检测,参数是rho和theta,而圆检测则需要三个参数(x, y, r)。对于半圆,可能需要额外判断圆弧的角度范围。或者,可以检测完整的圆,然后通过轮廓分析或边缘信息筛选出半圆。例如,检测到圆后,检查圆的边缘点是否主要集中在某个区域,从而判断是否为半圆。 另外,用户提到使用C++,所以需要参考引用中的C++代码示例,比如引用[2]中的HoughLinesP的使用。可能需要结合HoughCircles函数,但需要调整参数。例如,设置minRadius和maxRadius来限制半径范围,或者调整霍夫变换的阈值。 可能的步骤包括:图像预处理(灰度化、模糊)、边缘检测、霍夫变换检测圆,然后筛选半圆。或者,使用自定义的霍夫变换方法,手动实现半圆的参数空间累积。 需要注意的是,OpenCV的HoughCircles可能不适合直接检测半圆,因为它是为完整圆设计的。因此,可能需要结合其他方法,比如检测圆后,通过轮廓分析确定是否是半圆。例如,计算检测到的圆的边缘点分布,如果大部分点集中在某个角度范围内,则判定为半圆。 或者,可以修改霍夫变换的投票机制,只累积半圆可能的参数。例如,在参数空间中,对于每个可能的圆心和半径,只考虑180度的弧。这可能需要自定义实现,因为OpenCV的HoughCircles可能不直接支持这种参数设置。 总结可能的解决方案:1. 使用HoughCircles检测完整圆,然后通过后续处理筛选半圆;2. 自定义霍夫变换方法,调整参数空间以适应半圆。考虑到用户希望使用OpenCVC++,第一种方法可能更可行,因为实现自定义霍夫变换较为复杂。 然后,我需要检查用户提供的引用是否有相关示例。引用[1]中的HoughCircles的参数包括minRadius和maxRadius,但没有提到角度。因此,可能需要后续处理。例如,检测到圆后,分析边缘点的角度分布,或者使用掩膜来检查圆的哪部分存在边缘点。 另外,引用[5]提到HoughLines和HoughLinesP的参数,可能对于直线检测有用,但半圆可能需要圆的检测。因此,可能需要结合圆检测和角度筛选。 最后,代码示例可能需要先检测所有可能的圆,然后通过遍历每个检测到的圆,检查其边缘点是否构成半圆。例如,使用轮廓分析,或者计算边缘点的角度分布,判断是否有180度范围内的点较多。 可能的代码步骤: 1. 读取图像并转为灰度。 2. 高斯模糊减少噪声。 3. 使用Canny边缘检测。 4. 应用HoughCircles检测圆。 5. 对每个检测到的圆,分析其边缘点,判断是否为半圆。 例如,计算每个边缘点相对于圆心的角度,统计角度分布是否集中在某个半圆区域。 6. 绘制符合条件的圆。 但HoughCircles返回的是圆心和半径,无法直接得到边缘点的角度。因此,可能需要额外的步骤,比如在边缘图像中到属于该圆的边缘点,然后计算它们的角度。 或者,可以在HoughCircles之后,对每个候选圆,检查其周围的边缘点是否足够形成半圆。例如,使用圆的掩膜,统计边缘点的数量,如果只有一半左右,则可能是半圆。 这种方法可能存在误检,但可能是一个起点。另外,调整HoughCircles的参数,如降低param2(累加器阈值),可能会检测到更多不完整的圆,然后通过后续处理筛选。 综上,提供一个示例代码,使用HoughCircles检测候选圆,然后筛选半圆。需要用户根据实际图像调整参数。</think>在OpenCV C++检测半圆需要结合霍夫圆变换和边缘角度分析。由于标准$cv::HoughCircles$函数针对完整圆设计,可通过以下方法实现半圆检测: ### 分步实现方法 1. **图像预处理** ```cpp Mat gray, edges; cvtColor(src, gray, COLOR_BGR2GRAY); GaussianBlur(gray, gray, Size(9,9), 2, 2); Canny(gray, edges, 50, 150); ``` 2. **霍夫圆检测** ```cpp vector<Vec3f> circles; HoughCircles(gray, circles, HOUGH_GRADIENT, 1, gray.rows/8, // 最小圆心间距 70, // 高阈值 15, // 累加器阈值(降低以检测不完整圆) 50, 200); // 半径范围 ``` 3. **半圆验证** ```cpp for(size_t i=0; i<circles.size(); i++){ Point center(cvRound(circles[i][0]), cvRound(circles[i][1])); int radius = cvRound(circles[i][2]); // 提取圆形ROI区域 Mat mask = Mat::zeros(edges.size(), CV_8UC1); circle(mask, center, radius, Scalar(255), 1); Mat croppedEdges; bitwise_and(edges, mask, croppedEdges); // 计算有效边缘点角度分布 vector<Point> edgePoints; findNonZero(croppedEdges, edgePoints); vector<float> angles; for(Point p : edgePoints){ float dx = p.x - center.x; float dy = p.y - center.y; angles.push_back(atan2(dy, dx)*180/CV_PI); } // 判断是否集中在180度范围内 sort(angles.begin(), angles.end()); float maxGap = 0; for(size_t j=1; j<angles.size(); j++){ maxGap = max(maxGap, angles[j]-angles[j-1]); } if(maxGap < 180) continue; // 排除完整圆 // 绘制半圆检测结果 circle(src, center, radius, Scalar(0,255,0), 2); } ``` ### 参数调整建议 - 降低$param2$(累加器阈值)至15-25范围,允许检测不完整圆 - 设置合理的半径范围缩小检测区域 - 通过$minDist$控制相邻圆心最小间距 - 角度分析时可根据实际情况调整最大间隙阈值[^1][^4] ### 关键原理 该方法通过霍夫变换获取候选圆参数后,进一步分析边缘点的角度分布特征。完整圆的边缘点应均匀分布于0-360度,而半圆的边缘点会集中在180度范围内,通过计算最大角度间隙可有效区分[^5]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值