Hough Transform直線檢測

霍夫變換(Hough Transform)是Paul Hough於1962年提出來的,一開始是用於檢測圖像中的直線的,後來還擴展到檢測圓、檢測任意形狀的物體等。關於霍夫變換的博客多得不勝枚舉,並且不少都是很厲害,解釋的很是詳細並且通俗。如今我就寫一下我本身的理解,我的筆記,不必定準確。ios

1、原理理解部分

首先,咱們關於平面中的一條直線有以下表達算法

在笛卡爾座標中表示爲函數

 

 

這裏,咱們知道平面中一條直線會通過無數個離散點,而通過某個點的直線會有N多條:ui

 

如上圖所示,二維平面有P1~P7共7個點,而後就來擬合下,假設就擬合出上面L1~L5共5條直線,若是按照投票來算的話,那確定是L1最有可能存在,由於直線L1通過圖上的4個點,而其餘直線基本就通過2個點,因此假設圖像只有這幾個點的話,那最有可能存在的直線就是L1啦。此時,若是咱們知道直線L1的兩個參數\left ( w,b \right )那麼這條直線基本就肯定了。然而,咱們應該是不知道的吧,至少算法不是向咱們人眼同樣,一眼就以爲P一、P二、P5和P6能夠擬合一條直線,直接取其中兩個點計算斜率和截距就獲得直線了。咱們須要遍歷斜率和截距的全部可能值,而後計算圖上的點是否在某個斜率和截距表示的直線上,若是是的話,那麼這條直線的投票數量加1,最後票數最多的直線的參數就是所要擬合的直線的參數了。spa

要遍歷\left ( w,b \right )的話,咱們就把斜截式表示爲:code

而後假設斜率w的取值爲[0,10],那就遍歷w和圖上的點的座標\left ( x,y \right ),根據上式計算截距b,對應的[0,10]投票數加1,最後票數最多的\left ( w,b \right )就能夠擬合一條直線了。orm

咱們知道,斜截式能夠表示絕大部分的直線,可是有一種狀況是斜截式表示不了的,就是直線垂直x軸的狀況,此時斜率爲無窮,好像用斜截式表示不了吧。因此就想到用極座標的形式來表示,可是實際上應該不能算是極座標吧,由於依然仍是笛卡爾座標,只不過用了角度和距離來表示罷了,看起來有點相似極座標的表示罷了(挺多博客都直接說是極座標表示,我以爲應該不許確吧)。這裏仍是先上圖再推出公式的表達:blog

 

 

圖中直線過了點\left ( x,y \right ),原點到直線的距離是能夠計算的,這個距離用公式表達就是:原型

而且原點到直線的距離是惟一的,也就是說,若是角度θ肯定了,那麼不管取直線上的哪一個點都是能夠取得一個固定的距離ρ,不在同一直線上的點計算出來的ρ是不一樣的,因此在遍歷角度的時候,就統計一次θ肯定下不一樣ρ獲得的票數,而後改變θ,再次統計不一樣ρ獲得的票數,這樣遍歷完全部θ後咱們就有不少組(θ,ρ)的組合以及他們獲得的票數,當票數超過必定閾值或者取最大值的做爲直線的參數,獲得檢測到的直線。博客

 

2、OpenCV實現

OpenCV提供的實現霍夫變換的函數有兩個,一個是標準霍夫變換HoughLines,一個是機率霍夫變換HoughLinesP,HoughLines的函數原型爲:

void HoughLines( InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn=0, double stn=0 );

其參數意義以下:

image:輸入圖像,灰度圖

lines:直線集合,每一個直線包含兩個值,分別是ρ和θ

rho:距離精度,以像素爲單位

theta:角度精度

threshold:識別閾值,也就是累積超過這個值的纔會被認爲是直線的參數

srn:對於多尺度霍夫變換,它是距離精度rho的除數,粗距離的精度爲rho,精細的距離精度爲rho/srn;

stn:對於多尺度霍夫變換,它是角度精度theta的除數,粗角度精度爲theta,精細的角度精度爲theta/srn;

當srn和stn都設置爲0的時候,使用標準的霍夫變換。實現的例子(來自OpenCV官網例子修改)以下:

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

#include <iostream>

using namespace cv;
using namespace std;

void help()
{
	cout << 
	    "\nThis program demonstrates line finding with the Hough transform.\n"
	    "Usage:\n"
	    "./houghlines <image_name>, Default is pic1.jpg\n" << endl;
}

int main(int argc, char** argv)
{
	const char* filename = argc >= 2 ? argv[1] : "D:/building.jpg";

	Mat src = imread(filename, 0);
	if (src.empty())
	{
		help();
		cout << "can not open " << filename << endl;
		return -1;
	}

	Mat dst, cdst;
	Canny(src, dst, 50, 200, 3);
	cvtColor(dst, cdst, CV_GRAY2BGR);

	vector<Vec2f> lines;
	HoughLines(dst, lines, 1, CV_PI / 180, 250);

	for (size_t i = 0; i < lines.size(); i++)
	{
		float rho = lines[i][0], theta = lines[i][1];
		Point pt1, pt2;
		double a = cos(theta), b = sin(theta);
		double x0 = a*rho, y0 = b*rho;
		pt1.x = cvRound(x0 + 1000 * (-b));
		pt1.y = cvRound(y0 + 1000 * (a));
		pt2.x = cvRound(x0 - 1000 * (-b));
		pt2.y = cvRound(y0 - 1000 * (a));
		line(cdst, pt1, pt2, Scalar(0, 0, 255), 1, CV_AA);
	}
    
    imshow("source", src);
	imshow("detected lines", cdst);

	waitKey();

	return 0;
}

原圖和檢測圖以下:

HoughLinesP的函數原型以下:

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

前面的參數和標準霍夫變換是同樣的,後面兩個參數的意義以下:

minLineLength:最小線長,線長小於這個值的會被忽略

maxLineGap:同一條直線上,鏈接各點的最大容許間隔例子以下,一樣來自OpenCV官網例子修改:

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

#include <iostream>

using namespace cv;
using namespace std;

void help()
{
	cout << "\nThis program demonstrates line finding with the Hough transform.\n"
		"Usage:\n"
		"./houghlines <image_name>, Default is pic1.jpg\n" << endl;
}

int main(int argc, char** argv)
{
	const char* filename = argc >= 2 ? argv[1] : "D:/building.jpg";

	Mat src = imread(filename, 0);
	if (src.empty())
	{
		help();
		cout << "can not open " << filename << endl;
		return -1;
	}

	Mat dst, cdst;
	Canny(src, dst, 50, 200, 3);
	cvtColor(dst, cdst, CV_GRAY2BGR);

	vector<Vec4i> lines;
	HoughLinesP(dst, lines, 1, CV_PI / 180, 250, 50, 10);
	for (size_t i = 0; i < lines.size(); i++)
	{
		Vec4i l = lines[i];
		line(cdst, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0, 0, 255), 1, CV_AA);
	}
    
	imshow("source", src);
	imshow("detected lines", cdst);

	waitKey();

	return 0;
}

檢測結果以下:

莫唱當年長恨歌,

人間亦自有銀河。

石壕村裏夫妻別,

淚比長生殿上多。

  -- 袁枚 《馬嵬》

相關文章
相關標籤/搜索