使用OpenCV进行物体检测:基于阈值/相似度评分的特征匹配 - Java/C++

4
我正在创建一个小程序,它可以在大图像中检测对象(小图像),我正在使用OpenCV Java。由于需要考虑旋转和缩放,我使用了FeatureDetector.BRISK和DescriptorExtractor.BRISK。
以下方法用于过滤匹配结果以仅获取最佳匹配项。
我有两个问题:
  1. 是否有办法通过我所使用的循环来找到下面的min_dist和max_dist?
  2. 最重要的问题 - 现在问题是我需要使用这些匹配项来确定是否已找到对象(模板)。如果有人能在这里帮助我就太好了。
提前感谢。
    FeatureDetector  fd = FeatureDetector.create(FeatureDetector.BRISK); 
    final MatOfKeyPoint keyPointsLarge = new MatOfKeyPoint();
    final MatOfKeyPoint keyPointsSmall = new MatOfKeyPoint();

    fd.detect(largeImage, keyPointsLarge);
    fd.detect(smallImage, keyPointsSmall);

    System.out.println("keyPoints.size() : "+keyPointsLarge.size());
    System.out.println("keyPoints2.size() : "+keyPointsSmall.size());

    Mat descriptorsLarge = new Mat();
    Mat descriptorsSmall = new Mat();

    DescriptorExtractor extractor = DescriptorExtractor.create(DescriptorExtractor.BRISK);
    extractor.compute(largeImage, keyPointsLarge, descriptorsLarge);
    extractor.compute(smallImage, keyPointsSmall, descriptorsSmall);

    System.out.println("descriptorsA.size() : "+descriptorsLarge.size());
    System.out.println("descriptorsB.size() : "+descriptorsSmall.size());

    MatOfDMatch matches = new MatOfDMatch();

    DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMINGLUT);
    matcher.match(descriptorsLarge, descriptorsSmall, matches);

    System.out.println("matches.size() : "+matches.size());

    MatOfDMatch matchesFiltered = new MatOfDMatch();

    List<DMatch> matchesList = matches.toList();
    List<DMatch> bestMatches= new ArrayList<DMatch>();

    Double max_dist = 0.0;
    Double min_dist = 100.0;

    for (int i = 0; i < matchesList.size(); i++)
    {
        Double dist = (double) matchesList.get(i).distance;

        if (dist < min_dist && dist != 0)
        {
            min_dist = dist;
        }

        if (dist > max_dist)
        {
            max_dist = dist;
        }

    }

    System.out.println("max_dist : "+max_dist);
    System.out.println("min_dist : "+min_dist);

    double threshold = 3 * min_dist;
    double threshold2 = 2 * min_dist;

    if (threshold2 >= max_dist)
    {
        threshold = min_dist * 1.1;
    }
    else if (threshold >= max_dist)
    {
        threshold = threshold2 * 1.4;
    }

    System.out.println("Threshold : "+threshold);

    for (int i = 0; i < matchesList.size(); i++)
    {
        Double dist = (double) matchesList.get(i).distance;
        System.out.println(String.format(i + " match distance best : %s", dist));
        if (dist < threshold)
        {
            bestMatches.add(matches.toList().get(i));
            System.out.println(String.format(i + " best match added : %s", dist));
        }
    }


    matchesFiltered.fromList(bestMatches);

    System.out.println("matchesFiltered.size() : " + matchesFiltered.size());

编辑

我按照以下方式编辑了我的代码。我知道仅根据最佳匹配数量来判断是否找到对象还不是最好的方法。请分享您的看法。

    System.out.println("max_dist : "+max_dist);
    System.out.println("min_dist : "+min_dist);

    if(min_dist > 50 )
    {
        System.out.println("No match found");
        System.out.println("Just return ");
        return false;
    }

    double threshold = 3 * min_dist;
    double threshold2 = 2 * min_dist;

    if (threshold > 75)
    {
        threshold  = 75;
    }
    else if (threshold2 >= max_dist)
    {
        threshold = min_dist * 1.1;
    }
    else if (threshold >= max_dist)
    {
        threshold = threshold2 * 1.4;
    }

    System.out.println("Threshold : "+threshold);

    for (int i = 0; i < matchesList.size(); i++)
    {
        Double dist = (double) matchesList.get(i).distance;

        if (dist < threshold)
        {
            bestMatches.add(matches.toList().get(i));
            //System.out.println(String.format(i + " best match added : %s", dist));
        }
    }

    matchesFiltered.fromList(bestMatches);

    System.out.println("matchesFiltered.size() : " + matchesFiltered.size());


    if(matchesFiltered.rows() >= 1)
    {
        System.out.println("match found");
        return true;
    }
    else
    {
        return false;
    }

我尝试了你的代码,它给了我很多想法。非常感谢你的分享。但是有太多的误报。我认为否则你就不会在这里询问了。 - Tima
2个回答

1
你编辑的代码对我来说很好用,而且完美运行。
以下是我对你的代码进行的更改以检测大图中的对象(小图像):
1. 使用SURF方法进行特征检测和特征提取。(SURF在opencv 4.1.1及之前的版本中可用于Android,此后已从其中删除,因此我在这里使用了opencv 4.1.1)
2. 将匹配图像的阈值从1更改为4,在以下行中: if(matchesFiltered.rows() >= 1)
if(matchesFiltered.rows() >= 4)

对我来说,只有这些更改完美地起作用了,请确保对象/小图像具有丰富的纹理(至少应该有可以匹配的关键点)。


0

检测图像中的对象有几种方法。这里提供一些链接:

最后一个链接展示了一种计算最小值和最大值的方法,Java中应该差不多。所有链接都希望能展示一些匹配对象的想法。

我还注意到你的代码中有很多魔数。也许你可以将它们放入变量中,以减少错误的可能性并获得更好的概述。


谢谢提供的参考资料,实际上我已经开始按照第二个链接中的教程编写代码了 :-) 关于魔数,是的,这是我从我的代码中提取和修改后添加到论坛上的版本,我已经将这些数字转换为变量。感谢您指出这一点。我的问题是,尽管所有这些示例都展示了如何找到最佳匹配并绘制最终输出,但它们都没有讨论如何通过某个阈值来得出对象是否被找到的结论。如果我能得到这方面的建议,那就太好了。 - Emily Webb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接