Java OpenCV - 从knnMatch中提取好匹配

时间:2016-02-16 09:23:42

标签: java opencv

我正在尝试实现一个非常简单的程序来查找两个图像之间的相似性。

我正在使用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);

问题在于匹配太多,而且还包括那些相关的匹配。我似乎无法找到如何只提取好的匹配(超过某个阈值)?有人能指出我正确的方向或将我重定向到一些基本的工作示例吗?我已经花了几个小时在这上面,似乎迷路了......

修改

我试着看Keypoint matching just works two times...? (java opencv),但标准(非knn)匹配使用不同的结构,而且我无法使其工作。

1 个答案:

答案 0 :(得分:13)

如其他答案中所述,有几种方法可以删除异常值和不匹配。我猜您使用其中一些方法找到了使用match代替knnMatch的示例和教程。

所以,您可能知道不同之处在于knnMatch会为descriptor2中的每个描述符返回descriptor1中的n个最佳匹配项。这意味着,您可以获得匹配列表,而不是匹配列表。我想这就是你遇到问题的原因。

使用knnMatch的主要优点是您可以执行比率测试。因此,如果从descriptor1中的一个描述符到descriptor2中的两个最佳描述符的距离相似,则表明您的图像中存在重复模式(例如草前栅栏的尖端)。因此,这种匹配不可靠,应予以删除。 (我不确定你为什么要为每个描述符搜索五个最佳匹配 - 你传递5到knnMatch。而是搜索两个。)

如果您现在只想为每个描述符访问最佳匹配,则只需访问“子列表”的第一个元素。在下文中,您将找到使用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);

我希望这足以作为一个例子。其他过滤方法可以类似地应用。如果您还有其他问题,请随时询问。

修改 单应性滤波仅在大多数关键点位于场景中的同一平面上时才有效,如墙等。