/******************************************************************************* Detect Harris corners. image: input image sigma: standard deviation of Gaussian used to blur corner detector thres: Threshold for detecting corners interestPts: returned interest points numInterestPts: number of interest points returned imageDisplay: image returned to display (for debugging) *******************************************************************************/ void MainWindow::HarrisCornerDetector(QImage image, double sigma, double thres, CIntPt **interestPts, int &numInterestPts, QImage &imageDisplay) { int r, c; double harrisScaleFactor = 19.0; int imageWidth = image.width(); int imageHeight = image.height(); if((!imageWidth)||(!imageHeight)) { return; } double *buffer = new double [imageWidth * imageHeight]; QRgb pixel; numInterestPts = 0; // We'll compute the corner response using just the green channel for(r = 0; r < imageHeight; r++) { for(c = 0; c < imageWidth; c++) { pixel = image.pixel(c, r); buffer[r * imageWidth + c] = (double) qGreen(pixel); } } // Define a small window for examining the image. This window will be used // to compute the covariance matrix and Harris response at each point. // Since we'll be computing a Gaussian sum of the values in the window, // let's size it so the radius is sigma * 3 (99.7% of values are accounted for). // Let's expand the buffer so that we can calculate the derivatives // for the original buffer data, centered in the expanded buffer. int expandedBufferWidth = imageWidth + 2; int expandedBufferHeight = imageHeight + 2; double *expandedBuffer = new double [expandedBufferWidth * expandedBufferHeight]; zeroBorders(expandedBuffer, 1, expandedBufferWidth, expandedBufferHeight); // Now, fill in our expanded buffer with the contents of the first buffer (centered). for(r = 1; r < imageHeight + 1; r++) { for(c = 1; c < imageWidth + 1; c++) { expandedBuffer[r * (expandedBufferWidth) + c] = buffer[(r - 1) * imageWidth + (c - 1)]; } } // 1st-derivative kernel to convolve with the image double *firstDKernel = new double [3]; firstDKernel[0] = -1.0; firstDKernel[1] = 0.0; firstDKernel[2] = 1.0; // Set up image copies for the x and y derivatives, as well as the arrays // for which we'll use them double * x_deriv = new double [expandedBufferWidth * expandedBufferHeight]; double * y_deriv = new double [expandedBufferWidth * expandedBufferHeight]; double * y_deriv_squared = new double [imageWidth * imageHeight]; double * x_deriv_squared = new double [imageWidth * imageHeight]; double * xy_deriv = new double [imageWidth * imageHeight]; // Copy expanded buffer into the x- and y-derivative buffers for(r = 0; r < expandedBufferHeight; r++) { for(c = 0; c < expandedBufferWidth; c++) { x_deriv[r * expandedBufferWidth + c] = expandedBuffer[r * expandedBufferWidth + c]; y_deriv[r * expandedBufferWidth + c] = expandedBuffer[r * expandedBufferWidth + c]; } } // Calculate the x-derivative of the buffer image convolve(x_deriv, expandedBufferWidth, expandedBufferHeight, firstDKernel, 3, 1, 1, 1, false, false); // Calculate the y-derivative of the buffer image convolve(y_deriv, expandedBufferWidth, expandedBufferHeight, firstDKernel, 1, 3, 1, 1, false, false); // Calculate the x-derivative squared of the buffer image for(r = 0; r < imageHeight; r++) { for(c = 0; c < imageWidth; c++) { x_deriv_squared[r * imageWidth + c] = x_deriv[(r + 1) * expandedBufferWidth + (c + 1)] * x_deriv[(r + 1) * expandedBufferWidth + (c + 1)]; } } // Now blur it SeparableGaussianBlurImage(x_deriv_squared, imageWidth, imageHeight, sigma); // Calculate the y-derivative squared of the buffer image for(r = 0; r < imageHeight; r++) { for(c = 0; c < imageWidth; c++) { y_deriv_squared[r * imageWidth + c] = y_deriv[(r + 1) * expandedBufferWidth + (c + 1)] * y_deriv[(r + 1) * expandedBufferWidth + (c + 1)]; } } // Now blur it SeparableGaussianBlurImage(y_deriv_squared, imageWidth, imageHeight, sigma); // Calculate the x-derivative * y-derivative of the buffer image for(r = 0; r < imageHeight; r++) { for(c = 0; c < imageWidth; c++) { xy_deriv[r * imageWidth + c] = x_deriv[(r + 1) * expandedBufferWidth + (c + 1)] * y_deriv[(r + 1) * expandedBufferWidth + (c + 1)]; } } // Now blur it SeparableGaussianBlurImage(xy_deriv, imageWidth, imageHeight, sigma); // Set up a Harris response buffer to hold the Harris response for each pixel double *harrisResponseBuffer = new double [imageWidth * imageHeight]; double dx_squared, dy_squared, dxdy, determinant, trace; // For each pixel in the image for(r = 0; r < imageHeight; r++) { for(c = 0; c < imageWidth; c++) { // Get our dx^2 value dx_squared = x_deriv_squared[r * imageWidth + c]; // Get our dy^2 value dy_squared = y_deriv_squared[r * imageWidth + c]; // Get our dxdy value dxdy = xy_deriv[r * imageWidth + c]; // Assuming a matrix like this: // // dx^2 dxdy // [ ] // dxdy dy^2 // // Find the determinant of the matrix determinant = dx_squared * dy_squared - dxdy * dxdy; // Find the trace of the matrix trace = dx_squared + dy_squared; // Finally, plug the determinant/trace for this point into the Harris response buffer if(trace == 0) { harrisResponseBuffer[r * imageWidth + c] = 0.0; } else { harrisResponseBuffer[r * imageWidth + c] = abs(determinant / trace) / harrisScaleFactor; } } } double min = 0.0; double max = 0.0; // Now that we've populated the Harris response buffer, let's cull out responses below the threshold for(r = 0; r < imageHeight; r++) { for(c = 0; c < imageWidth; c++) { if((harrisResponseBuffer[r * imageWidth + c] != 0)&&(harrisResponseBuffer[r * imageWidth + c] < min)) { min = harrisResponseBuffer[r * imageWidth + c]; } if(harrisResponseBuffer[r * imageWidth + c] > max) { max = harrisResponseBuffer[r * imageWidth + c]; } if(harrisResponseBuffer[r * imageWidth + c] < thres) { harrisResponseBuffer[r * imageWidth + c] = 0.0; } } } // Cull out any points in the Harris response that aren't local peaks in their 3x3 neighborhood localMaxima(harrisResponseBuffer, imageWidth, imageHeight, 5); // Count interest points in Harris response numInterestPts = 0; for(r = 0; r < imageHeight; r++) { for(c = 0; c < imageWidth; c++) { if(harrisResponseBuffer[r * imageWidth + c] > 0.0) { numInterestPts++; } } } // Allocate an array in which store our interest points *interestPts = new CIntPt[numInterestPts]; // Store our interest points int i = 0; for(r = 0; r < imageHeight; r++) { for(c = 0; c < imageWidth; c++) { if(harrisResponseBuffer[r * imageWidth + c] > 0.0) { (*interestPts)[i].m_X = c; (*interestPts)[i].m_Y = r; i++; } } } // Once you are done finding the interest points, display them on the image DrawInterestPoints(*interestPts, numInterestPts, imageDisplay); delete [] buffer; delete [] firstDKernel; delete [] harrisResponseBuffer; delete [] x_deriv; delete [] y_deriv; delete [] x_deriv_squared; delete [] y_deriv_squared; delete [] xy_deriv; delete [] expandedBuffer; }
/******************************************************************************* Detect Harris corners. image - input image sigma - standard deviation of Gaussian used to blur corner detector thres - Threshold for detecting corners interestPts - returned interest points numInterestsPts - number of interest points returned imageDisplay - image returned to display (for debugging) *******************************************************************************/ void MainWindow::HarrisCornerDetector(QImage image, double sigma, double thres, CIntPt **interestPts, int &numInterestsPts, QImage &imageDisplay) { int r, c; int w = image.width(); int h = image.height(); double *buffer = new double [w*h]; QRgb pixel; numInterestsPts = 0; // Set up matrix double *ix = new double[w * h]; double *iy = new double[w * h]; double *ixy = new double[w * h]; double *harris = new double[w * h]; // Compute the corner response using green band only // Initialize ixix, iyiy, ixiy for(r = 0; r < h; r++) { for(c = 0; c < w; c++) { pixel = image.pixel(c, r); buffer[r * w + c] = (double) qGreen(pixel); ix[r * w + c] = 0.0; iy[r * w + c] = 0.0; ixy[r * w + c] = 0.0; } } // Compute the ixix, iyiy, ixiy for(r = 1; r < h - 1; r++) { for (c = 1; c < w - 1; c++) { double left = buffer[r * w + c - 1]; double right = buffer[r * w + c + 1]; double bottom = buffer[(r + 1) * w + c]; double top = buffer[(r - 1) * w + c]; ix[r * w + c] = pow(right - left, 2); iy[r * w + c] = pow(bottom - top, 2); ixy[r * w + c] = (right - left) * (bottom - top); } } // Smooth SeparableGaussianBlurImage(ix, w, h, sigma); SeparableGaussianBlurImage(iy, w, h, sigma); SeparableGaussianBlurImage(ixy, w, h, sigma); // Compute Harris = det(H) / trace(H) for(r = 1; r < h - 1; r++) { for(c = 1; c < w - 1; c++) { double xx = ix[r * w + c]; double yy = iy[r * w + c]; double xy = ixy[r * w + c]; double det = xx * yy - xy * xy; double trace = xx + yy; double val = 0.0; if(trace > 0.0) val = det / trace; harris[r * w + c] = val; // Scale for displaying val = max(50.0, min(255.0, val)); imageDisplay.setPixel(c, r, qRgb(val, val, val)); } } // Compute interest points and display for(r = 1; r < h - 1; r++) { for(c = 1; c < w - 1; c++) { int x, y; bool max = true; // The corner response > threshold // Might be interesting to look at if(harris[r * w + c] > thres) { for(x = -1; x <= 1; x++) { for(y = -1; y <= 1; y++) { // Not local miximum of surrounding pixels if(harris[r * w + c] < harris[(r + x) * w + c + y]) { max = false; } } } // Local maximum, corner if(max) { numInterestsPts++; imageDisplay.setPixel(c, r, qRgb((int)255, (int)0, (int)0)); } } } } // Store interest points // The descriptor of the interest point is stored in m_Desc // The length of the descriptor is m_DescSize, if m_DescSize = 0, then it is not valid. *interestPts = new CIntPt [numInterestsPts]; int i = 0; for(r = 1; r < h - 1; r++) { for(c = 1; c < w - 1; c++) { pixel = imageDisplay.pixel(c, r); // Interest point value if(qRed(pixel) == 255 && qGreen(pixel) == 0 && qBlue(pixel) == 0) { (*interestPts)[i].m_X = (double) c; (*interestPts)[i].m_Y = (double) r; i++; } } } // Once you are done finding the interest points, display them on the image DrawInterestPoints(*interestPts, numInterestsPts, imageDisplay); // Clean up delete [] buffer; delete [] ix; delete [] ixy; delete [] iy; delete [] harris; }