opencv角點檢測(一)
Harris角點檢測算法原理簡介
harris角點檢測算法首先對圖像中的每個像素計算2*2的協方差矩陣M,然後求出如下表達式的值:
R=det(M) -k*(trace(M)^2) (一般k的取值在0.04~0.06之間,opencv中取值範圍更大)
det(M)=λ1*λ2 trace(M)=λ1+λ2, λ1、λ2爲協方差矩陣M的特徵值;
R值爲正且比較大時爲角點位置,R值爲負時爲邊緣,R值很小時是平坦區域。
opencv中cornerHarris函數實現harris角點的提取,具體代碼如下:
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
//添加函數中用到的模塊
#include <iostream>
using namespace std;
using namespace cv;
//定義全局變量,與Trackbar相關聯的變量聲明爲全局變量,調用比較方便;
Mat src,src_gray;
int thresh = 150;
int max_thresh = 255;
char* sourceimg = "Source image";
char* cornersimg = "Corners detected";
void Harris_demo(int,void*);
int main(int argc,char* argv[])
{
src=imread("road.jpg");//讀取圖像,在當前文件夾下
cvtColor(src,src_gray,CV_RGB2GRAY);//將圖像轉換成灰度圖
namedWindow(sourceimg,CV_WINDOW_AUTOSIZE);//一般在創建控制條前,先創建一個窗口用來放置控制條;
createTrackbar("thresh:",sourceimg,&thresh,max_thresh,Harris_demo);//創建一個控制條,用來改變檢測角點的閾值;
imshow(sourceimg,src);
Harris_demo(0,0);//調用回調函數;將<span style="font-family: verdana, Arial, helvetica, sans-seriff;">thresh定義爲全局變量,不用傳遞參數;</span>
waitKey(0);//回車結束
return 0;
}
void Harris_demo(int,void*)
{
Mat dst,dst_norm,dst_norm_scaled;
dst = Mat::zeros(src.size(),CV_32FC1);
int blocksize = 2;
int kernel =3;
double k = 0.04;
cornerHarris(src_gray,dst,blocksize,kernel,k,4);//進行harris角點檢測
normalize(dst,dst_norm,0,255,NORM_MINMAX,-1,Mat());//對檢測的結果進行歸一化處理,0到255;
convertScaleAbs(dst_norm,dst_norm_scaled);//求其絕對值;這個函數會將結果轉換成8bit;
for(int j = 0; j < dst_norm.rows ; j++)
{
for(int i = 0; i < dst_norm.cols ; i++)
{
if((int) dst_norm.at<float>(j,i) > thresh)//在滿足閾值條件的角點處畫圓;
{
circle(src,Point(i,j),5,Scalar(0,0,255),2,8);
}
}
}
namedWindow(cornersimg,CV_WINDOW_AUTOSIZE);
imshow(cornersimg,src);
}
閾值thresh的值爲80,運行結果:
可以看出,檢測出的有些角點粘連在了一起,可以採用膨脹處理進行非極大值抑制。
2、其它Harris角點檢測程序
在CSDN上查找資料時,在http://blog.csdn.net/crzy_sparrow/article/details/7391511博客上看到了一個不錯的Harris角點檢測程序;其先定義了一個harris類,並將具體的檢測過程在類的方法中進行實現;有助於理解角點檢測的具體過程,而且還能加深對c++類和對象的理解;故將其程序進行整理和重新編寫如下:
harris.h
<span style="font-size:14px;">#ifndef HARRIS_H
#define HARRIS_H
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace cv;
class harris
{
private:
Mat cornerStrength;//opencv harris函數檢測的結果,即每個像素點的角點響應函數值;
Mat cornerThresh;//cornerStrength閾值化的結果
Mat localMax;//局部最大值的結果
int neighbourhood;//鄰域窗口的大小
int aperture;//sobel邊緣檢測窗口的大小
double k;//cornerHarris函數的係數0.04-0.05
double maxStrength;//角點響應函數的最大值
double thresh;//角點檢測的閾值
int nonMaxSize;//最大值抑制的鄰域窗口的大小
Mat kernel;//最大值抑制的核,即膨脹用到的核
public:
harris():neighbourhood(3),aperture(3),k(0.04),maxStrength(0.0),thresh(0.01),nonMaxSize(3)
{}
void setLocalMaxWindowsize(int nonMaxSize)
{
this->nonMaxSize = nonMaxSize;
}
void detect(const Mat image);
Mat getCornerMap(double qualityLevel);
void getCorners(vector<Point> &points,double qualityLevel);
//void getCorners(vector<Point> &points,const Mat &cornerMap);
void drawOnImage(Mat &image,const vector<Point> &points,Scalar color = Scalar(255,255,255),
int radius = 3,int thickness = 2);
};
#endif</span>
<span style="font-size:14px;">
</span>
</pre><pre>
harris.cpp
<span style="font-size:14px;">#include "harris.h"
//計算角點的響應函數以及非極大值抑制
void harris::detect(const Mat image)
{
//用opencv自帶的函數求解角點的響應函數
cornerHarris(image,cornerStrength,neighbourhood,aperture,k,4);
double minStrength;
//求解相應的最大最小值
minMaxLoc(cornerStrength,&minStrength,&maxStrength);
Mat dilated;
//採用默認的3*3的核膨脹,膨脹之後,除了局部最大點和原來相同,其他非局部最大值
//被3*3鄰域內的最大值取代
dilate(cornerStrength,dilated,Mat());
compare(cornerStrength,dilated,localMax,CMP_EQ);
}
//獲取焦點圖像
Mat harris::getCornerMap(double qualityLevel)
{
Mat cornerMap;
thresh = qualityLevel*maxStrength;
threshold(cornerStrength,cornerThresh,thresh,255,THRESH_BINARY);
cornerThresh.convertTo(cornerMap,CV_8U);
//和局部最大值與,剩下局部最大值,即完成非極大值抑制,且滿足閾值條件
bitwise_and(cornerMap,localMax,cornerMap);
return cornerMap;
}
void harris::getCorners(vector<Point> &points,double qualityLevel)
{
Mat cornerMap = getCornerMap(qualityLevel);
for(int y = 0; y < cornerMap.rows; y++)
{
//取圖像每一行的指針
const uchar* rowPtr = cornerMap.ptr<uchar>(y);
for(int x = 0; x < cornerMap.cols; x++)
{
if(rowPtr[x])
points.push_back(Point(x,y));
}
}
}
void harris::drawOnImage(Mat &image,const vector<Point> &points,Scalar color,int radius,int thickness )
{
vector<Point>::const_iterator it = points.begin();
while(it != points.end())
{
circle(image,*it,radius,color,thickness);
it++;
}
}</span>
main.cpp
</pre><pre name="code" class="cpp"><span style="font-size:14px;">#include <iostream>
#include "harris.h"
using namespace std;
int main(int argc,char* argv[])
{
Mat src,src_gray;
src = imread("road.jpg");
cvtColor(src,src_gray,CV_BGR2GRAY);
harris Harris1;
Harris1.detect(src_gray);
//獲得角點
vector<Point> POINTS;
Harris1.getCorners(POINTS,0.03);
//標記角點
Harris1.drawOnImage(src,POINTS,Scalar(0,0,255));
namedWindow("road",CV_WINDOW_AUTOSIZE);
imshow("road",src);
waitKey();
return 0;
}</span>
程序運行的結果如下:
其它角點檢測算法和亞像素角點檢測見《opencv圖像角點提取(二)》。