OpenCV2编程手册笔记之 7.3使用霍夫变换检测直线
来源:互联网 发布:linux bz2 解压 编辑:程序博客网 时间:2024/05/16 14:24
在检测边缘了解后,我们介绍一种用来检测直线的方法,那就是霍夫变换,这是一个检测直线的经典算法。
霍夫变换的数学原理较为复杂,我们先在应用层面介绍霍夫变换:
void 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 );我们可以看到,霍夫变换有四个初始化参数,那么我们就只需要确定前几个就可以了
霍夫变换的输入图像是包含一组点的二值图像,我们一般通过Canny算子来得到这幅图像
cv::Canny(image, contours, 125, 350);这里的contours就是HoughLines的输入图像
函数的输出是一组向量,每个向量都储存着检测到的直线的信息,也就是一个n*2的数组
第三个和第四个参数代表着搜索时的步进尺寸,我们一般定义:
rho为1,theta为CV_PI / 180
第五个参数为阈值,推荐使用大一些的数,比如超过200,因为如果小了opencv就直接报错了。
但是这里需要根据图像进行自我调整,比如我们的例子中100就是比较合适的数据
这样,我们就得到了目标图像上的所有直线,下一步就是把他们画在图像上
-----------------------下面是画直线--------------------------
我们通过使用迭代器遍历向量Lines_Hough的方法来进行可视化直线操作
由于返回的数据是一个n*2数组,我们采用如下的迭代器:
vector<cv::Vec2f>::const_iterator it = lines_Hough.begin();通过it的自加一操作,就可以得到所有的向量数据
在这里,我们需要了解一下霍夫变换中蕴含的数学原理,这样,我们才能正确的编写代码
---------------------原理-----------------------
这是电脑中图像的坐标系
A是目标直线
B是目标直线的垂线
rho是垂直距离
theta是偏角,范围是0~2*PI
在极坐标系下,所有的直线都可以表示为
这样,我们就可以知道霍夫变换中得到的就是所有可能情况下的rho和theta
依据这两个参数,我们就可以算出所有可能情况的直线
----------------------原理-------------------------
在迭代器遍历时,我们将theta分为[0, PI/4]并[3*PI/4, PI]和[ PI/4,3*PI/4]两部分
这样,在theta为[0, PI/4]并[3*PI/4, PI]时,直线和图像第一行的交点为
直线和图像最后一行交点为
这样,我们使用cv::line就可以做出直线
对于theta在另外的取值范围内的情况,基本同理
这种简单情况下的霍夫变换得到的结果为
我们不难发现,这种结果产生了错误的直线检测,并且由于cv::line方法绘出的是直线,造成了直线的失真,因此,我们采取改进的概率霍夫变换来解决上述出现的问题。
我们先来了解一下概率霍夫变换 HoughLineP以及他的数学原理:
数学原理:
概率霍夫变换将会检测图像上所有的点,并识别出经过每个点的所有直线,并标记这条直线,记为每条直线的投票数。如果一条直线被标记了多次,也就是这条直线的投票数超过我们规定的值,那么我们就认为这是一条可以选定的直线,这就是概率霍夫变换的作用原理。
opencv函数实现:
void HoughLinesP( InputArray image, OutputArray lines, double rho, double theta, int threshold, double minLineLength = 0, double maxLineGap = 0 );概率霍夫变换和普通霍夫变换的输入是一样的,都是由Canny算子得到的二值图像
返回的是一个Vec4i类的向量,这是一个n*4的数组,数组的第一个和第二个数据是直线一个端点的x、y;第三个和第四个数据是直线另一个端点的x、y数据
rho和theta的定义与普通霍夫变换一样
threshold就是投票数阈值
minLineLength是直线的最小长度
maxLineGap是沿直线方向的最大缺口
为了更好的使用概率霍夫变换,我们将这个方法封装到一个LineFinder类中
class LineFinder{private: //原图 cv::Mat img; //检测到的直线的端点 vector<cv::Vec4i>lines; //投票累加器的分辨率 double deltaRho; double deltaTheta; //直线合法的最小投票数 int minVote; //直线的最小长度 double minLength; //沿直线的最大缺口 double maxGap;public: //重定义 LineFinder() { deltaRho = 1; deltaTheta = CV_PI / 180; minVote = 10; minLength = 0.0; maxGap = 0.0; } //数据重定义 void setAccResolution(double dRho, double dTheta); void setMinVote(int minv); void setLineLengthAndGap(double length, double gap); //寻找合法直线 std::vector<cv::Vec4i> findLines(cv::Mat& binary); //画出直线 void drawDetectedLines(cv::Mat &image, cv::Scalar color = cv::Scalar(0, 255, 0));};这个类中初始化了所有需要的数值,并且定义了几种需要的方法
在霍夫变换给出的结果中(Lines),我们就得到了直线段的所有数据,这样,我们就可以在原图中画出确定的直线。
普通霍夫变换HoughLines:
int main(){cv::Mat image = cv::imread("F:\\Image\\road.jpg");if (!image.data){exit(1);}cv::Mat contours;cv::Canny(image, contours, 125, 350);vector<cv::Vec2f>lines_Hough;cv::HoughLines(contours, lines_Hough, 1, CV_PI / 180, 100);vector<cv::Vec2f>::const_iterator it = lines_Hough.begin();while (it != lines_Hough.end()){float rho = (*it)[0];float theta = (*it)[1];if (theta < CV_PI / 4. || theta > 3.*CV_PI / 4.){//直线和第一行的交点cv::Point pt1(rho / cos(theta), 0);//直线和最后一行的交点cv::Point pt2((rho - contours.rows*sin(theta)) / cos(theta), contours.rows);cv::line(image, pt1, pt2, cv::Scalar(255), 1);}else{//直线和第一列的交点cv::Point pt1(0, rho / sin(theta));//直线和最后一列交点cv::Point pt2(contours.cols, (rho - contours.cols*cos(theta)) / sin(theta));cv::line(image, pt1, pt2, cv::Scalar(255), 1);}std::cout << "line: (" << rho << "," << theta << ")\n";++it;}cv::imshow("11", image);cv::waitKey(0); return 0;}
概率霍夫变换:
LineFinder类#pragma once#include "stdafx.h"#include <opencv2\opencv.hpp>using namespace std;class LineFinder{private://原图cv::Mat img;//检测到的直线的端点vector<cv::Vec4i>lines;//投票累加器的分辨率double deltaRho;double deltaTheta;//直线合法的最小投票数int minVote;//直线的最小长度double minLength;//沿直线的最大缺口double maxGap;public://重定义LineFinder(){deltaRho = 1;deltaTheta = CV_PI / 180;minVote = 10;minLength = 0.0;maxGap = 0.0;}//数据重定义void setAccResolution(double dRho, double dTheta);void setMinVote(int minv);void setLineLengthAndGap(double length, double gap);//寻找合法直线std::vector<cv::Vec4i> findLines(cv::Mat& binary);//画出直线void drawDetectedLines(cv::Mat &image, cv::Scalar color = cv::Scalar(0, 255, 0));};
#include "stdafx.h"#include "LineFinder.h"void LineFinder::setAccResolution(double dRho, double dTheta){deltaRho = dRho;deltaTheta = dTheta;}void LineFinder::setMinVote(int minv){minVote = minv;}void LineFinder::setLineLengthAndGap(double length, double gap){minLength = length;maxGap = gap;}std::vector<cv::Vec4i> LineFinder::findLines(cv::Mat& binary){lines.clear();cv::HoughLinesP(binary, lines, deltaRho, deltaTheta, minVote, minLength, maxGap);return lines;}void LineFinder::drawDetectedLines(cv::Mat &image, cv::Scalar color){std::vector<cv::Vec4i>::const_iterator it2 = lines.begin();while (it2 != lines.end()){cv::Point pt1((*it2)[0], (*it2)[1]);cv::Point pt2((*it2)[2], (*it2)[3]);cv::line(image, pt1, pt2, color);++it2;}}
cv::Mat image = cv::imread("F:\\Image\\road.jpg");if (!image.data){exit(1);}cv::Mat contours;cv::Canny(image, contours, 125, 350);LineFinder finder;finder.setLineLengthAndGap(100, 20);finder.setMinVote(80);vector<cv::Vec4i>lines = finder.findLines(contours);finder.drawDetectedLines(image, cv::Scalar(255, 0, 0));cv::imshow("123", image);cv::waitKey(0); return 0;
这样,我们就得到了改进的概率霍夫变换图像
- OpenCV2编程手册笔记之 7.3使用霍夫变换检测直线
- OpenCV2学习笔记(八):使用霍夫变换检测直线和圆
- OpenCV2编程手册笔记之 8.2检测Harris角点
- OpenCV2编程手册笔记之 8.3检测FAST特征
- OpenCV2编程手册笔记之 7.2使用Canny算子检测轮廓
- 霍夫变换直线检测
- 霍夫变换检测直线
- 霍夫变换检测直线
- 霍夫变换检测直线
- 霍夫变换-检测直线
- 霍夫变换 检测直线
- OpenCV2编程手册笔记之 7.4用直线拟合一组点
- OpenCV笔记11:霍夫变换检测直线
- OpenCV2编程手册笔记之 6.2使用低通滤波器
- OpenCV2编程手册笔记之 6.5计算图像的拉普拉斯变换
- 图像处理之霍夫变换(直线检测算法)
- 图像处理之霍夫变换(直线检测算法)
- 图像处理之霍夫变换(直线检测算法)
- 要什么iPhoneX,最新的苹果产品最值得买的是它!
- iPhone X:我的“刘海”真的还可以拯救一下!
- 真枪实弹 单机竟然近300万TPM!
- 一道关于继承的题目
- 开源ckplayer 网页播放器去logo去广告去水印修改
- OpenCV2编程手册笔记之 7.3使用霍夫变换检测直线
- JS只能输入数字,数字和字母等的正则表达式
- 2.2.4
- java编程思想学习笔记--private关键字
- 面向对象 子类中 < 重载 重写 >父类的方法
- Mesh Filter和Mesh Renderer
- MOF原理和实现
- 字符集、codepages、setlocal
- 李文星疑被饿死,尸检显示胃里“毫无食物”; 小米可穿戴设备全球第一,出货量超苹果30%