上一篇分析了Harris基本算法,基础的Harris算法具有旋转不变性,但对尺度变化敏感。
Harris-Laplace算法结合Harris算法和Laplace尺度空间(见LOG算法),实现了尺度不变性。
Harris-Laplace算法的基本思路很简单:
(1)首先预设一组尺度(高斯函数的方差),在每个尺度下进行Harris检测
(2)对当前图像,生成一组不同尺度下的图像集
(3)对(1)中找到的每个角点,在(2)中相同位置下比较其26个邻域的LOG相应值,如果也为局部极大值,则认为该点为最终角点,通过对应的尺度也可以得到该角点的空间尺度(“半径”)
(1)Harris-Laplace算法在进行特定尺度下Harris检测时不再使用近似的梯度算子([-1 0 1]),而是使用当前尺度(方差sigma)乘以0.7(经验值)作为梯度算子高斯核的方差,相应的,该算子半径为 3*0.7*sigma
(2)生成Laplace尺度空间时,使用的是当前尺度sigma,高斯核半径为3*sigma
#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; }