/******************************************************************************* Find matching interest points between images. image1 - first input image interestPts1 - interest points corresponding to image 1 numInterestsPts1 - number of interest points in image 1 image2 - second input image interestPts2 - interest points corresponding to image 2 numInterestsPts2 - number of interest points in image 2 matches - set of matching points to be returned numMatches - number of matching points returned image1Display - image used to display matches image2Display - image used to display matches *******************************************************************************/ void MainWindow::MatchInterestPoints(QImage image1, CIntPt *interestPts1, int numInterestsPts1, QImage image2, CIntPt *interestPts2, int numInterestsPts2, CMatches **matches, int &numMatches, QImage &image1Display, QImage &image2Display) { // Compute the descriptors for each interest point. // You can access the descriptor for each interest point using interestPts1[i].m_Desc[j]. // If interestPts1[i].m_DescSize = 0, it was not able to compute a descriptor for that point ComputeDescriptors(image1, interestPts1, numInterestsPts1); ComputeDescriptors(image2, interestPts2, numInterestsPts2); *matches = new CMatches[numInterestsPts1]; int x, y, z; numMatches = 0; // Compute best match from image 2 for each interest point in image 1 // Best matching point has the smallest norm distance for(x = 0; x < numInterestsPts1; x++) { // Check if descriptor valid if(interestPts1[x].m_DescSize > 0) { CIntPt pt1 = interestPts1[x]; int min = 0; double mindist = INFINITY; for(y = 0; y < numInterestsPts2; y++) { // Check if descriptor valid if(interestPts2[y].m_DescSize > 0) { CIntPt pt2 = interestPts2[y]; double dist = 0.0; for(z = 0; z < DESC_SIZE; z++) { dist += pow(pt1.m_Desc[z] - pt2.m_Desc[z], 2); } if(dist < mindist) { mindist = dist; min = y; } } } // Store matching points (*matches)[numMatches].m_X1 = interestPts1[x].m_X; (*matches)[numMatches].m_Y1 = interestPts1[x].m_Y; (*matches)[numMatches].m_X2 = interestPts2[min].m_X; (*matches)[numMatches].m_Y2 = interestPts2[min].m_Y; numMatches++; } } // The position of the interest point in iamge 1 is (m_X1, m_Y1) // Draw the matches DrawMatches(*matches, numMatches, image1Display, image2Display); }
void DrawMatches( const std::string& title, const cv::Mat image1, const cv::Mat image2, const cv::Mat points1, const cv::Mat points2, const cv::Scalar& color, bool draw_image_borders) { cv::Mat image_out; DrawMatches(image_out, image1, image2, points1, points2, color, draw_image_borders); opencv_util::ShowScaled(title, image_out); }
void CubicleWrapper::DrawOverlay(IplImage* iplImage, int overlay_level) const { if (overlay_level>=1) { DrawMatches(iplImage, overlay_level); } }
/******************************************************************************* Find matching interest points between images. image1: first input image interestPts1: interest points corresponding to image 1 numInterestPts1: number of interest points in image 1 image2: second input image interestPts2: interest points corresponding to image 2 numInterestPts2: number of interest points in image 2 matches: set of matching points to be returned numMatches: number of matching points returned image1Display: image used to display matches image2Display: image used to display matches *******************************************************************************/ void MainWindow::MatchInterestPoints(QImage image1, CIntPt *interestPts1, int numInterestPts1, QImage image2, CIntPt *interestPts2, int numInterestPts2, CMatches **matches, int &numMatches, QImage &image1Display, QImage &image2Display) { numMatches = numInterestPts1; // Compute the descriptors for each interest point. // You can access the descriptor for each interest point using interestPts1[i].m_Desc[j]. // If interestPts1[i].m_DescSize = 0, it was not able to compute a descriptor for that point ComputeDescriptors(image1, interestPts1, numInterestPts1); ComputeDescriptors(image2, interestPts2, numInterestPts2); // The position of the interest point in image 1 is (m_X1, m_Y1) // The position of the interest point in image 2 is (m_X2, m_Y2) *matches = new CMatches[numMatches]; double minL2distance; int matchNo = 0; int attemptNo = 0; int rejectNo = 0; for(int image1pt = 0; image1pt < numInterestPts1; image1pt++) { // If the current point doesn't have a descriptor, skip it if(interestPts1[image1pt].m_DescSize == 0) { rejectNo++; continue; } for(int image2pt = 0; image2pt < numInterestPts2; image2pt++) { // If the current point doesn't have a descriptor, skip it if(interestPts2[image2pt].m_DescSize == 0) { continue; } // If it's the first match attempted, assume it's right and get the distance if(attemptNo == 0) { (*matches)[matchNo].m_X1 = interestPts1[image1pt].m_X; (*matches)[matchNo].m_Y1 = interestPts1[image1pt].m_Y; (*matches)[matchNo].m_X2 = interestPts2[image2pt].m_X; (*matches)[matchNo].m_Y2 = interestPts2[image2pt].m_Y; minL2distance = getDistance(interestPts1[image1pt], interestPts2[image2pt]); attemptNo++; } else { // If the distance is shorter, this is a better match if(getDistance(interestPts1[image1pt], interestPts2[image2pt]) < minL2distance) { (*matches)[matchNo].m_X1 = interestPts1[image1pt].m_X; (*matches)[matchNo].m_Y1 = interestPts1[image1pt].m_Y; (*matches)[matchNo].m_X2 = interestPts2[image2pt].m_X; (*matches)[matchNo].m_Y2 = interestPts2[image2pt].m_Y; minL2distance = getDistance(interestPts1[image1pt], interestPts2[image2pt]); attemptNo++; } } } attemptNo = 0; matchNo++; } numMatches = numMatches - rejectNo; // Draw the matches DrawMatches(*matches, numMatches, image1Display, image2Display); //delete [] (*matches); }
/******************************************************************************* Compute homography transformation between images using RANSAC. matches: set of matching points between images numMatches: number of matching points numIterations: number of iterations to run RANSAC inlierThreshold: maximum distance between points that are considered to be inliers hom: returned homography transformation (image1 -> image2) homInv: returned inverse homography transformation (image2 -> image1) image1Display: first image used to display matches image2Display: second image used to display matches *******************************************************************************/ void MainWindow::RANSAC(CMatches *matches, int numMatches, int numIterations, double inlierThreshold, double hom[3][3], double homInv[3][3], QImage &image1Display, QImage &image2Display) { // We'll be comparing groups of 4 points #define MATCH_GROUP_SIZE 4 // If there are fewer than matchGroupSize matches, this won't work, so return. if(numMatches < MATCH_GROUP_SIZE) { return; } CMatches potentialInliers[MATCH_GROUP_SIZE]; int numInliers, maxInliers, randomMatchID; int usedMatchIDs[MATCH_GROUP_SIZE]; double potentialInlierHom[3][3]; double bestHom[3][3]; for(int i = 0; i < MATCH_GROUP_SIZE; i++) { usedMatchIDs[i] = -1; } // Initialize random seed srand( time(NULL) ); maxInliers = -1; for (int iter = 0; iter < numIterations; iter++) { // Randomly select 4 pairs of potentially matching points from matches for(int i = 0; i < MATCH_GROUP_SIZE; i++) { // Make sure that the match selected is unique bool matchUnique = false; while(!matchUnique) { matchUnique = true; randomMatchID = rand() % numMatches; // Compare generated match to every other match, // and make sure we're not reusing one for(int j = 0; j < i; j++) { // Check for a match with a previously used match if(randomMatchID == usedMatchIDs[j]) { matchUnique = false; continue; } } } // Once we've reached this point, we know that we have a unique match, // so let's add the randomly generated match to the array of potential inliers potentialInliers[i] = matches[randomMatchID]; // Make sure we don't use the same match again usedMatchIDs[i] = randomMatchID; } // Now that we have four unique, randomly selected matches, // compute the homography relating the four selected matches ComputeHomography(potentialInliers, MATCH_GROUP_SIZE, potentialInlierHom, true); // Using the computed homography, compute the number of inliers against all of the matches numInliers = ComputeInlierCount(potentialInlierHom, matches, numMatches, inlierThreshold); // If this homography produces the highest number of inliers, store it as the best homography if(numInliers > maxInliers) { maxInliers = numInliers; for(int i = 0; i < 3; i++) { for(int j = 0; j < 3; j++) { bestHom[i][j] = potentialInlierHom[i][j]; } } } } CMatches *inliers = new CMatches[maxInliers]; // Given the highest scoring homography, once again find all the inliers GetInliers(bestHom, matches, numMatches, inlierThreshold, inliers); numInliers = ComputeInlierCount(bestHom, matches, numMatches, inlierThreshold); // Compute a new refined homography using all of the inliers ComputeHomography(inliers, maxInliers, hom, true); // Compute an inverse homography as well ComputeHomography(inliers, maxInliers, homInv, false); // Display the inlier matches DrawMatches(inliers, numInliers, image1Display, image2Display); delete inliers; }
/******************************************************************************* Compute homography transformation between images using RANSAC. matches - set of matching points between images numMatches - number of matching points numIterations - number of iterations to run RANSAC inlierThreshold - maximum distance between points that are considered to be inliers hom - returned homography transformation (image1 -> image2) homInv - returned inverse homography transformation (image2 -> image1) image1Display - image used to display matches image2Display - image used to display matches *******************************************************************************/ void MainWindow::RANSAC(CMatches *matches, int numMatches, int numIterations, double inlierThreshold, double hom[3][3], double homInv[3][3], QImage &image1Display, QImage &image2Display) { int i, j; CMatches randomMatches[4]; double h[3][3]; int max = 0; for(i = 0; i < numIterations; i++) { // Randomly select 4 matching pairs for(j = 0; j < 4; j++) { int num = rand() % numMatches; randomMatches[j].m_X1 = matches[num].m_X1; randomMatches[j].m_X2 = matches[num].m_X2; randomMatches[j].m_Y1 = matches[num].m_Y1; randomMatches[j].m_Y2 = matches[num].m_Y2; } // Compute Homography for the 4 random matches and inliers count if(ComputeHomography(randomMatches, 4, h, true)) { int count = ComputeInlierCount(h, matches, numMatches, inlierThreshold); // Check if the homography is the best if(count > max) { max = count; int x, y; for(x = 0; x < 3; x++) { for(y = 0; y < 3; y++) { hom[x][y] = h[x][y]; } } } } } CMatches *inliers = new CMatches[max]; int numInliers = 0; for(i = 0; i < numMatches; i++) { double x2, y2; Project(matches[i].m_X1, matches[i].m_Y1, x2, y2, hom); double distance = pow(x2 - matches[i].m_X2, 2.0) + pow(y2 - matches[i].m_Y2, 2.0); if(distance < inlierThreshold * inlierThreshold) { inliers[numInliers].m_X1 = matches[i].m_X1; inliers[numInliers].m_Y1 = matches[i].m_Y1; inliers[numInliers].m_X2 = matches[i].m_X2; inliers[numInliers].m_Y2 = matches[i].m_Y2; numInliers++; } } // Recompute the best homography and inverse homography ComputeHomography(inliers, numInliers, hom, true); ComputeHomography(inliers, numInliers, homInv, false); // After you're done computing the inliers, display the corresponding matches. DrawMatches(inliers, numInliers, image1Display, image2Display); // Clean up delete [] inliers; }