Java OpenCV - 使用 knnMatch 和 findHomography 显示重复项

标签 java android opencv image-processing

我是 OpenCV java 新手,我有一个 Android 应用程序,可以使用 ORB FeatureDetector 和 DescriptorExtractor 匹配两个图像。我使用 DescriptorMatcher BRUTEFORCE_HAMMING。匹配器始终有效,但有时它会显示关键点的重复项。当场景上的图像太亮或太暗时,它会显示重复的关键点,这不是我想要的。

准确匹配的图像:

enter image description here

不匹配的图像:

enter image description here

try {
 bmpObjToRecognize = bmpObjToRecognize.copy(Bitmap.Config.ARGB_8888, true);
 bmpScene = bmpScene.copy(Bitmap.Config.ARGB_8888, true);

 img1 = new Mat();
 img2 = new Mat();
 Utils.bitmapToMat(bmpObjToRecognize, img1);
 Utils.bitmapToMat(bmpScene, img2);
 Imgproc.cvtColor(img1, img1, Imgproc.COLOR_RGBA2GRAY);
 Imgproc.cvtColor(img2, img2, Imgproc.COLOR_RGBA2GRAY);
 Imgproc.equalizeHist(img1, img1);
 Imgproc.equalizeHist(img2, img2);
 detector = FeatureDetector.create(FeatureDetector.ORB);
 descExtractor = DescriptorExtractor.create(DescriptorExtractor.ORB);
 matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMING);

 keypoints1 = new MatOfKeyPoint();
 keypoints2 = new MatOfKeyPoint();
 descriptors = new Mat();
 dupDescriptors = new Mat();

 detector.detect(img1, keypoints1);
 Log.d("LOG!", "number of query Keypoints= " + keypoints1.size());
 detector.detect(img2, keypoints2);
 Log.d("LOG!", "number of dup Keypoints= " + keypoints2.size());
  // Descript keypoints1
  descExtractor.compute(img1, keypoints1, descriptors);
  descExtractor.compute(img2, keypoints2, dupDescriptors);
  // matching descriptors
  List<MatOfDMatch> knnMatches = new ArrayList<>();
  matcher.knnMatch(descriptors, dupDescriptors, knnMatches, DescriptorMatcher.BRUTEFORCE);
  goodMatches = new ArrayList<>();
  knnMatchesValue = knnMatches.size();
  Log.i("xxx", "xxx match count knnMatches = " + knnMatches.size());
  for (int i = 0; i < knnMatches.size(); i++) {
   if (knnMatches.get(i).rows() > 1) {
    DMatch[] matches = knnMatches.get(i).toArray();
    if (matches[0].distance < 0.89f * matches[1].distance) {
     goodMatches.add(matches[0]);
    }
   }
  }

  // get keypoint coordinates of good matches to find homography and remove outliers using ransac
  List<Point> pts1 = new ArrayList<>();
  List<Point> pts2 = new ArrayList<>();
  for (int i = 0; i < goodMatches.size(); i++) {
   Point destinationPoint = keypoints2.toList().get(goodMatches.get(i).trainIdx).pt;
   pts1.add(keypoints1.toList().get(goodMatches.get(i).queryIdx).pt);
   pts2.add(destinationPoint);
  }

  // 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
  better_matches = new LinkedList<>();
  for (int i = 0; i < goodMatches.size(); i++) {
   if (outputMask.get(i, 0)[0] != 0.0) {
    better_matches.add(goodMatches.get(i));
   }
  }

  matches_final_mat = new MatOfDMatch();
  matches_final_mat.fromList(better_matches);

  imgOutputMat = new Mat();
  MatOfByte drawnMatches = new MatOfByte();
  Features2d.drawMatches(img1, keypoints1, img2, keypoints2, matches_final_mat, 
   imgOutputMat, GREEN, RED, drawnMatches, Features2d.NOT_DRAW_SINGLE_POINTS);
  bmp = Bitmap.createBitmap(imgOutputMat.cols(), imgOutputMat.rows(), Bitmap.Config.ARGB_8888);
  Imgproc.cvtColor(imgOutputMat, imgOutputMat, Imgproc.COLOR_BGR2RGB);
  Utils.matToBitmap(imgOutputMat, bmp);
  List<DMatch> betterMatchesList = matches_final_mat.toList();
  final int matchesFound = betterMatchesList.size();


} catch (Exception e) {
 e.printStackTrace();
}

我是否缺少部分代码?

最佳答案

TL;DR 使用类 BFMatcher 及其 create显式地调用方法,然后您就可以将crosscheck标志设置为true。这将启用您想要的“反之亦然检查”。

引用knnMatch的OpenCV文档及其标题:

Finds the k best matches for each descriptor from a query set.

knnMatch(InputArray queryDescriptors, InputArray trainDescriptors, ...)

因此,这意味着可能有多个“查询描述符”与“训练集”中的同一描述符匹配。它只是为您提供 k 个最佳值,如果查询描述符多于训练描述符,您将不可避免地得到重复项。特别是,当你在训练图像/集中几乎没有特征和描述符时(由于缺乏任何纹理,例如黑色输入),情况就会如此。

如果您想删除重复项,请将 BFMatcher 的“crosscheck”标志设置为 true。否则(即其他匹配器),您将需要通过相应的训练描述符对匹配项进行“分组”,并删除除距离最小的匹配项之外的所有匹配项。

关于Java OpenCV - 使用 knnMatch 和 findHomography 显示重复项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58639964/

相关文章:

opencv - 为什么在提供相同的点向量时 opencv 的 recoverPose 返回非原点位置?

c++ - OpenCV 视频编辑?

java - 我的正则表达式有什么问题 -?\\d{2}\\.?\\d{6}

python - 如何使用 python 3.x 检测字符的位置

java - 如何使用 imgscalr 将文本添加到图像

java - stopService() 方法不会停止服务

android - 权限拒绝 : startForeground requires android. 权限.FOREGROUND_SERVICE

Android Studio - 看不到 Java 目录

java - node.js 和 Scala (Java) 之间的连接器

java - 无法启动 Oryx - NoSuchMethodError