引言
SUSAN算子很好聽的一個名字,其實SUSAN算子除了名字好聽外,她還很實用,而且也好用,SUSAN的全名是:Smallest Univalue Segment Assimilating Nucleus)。它是一種很有特色高效的邊緣和角點檢測算子,它不僅可以檢測圖像目標的邊界點,而且能夠較Robust地檢測目標的角點。並且具有結構保留的降噪功能。
基本SUSAN原理
先借助如圖所示來解釋檢測的原理,其中圖片是白色背景,有一個顏色比較暗淡的矩形(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個像素的圓形窗口,如圖:
閾值的分析
通過上面對a、b、c、d、e等幾個圓形模板的USAN值的分析,當模板的中心位於角點處時,USAN的值最小。
下面簡單敘述下利用SUSAN算子檢測角點的步驟:
- 利用圓形模板遍歷圖像,計算每點處的USAN值。
- 設置一閾值g,一般取值爲1/2(Max(n), 也即取值爲USAN最大值的一半,進行閾值化,得到角點響應。
- 使用非極大值抑制來尋找角點。
- 計算USAN區域的重心,然後計算重心和模板中心的距離,如果距離較小則不是正確的角點;
- 判斷USAN區域的重心和模板中心的連線所經過的像素都是否屬於USAN區域的像素,如果屬於那麼這個模板中心的點就是角點。
SUSAN角點檢測代碼
1.#include <stdio.h>
2.#include <cv.h>
3.#include <highgui.h>
4.
5.#define max_corners 300
6.int main( int argc, char** argv )
7.{
8. int cornerCount=max_corners;
9. CvPoint2D32f corners[max_corners];
10. double qualityLevel = 0.05;
11. double minDistance = 5;
12. IplImage *srcImage = 0, *grayImage = 0, *corners1 = 0, *corners2 = 0;
13. int i;
14. CvScalar color = CV_RGB(255,0,0);
15. cvNamedWindow( "image", 2);
16. srcImage = cvLoadImage("test.jpg", 1);
17. grayImage = cvCreateImage(cvGetSize(srcImage), IPL_DEPTH_8U, 1);
18.
19. cvCvtColor(srcImage, grayImage, CV_BGR2GRAY);
20. corners1= cvCreateImage(cvGetSize(srcImage), IPL_DEPTH_32F, 1);
21. corners2= cvCreateImage(cvGetSize(srcImage),IPL_DEPTH_32F, 1);
22. cvGoodFeaturesToTrack (grayImage, corners1, corners2, corners,&cornerCount,
23. qualityLevel, minDistance, 0);
24. printf("num corners found: %d\n", cornerCount);
25. if(cornerCount>0)
26. {
27. for (i = 0; i < cornerCount; ++i)
28. {
29. cvCircle(srcImage,cvPoint((int)(corners[i].x),(int)(corners[i].y)),
30. 3, color, 1, CV_AA, 0);
31. }
32. }
33. cvShowImage("image", srcImage);
34. cvWaitKey(0);
35. cvReleaseImage(&srcImage);
36. cvReleaseImage(&grayImage);
37. cvReleaseImage(&corners1);
38. cvReleaseImage(&corners2);
39.
40. return 0;
41.}
#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角點測測另一種應用。
檢測動態匹配輪廓的SUSAN代碼:
1.#include<math.h>
2.#include<stdlib.h>
3.#include<stdio.h>
4.#include "cv.h"
5.#include "highgui.h"
6.
7.int main( int argc, char** argv )
8.{
9. int height ,width ,step ,channels ;
10. int i,j,k,same ,max,min,sum;
11. float thresh;
12. uchar *data0,*data1 ;
13. //char *filename="result.bmp";
14. IplImage* Img, *nimg;
15.
16. Img = cvLoadImage( "test.jpg",0);
17. cvNamedWindow( "Image", 2);
18. cvShowImage( "Image", Img );
19.
20. nimg = cvCreateImage(cvGetSize(Img),8,1);
21.
22. height = Img->height;
23. width = Img->width;
24. step = Img->widthStep/sizeof(uchar);
25. channels = Img->nChannels;
26. data0 = (uchar*)Img->imageData;
27. data1 = (uchar*)nimg->imageData;
28.
29. printf("Processing a %d X %d image with %d channels\n",width,height,channels);
30. int OffSetX[37] =
31. { -1, 0, 1,
32. -2,-1, 0, 1, 2,
33. -3,-2,-1, 0, 1, 2, 3,
34. -3,-2,-1, 0, 1, 2, 3,
35. -3,-2,-1, 0, 1, 2, 3,
36. -2,-1, 0, 1, 2,
37. -1, 0, 1 };
38. int OffSetY[37] =
39. {
40. -3,-3,-3,
41. -2,-2,-2,-2,-2,
42. -1,-1,-1,-1,-1,-1,-1,
43. 0, 0, 0, 0, 0, 0, 0,
44. 1, 1, 1, 1, 1, 1, 1,
45. 2, 2, 2, 2, 2,
46. 3, 3, 3
47. };
48.
49. max = min = data0[0];
50.
51. for(i=3;i<height-3;i++)
52. for(j=3;j<width-3;j++)
53. {
54. same =0;
55. sum = 0;
56. for(k=0;k<37;k++)
57. {
58. sum+=data0[(i+OffSetY[k])*step+(j+OffSetX[k])];
59. thresh = (float)sum/37;
60. float data_fabs;
61. data_fabs= (float)(data0[(i+OffSetY[k])*step+(j+OffSetX[k])]-data0[i*step+j]);
62.
63. if(fabs( data_fabs)<=thresh)
64. same++;
65. }
66.
67. if(same<18)
68. nimg->imageData[i*step+j] = 255;
69. else
70. nimg->imageData[i*step+j] = 0;
71.
72. printf("same = %d\n", same);
73.
74. }
75.
76. cvNamedWindow( "Image", 2);
77. cvShowImage( "Image", nimg );
78. cvWaitKey(0);
79. cvDestroyWindow( "Image" );
80. cvReleaseImage( &Img );
81. cvReleaseImage( &nimg );
82. return 0;
83.}
#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;
}
測試輸出結果
補充事項
- 情況一:在點B的處的USAN區域如右邊的白色區域所示,呈帶狀分佈在中間,並且是單像素的帶狀。此時計算出的角點相應量會比較大,會誤認爲是角點,而實際上是邊緣點。解決方法:計算出USAN區域的重心,如果重心與圓心的距離大於一定的閾值才認爲是角點。
- 情況二:多個噪聲點或是複雜的結構。例如單點噪聲,其計算出來的角點量很大,會誤認爲是角點。解決方法:計算出USAN區域的重心,從中心指向USAN區域重心的直線上的所有像素都必須是USAN的一部分,否則認爲不是角點。