OpenCV中霍夫變換:直線檢測和圓檢測

1、簡介

霍夫變換(Hough Transform)是圖像處理中的一種特徵提取技術,它通過一種投票算法檢測具有特定形狀的物體。Hough變換是圖像處理中從圖像中識別幾何形狀的基本方法之一。Hough變換的基本原理在於利用點與線的對偶性,將原始圖像空間的給定的曲線通過曲線表達形式變爲參數空間的一個點。這樣就把原始圖像中給定曲線的檢測問題轉化爲尋找參數空間中的峯值問題。也即把檢測整體特性轉化爲檢測局部特性。比如直線、橢圓、圓、弧線等。

2、霍夫直線變換(Hough Lines Transform)

2.1、基本介紹

霍夫線變換是一種在圖像中尋找直線的方法。OpenCV中支持三種霍夫線變換,分別是標準霍夫線變換、多尺度霍夫線變換、累計概率霍夫線變換。

在OpenCV中可以調用函數HoughLines來調用標準霍夫線變換和多尺度霍夫線變換。HoughLinesP函數用於調用累積概率霍夫線變換。

我們都知道,二維座標軸上表示一條直線的方程式y = a*x + b,我們想求出一條直線就得想方設法求出其中的a和b的值。如果用極座標來表示就是:

theta就是直線與水平線所成的角度,而rho就是圓的半徑(也可以理解爲原點到直線的距離),同樣地,這兩個參數也是表徵一條直線的重要參數,確定他們倆了,也就確定一條直線了。正如下圖所示:

2.2、霍夫變換檢測直線具體步驟

  1. 彩色圖像->灰度圖
  2. 去噪(高斯核)
  3. 邊緣提取(梯度算子、拉普拉斯算子、canny、sobel)
  4. 二值化(判斷此處是否爲邊緣點,就看灰度值==255)
  5. .映射到霍夫空間(準備兩個容器,一個用來展示hough-space概況,一個數組hough-space用來儲存voting的值,因爲投票過程往往有某個極大值超過閾值,多達幾千,不能直接用灰度圖來記錄投票信息)
  6. 取局部極大值,設定閾值,過濾干擾直線
  7. 繪製直線、標定角點

2.3、示例與應用

2.3.1、HoughLines:標準霍夫變換、多尺度霍夫變換

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 );
參數說明:
InputArray image:輸入圖像,必須是8位單通道圖像,推薦使用canny邊緣檢測的結果圖像。 
OutputArray lines:檢測到的線條參數集合。 
double rho:以像素爲單位的距離步長,推薦用1.0 。 
double theta:以弧度爲單位的角度步長,推薦用CV_PI/180。 
int threshold:累加計數值的閾值參數,當參數空間某個交點的累加計數的值超過該閾值,則認爲該交點對應了圖像空間的一條直線。 
double srn:默認值爲0,用於在多尺度霍夫變換中作爲參數rho的除數,rho=rho/srn。 
double stn:默認值爲0,用於在多尺度霍夫變換中作爲參數theta的除數,theta=theta/stn。
如果srn和stn同時爲0,就表示HoughLines函數執行標準霍夫變換,否則就是執行多尺度霍夫變換。

說明:HoughLines函數輸出檢測到直線的矢量(lines)表示集合,每一條直線由具有兩個元素的矢量(ρ, θ)表示,其中ρ表示直線距離原點(0, 0)的長度,θ表示直線的角度(以弧度爲單位)。

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

int main() {
	Mat img = imread("D:/test01.jpg");
	if (img.empty()) {
		cout << "could not find src1" << endl;
		return -1;
	}
	imshow("原圖", img);
	Mat gray, dst;
	//灰度化
	cvtColor(img, gray, CV_BGR2GRAY);
	//二值化
	threshold(gray, gray, 100, 255, THRESH_BINARY);
	//邊緣檢測
	Canny(gray, gray, 50, 200, 3);
	//灰度轉bgr
	cvtColor(gray, dst, CV_GRAY2BGR);
	vector<Vec2f> lines;
	HoughLines(gray, lines, 1, CV_PI/180, 100, 0, 0);

	for (size_t i = 0; i < lines.size(); i++) {
		float rho = lines[i][0];
		float 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(dst, pt1, pt2, Scalar(55, 100, 195), 1, LINE_AA);
	}
	imshow("霍夫直線檢測後的圖", dst);

	waitKey();
	return 0;
}

2.3.2、HoughLinesP:漸進概率式霍夫變換

void HoughLinesP( InputArray image, OutputArray lines, 
  double rho, double theta, int threshold, 
  double minLineLength = 0, double maxLineGap = 0 ); 
參數解釋:
InputArray image:輸入圖像,必須是8位單通道圖像。 
OutputArray lines:檢測到的線條參數集合,是一個vector<Vec4i>,Vec4i是一個包含4個int數據類型的結構體,[x1,y1,x2,y2],可以表示一個線段。
double rho:直線搜索時的距離步長,以像素爲單位, 推薦用1.0 。 
double theta:直線搜索時的角度步長,以弧度爲單位, 推薦CV_PI/180。 
int threshold:累加計數值的閾值參數,當參數空間某個交點的累加計數的值超過該閾值,則認爲該交點對應了圖像空間的一條直線。 
double minLineLength:默認值爲0,表示最小線段長度閾值(像素)。 
double maxLineGap:線段上最近兩點之間的閾值.默認值爲0,表示直線斷裂的最大間隔距離閾值。即如果有兩條線段是在一條直線上,但它們之間有間隙,那麼如果這個間隔距離小於該值,則被認爲是一條線段,否則認爲是兩條線段。 
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;

int main() {
	Mat img = imread("D:/test01.jpg");
	if (img.empty()) {
		cout << "could not find src1" << endl;
		return -1;
	}
	imshow("原圖", img);
	Mat gray, dst;
	//灰度化
	cvtColor(img, gray, CV_BGR2GRAY);
	//二值化
	threshold(gray, gray, 100, 255, THRESH_BINARY);
	//邊緣檢測
	Canny(gray, gray, 20, 200, 3);
	//灰度轉bgr
	cvtColor(gray, dst, CV_GRAY2BGR);
	vector<Vec4f> lines;
	HoughLinesP(gray, lines, 1, CV_PI/180, 20, 0, 0);

	for (size_t i = 0; i < lines.size(); i++) {
		Vec4f pline = lines[i];
		Point pt1, pt2;
		pt1 = Point(pline[0], pline[1]);
		pt2 = Point(pline[2], pline[3]);

		line(dst, pt1, pt2, Scalar(55, 100, 195), 1, LINE_AA);
	}
	imshow("霍夫直線檢測後的圖", dst);

	waitKey();
	return 0;
}

3、霍夫圓變換(Hough Circles Transform)

3.1、基本介紹

霍夫圓變換的基本思路是認爲圖像上每一個非零像素點都有可能是一個潛在的圓上的一點,跟霍夫線變換一樣,也是通過投票,生成累積座標平面,設置一個累積權重來定位圓。

3.2、應用示例

void HoughCircles( InputArray image, OutputArray circles,
                   int method, double dp, double minDist,
                   double param1=100, double param2=100,
                   int minRadius=0, int maxRadius=0 )
參數解釋:
第一個參數image是輸入圖像矩陣,要求是灰度圖像;
第二個參數 circles是一個包含檢測到的圓的信息的向量,向量內第一個元素是圓的橫座標,第二個是縱座標,第三個是半徑大小;
第三個參數 methodmethod是所使用的圓檢測算法,目前只有CV_HOUGH_GRADIENT一個可選;
第四個參數 dp是累加面與原始圖像相比的分辨率的反比參數,dp=2時累計面分辨率是元素圖像的一半,寬高都縮減爲原來的一半,dp=1時,兩者相同。(關於這個分辨率的概念沒有理解透,按道理低分辨率應該意味着更快的檢測速度,然而實測恰恰相反)
第五個參數 minDist定義了兩個圓心之間的最小距離;
第六個參數param1是Canny邊緣檢測的高閾值,低閾值被自動置爲高閾值的一半;
第七個參數param2是累加平面對是否是圓的判定閾值;
第八和第九個參數定義了檢測到的圓的半徑的最大值和最小值;
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;

int main() {
	Mat img = imread("D:/test02.jpg");
	if (img.empty()) {
		cout << "could not find src1" << endl;
		return -1;
	}
	imshow("原圖", img);
	Mat gray, dst;
	//灰度化
	cvtColor(img, gray, CV_BGR2GRAY);
	//灰度轉bgr
	cvtColor(gray, dst, CV_GRAY2BGR);
	vector<Vec3f>circles;
	HoughCircles(gray, circles, CV_HOUGH_GRADIENT, 1, 15, 100, 155);
	for (size_t i = 0; i < circles.size(); i++) {
		Point circleCenter(circles[i][0], circles[i][1]);
		int radius = circles[i][2];
		circle(dst, circleCenter, radius, Scalar(0, 0, 255), 3); //做圓
	}
	imshow("霍夫圓檢測後的圖", dst);

	waitKey();
	return 0;
}

4、參考

https://www.cnblogs.com/skyfsm/p/6881686.html

https://blog.csdn.net/leonardohaig/article/details/87907462

https://blog.csdn.net/qq_39861376/article/details/82119472

https://blog.csdn.net/weixin_38416696/article/details/90755605

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章