#include <opencv2\opencv.hpp>
#include "cv.h"
#include "cxcore.h"
#include "highgui.h"
/**********************************************************************************
*函數 int getMoravec(IplImage* src,CvSeq* corners)
*輸入:
*src : 單通道圖像
*corners : 用來保存提取到的角點
*threshold : 角點量的閾值 (具體函數 請看視頻)
*輸出
*corners : 用來保存提取到的角點
*返回值
*角點的個數
***************************************************************************************/
int getMoravec(IplImage* src,CvSeq* corners , float threshold)
{
//窗口大小
const int winSize=5;
int x,y,halfWinSize=winSize/2;
//保存最小的變化量
IplImage* diffDst = cvCreateImage(cvGetSize(src),32,1);
cvZero(diffDst);
//保存角點個數
int cornersCount=0;
//1.計算圖像上每一個點上的 在4個方向上的變化量 並計算出最小值 保存在矩陣diffDst中
for(y=halfWinSize;y<src->height-halfWinSize;y++)
{
for(x=halfWinSize;x<src->width-halfWinSize;x++)
{
//compute the reaction in the four directions(0,45,90,135)
int winx;
//數組reaction[4] 用於保持 在四個方向上的灰度值變化量
//minValue用於保存4個變化量中的最小值
float reaction[4],minValue;
reaction[0]=0;
reaction[1]=0;
reaction[2]=0;
reaction[3]=0;
//提示 下面的4個循環 可以綜合成一個循環
//0 度方向的變化量保存在reaction[0]中
for( winx=-halfWinSize;winx<halfWinSize;winx++)
{
reaction[0] = reaction[0] + pow( cvGetReal2D(src,y,x+winx)-cvGetReal2D(src,y,x+winx+1), 2 );
}
//45 度方向的變化量保存在reaction[1]中
for( winx=-halfWinSize;winx<halfWinSize;winx++)
{
reaction[1] = reaction[1]+pow(cvGetReal2D(src,y+winx,x+winx)-cvGetReal2D(src,y+winx+1,x+winx+1),2);
}
//90 度方向的變化量保存在reaction[2]中
for( winx=-halfWinSize;winx<halfWinSize;winx++)
{
reaction[2] = reaction[2]+pow(cvGetReal2D(src,y+winx,x)-cvGetReal2D(src,y+winx+1,x),2);
}
//135 度方向的變化量保存在reaction[3]中
for( winx=-halfWinSize;winx<halfWinSize;winx++)
{
reaction[3] = reaction[3]+pow(cvGetReal2D(src,y+winx,x-winx)-cvGetReal2D(src,y+winx+1,x-winx-1),2);
}
//計算4個量中最小值 保存到minValue
minValue = reaction[0];
minValue = minValue > reaction[1] ? reaction[1] : minValue;
minValue = minValue > reaction[2] ? reaction[2] : minValue;
minValue = minValue > reaction[3] ? reaction[3] : minValue;
//將最小的變化量保存到矩陣
cvSetReal2D(diffDst,y,x,minValue);
}
}
//2.獲取角點座標
for(y=halfWinSize;y<src->height-halfWinSize;)
{
for(x=halfWinSize;x<src->width-halfWinSize;)
{
float max=0;
int flag = 0 ;
CvPoint maxLoc;
maxLoc.x = -1;
maxLoc.y = -1;
//首先計算以點(x,y)位中心的winSize*winSize的窗口內部的局部極大值
for(int winy=-halfWinSize;winy<=halfWinSize;winy++)
{
for(int winx=-halfWinSize;winx<=halfWinSize;winx++)
{
float value ;
value = cvGetReal2D(diffDst,y+winy,x+winx);
//計算該窗口內 最大值 保存到max 並保存其座標到maxLoc
if(value>max)
{
max = value;
maxLoc.x = x+winx;
maxLoc.y = y+winy;
flag = 1;
}
}
}
//如果找到局部極大值 並且該值大於預先設定的閾值 則認爲是角點
if(flag==1 && max>threshold)
{
cvSeqPush(corners,&maxLoc);
cornersCount++;
}
//下一個窗口
x=x+halfWinSize;
}
//下一行的第一個窗口
y=y+halfWinSize;
}
cvReleaseImage(&diffDst);
return cornersCount;
}
int main(int argc, char* argv[])
{
IplImage* src;
//加載源圖像
src = cvLoadImage("3.jpg",CV_LOAD_IMAGE_GRAYSCALE);
if(!src)
{
printf("圖像加載失敗");
}
//用於保存最終角點的空間
CvMemStorage* mem = cvCreateMemStorage(0);
//角點將會保存在一個CvSeq中
CvSeq* corners;
corners = cvCreateSeq(0,sizeof(CvSeq),sizeof(CvPoint),mem);
//角點的個數
int cornersCount;
//調用函數getMoravec計算角點
cornersCount = getMoravec(src,corners,25000);
//圖像show用於顯示角的提取結果
IplImage* show= cvCreateImage(cvGetSize(src),8,3);
cvCvtColor(src,show,CV_GRAY2BGR);
//獲取每一個角點的座標
for(int x=0;x<cornersCount;x++)
{
CvPoint* pt = (CvPoint*)cvGetSeqElem(corners,x);
//以角點座標爲中心 繪製一個半徑爲5的圓
cvCircle(show,*pt,10,cvScalar(255,0,0,0));
}
//顯示結果
cvNamedWindow("dst");
cvShowImage("dst",show);
cvWaitKey(0);
cvReleaseImage(&src);
cvReleaseImage(&show);
cvReleaseMemStorage(&mem);
return 0;
}
運算結果:
角點的數量取決於所設定的閾值的大小。
閾值越小,角點數量越多,精度越低~
【1】moravec角點檢測步驟:
(1)對於每一個像素點,計算在E(u,v),在我們的算法中,(u,v)的取值是((1,0), (1,1), (0,1), (-1, 1).當然,你自己可以改成(1,0),(1,1),(0,1),(-1,1),(-1,0),(-1,-1),(0,-1),(1,-1) 8種情況
(2)計算最小值對每個位置minValue = min{E(u,v)},其中(u,v) = (1,0), (1,1), (0,1), (-1, 1).
(計算每個像素點的興趣值, 即以該像素點爲中心, 取一個w*w(如:5x5)的方形窗口, 計算0度、45度、90度、135度四個方向灰度差的平方和,
取其中的最小值作爲該像素點的興趣值。)
(3)對每個位置minValue 進行判斷,是不是大於設定閾值,如果是大於設定閾值,接着判斷是不是局部極大值