OpenCV2编程手册笔记之 7.3使用霍夫变换检测直线

    在检测边缘了解后,我们介绍一种用来检测直线的方法,那就是霍夫变换,这是一个检测直线的经典算法。

    霍夫变换的数学原理较为复杂,我们先在应用层面介绍霍夫变换:

  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;


    这样,我们就得到了改进的概率霍夫变换图像



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值