學數字圖像處理有挺長時間了,正好最近這段時間有空,學習了一下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提出檢測直線的問題可以轉化為檢測點的問題,交點的位置涵蓋了原圖中直線的位置資訊和方向。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiIXZ05WZD9CX5RXa2Fmcn9CXwczLcVmds92czlGZvwVP9EUTDZ0aRJkSwk0LcxGbpZ2LcBDM08CXlpXazRnbvZ2LcRlMMVDT2EWNvwFdu9mZvwVNBRVT1kkeNRTT6hVdsdUZwZlMkZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39DMwMzNwMTN4EDOwYDM3EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
圖1 圖2
但是這還不夠,我們想一下,當原始圖像中有一條垂直于x軸的直線怎麼辦?斜率k為無窮大啊,沒法坐标轉化。但是問題還得解決。Hart、Dual提出了将直線換一下表達形式,即将笛卡爾直角坐标系下y=kx+b先變換一下,變換為極坐标系下的p=x*cos(theta)+y*sin(theta);(原諒我這麼寫,沒法打公式啊)
。同上,在轉換後再進行一步轉換坐标系,原圖像直線上的每個點都對應轉換坐标的一條正弦曲線,許多點一起變換坐标系又能産生一個交點。如下所示:
圖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;
}
實驗結果如下: