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;
}
效果