1. 程式人生 > >【學習opencv】實現霍夫變換(1)檢測直線

【學習opencv】實現霍夫變換(1)檢測直線

目前想對於霍夫圓檢測進行修改,想法是若能在固定圓心的橫座標的情景下去搜索圓,若要實現就需要對霍夫檢測有一定的深入瞭解。

霍夫變換原理

霍夫變換原理實則就是引數空間的轉變。

極座標轉換

首先因為直角座標系中垂直於x軸的直線不存在,即轉換用極座標表示,即用ρ,θ表示,對應於下圖中的rho,degree。下圖中x,y軸的畫法是根據影象在視窗中顯示的座標方法。

這裡寫圖片描述

ρ,θ表示的直線應該為:

y=cosθsinθx+ρsinθ

推導方法:根據ρ是過座標原點向已知直線做的垂線段,再根據角度,求取經過x,y軸的座標點,然後根據兩點之間的座標公式求得。

引數空間的轉換

將x,y的引數空間轉化為ρ,θ空間,這樣做的目的是因為,將直角座標的許多點(x,y),如若對映到ρ,θ空間,會對映成許多正弦曲線,而其中許多曲線若相交於點(ρ1,θ1),說明直角座標系中許多個點共這一個(ρ1,θ1),即找到一條直線。

找出ρ,θ引數空間的最值

這裡找出的最值應該事區域極值,但前期為了方便測試原理,只是簡單在ρ,θ矩陣中尋找最大值。將找到的最值轉換為斜率截距並顯示在圖片中

注意點

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

需要完善的地方很多!例如求八鄰域極值,避免重複檢測等,後期還需要了解如何實現霍夫圓檢測。