天天看點

Hough Transform直線檢測一、原理了解部分二、OpenCV實作

霍夫變換(Hough Transform)是Paul Hough于1962年提出來的,一開始是用于檢測圖像中的直線的,後來還擴充到檢測圓、檢測任意形狀的物體等。關于霍夫變換的部落格多得不勝枚舉,而且很多都是很厲害,解釋的非常詳細而且通俗。現在我就寫一下我自己的了解,個人筆記,不一定準确。

一、原理了解部分

首先,我們關于平面中的一條直線有如下表達

Hough Transform直線檢測一、原理了解部分二、OpenCV實作

在笛卡爾坐标中表示為

Hough Transform直線檢測一、原理了解部分二、OpenCV實作

這裡,我們知道平面中一條直線會經過無數個離散點,而經過某個點的直線會有N多條:

Hough Transform直線檢測一、原理了解部分二、OpenCV實作

如上圖所示,二維平面有P1~P7共7個點,然後就來拟合下,假設就拟合出上面L1~L5共5條直線,如果按照投票來算的話,那肯定是L1最有可能存在,因為直線L1經過圖上的4個點,而其他直線基本就經過2個點,是以假設圖像隻有這幾個點的話,那最有可能存在的直線就是L1啦。此時,如果我們知道直線L1的兩個參數

Hough Transform直線檢測一、原理了解部分二、OpenCV實作

那麼這條直線基本就确定了。然而,我們應該是不知道的吧,至少算法不是向我們人眼一樣,一眼就覺得P1、P2、P5和P6可以拟合一條直線,直接取其中兩個點計算斜率和截距就得到直線了。我們需要周遊斜率和截距的所有可能值,然後計算圖上的點是否在某個斜率和截距表示的直線上,如果是的話,那麼這條直線的投票數量加1,最後票數最多的直線的參數就是所要拟合的直線的參數了。

要周遊

Hough Transform直線檢測一、原理了解部分二、OpenCV實作

的話,我們就把斜截式表示為:

Hough Transform直線檢測一、原理了解部分二、OpenCV實作

然後假設斜率w的取值為[0,10],那就周遊w和圖上的點的坐标

Hough Transform直線檢測一、原理了解部分二、OpenCV實作

,根據上式計算截距b,對應的[0,10]投票數加1,最後票數最多的

Hough Transform直線檢測一、原理了解部分二、OpenCV實作

就可以拟合一條直線了。

我們知道,斜截式可以表示絕大部分的直線,但是有一種情況是斜截式表示不了的,就是直線垂直x軸的情況,此時斜率為無窮,好像用斜截式表示不了吧。是以就想到用極坐标的形式來表示,但是實際上應該不能算是極坐标吧,因為依然還是笛卡爾坐标,隻不過用了角度和距離來表示罷了,看起來有點類似極坐标的表示罷了(挺多部落格都直接說是極坐标表示,我覺得應該不準确吧)。這裡還是先上圖再推出公式的表達:

Hough Transform直線檢測一、原理了解部分二、OpenCV實作
Hough Transform直線檢測一、原理了解部分二、OpenCV實作

圖中直線過了點

Hough Transform直線檢測一、原理了解部分二、OpenCV實作

,原點到直線的距離是可以計算的,這個距離用公式表達就是:

Hough Transform直線檢測一、原理了解部分二、OpenCV實作

并且原點到直線的距離是唯一的,也就是說,如果角度θ确定了,那麼無論取直線上的哪個點都是可以取得一個固定的距離ρ,不在同一直線上的點計算出來的ρ是不同的,是以在周遊角度的時候,就統計一次θ确定下不同ρ得到的票數,然後改變θ,再次統計不同ρ得到的票數,這樣周遊完所有θ後我們就有很多組(θ,ρ)的組合以及他們得到的票數,當票數超過一定門檻值或者取最大值的作為直線的參數,得到檢測到的直線。

二、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;
}
           

原圖和檢測圖如下:

Hough Transform直線檢測一、原理了解部分二、OpenCV實作
Hough Transform直線檢測一、原理了解部分二、OpenCV實作

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;
}
           

檢測結果如下:

Hough Transform直線檢測一、原理了解部分二、OpenCV實作

莫唱當年長恨歌,

人間亦自有銀河。

石壕村裡夫妻别,

淚比長生殿上多。

  -- 袁枚 《馬嵬》