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

OpenCV:如何使用findHomography()/findFundamental()和RANSAC获取内部点,opencv,mask,extraction,points,ransac,Opencv,Mask,Extraction,Points,Ransac,OpenCV本身不提供RANSAC函数,或者至少不提供这样一种形式,即您可以直接调用它并使用它(例如,cv::RANSAC(…))。所有能够使用RANSAC的函数/方法都有一个启用它的标志。但是,如果您在估计单应矩阵/基本矩阵后,确实希望对Inliners RANSAC计算执行其他操作,例如,在倍频程或类似软件/点库中创建一个漂亮的绘图,在剩余的过滤匹配集上应用其他算法等,则这并不总是有用的 在匹配两个图像后,一个得到匹配向量。除此之外,我们还有两组关键点(每幅图像一个)用于匹配过程。使用匹配项

OpenCV本身不提供RANSAC函数,或者至少不提供这样一种形式,即您可以直接调用它并使用它(例如,
cv::RANSAC(…)
)。所有能够使用RANSAC的函数/方法都有一个启用它的标志。但是,如果您在估计单应矩阵/基本矩阵后,确实希望对Inliners RANSAC计算执行其他操作,例如,在倍频程或类似软件/点库中创建一个漂亮的绘图,在剩余的过滤匹配集上应用其他算法等,则这并不总是有用的

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


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

解决方案实际上非常简单。正如我们所知道的,掩码中的每一行都给出了我们是否有一个内点或异常值的信息。但是,我们有两组点作为输入,那么包含单个值的行如何准确地表示两个点呢?这种索引的本质出现在我的脑海中,当时我在思考这两组点在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);
要获得实际的inlier,我们只需检查掩码中的当前行是否实际包含0或非零值:

if((unsigned int)mask.at<uchar>(maskRow))
  // store match or keypoints or points somewhere where you can access them later
if((无符号整数)mask.at(maskRow))
//将匹配项、关键点或点存储在以后可以访问的位置

在另一个注释上。在OpenCV中,RANSAC本身不可能作为函数存在,因为RANSAC是一种拒绝异常值的抽象技术。RANSAC依靠一个基本模型来执行异常值拒绝。现在基本模型非常通用。它可以是任何东西(不一定是相互之间有某种关系的点)。这可能就是为什么RANSAC仅作为一个功能存在于其他函数中的原因,这些函数执行一些定义的任务,这些任务具有一些定义的范围,如
findHomography
findFundamentalMat
,等等。

对于第一句话,实际上掩码中的行并不代表一个inlier。该行表示内部或异常值的状态。参考这篇文章进一步解释。是的,这是正确的,这就是我的意思,但现在我重读这句话,我明白了它是如何被误解的。我会更改它。但行表示内部或异常值的状态,但在每个内部或异常值状态上并没有关键点。关键点存储在向量中。那个么,我们怎样才能在面具中得到相应的内里尔的关键点呢?我真的很难理解你们的要求。也许你可以发布一个新问题(如果你愿意,你可以链接到我的问题,帮助听众更好地了解你的案例)?
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)
if((unsigned int)mask.at<uchar>(maskRow))
  // store match or keypoints or points somewhere where you can access them later