/******************************************************************************* Gaussian blur the match score. matchCost - The match cost between pixels w, h - The width and height of the image numDisparities - The number of disparity levels sigma - The standard deviation of the blur kernel I would recommend using SeparableGaussianBlurImage as a helper function. *******************************************************************************/ void MainWindow::GaussianBlurMatchScore(double *matchCost, int w, int h, int numDisparities, double sigma) { int d; for(d=0;d<numDisparities;d++) SeparableGaussianBlurImage(&(matchCost[d*w*h]), w, h, sigma); }
/******************************************************************************* Compute simple 8d descriptors for each interest point (corner). image: input image interestPts: array of interest points numInterestPts: number of interest points If the descriptor cannot be computed, i.e. it's too close to the boundary of the image, its descriptor length will be set to 0. *******************************************************************************/ void MainWindow::ComputeDescriptors(QImage image, CIntPt *interestPts, int numInterestPts) { if(!numInterestPts) { return; } int r, c, cd, rd, i; int imageWidth = image.width(); int imageHeight = image.height(); double *buffer = new double [imageWidth * imageHeight]; QRgb pixel; // Descriptor parameters double sigma = 2.0; int rad = 4; // Compute descriptors from 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); } // Blur SeparableGaussianBlurImage(buffer, imageWidth, imageHeight, sigma); // Compute the descriptor from the difference between the point sampled at its center // and eight points sampled around it. for(i=0;i<numInterestPts;i++) { int c = (int) interestPts[i].m_X; int r = (int) interestPts[i].m_Y; // If any of the sampled points falls outside of the image, reject it if(c >= rad && c < imageWidth - rad && r >= rad && r < imageHeight - rad) { double centerValue = buffer[(r)*imageWidth + c]; int j = 0; for(rd=-1;rd<=1;rd++) for(cd=-1;cd<=1;cd++) if(rd != 0 || cd != 0) { interestPts[i].m_Desc[j] = buffer[(r + rd*rad)*imageWidth + c + cd*rad] - centerValue; j++; } interestPts[i].m_DescSize = DESC_SIZE; } else { interestPts[i].m_DescSize = 0; } } delete [] buffer; }
void MainWindow::GaussianBlurImage() { double sigma = ui->blurSpinBox->value(); if(ui->separableCheckBox->isChecked()) SeparableGaussianBlurImage(&outImage, sigma); else GaussianBlurImage(&outImage, sigma); DrawDisplayImage(); }
/******************************************************************************* Compute interest point descriptors image - input image interestPts - array of interest points numInterestsPts - number of interest points If the descriptor cannot be computed, i.e. it's too close to the boundary of the image, its descriptor length will be set to 0. I've implemented a very simple 8 dimensional descriptor. Feel free to improve upon this. *******************************************************************************/ void MainWindow::ComputeDescriptors(QImage image, CIntPt *interestPts, int numInterestsPts) { int r, c, cd, rd, i, j; int w = image.width(); int h = image.height(); double *buffer = new double [w*h]; QRgb pixel; // Descriptor parameters double sigma = 2.0; int rad = 4; // Computer descriptors from green channel for(r=0;r<h;r++) for(c=0;c<w;c++) { pixel = image.pixel(c, r); buffer[r*w + c] = (double) qGreen(pixel); } // Blur SeparableGaussianBlurImage(buffer, w, h, sigma); // Compute the desciptor from the difference between the point sampled at its center // and eight points sampled around it. for(i=0;i<numInterestsPts;i++) { int c = (int) interestPts[i].m_X; int r = (int) interestPts[i].m_Y; if(c >= rad && c < w - rad && r >= rad && r < h - rad) { double centerValue = buffer[(r)*w + c]; int j = 0; for(rd=-1;rd<=1;rd++) for(cd=-1;cd<=1;cd++) if(rd != 0 || cd != 0) { interestPts[i].m_Desc[j] = buffer[(r + rd*rad)*w + c + cd*rad] - centerValue; j++; } interestPts[i].m_DescSize = DESC_SIZE; } else { interestPts[i].m_DescSize = 0; } } delete [] buffer; }
/******************************************************************************* 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; }