(1)首先我们来看三幅图片理解什么是角点:
我们在图片以某像素点为中心,取一窗口,当窗口向各个方向移动时,其内部灰度值变化不是很明显,则该点即处在平坦区域(如左边图);当其内部灰度值只在几个固定的方向上变化较为明显,那么该点则处在边缘区域(如图中间部分);当向各个方向移动,其变化都是很明显,则该点为角点(如图右)。
当然,上面所说的变化明显与否,是与我们事先设定的阀值进行对比的。
(2)moravec算法对角点定义:
窗口在各个方向上移动,窗口内的灰度值都会产生较大的变化。但实际程序中,这里的各个方向实际只有8个方向。即米形0°,45°,90°,135°,180°,225°,270°,315°。
例如:
这里我们为了简单起见,我们只取了四个方向(0°,45°,90°,135°),取一个w*w(如:5x5)的方形窗口, 计算0度、45度、90度、135度四个方向灰度差的平方和, 取其中的最小值作为该像素点的兴趣值(如下图)。
(3)moravec角点检测实现:
步骤:
<1>对于每一个像素点,计算在E(u,v),在我们的算法中,(u,v)的取值是((1,0), (1,1), (0,1), (-1, 1),这里只取了四个方向
<2>计算最小值对应的每个位置minValue = min{E(u,v)}
<3>对每个位置minValue进行判定,是不是大于设定的阀值,其中还有个过程是判断其是否为局部最大值以防止产生重复的角点
代码:
#include <iostream> #include "cv.h" #include "highgui.h" #include "cxcore.h" using namespace std; int getMoravec(IplImage *src, CvSeq *corners, float threshold) { int winSize = 5; int y, x; int halfwin = winSize/2; int win; IplImage *diffDst = cvCreateImage(cvGetSize(src), 32, 1); // 保存最小的变量值 int cornersCount = 0; // 保存角点个数 for(y = halfwin; y < src->height - halfwin; y++) { for(x = halfwin; x < src->width - halfwin; x++) { float cornersResult[4]; cornersResult[0] = 0; cornersResult[1] = 0; cornersResult[2] = 0; cornersResult[3] = 0; float minValue; for(win = -halfwin; win < halfwin; win++) { cornersResult[0] += pow(cvGetReal2D(src, y, x+win) - cvGetReal2D(src, y, x+win+1), 2); cornersResult[1] += pow(cvGetReal2D(src, y+win, x+win) - cvGetReal2D(src, y+win+1, x+win+1), 2); cornersResult[2] += pow(cvGetReal2D(src, y+win, x) - cvGetReal2D(src, y+win+1, x), 2); cornersResult[3] += pow(cvGetReal2D(src, y+win, x-win) - cvGetReal2D(src, y+win+1, x-win-1),2); } minValue = cornersResult[0]; minValue = minValue < cornersResult[1] ? minValue : cornersResult[1]; minValue = minValue < cornersResult[2] ? minValue : cornersResult[2]; minValue = minValue < cornersResult[3] ? minValue : cornersResult[3]; cvSetReal2D(diffDst, y, x, minValue); } } int yywin, xxwin, maxValue; CvPoint resultLoc; for(y = halfwin; y < src->height - halfwin; ) { for(x = halfwin; x < src->width - halfwin;) { resultLoc.x = -1; resultLoc.y = -1; maxValue = 0; for(yywin = -halfwin; yywin <= halfwin; yywin++) { for(xxwin = -halfwin; xxwin < halfwin; xxwin++) { if(cvGetReal2D(diffDst, y+yywin, x+xxwin) > maxValue) { maxValue = cvGetReal2D(diffDst, y+yywin, x+xxwin); resultLoc.y = y+yywin; resultLoc.x = x+xxwin; } } } if(maxValue > threshold) { cvSeqPush(corners, &resultLoc); // 将角点加入到corners中 这个函数需要记住******* cornersCount ++; } x += winSize; // ++ 可能会出现多个corners } y += winSize; } cvReleaseImage(&diffDst); return cornersCount; } int main() { IplImage *src = cvLoadImage("E:\\study_opencv_video\\lesson17_1\\1.bmp", 0); if(!src) { cout << "No Image loading..." << endl; return 0; } CvSeq *corners; CvMemStorage *mem = cvCreateMemStorage(0); // 创建内存序列 用于保存最终角点的空间 corners = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvPoint), mem); // corners指向该内存序列 角点将会保存在一个CvSeq中 float threshold =30000; // 调用函数计算角点 int cornersCount = getMoravec(src, corners, threshold); //图像show用于显示角的提取结果 IplImage* show= cvCreateImage(cvGetSize(src),8,3); cvCvtColor(src,show,CV_GRAY2BGR); // 获取每一个角点的坐标 for(int i = 0; i < cornersCount; i++) { // 以角点坐标为中心, 绘制一个半斤为5的圆 CvPoint *pt = (CvPoint *)cvGetSeqElem(corners, i); // 这个也得记住******* cvCircle(show, *pt, 5, cvScalar(0,0,255)); } cvNamedWindow("show"); cvShowImage("show", show); cvWaitKey(0); cvReleaseImage(&src); cvReleaseImage(&show); cvReleaseMemStorage(&mem); // 这个也得记住*** cvDestroyWindow("show"); return 0; }效果:
附加:简单解释下代码中的cvSeq和cvMemStorage,我用的不是太熟,当做笔记了哈哈~~:
CvMemStorage *mem = cvCreateMemStorage(0); //创建内存序列 CvSeq*corners = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvPoint), mem); // corners指向内存序列 cvSeqPush(corners, &resultLoc); // 将点加入到内存序列中 CvPoint *pt = (CvPoint *)cvGetSeqElem(corners, i); // 获得seq中第i个元素 cvReleaseMemStorage(&mem); //释放内存序列空间
cvSeqRemove(corners,i); // 移除第i个点
cvSeqSort(corners,cmpFunc,0);// 对内存序列中的点进行排序
//对seq排序时的比较函数 static int cmpFunc(const void* _cur , const void* _next , void* userdata) { PHARRISPOINT cur = (PHARRISPOINT)_cur; PHARRISPOINT next = (PHARRISPOINT)_next; return cur->cornerness < next->cornerness ? 1 : -1; }
(4)moravec角点检测的缺点
moravec角点检测主要有两个缺点:
一:不具有旋转不变性
二:对边缘点的反应比较强烈
作者:小村长 出处:http://blog.csdn.net/lu597203933 欢迎转载或分享,但请务必声明文章出处。 (新浪微博:小村长zack, 欢迎交流!)