【學習opencv】實現霍夫變換(1)檢測直線
阿新 • • 發佈:2019-01-09
目前想對於霍夫圓檢測進行修改,想法是若能在固定圓心的橫座標的情景下去搜索圓,若要實現就需要對霍夫檢測有一定的深入瞭解。
霍夫變換原理
霍夫變換原理實則就是引數空間的轉變。
極座標轉換
首先因為直角座標系中垂直於x軸的直線不存在,即轉換用極座標表示,即用表示,對應於下圖中的rho,degree。下圖中x,y軸的畫法是根據影象在視窗中顯示的座標方法。
用表示的直線應該為:
推導方法:根據是過座標原點向已知直線做的垂線段,再根據角度,求取經過x,y軸的座標點,然後根據兩點之間的座標公式求得。
引數空間的轉換
將x,y的引數空間轉化為空間,這樣做的目的是因為,將直角座標的許多點(x,y),如若對映到空間,會對映成許多正弦曲線,而其中許多曲線若相交於點(),說明直角座標系中許多個點共這一個(),即找到一條直線。
找出引數空間的最值
這裡找出的最值應該事區域極值,但前期為了方便測試原理,只是簡單在矩陣中尋找最大值。將找到的最值轉換為斜率截距並顯示在圖片中
注意點
1.需要注意的是霍夫變換輸入的圖片是二值化的圖片,即若圖片某點畫素值為255,則代表該點存在,即可轉換到空間
2.canny邊緣檢測的圖片更容易檢測出尖銳的邊緣部分,更容易找到直線,而如果採用閾值函式二值化的話,直線上某些點可能會被設定的閾值過濾掉。
程式設計實現
#include<iostream>
#include<opencv2\opencv.hpp>
using namespace cv;
using namespace std;
#define Blue Scalar(255,0,0)
//角度轉弧度 π / 180×角度 弧度變角度 180 / π×弧度
#define Pi 3.1415926
void paint(Mat img, double k, double b)
{
for (int x = 0; x < img.cols; x++)
{
double y = k * x + b;
if (round(y) >= 0 && round(y) <= img.rows)
{
circle(img, Point(x, y), 1, Blue, 1, 8);
}
}
}
int main()
{
Mat img = imread("test.jpg");
Mat gray;
cvtColor(img,gray,CV_BGR2GRAY);
GaussianBlur(gray,gray,Size(5,5),1.6);
namedWindow("img",CV_WINDOW_NORMAL);
namedWindow("gray", CV_WINDOW_NORMAL);
//threshold(img,img,80,255,CV_THRESH_BINARY);
//adaptiveThreshold(gray,gray,255, ADAPTIVE_THRESH_MEAN_C,CV_THRESH_BINARY,3,3);
Canny(gray,gray,100,200);//canny引數
double MaxR = sqrt(gray.cols*gray.cols+gray.rows*gray.rows);//距離範圍應該為[0,√]
double MaxDegree = 180;//角度範圍為0-180
double interVal = 0.5;//間距為0.5
//floor向下取整,round向上取整
vector<vector<int>>A(round(MaxR),vector<int>(MaxDegree));
uchar *data =gray.data;
int step =gray.step[0];
for (int x = 0; x< gray.cols;x++)
{
for (int y = 0; y < gray.rows; y++)
{
if ((int)data[y*step+x] == 255)
{//遍歷所有存在的點
for (double degree =0; degree <MaxDegree; degree++)
{//遍歷所有斜率k的取值
double dis = x*cos((Pi*degree)/180.0)+y*sin((Pi*degree) / 180.0);
if (dis >= MaxR || dis <0)continue;
A[round(dis)][degree]++;
}
}
}
}
cout << "test1" << endl;
//尋找累加器矩陣中的極值點
int max = 0,degree = 0, dis = 0;
for (int i = 0; i < A.size(); i++)
{
for (int j = 0; j < A[0].size(); j++)
{
if (A[i][j] > max) { max = A[i][j]; dis= i; degree = j; }
}
}
//cout << "test2" << endl;
cout << "找到的角度" << degree << endl;
cout << "距離:" << dis << endl;
double k = -cos((Pi*degree) / 180.0) / sin((Pi*degree) / 180.0);//若用tan,考慮什麼時候為0
double b = dis / sin((Pi*degree) / 180.0);
cout << "斜率:" << k << endl;
cout << "截距:" << b << endl;
//畫出找到的曲線
paint(img, k,b);
imshow("img",img);
imshow("gray",gray);
waitKey(0);
destroyAllWindows();
return 0;
}
需要完善的地方很多!例如求八鄰域極值,避免重複檢測等,後期還需要了解如何實現霍夫圓檢測。