Harris角点检测(2)-- Harris-Laplace

1. 原理

上一篇分析了Harris基本算法,基础的Harris算法具有旋转不变性,但对尺度变化敏感

Harris-Laplace算法结合Harris算法和Laplace尺度空间(见LOG算法),实现了尺度不变性

Harris-Laplace算法的基本思路很简单:

(1)首先预设一组尺度(高斯函数的方差),在每个尺度下进行Harris检测

(2)对当前图像,生成一组不同尺度下的图像集

(3)对(1)中找到的每个角点,在(2)中相同位置下比较其26个邻域的LOG相应值,如果也为局部极大值,则认为该点为最终角点,通过对应的尺度也可以得到该角点的空间尺度(“半径”)


2. 细节

(1)Harris-Laplace算法在进行特定尺度下Harris检测时不再使用近似的梯度算子([-1 0 1]),而是使用当前尺度(方差sigma)乘以0.7(经验值)作为梯度算子高斯核的方差,相应的,该算子半径为 3*0.7*sigma

(2)生成Laplace尺度空间时,使用的是当前尺度sigma,高斯核半径为3*sigma


3. 代码

#define PI 3.14159

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

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

typedef struct myPointR
{
	int x;
	int y;
	int radius;
}_pointR;

template<typename T1, typename T2>
void convolution(T1* img, T2** out, _size imgSize, T2* kernel, _size kerSize);
template<typename T1>
void dotMultiply(T1* src1, T1* src2, T1** dst, _size srcSize);
template<typename T1>
void gaussian(int* src, float** out, _size imgSize, _size gaussSize, float sita);
void strengthR(float* A, float* B, float* C, float** R, float* maxR, _size imgSize, float alpha);
void depressR(float* srcR, _point* dstR, int* numDst, _size imgSize, _size depressWin, float threshold);
void getLOGkernel(int halfWin, float sita, float** kernel);
bool hasHarrisCorner(_point* pointsSet, int numPoints, _point pos);
void _cornerHarrisLaplace(unsigned char* src, _size srcSize, _pointR* corners, int* numCorner, float alpha);

void main()
{
	cv::Mat img = cv::imread("../file/butterfly.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;

	_pointR* corners = new _pointR[srcSize.height*srcSize.width];
	int numCorners = 0;
	_cornerHarrisLaplace(imgBuffer, srcSize, corners, &numCorners, 0.04);

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

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

template<typename T1, typename T2>
void convolution(T1* img, T2** out, _size imgSize, T2* kernel, _size kerSize)
{
	for(int i=0; i<imgSize.height; i++)
	{
		for(int j=0; j<imgSize.width; j++)
		{
			int count = 0;
			T2 sumValue = 0;
			for(int m=i-kerSize.height; m<=i+kerSize.height; m++)
			{
				for(int n=j-kerSize.width; n<=j+kerSize.width; n++)
				{
					if(m>=0 && m<imgSize.height && n>=0 && n<imgSize.width)
						sumValue += T2(img[m*imgSize.width+n])*kernel[count];
					count++;
				}
			}
			(*out)[i*imgSize.width+j] = sumValue;
		}
	}
}

template<typename T1>
void dotMultiply(T1* src1, T1* src2, T1** dst, _size srcSize)
{
	for(int i=0; i<srcSize.height; i++)
	{
		for(int j=0; j<srcSize.width; j++)
			(*dst)[i*srcSize.width+j] = src1[i*srcSize.width+j]*src2[i*srcSize.width+j];
	}
}

template<typename T1>
void gaussian(T1* src, float** out, _size imgSize, _size gaussSize, float sita)
{
	int sizeKernel = (2*gaussSize.height+1) * (2*gaussSize.width+1);
	float* gaussKernel = new float[sizeKernel];
	for(int i=-gaussSize.height; i<=gaussSize.height; i++)
	{
		for(int j=-gaussSize.width; j<=gaussSize.width; j++)
		{
			float tmp = -1*(i*i+j*j)/(2*sita*sita);
			gaussKernel[(i+gaussSize.height)*(2*gaussSize.width+1)+(j+gaussSize.width)] =
				exp(tmp)/(2*PI*sita*sita);
		}
	}

	convolution(src, out, imgSize, gaussKernel, gaussSize);
	delete[] gaussKernel;
}

void strengthR(float* A, float* B, float* C, float** R, float* maxR, _size imgSize, float alpha)
{
	*maxR = 0;
	for(int i=0; i<imgSize.height; i++)
	{
		for(int j=0; j<imgSize.width; j++)
		{
			float detM = A[i*imgSize.width+j]*B[i*imgSize.width+j] - C[i*imgSize.width+j]*C[i*imgSize.width+j];
			float traceM = A[i*imgSize.width+j] + B[i*imgSize.width+j];
			float result = detM - alpha*(traceM*traceM);
			if(result > *maxR)
				*maxR = result;
			(*R)[i*imgSize.width+j] = result;
		}
	}
}

void depressR(float* srcR, _point* dstR, int* numDst, _size imgSize, _size depressWin, float threshold)
{
	*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]<threshold?0: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]<threshold?0: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;
			}
		}
	}
}

void getLOGkernel(int halfWin, float sita, float** kernel)  
{  
	int winSize = 2*halfWin+1;  
	float tmp1, tmp2, sumValue = 0;  
	float powsita = sita*sita;  
	for(int i=-halfWin; i<=halfWin; i++)  
	{  
		for(int j=-halfWin; j<=halfWin; j++)  
		{  
			tmp1 = -1*(i*i+j*j)/(2*powsita);  
			tmp2 = exp(tmp1)*(i*i+j*j-2*powsita);//exp(tmp1)*(1+tmp1)/(-1*powsita*powsita);  
			sumValue += tmp2;  
			(*kernel)[(i+halfWin)*winSize+(j+halfWin)] = tmp2;  
		}  
	}  
	for(int i=0; i<winSize*winSize; i++)  
		(*kernel)[i] -= sumValue/(winSize*winSize);  
}  

bool hasHarrisCorner(_point* pointsSet, int numPoints, _point pos)
{
	for(int i=0; i<numPoints; i++)
	{
		if(pointsSet[i].x == pos.x && pointsSet[i].y == pos.y)
			return true;
	}
	return false;
}

void _cornerHarrisLaplace(unsigned char* src, _size srcSize, _pointR* corners, int* numCorner, float alpha)
{
	float sigma_begin = 1.5;
	float sigma_step = 1.2;
	int sigma_n = 13;
	float* sigma = new float[sigma_n];
	int* numcorner_n = new int[sigma_n];
	for(int i=0; i<sigma_n; i++)
		sigma[i] = sigma_begin*pow(sigma_step, i);

	_point** allPoints = new _point*[sigma_n];
	for(int i=0; i<sigma_n; i++)
		allPoints[i] = new _point[srcSize.height*srcSize.width];

	//Harris
	for(int i=0; i<sigma_n; i++)
	{
		float sigma_d = sigma[i]*0.7;
		_size sizeX, sizeY;
		sizeX.height = 0; sizeY.width = 0;
		sizeX.width = int(3*sigma_d+0.5);
		sizeY.height = sizeX.width;

		float* kernel = new float[2*sizeX.width+1];
		for(int j=0; j<2*sizeX.width+1; j++)
		{
			int x = j-sizeX.width;
			kernel[j] = exp(-1*x*x/(2*sigma_d*sigma_d))/(sqrt(2*PI)*sigma_d*sigma_d*sigma_d)*x;
		}

		float* gradientX = new float[srcSize.height*srcSize.width];
		float* gradientY = new float[srcSize.height*srcSize.width];
		convolution(src, &gradientX, srcSize, kernel, sizeX);
		convolution(src, &gradientY, srcSize, kernel, sizeY);

		delete[] kernel;

		float* gradientXX = new float[srcSize.height*srcSize.width];
		float* gradientYY = new float[srcSize.height*srcSize.width];
		float* gradientXY = new float[srcSize.height*srcSize.width];
		dotMultiply(gradientX, gradientX, &gradientXX, srcSize);
		dotMultiply(gradientY, gradientY, &gradientYY, srcSize);
		dotMultiply(gradientX, gradientY, &gradientXY, srcSize);

		delete[] gradientX;
		delete[] gradientY;

		_size gaussSize;
		gaussSize.height = sizeX.width;  gaussSize.width = sizeX.width;
		float sita = sigma[i];
		float* M_A = new float[srcSize.height*srcSize.width];
		float* M_B = new float[srcSize.height*srcSize.width];
		float* M_C = new float[srcSize.height*srcSize.width];
		gaussian(gradientXX, &M_A, srcSize, gaussSize, sita);
		gaussian(gradientYY, &M_B, srcSize, gaussSize, sita);
		gaussian(gradientXY, &M_C, srcSize, gaussSize, sita);

		delete[] gradientXX;
		delete[] gradientYY;
		delete[] gradientXY;

		float maxR = 0;
		float* R = new float[srcSize.height*srcSize.width];
		strengthR(M_A, M_B, M_C, &R, &maxR, srcSize, alpha);

		delete[] M_A;
		delete[] M_B;
		delete[] M_C;

		float threshold = 0.1;
		threshold *= maxR;
		_size depressSize;
		depressSize.height = 1; depressSize.width = 1;
		int num;
		depressR(R, allPoints[i], &num, srcSize, depressSize, threshold);
		numcorner_n[i] = num;

		delete[] R;
	}

	// Laplace
	float** laplaceSnlo = new float*[sigma_n];
	for(int i=0; i<sigma_n; i++)
		laplaceSnlo[i] = new float[srcSize.height*srcSize.width];

	for(int i=0; i<sigma_n; i++)
	{
		float sita = sigma[i];
		_size kernelSize;
		kernelSize.height = 3*sita;
		kernelSize.width = 3*sita;
		float *kernel = new float[(2*kernelSize.height+1)*(2*kernelSize.height+1)];
		getLOGkernel(kernelSize.height, sita, &kernel);
		convolution(src, &(laplaceSnlo[i]), srcSize, kernel, kernelSize);

		delete[] kernel;
	}

	*numCorner = 0;
	for(int i=0; i<srcSize.height; i++)
	{
		for(int j=0; j<srcSize.width; j++)
		{
			for(int k=0; k<sigma_n; k++)
			{
				_point pos;
				pos.x = j; pos.y = i;
				bool isFind = hasHarrisCorner(allPoints[k], numcorner_n[k], pos);
				if(isFind)
				{
					if(k==0)
					{
						if(laplaceSnlo[k][i*srcSize.width+j]>laplaceSnlo[k+1][i*srcSize.width+j])
						{
							corners[*numCorner].x = j;
							corners[*numCorner].y = i;
							corners[*numCorner].radius = int(PI*sigma[k]+0.5);
							(*numCorner)++;

							break;
						}
					}
					else if(k==sigma_n-1)
					{
						if(laplaceSnlo[k][i*srcSize.width+j]>laplaceSnlo[k-1][i*srcSize.width+j])
						{
							corners[*numCorner].x = j;
							corners[*numCorner].y = i;
							corners[*numCorner].radius = int(PI*sigma[k]+0.5);
							(*numCorner)++;

							break;
						}
					}
					else
					{
						if(laplaceSnlo[k][i*srcSize.width+j]>laplaceSnlo[k-1][i*srcSize.width+j] &&
							laplaceSnlo[k][i*srcSize.width+j]>laplaceSnlo[k+1][i*srcSize.width+j])
						{
							corners[*numCorner].x = j;
							corners[*numCorner].y = i;
							corners[*numCorner].radius = int(PI*sigma[k]+0.5);
							(*numCorner)++;

							break;
						}
					}
				}
			}
		}
	}
	for(int i=0; i<sigma_n; i++)
	{
		delete[] laplaceSnlo[i];
		delete[] allPoints[i];
	}
	delete[] laplaceSnlo;
	delete[] allPoints;
	delete[] sigma;
	delete[] numcorner_n;
}

效果


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值