SUSAN算子很好听的一个名字,其实SUSAN算子除了名字好听外,她还很实用,而且也好用,SUSAN的全名是:Smallest Univalue Segment Assimilating Nucleus)。它是一种很有特色高效的边缘和角点检测算子,它不仅可以检测图像目标的边界点,而且能够较Robust地检测目标的角点。并且具有结构保留的降噪功能。
先借助如图所示来解释检测的原理,其中图片是白色背景,有一个颜色比较暗淡的矩形(dark area)。用一个园形模板在图像上移动,若模板内的像素灰度与模板中心的像素(被称为核Nucleus)灰度值小于一定的阈值,则认为该点与核Nucleus具有相同的灰度,满足该条件的像素组成的区域就称为USAN(Univalue Segment Assimilating Nucleus)。在图片上有5个圆形区域。圆形区域表示的是掩码区域。把圆形区域内的每一个位置的像素值与圆心处的像素值相比较,那么圆中的的像素可以分为两类,一类是像素值与圆心处的像素值相近的,另一类是像素值与圆心的处的像素值相差比较大的。
如果将模板中各个像素的灰度都与模板中心的核像素的灰度进行比较,那么就会发现总有一部分模板区域和灰度与核像素的灰度相同或相似,这部分区域可以称为USAN(Univalue Segment Assimilating Nuclues).USAN区域包含很多与图像结构有关的信息。利用这种区域的尺寸、重心、二阶矩的分析,可以得到图像中的角点,边缘等信息。从上图所示,当核像素处在图像中的灰度一致区域时,USAN的面积会达到最大。第e个模板就是属于这种情况。
SUSAN(Smallest Univalue Segment Assimilating Nuclues)进行角点检测时,遵循了常规的思路:使用一个窗口在图像上逐点滑动,在每一个位置上计算出一个角点量,再进行局部极大值抑制,得到最终的角点。我们这里使用的窗口是圆形窗口,最小的窗口是3*3的,本文中使用的是37个像素的圆形窗口,如图:
#include <stdio.h> #include <cv.h> #include <highgui.h> #define max_corners 300 int main( int argc, char** argv ) { int cornerCount=max_corners; CvPoint2D32f corners[max_corners]; double qualityLevel = 0.05; double minDistance = 5; IplImage *srcImage = 0, *grayImage = 0, *corners1 = 0, *corners2 = 0; int i; CvScalar color = CV_RGB(255,0,0); cvNamedWindow( "image", 2); srcImage = cvLoadImage("test.jpg", 1); grayImage = cvCreateImage(cvGetSize(srcImage), IPL_DEPTH_8U, 1); cvCvtColor(srcImage, grayImage, CV_BGR2GRAY); corners1= cvCreateImage(cvGetSize(srcImage), IPL_DEPTH_32F, 1); corners2= cvCreateImage(cvGetSize(srcImage),IPL_DEPTH_32F, 1); cvGoodFeaturesToTrack (grayImage, corners1, corners2, corners,&cornerCount, qualityLevel, minDistance, 0); printf("num corners found: %d\n", cornerCount); if(cornerCount>0) { for (i = 0; i < cornerCount; ++i) { cvCircle(srcImage,cvPoint((int)(corners[i].x),(int)(corners[i].y)), 3, color, 1, CV_AA, 0); } } cvShowImage("image", srcImage); cvWaitKey(0); cvReleaseImage(&srcImage); cvReleaseImage(&grayImage); cvReleaseImage(&corners1); cvReleaseImage(&corners2); return 0; }
在些,我们给出SUSAN角点测测另一种应用。
#include<math.h> #include<stdlib.h> #include<stdio.h> #include "cv.h" #include "highgui.h" int main( int argc, char** argv ) { int height ,width ,step ,channels ; int i,j,k,same ,max,min,sum; float thresh; uchar *data0,*data1 ; //char *filename="result.bmp"; IplImage* Img, *nimg; Img = cvLoadImage( "test.jpg",0); cvNamedWindow( "Image", 2); cvShowImage( "Image", Img ); nimg = cvCreateImage(cvGetSize(Img),8,1); height = Img->height; width = Img->width; step = Img->widthStep/sizeof(uchar); channels = Img->nChannels; data0 = (uchar*)Img->imageData; data1 = (uchar*)nimg->imageData; printf("Processing a %d X %d image with %d channels\n",width,height,channels); int OffSetX[37] = { -1, 0, 1, -2,-1, 0, 1, 2, -3,-2,-1, 0, 1, 2, 3, -3,-2,-1, 0, 1, 2, 3, -3,-2,-1, 0, 1, 2, 3, -2,-1, 0, 1, 2, -1, 0, 1 }; int OffSetY[37] = { -3,-3,-3, -2,-2,-2,-2,-2, -1,-1,-1,-1,-1,-1,-1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3 }; max = min = data0[0]; for(i=3;i<height-3;i++) for(j=3;j<width-3;j++) { same =0; sum = 0; for(k=0;k<37;k++) { sum+=data0[(i+OffSetY[k])*step+(j+OffSetX[k])]; thresh = (float)sum/37; float data_fabs; data_fabs= (float)(data0[(i+OffSetY[k])*step+(j+OffSetX[k])]-data0[i*step+j]); if(fabs( data_fabs)<=thresh) same++; } if(same<18) nimg->imageData[i*step+j] = 255; else nimg->imageData[i*step+j] = 0; printf("same = %d\n", same); } cvNamedWindow( "Image", 2); cvShowImage( "Image", nimg ); cvWaitKey(0); cvDestroyWindow( "Image" ); cvReleaseImage( &Img ); cvReleaseImage( &nimg ); return 0; }
关于Image Engineering & Computer Vision的更多讨论与交流,敬请关注本博和新浪微博songzi_tea.