SUSAN角点检测

SUSAN(Smallest Univalue Segment Assimilating Nucleus 最小核值相似区)算子不再沿用Harris算法的微分思想,通过统计圆形模板内符合要求的像素数目(本质是一个积分过程)检测角点,计算简便,抗噪声能力强,具有旋转和平移不变性

1.理论

SUSAN使用一个圆形的模板(图1-1),在这个模板内,比较每个点与中心点的相似度(为了使相似度的边界更平滑,一般用公式来近似,显然 t 越大得到的角点越少)。


图1-1. SUSAN 半径为3的圆形模板

统计模板内相似像素的个数。从图1-2很容易看到,当模板接近、进入、移出角点时,该统计值(也称USAN,核值相似区)会经历一个由大到小,再由小到大的过程,在角点处取最小值将这个统计值与给定阈值(一般取n(r)的理论最大值(半径为3时为37-1)的一半,如果检测边缘则取其3/4)比较,若小于阈值,则处理后作为角点响应值(),最后做一个最大值抑制就可以了。


图1-2. USAN在a,d位置取最大值,在角点位置e取最小值


2.代码

typedef struct myPoint
{
	int x;
	int y;
}_point;

typedef struct mySize
{
	int height;
	int width;
}_size;

void _cornerSUSAN(unsigned char* srcImg, _size srcSize, _point* corners, int* numCorner, float thresholdT);
void getSUSANkernel(bool* kernel, int radius);
void convolutionSUSAN(unsigned char* srcImg, float* R, _size srcSize, bool* kernel, int kernelSize, float thresholdT);
void depressR(float* srcR, _point* dstR, int* numDst, _size imgSize, _size depressWin);

void main()
{
	cv::Mat img = cv::imread("../file/einstein.jpg", 0);
	unsigned char* imgBuffer = new unsigned char[img.rows*img.cols];
	for(int i=0; i<img.rows; i++)
	{
		uchar* ptr = img.ptr<uchar>(i);
		for(int j=0; j<img.cols; j++)
			imgBuffer[i*img.cols+j] = ptr[j];
	}

	_size srcSize;
	srcSize.height = img.rows;
	srcSize.width = img.cols;

	_point* corners = new _point[srcSize.height*srcSize.width];
	int numCorners = 0;
	_cornerSUSAN(imgBuffer, srcSize, corners, &numCorners, 40);

	cv::Mat colorImg;
	cv::cvtColor(img, colorImg, CV_GRAY2BGR);
	for(int i=0; i<numCorners; i++)
	{
		cv::Point pt(corners[i].x, corners[i].y);
		cv::circle(colorImg, pt, 1, cv::Scalar(0,0,255));
	}

	cv::namedWindow("show");
	cv::imshow("show", colorImg);
	cv::waitKey(0);
}

void _cornerSUSAN(unsigned char* srcImg, _size srcSize, _point* corners, int* numCorner, float thresholdT)
{
	int kernelSize = 3;
	bool* kernel = new bool[(2*kernelSize+1)*(2*kernelSize+1)];
	getSUSANkernel(kernel, kernelSize);

	float* R = new float[srcSize.height*srcSize.width];
	convolutionSUSAN(srcImg, R, srcSize, kernel, kernelSize, thresholdT);

	delete[] kernel;

	_size depressSize;
	depressSize.height = 3; depressSize.width = 3;
	depressR(R, corners, numCorner, srcSize, depressSize);

	delete[] R;
}

void getSUSANkernel(bool* kernel, int radius)
{
	for(int i=0; i<2*radius+1; i++)
	{
		for(int j=0; j<2*radius+1; j++)
		{
			float realRadius = sqrtf((i-radius)*(i-radius)+(j-radius)*(j-radius));
			int intRadius = int(realRadius+0.5);
			if(intRadius<=radius)
				kernel[i*(2*radius+1)+j] = true;
			else
				kernel[i*(2*radius+1)+j] = false;
		}
	}
}

void convolutionSUSAN(unsigned char* srcImg, float* R, _size srcSize, bool* kernel, int kernelSize, float thresholdT)
{
	for(int i=0; i<srcSize.height; i++)
	{
		for(int j=0; j<srcSize.width; j++)
		{
			float sumValue = 0;
			int count = 0;
			for(int m=-kernelSize; m<=kernelSize; m++)
			{
				for(int n=-kernelSize; n<=kernelSize; n++)
				{
					int indexX = j+n;
					int indexY = i+m;
					if(indexX>=0 && indexX<srcSize.width && indexY>=0 && indexY<srcSize.height)
					{
						if(kernel[(m+kernelSize)*(2*kernelSize+1)+(n+kernelSize)])
						{
							int mValue = int(srcImg[i*srcSize.width+j]) - int(srcImg[indexY*srcSize.width+indexX]);
							float fValue = -1*pow(mValue/thresholdT, 6);
							float eValue = exp(fValue);
							sumValue += eValue;
							count++;
						}
					}
				}
			}
			R[i*srcSize.width+j] = sumValue<(0.5*(count-1))?(0.5*(count-1)-sumValue):0;
		}
	}
}

void depressR(float* srcR, _point* dstR, int* numDst, _size imgSize, _size depressWin)  
{  
	*numDst = 0;  
	for(int i=0; i<imgSize.height; i++)  
	{  
		for(int j=0; j<imgSize.width; j++)  
		{  
			float flagValue = srcR[i*imgSize.width+j];  
			int numPoint = 0, numPass = 0;  
			for(int m=i-depressWin.height; m<=i+depressWin.height; m++)  
			{  
				for(int n=j-depressWin.width; n<=j+depressWin.width; n++)  
				{  
					if(m>=0 && m<imgSize.height && n>=0 && n<imgSize.width)  
					{  
						float compareValue = srcR[m*imgSize.width+n];  
						if(flagValue > compareValue)  
							numPass ++;  
						numPoint ++;  
					}  
				}  
			}  
			if(numPoint == numPass+1)  
			{  
				_point corner;  
				corner.x = j;   
				corner.y = i;  
				dstR[(*numDst)++] = corner;  
			}  
		}  
	}  
}  


效果:



  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值