OpenCV:如何使用 findHomography()/findFundamental() 和 RANSAC 获取内点

标签 opencv mask extract points ransac

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

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

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

最佳答案

这个解决方案其实很简单。正如我们所知,掩码中的每一行都提供了我们是否有异常值的信息。但是,我们有 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

关于OpenCV:如何使用 findHomography()/findFundamental() 和 RANSAC 获取内点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24456788/

相关文章:

python-3.x - 从图像中裁剪矩形纸

c++ - OpenCV C++ 中的矩阵复共轭

python - OpenCV Python : Error with using Mask parameter in GoodFeatureToDetect

python - zipfile.BadZipFile:提取受密码保护的.zip和.zip时,提取时损坏了CRC-32错误

python - np.array 中行提取的时间

C:如何在没有 strtol 的情况下从字符串中提取数字

c++ - 没有卡尔曼滤波器的 BackgroundSubtractorGMG?

python - 如何使用多处理并行处理 OpenCV 图像?

ios - 更改嵌入按钮的图像颜色(Swift3)

Pandas 使用掩码对数据帧进行子集化的最佳方法