【问题标题】:OpenCV: How to get inlier points using findHomography()/findFundamental() and RANSACOpenCV:如何使用 findHomography()/findFundamental() 和 RANSAC 获得内点
【发布时间】:2014-06-27 16:36:32
【问题描述】:

OpenCV 本身不提供 RANSAC 函数,或者至少不以您可以调用它并完成它的形式提供(例如cv::ransac(...))。所有能够使用 RANSAC 的函数/方法都有一个启用它的标志。但是,如果您在估计单应性/基本矩阵之后确实想对 RANSAC 计算的内点执行其他操作,例如在 Octave 或类似的软件/点库中创建一个漂亮的图,则这并不总是有用的,在剩余的过滤匹配集等。

匹配两个图像后,一个得到一个匹配向量。除此之外,我们当然还有 2 组用于匹配过程的关键点(每个图像一个)。使用匹配和关键点,我们创建了两个点向量(例如cv::Point2f points)并将它们传递给findHomography()。从thisthis 的帖子中,我发现了如何使用掩码准确地标记内点,并将其传递给该函数。掩码内的每一行都与一个内点/异常值相关。但是我无法弄清楚如何使用我的两组点中的行索引信息。查看 OpenCV 的源代码并没有让我走得太远。在findFundamental()(类似于findHomography() 的签名和掩码部分)中,他们使用compressPoints(),这似乎以某种方式将我们作为输入的两组(源点和目标点)合并为一个。在进行测试以确定面具的性质时,我尝试了 2 组匹配点(将 cv::Keypoints 转换为 cv::Point2f - 标准程序)。每组包含 300 分,所以我们总共有 600 分。返回的掩码包含 300 行(值对于手头的这个主题并不重要)。

编辑:在写这篇文章时,我发现了答案(见下文),但还是决定发布这个问题,以防有人尽快以紧凑的形式需要这些信息。请注意,我们仍然需要支持 RANSAC 的 OpenCV 功能之一。因此,如果您有一组点但无意计算单应性或基本矩阵,这显然不是方法,我敢说我无法在 OpenCV 的 API 中找到任何有用的东西来帮助避免这个障碍,因此您需要使用一个外部库。

【问题讨论】:

    标签: opencv mask extraction points ransac


    【解决方案1】:

    解决方案实际上很简单。正如我们所知,我们的掩码中的每一行都会提供信息,如果我们有一个内部值或一个异常值。但是,我们有 2 组点作为输入,那么包含单个值的行究竟如何代表两个点?这种索引的性质出现在我的脑海中,同时思考这两组点实际上是如何出现在 findHomography() 中的(在我的例子中,我正在计算两个图像之间的单应性)。两个集合中的点数相同,因为它们是从我们的一对图像之间的匹配中提取的一个简单事实。这意味着我们的掩码中的一行是两个集合中点的实际索引,也是两个图像匹配向量中的索引。我已经成功地基于此手动引用了一小部分匹配点,结果符合预期。重要的是,您不要使用每个 cv::DMatch 中引用的关键点来更改匹配的顺序以及从中提取的 2D 点。您可以在下面看到一对内点的简单示例。

    for(int i = 0; i < matchesObjectScene.size(); ++i)
    {
       // extract points from keypoints based on matches
       pointsObject.push_back(keypointsObject.at(matchesObjectScene.at(i).queryIdx).pt);
       pointsScene.push_back(keypointsScene.at(matchesObjectScene.at(i).trainIdx).pt);
    }
    // compute homography using RANSAC
    cv::Mat mask;
    cv::Mat H = cv::findHomography(pointsObject, pointsScene, CV_RANSAC, ransacThreshold, mask);
    

    在上面的例子中,如果我们打印一些内部变量

    int maskRow = 10;
    std::cout << "POINTS: object(" << pointsObject.at(maskRow).x << "," << pointsObject.at(maskRow).y << ") - scene(" << pointsScene.at(maskRow).x << "," << pointsScene.at(maskRow).y << ")" << std::endl;
    

    然后再一次,但这次使用我们的关键点(也可以使用提取的 2D 点来完成)

    std::cout << "POINTS (via match-set): object(" << keypointsObject.at(matchesCurrentObject.at(maskRow).queryIdx).pt.x << "," << keypointsObject.at(matchesCurrentObject.at(maskRow).queryIdx).pt.y << ") - scene(" << keypointsScene.at(matchesCurrentObject.at(maskRow).trainIdx).pt.x << "," << keypointsScene.at(matchesCurrentObject.at(maskRow).trainIdx).pt.y << ")" << std::endl;
    

    我们实际上得到了相同的输出:

    POINTS: object(462,199) - sscene(485,49)
    POINTS (via match-set): object(462,199) - scene(485,49)
    

    要获得实际的内点,我们只需检查掩码中的当前行是否实际包含 0 或非零值:

    if((unsigned int)mask.at<uchar>(maskRow))
      // store match or keypoints or points somewhere where you can access them later
    

    【讨论】:

    • 对于第一句话,实际上掩码中的行并不代表内点。该行表示内点或异常点的状态。更多解释请参考这篇帖子link
    • 是的,这是正确的,这就是我的意思,但现在我重新阅读了这句话,我明白它是如何被误解的。我会改的。
    • 但行表示内点或异常值的状态,但每个内点或异常值状态没有关键点。关键点存储在向量中。那么我们如何才能得到mask中对应的inlier关键点呢?
    • 我很难理解你在问什么。也许您可以发布一个新问题(如果您愿意,可以链接到我的问题以帮助观众更好地了解您的案例)?
    【解决方案2】:

    换个说法。 RANSAC 可能无法在 OpenCV 中作为一个函数单独存在,因为 RANSAC 是一种拒绝异常值的抽象技术。 RANSAC 依赖于一个基本模型来执行异常值拒绝。现在基本模型非常通用。它可以是任何东西(不一定是它们之间有某种关系的点)。这可能是为什么 RANSAC 仅作为其他功能中的一个功能存在的原因,这些功能执行一些已定义的任务,这些任务具有一定的范围,如 findHomographyfindFundamentalMat 等。

    【讨论】:

      猜你喜欢
      • 2019-11-12
      • 2015-10-13
      • 2020-06-01
      • 2012-08-16
      • 2014-09-01
      • 2021-04-23
      • 2016-01-31
      • 1970-01-01
      • 2016-11-24
      相关资源
      最近更新 更多