HoughLinesP参数设置(看完还不会你砍死我!!!)

一、我是个标题

1、随便在PPt画个图如下(1602*750):

 2、测试代码

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

int detect(cv::Mat img) {
	cv::Mat can_img, gray_img;
	cv::cvtColor(img, gray_img, CV_BGR2GRAY);
	cv::Mat input_img = gray_img.clone();
	cv::Canny(input_img, can_img, 100, 200, 3);
	std::vector<cv::Vec4i> lines;
	cv::HoughLinesP(can_img, lines, 1., CV_PI / 180, 500, 100, 30);
	for (int i = 0; i < lines.size(); ++i) {
		cv::Vec4i line_ = lines[i];
		cv::line(img, cv::Point(line_[0], line_[1]), cv::Point(line_[2], line_[3]), cv::Scalar(0, 0,255), 2, CV_AA);
	}
	return 0;
}
int main() {
	for (;;) {
		cv::Mat img = imread("1.png");
		detect(img);
	}
}

二、参数实例讲解 

下面讨论一下霍夫变换中的一些参数,网上截了一段,一般修改的是后面三参数。

 1、倒数第一个参数

首先我们看一下最后一个参数,我的理解是表示两段直线在同一个方向,要不要连成一条所需设置的阈值,比如下图中,检测红色框中白线的时候要不要连起来就和这个参数相关,下面看一下这里有几个像素

上图红框中的白线被隔断,直线检测如果要将他们连起来看成一条直线就要设置倒数第一个参数了,我放大它并数了一下,像素间隔在18左右,可以分别设置在10和30分别看一下是否连起来

倒数第1个参数设为10,其他参数先不要管,因为10<18可以预测这两段直线不会连起来

cv::HoughLinesP(can_img, lines, 1., CV_PI / 180, 50, 100, 10);

哇塞,真的没有连起来耶!

 

倒数第1个参数设为30,其他参数先不要管 ,因为30>18可以预测这两段直线会连起来

 cv::HoughLinesP(can_img, lines, 1., CV_PI / 180, 50, 100, 30);

天啊,真的连起来了~~

2、倒数第二个参数

这个好理解,表示检测直线的最小长度,如果检测的直线的长度小于这个设定的值就丢弃,设置为100和500看看

 倒数第2个参数设为100,其他参数先不要管

 cv::HoughLinesP(can_img, lines, 1., CV_PI / 180, 50, 100, 30);

倒数第2个参数设为500,其他参数先不要管  

 cv::HoughLinesP(can_img, lines, 1., CV_PI / 180, 50, 500, 30);

这样对比就很明显了,直线长度比设定长度小的不会被检测出来 。

3、倒数第三个参

需要理解一下霍夫直线检测原理,其实我也不懂,反正看一下有个印象:直角坐标系下的点转换到极坐标系就变成一条直线了,直角坐标系中属于同一条直线的点在极坐标系对应的直线都会香蕉于同一点网上找到的解析如下

所以这个参数其实表示检测的直线中 点的最少个数了,但是感觉和minlength差不多,注意如果这个数设置大于minlength是不可以的 这个值应该可以设置大于minlength的,实际上被检测的直线的像素点超过minlength的大小应该就可以检测出来,比如下图红框中是124个像素的直线,minlength设置为100直线是可以被检测出来的,但是如果倒数第三个参数threshold设置的为200,这就检测不出来了,因为在极坐标系中最多提供124条直线相交于一点。threshold设置为110是否可以呢?虽然这个值大于minlength的值,但是实际直线能提供124个像素点,有兴趣的朋友可以在评论区验证一下。

设置threshold:50,minlength:100,可以检测 ,效果如下图所示

cv::HoughLinesP(can_img, lines, 1., CV_PI / 180, 50, 100, 2);

设置threshold:200,minlength:100,上图黑框位置直线检测失败因为该直线只有124个像素对应极坐标系就是124条直线相交于同一点,无法满足200的要求,所以没有被检测出来。threshold设置为110是否可以呢?虽然这个值大于minlength的值,但是实际直线能提供124个像素点,有兴趣的朋友可以在评论区验证一下。

cv::HoughLinesP(can_img, lines, 1., CV_PI / 180, 200, 100, 2);

 

 三、练练手(可忽略)

题目:将原图中的水平和垂直直线检测出来,水平和垂直角度浮动范围在[-1,1]之间,要求检测的直线长度大于图形宽度的1/3,并且检测到相邻直线在20个像素内只保留其中一条即可,得到如下效果:

思路:

先用霍夫直线检测将直线检测出来再根据角度分别存水平和垂直的直线,然后设置阈值GAP过滤邻近的直线,最后绘制即可。

#include<iostream>
#include<opencv2/opencv.hpp>
#include<algorithm>

using namespace std;
using namespace cv;

#define PI 3.1415926
#define GAP 20 //设置像素间距
#define SHOW

bool cmp1(const Vec4i &a, const Vec4i &b) {
	return a[1] < b[1];//x1,y1,x2,y2,这里按照y1升排
}
bool cmp2(const Vec4i&a, const Vec4i&b) {
	return a[0] < b[0];//x1,y1,x2,y2,这里按照x1升排
}
int detect(cv::Mat img) {
	cv::Mat can_img, dst_img, gray_img;
	cv::cvtColor(img, gray_img, CV_BGR2GRAY);
	cv::Mat input_img = gray_img.clone();
	cv::Canny(input_img, can_img, 100, 200, 3);
	std::vector<cv::Vec4i> lines;
	cv::HoughLinesP(can_img, lines, 1., CV_PI / 180, 100, can_img.rows / 3, 20);

	std::vector<cv::Vec4i> lines_horizon, lines_vertical;
	for (int i = 0; i < lines.size(); ++i) {
		cv::Vec4i line_ = lines[i];
		double k = (double)(line_[3] - line_[1]) / (double)(line_[2] - line_[0]);//直线的斜率
		double angle = atan(k) * 180 / PI;//直线角度
		//水平方向0°/180°/-180°/360°
		if ((angle >= -1 && angle <= 1) || (angle >= 359 && angle <= 361) || (angle >= 179 && angle <= 181) || (angle >= -181 && angle <= -179)) {//水平条纹
			lines_horizon.push_back(line_);
		}//竖直方向90°/-90°/270°/-270°
		else if ((angle >= 89 && angle <= 91) || (angle >= -91 && angle <= -89) || (angle >= 269 && angle <= 271) || (angle >= -271 && angle <= -269)) {//垂直条纹
			lines_vertical.push_back(line_);
		}
	}
	sort(lines_horizon.begin(), lines_horizon.end(), cmp1);//水平方向按照y升排
	sort(lines_vertical.begin(), lines_vertical.end(), cmp2);//垂直方向按照x升排

	std::vector<cv::Vec4i> filter_horizon, filter_vertical;
	int streak_gap = 1 * GAP;

	//过滤水平条纹
	if (lines_horizon.size() > 0) {
		filter_horizon.push_back(lines_horizon[0]);
		for (int i = 1; i < lines_horizon.size(); ++i) {
			int gap = lines_horizon[i][1] - lines_horizon[i - 1][1];
			if (gap > streak_gap) {
				filter_horizon.push_back(lines_horizon[i]);//保留第一个
			}
		}
	}
	//过滤垂直条纹
	if (lines_vertical.size() > 0) {
		filter_vertical.push_back(lines_vertical[0]);
		for (int i = 1; i < lines_vertical.size(); ++i) {
			int gap = lines_vertical[i][0] - lines_vertical[i - 1][0];
			if (gap > streak_gap) {
				filter_vertical.push_back(lines_vertical[i]);
			}
		}
	}
	int res = filter_horizon.size() + filter_vertical.size();
#ifdef SHOW
	for (int i = 0; i < filter_horizon.size(); ++i) {
		cv::Vec4i line_ = filter_horizon[i];
		cv::line(img, cv::Point(line_[0], line_[1]), cv::Point(line_[2], line_[3]), cv::Scalar(0, 0, 255), 2, CV_AA);
	}
	for (int i = 0; i < filter_vertical.size(); ++i) {
		cv::Vec4i line_ = filter_vertical[i];
		cv::line(img, cv::Point(line_[0], line_[1]), cv::Point(line_[2], line_[3]), cv::Scalar(0, 0, 255), 2, CV_AA);
	}
#endif // DEBUG
	return res;
}
int main() {
	for (;;) {
		Mat img = imread("1.png");
		detect(img);
	}
}

上面过滤相邻小于20像素点使用过滤算法有缺陷,比如如果刚好是60,70,80,90,则只会保留60而不是60和80,可以使用如下方法替代。

//过滤水平条纹
	if (lines_horizon.size() > 0) {
		int base_index = 0;
		for (int i = 1; i < lines_horizon.size(); ++i) {
			int gap = lines_horizon[i][1] - lines_horizon[base_index][1];
			if (gap > streak_gap) {
				filter_horizon.push_back(lines_horizon[base_index]);
				base_index = i;//更新基准
				if (lines_horizon.size()-1 == i) {//别漏了最后一个
					filter_horizon.push_back(lines_horizon[i]);
				}
			}
		}
	}
	//过滤垂直条纹
	if (lines_vertical.size() > 0) {
		int base_index = 0;//设置基准
		for (int i = 1; i < lines_vertical.size(); ++i) {
			int gap = lines_vertical[i][0] - lines_vertical[base_index][0];
			if (gap > streak_gap) {
				filter_vertical.push_back(lines_vertical[base_index]);
				base_index = i;//更新基准
				if (lines_vertical.size()-1 == i ) {//别漏了最后一个
					filter_vertical.push_back(lines_vertical[i]);
				}
			}
		}
	}

如果限定完全水平和完全竖直条纹,霍夫变换检测出来的直线用两个点表示Vec4i就是[x1,y1,x2,y2],这个点的走向是从做到右边,从上到下的,那么角度应该为0°和-90度。

 

  • 81
    点赞
  • 262
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 18
    评论
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无情的搬砖机器

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值