天天看點

基于Hough變換的直線和圓的檢測與提取

學數字圖像處理有挺長時間了,正好最近這段時間有空,學習了一下Hough變換,作了幾個比較簡單的東西,分享出來,希望能和大家一起學習,也希望各位能提出寶貴的意見,共同進步,文采不好,歡迎拍磚,下面正式進入正題。

Hough變換于1962年被Paul Hough提出以來,在數字圖像中基本幾何形狀的檢測與提取方面,受到了各界學者的廣泛關注。Hough變換因其具有很好地魯棒性、對圖像資料的不完全性、以及檢測的準确性等優點被廣泛應用生産生活中。舉幾個最常見例子,在工業生産流水線上,紙張或鋁闆在出廠的過程中,破損與劃痕在所難免,而這些破損與劃痕普遍的形狀無非就是直線和原點。我們要在減小出廠産品破損率,檢測成本做到最低,最好的方法無非就是利用數字圖像處理的方法了。又扯遠了。。。言歸正傳,那麼什麼是Hough變換呢?一聽這個Hough變換這個名字,相信很多初學者就被吓蒙了,沒事,聽我細細道來。

Hough變換就是在兩個兩個坐标系下進行的變換。

step1:假設直線方程是:y=kx+b,其中s1和s2是直線上的兩個點,如圖1所示。

step2:就是在進行坐标系變化把y=kx+b變成b=-xk+y。直線上的點s1(x1,y1)在轉換坐标系後變為一條直線,s2(x2,y2)這個點在轉換坐标系後變成另一條直線,依次類推。在變換坐标系後,Hough發現了一個規律,在變換後所有直線都相交于一個點!于是,Hough提出檢測直線的問題可以轉化為檢測點的問題,交點的位置涵蓋了原圖中直線的位置資訊和方向。

基于Hough變換的直線和圓的檢測與提取
基于Hough變換的直線和圓的檢測與提取

圖1 圖2

但是這還不夠,我們想一下,當原始圖像中有一條垂直于x軸的直線怎麼辦?斜率k為無窮大啊,沒法坐标轉化。但是問題還得解決。Hart、Dual提出了将直線換一下表達形式,即将笛卡爾直角坐标系下y=kx+b先變換一下,變換為極坐标系下的p=x*cos(theta)+y*sin(theta);(原諒我這麼寫,沒法打公式啊)

基于Hough變換的直線和圓的檢測與提取

。同上,在轉換後再進行一步轉換坐标系,原圖像直線上的每個點都對應轉換坐标的一條正弦曲線,許多點一起變換坐标系又能産生一個交點。如下所示:

基于Hough變換的直線和圓的檢測與提取
基于Hough變換的直線和圓的檢測與提取

圖3 圖4

這便是标準Hough變換檢測直線的基本原理。

圓的檢測是Hough變換直線檢測的延伸,這裡就不給原理了,直接給代碼吧。

下面給出用opencv實作的代碼:(vs2015+opencv3.1.0)

vs2015下載下傳

http://www.ithome.com/html/win10/164028.htm

opencv下載下傳

https://sourceforge.net/projects/opencvlibrary/files/opencv-win/3.1.0/opencv-3.1.0.exe/download

vs2015+opencv3.1.0的配置如下沒有配置的或者配置不對的可以去看看:

http://blog.csdn.net/liu798675179/article/details/51233742

#include "stdafx.h"
#include <opencv2/opencv.hpp>  
using namespace std;
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")  
int main()
{
	const char *pstrWindowsSrcTitle = "原圖";
	const char *pstrWindowsLineName = "hough變換後圖像";

	// 從檔案中加載原圖  
	IplImage *pSrcImage = cvLoadImage("c://pic//line.jpg", CV_LOAD_IMAGE_UNCHANGED);
	// 灰階圖  
	IplImage *pGrayImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 1);

	cvCvtColor(pSrcImage, pGrayImage, CV_BGR2GRAY);
	//邊緣圖  
	cvSmooth(pGrayImage, pGrayImage, CV_GAUSSIAN,5,5);
	//平滑濾波

	IplImage *pCannyImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 1);
	cvCanny(pGrayImage, pCannyImage, 80, 90);
 
	//線段檢測(二值圖像) 
	CvMemStorage *pcvMStorage = cvCreateMemStorage();
	double fRho = 1;
	double fTheta = CV_PI / 180;
	int nMaxLineNumber = 5;   //最多檢測條直線  
	double fMinLineLen = 50;   //最小線段長度  
	double fMinLineGap = 10;   //最小線段間隔  
	CvSeq *pcvSeqLines = cvHoughLines2(pCannyImage, pcvMStorage, CV_HOUGH_PROBABILISTIC, fRho, fTheta, nMaxLineNumber, fMinLineLen, fMinLineGap);
	//圓檢測(灰階圖)  
	double fMinCircleGap = pGrayImage->height / 10;
	CvSeq *pcvSeqCircles = cvHoughCircles(pGrayImage, pcvMStorage, CV_HOUGH_GRADIENT, 1, fMinCircleGap,100);//每個圓由三個浮點數表示:圓心坐标(x,y)和半徑  
	

	//繪制線段(用紅色線條表示)  
	IplImage *pColorImage = cvCreateImage(cvGetSize(pSrcImage), IPL_DEPTH_8U, 3);

	cvCvtColor(pCannyImage, pColorImage, CV_GRAY2BGR);
	int i;
	for (i = 0; i < pcvSeqLines->total; i++)
	{
		CvPoint* line = (CvPoint*)cvGetSeqElem(pcvSeqLines, i);
		cvLine(pColorImage, line[0], line[1], CV_RGB(255, 0, 0), 2);
	}
	//繪制圓形(紅色線段表示)
	int j;
	for (j = 0; j < pcvSeqCircles->total; j++)
	{
		float* p = (float*)cvGetSeqElem(pcvSeqCircles, j);

		cvCircle(pColorImage, cvPoint(cvRound(p[0]), cvRound(p[1])), cvRound(p[2]), CV_RGB(255, 0, 0), 2);
	}

	cvNamedWindow(pstrWindowsSrcTitle, CV_WINDOW_AUTOSIZE);
	cvShowImage(pstrWindowsSrcTitle, pSrcImage);
	cvNamedWindow(pstrWindowsLineName, CV_WINDOW_AUTOSIZE);
	cvShowImage(pstrWindowsLineName, pColorImage);

	cvWaitKey(0);

	cvReleaseMemStorage(&pcvMStorage);
	cvDestroyWindow(pstrWindowsSrcTitle);
	cvDestroyWindow(pstrWindowsLineName);
	cvReleaseImage(&pSrcImage);
	cvReleaseImage(&pGrayImage);
	cvReleaseImage(&pCannyImage);
	cvReleaseImage(&pColorImage);
	return 0;
}
           

實驗結果如下:

基于Hough變換的直線和圓的檢測與提取
基于Hough變換的直線和圓的檢測與提取

繼續閱讀