我正在尝试实现一个非常简单的程序来查找两个图像之间的相似性。
我正在为此任务使用ORB特征检测器和图像描述符,并且正在使用 knnMatch 识别匹配 项 :
FeatureDetector detector = FeatureDetector.create(FeatureDetector.ORB); DescriptorExtractor descriptor = DescriptorExtractor.create(DescriptorExtractor.ORB); DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMING); // DETECTION // first image Mat img1 = Imgcodecs.imread(path1, Imgcodecs.CV_LOAD_IMAGE_GRAYSCALE); Mat descriptors1 = new Mat(); MatOfKeyPoint keypoints1 = new MatOfKeyPoint(); detector.detect(img1, keypoints1); descriptor.compute(img1, keypoints1, descriptors1); // second image Mat img2 = Imgcodecs.imread(path2, Imgcodecs.CV_LOAD_IMAGE_GRAYSCALE); Mat descriptors2 = new Mat(); MatOfKeyPoint keypoints2 = new MatOfKeyPoint(); detector.detect(img2, keypoints2); descriptor.compute(img2, keypoints2, descriptors2); // MATCHING // match these two keypoints sets List<MatOfDMatch> matches = new ArrayList<MatOfDMatch>(); matcher.knnMatch(descriptors1, descriptors2, matches, 5);
我能够按照以下方式绘制比赛:
// DRAWING OUTPUT Mat outputImg = new Mat(); // this will draw all matches, works fine Features2d.drawMatches2(img1, keypoints1, img2, keypoints2, matches, outputImg); // save image Imgcodecs.imwrite("result.jpg", outputImg);
问题是比赛太多,而且还包括相距遥远的比赛。我似乎找不到如何仅提取好的匹配项(超过某个阈值)?有人可以指出我正确的方向还是将我重定向到一些基本的工作示例?我已经花了几个小时,似乎迷路了。
如其他答案所述,有几种方法可以消除异常值和不良匹配项。我猜您找到了示例和教程,match而不是knnMatch利用其中的一些方法。
match
knnMatch
因此,您可能知道不同之处在于knnMatch,descriptor2针对中的每个描述符返回n个最佳匹配descriptor1。这意味着,您将获得匹配列表的列表,而不是匹配列表。我想这就是您遇到问题的原因。
descriptor2
descriptor1
使用的主要优点knnMatch是可以执行比率测试。因此,如果从一个描述符输入descriptor1到两个最佳描述符的descriptor2距离相似,则表明图像中存在重复的图案(例如,草丛前的栅栏尖端)。因此,此类匹配不可靠,应将其删除。(我不确定为什么要搜索五个最佳匹配- knnMatch每个描述符将5传递给-。而是搜索两个。)
如果现在只想访问每个描述符的最佳匹配,则只需要访问“子列表”的第一个元素。在下面的示例中,您将找到使用RANSAC进行比率测试和单应性估计的示例(我在替换了所有内容knnMatch):
// ratio test LinkedList<DMatch> good_matches = new LinkedList<DMatch>(); for (Iterator<MatOfDMatch> iterator = matches.iterator(); iterator.hasNext();) { MatOfDMatch matOfDMatch = (MatOfDMatch) iterator.next(); if (matOfDMatch.toArray()[0].distance / matOfDMatch.toArray()[1].distance < 0.9) { good_matches.add(matOfDMatch.toArray()[0]); } } // get keypoint coordinates of good matches to find homography and remove outliers using ransac List<Point> pts1 = new ArrayList<Point>(); List<Point> pts2 = new ArrayList<Point>(); for(int i = 0; i<good_matches.size(); i++){ pts1.add(keypoints1.toList().get(good_matches.get(i).queryIdx).pt); pts2.add(keypoints2.toList().get(good_matches.get(i).trainIdx).pt); } // convertion of data types - there is maybe a more beautiful way Mat outputMask = new Mat(); MatOfPoint2f pts1Mat = new MatOfPoint2f(); pts1Mat.fromList(pts1); MatOfPoint2f pts2Mat = new MatOfPoint2f(); pts2Mat.fromList(pts2); // Find homography - here just used to perform match filtering with RANSAC, but could be used to e.g. stitch images // the smaller the allowed reprojection error (here 15), the more matches are filtered Mat Homog = Calib3d.findHomography(pts1Mat, pts2Mat, Calib3d.RANSAC, 15, outputMask, 2000, 0.995); // outputMask contains zeros and ones indicating which matches are filtered LinkedList<DMatch> better_matches = new LinkedList<DMatch>(); for (int i = 0; i < good_matches.size(); i++) { if (outputMask.get(i, 0)[0] != 0.0) { better_matches.add(good_matches.get(i)); } } // DRAWING OUTPUT Mat outputImg = new Mat(); // this will draw all matches, works fine MatOfDMatch better_matches_mat = new MatOfDMatch(); better_matches_mat.fromList(better_matches); Features2d.drawMatches(img1, keypoints1, img2, keypoints2, better_matches_mat, outputImg); // save image Imgcodecs.imwrite("result.jpg", outputImg);
我希望这足以作为一个例子。可以类似地应用其他过滤方法。如果您还有其他问题,请随时询问。
编辑: 单应过滤仅在您的大多数关键点位于场景中的同一平面(例如墙等)上时才有效。