SUSAN算子——邊緣檢測

引言

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個像素的圓形窗口,如圖:


閾值的分析

在角點檢測中,有兩種類型的閾值,一種用來約束角點的數量,另一種用來約束角點的質量。當然,一個閾值不能完全做到隻影響質量或數量,只是會有一個側重點。那麼,SUSAN中的兩個閾值tg在角點檢測中起到一個什麼樣的作用呢?很明顯,閾值g是角點質量的。儘管也會影響數量,但是相對來說更側重於影響質量(角點的形狀)。例如,g值減小,那麼SUSAN會更加側重於檢測到更加“尖銳”的角點。所以,可以更加自己的實際需求來確定閾值g而閾值t,是角點的數量。當t減小時,會檢測到更多的角點。所以,閾值t可以在不影響角點質量的情況下,控制檢測到的角點的數量。在大多數情況下,設t25比較合適,如果圖像的對比度比較低,可以修改t值以適應變化。
通過上面對a、b、c、d、e等幾個圓形模板的USAN值的分析,當模板的中心位於角點處時,USAN的值最小。
下面簡單敘述下利用SUSAN算子檢測角點的步驟:
  1. 利用圓形模板遍歷圖像,計算每點處的USAN值。
  2. 設置一閾值g,一般取值爲1/2(Max(n), 也即取值爲USAN最大值的一半,進行閾值化,得到角點響應。
  3. 使用非極大值抑制來尋找角點。
通過上面的方式得到的角點,存在很大僞角點。爲了去除僞角點,SUSAN算子可以由以下方法實現:
  1. 計算USAN區域的重心,然後計算重心和模板中心的距離,如果距離較小則不是正確的角點;
  2. 判斷USAN區域的重心和模板中心的連線所經過的像素都是否屬於USAN區域的像素,如果屬於那麼這個模板中心的點就是角點。
 綜上所述,我們基本知道SUSAN角點檢測算法。

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角點測測另一種應用。

檢測動態匹配輪廓的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;
}

測試輸出結果


源圖像

輸出結果

補充事項

SUSAN算子是一個原理簡單、易於瞭解的算子。由於其指數基於對周邊象素的 灰度比較,完全不涉及梯度的運算,因此其抗噪聲能力很強,運算量也比較小;同時,SUSAN算子還是一個各向同性的算子;最後,通過控制參數t和g,可以根據具體情況很容易地對不同對比度、不同形狀的圖像通過設置恰當的t和g進行控制。比如圖像的對比度較大,則可選取較大的t值,而圖像的對比度較小,則可選取較小的t值。當SUSAN進行角點檢測時,在某些情況會產生誤報:
  • 情況一在點B的處的USAN區域如右邊的白色區域所示,呈帶狀分佈在中間,並且是單像素的帶狀。此時計算出的角點相應量會比較大,會誤認爲是角點,而實際上是邊緣點。解決方法:計算出USAN區域的重心,如果重心與圓心的距離大於一定的閾值才認爲是角點。
  • 情況二:多個噪聲點或是複雜的結構。例如單點噪聲,其計算出來的角點量很大,會誤認爲是角點。解決方法:計算出USAN區域的重心,從中心指向USAN區域重心的直線上的所有像素都必須是USAN的一部分,否則認爲不是角點。
總之,SUSAN算子是一個非常難得的算子,不僅具有很好的邊緣檢測性能;而且對角點檢測也具有很好的效果

參考文獻

[1]SUSAN Low Level Image Processing.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章