在不同图像中比较关键点位置的OpenCV C++实现

3
比较两张图片的时候,如何比较关键点距离以便忽略那些明显不正确的匹配?我发现当相似图片相互比较时,大多数情况下可以相当准确,但有时会出现完全不同的匹配。因此,我需要一种方法来查看两张图片中的两组关键点,并确定匹配的关键点是否在两张图片上相对位置相同。也就是说,它知道图像1上的关键点1、2和3相距很远,因此在图像2上匹配的相应关键点应该相互之间距离相当接近。我过去使用了RANSAC和最小距离检查,但只有一定效果,它们似乎没有我想要的彻底性。(使用ORB和BruteForce)
编辑:将“x、y和z”更改为“1、2和3”
编辑2 - 我将尝试用快速绘制的示例进一步解释:
假设这是我的图像:
(图片无法翻译,请见谅)
并且我给它这个图片进行比较:
(图片无法翻译,请见谅)
这是原始图片的裁剪和压缩版本,但显然相似。
现在,假设你运行它通过特征检测,并为两个图像返回以下结果的关键点:
(图片无法翻译,请见谅)
两张图片上的关键点大致在相同的区域,比例上距离相互之间相等。取我圈出的关键点,我们称其为“图像1关键点1”。
(图片无法翻译,请见谅)
我们可以看到它周围有5个关键点。我需要获取这些点与"图像1关键点1"之间的距离,以便将它们与同一区域内"图像2关键点1"及其周围的5个关键点进行比较(如下所示),从而不仅对一个关键点进行比较,而是根据关键点的位置比较已知形状

enter image description here

--

这样说清楚了吗?


你的例子过于简单和构造化了。实际上,你不能对翻译/旋转/剪切的数量做出任何假设,这就是为什么你必须依靠比较特征而不是关键点位置的原因。 - berak
这只是一个非常简化的例子,但无论如何,你肯定可以使用已知“关键点”的图像,并了解所有关键点之间的关系(即它们之间的相对距离和“关键点”的“形状”),然后将其与另一幅图像的“关键点”进行比较,以去除那些不符合从自己的图像中确定的已知“形状”的“关键点”,当然还要进行特征比较。 - fakeaccount
3个回答

5

关键点匹配是一个涉及多个维度的问题。这些维度包括:

  • 空间距离,即从不同图像中两个关键点的位置测量出的(x,y)距离。
  • 特征距离,即描述两个关键点相似程度的距离。

根据您的上下文环境,您可能不想计算相同的距离,或者您想将两者结合起来。以下是一些使用情况:

  • 光流,由opencv的稀疏Lucas-Kanade光流实现。在这种情况下,在每个帧中计算称为好特征的关键点,然后基于空间距离进行匹配。这是因为图像应该相对缓慢地变化(输入帧具有视频帧速率);
  • 图像拼接,您可以从opencv的features2d(免费或非免费)中实现。在这种情况下,图像会发生根本性变化,因为您移动了相机。然后,您的目标是找到稳定的点,即在两个或多个图像中存在的点,无论它们的位置如何。在这种情况下,您将使用特征距离。当您有一个要在查询图像中查找的对象的模板图像时,这也适用。
为了计算特征距离,您需要计算它们外观的编码版本。这个操作由DescriptorExtractor类执行。 然后,您可以计算描述输出之间的距离:如果两个描述之间的距离很小,则原始关键点很可能对应于同一场景点。
在计算距离时,请注意使用正确的距离函数:ORB、FREAK、BRISK依赖于汉明距离,而SIFt和SURF使用更常见的L2距离。 匹配过滤 当您拥有单个匹配时,您可能希望执行匹配过滤,以拒绝可能来自场景歧义的良好单个匹配。例如,考虑一个起源于房子窗户角落的关键点。那么它很可能与另一个房子中的另一个窗户相匹配,但这可能不是正确的房子或正确的窗户。
您有几种方法可以做到这一点:
  • RANSAC通过使用当前解决方案的计算匹配进行一致性检查。基本上,它随机选择一些匹配项,计算问题的解决方案(通常是两个图像之间的几何变换),然后计算有多少匹配项与此估计相符。具有更高内点数的估计获胜;
  • David Lowe在原始SIFT论文中执行了另一种过滤。他保留了与给定查询关键点最佳匹配的两个候选项,即具有最低距离(或最高相似度)的点。然后,他计算比率similarity(query, best)/similarity(query, 2nd best)。如果这个比率太低,那么第二个最佳匹配项也是一个好的匹配项,匹配结果被称为模糊并被拒绝。

在你的情况下,如何做到最好很可能取决于你的确切应用。

您的具体情况

在您的情况下,您想开发一种基于相邻关键点的备用特征描述符。这里显然没有限制,但以下是我会遵循的一些步骤:

  1. make your descriptor rotation and scale invariant by computing the PCA of the keypoints :

    // Form a matrix from KP locations in current image
    cv::Mat allKeyPointsMatrix = gatherAllKeypoints(keypoints); 
    
    // Compute PCA basis
    cv::PCA currentPCA(allKeyPointsMatrix, 2);
    
    // Reproject keypoints in new basis
    cv::Mat normalizedKeyPoints = currentPCA.project(allKeyPointsMatrix);
    
  2. (optional) sort the keypoints in a quadtree or kd-tree for faster spatial indexing

  3. Compute for each keypoint a descriptor that is (for example) the offsets in normalized coordinates of the 4 or 5 closest keypoints
  4. Do the same in your query image
  5. Match keypoints from both mages based on these new descriptors.

我已经添加了一个更具体地解决您问题的部分。 - sansuiso
我过去几天一直在研究这个,但是它太令人困惑了... 你能再详细解释一下吗?@sansuiso - fakeaccount

0
你可以根据两个关键点之间的像素距离进行筛选。 假设matches是你的匹配向量,kp_1是第一张图片上的关键点向量,kp_2是第二张图片上的关键点向量。你可以使用上面的代码来消除明显不正确的匹配。你只需要设定一个阈值即可。
double threshold= YourValue;
vector<DMatch> good_matches;
for (int i = 0; i < matches.size(); i++)
{
    double dist_p = sqrt(pow(abs(kp_1[matches[i][0].queryIdx].pt.x - kp_2[matches[i][0].trainIdx].pt.x), 2) + pow(abs(kp_1[matches[i][0].queryIdx].pt.y - kp_2[matches[i][0].trainIdx].pt.y), 2));
    if (dist_p < threshold)
    {
        good_matches.push_back(matches[i][0]);
    }
}

0

你具体想做什么?需要更多信息才能给出好的答案。否则,答案将会非常笼统,很可能无法满足你的需求。

而你所说的“确定匹配的关键点是否在两个图像之间的同一位置”,是指字面上的两个图像之间的相同x、y位置吗?

我建议尝试SURF算法。它非常适合你上述描述的任务(尽管我发现它有点慢,除非你使用GPU加速,5fps对34fps)。

这里是SURF的教程,我个人认为非常有用,但可执行文件仅适用于Linux用户。但是,你可以简单地从源代码中删除特定于操作系统的绑定,并仅保留与OpenCV相关的绑定,然后在Linux上编译和运行。

https://code.google.com/p/find-object/#Tutorials

希望这对你有所帮助!

你误读了我的写作,看看我的编辑--我的意思是关键点x、y和z的任意位置,而不是关键点的x、y坐标。 - fakeaccount

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