【问题标题】:Matching thermographic / non-thermographic images with OpenCV feature detectors使用 OpenCV 特征检测器匹配热成像/非热成像图像
【发布时间】:2013-05-11 22:19:20
【问题描述】:

我目前正在开发可以匹配使用热像仪从固定点拍摄的红外和非红外图像的软件。

用例如下:使用红外热像仪和标准相机使用固定点的三脚架拍摄照片。拍摄完照片后,摄影师想要匹配来自每个相机的图像。在某些情况下,仅使用一个相机拍摄图像,因为不需要其他图像类型。是的,可以使用时间戳来匹配图像,但最终用户要求它们使用计算机视觉进行匹配。

我查看了 StackOverflow 上的其他图像匹配帖子——他们经常专注于使用直方图匹配和特征检测器。直方图匹配在这里不是一个选项,因为我们无法匹配两种图像类型之间的颜色。结果,我开发了一个进行特征检测的应用程序。除了标准特征检测之外,我还添加了一些逻辑,即如果两个关键点不在一定的范围内,则它们不能匹配(查询图像最左侧的关键点无法匹配上的关键点)候选图像的最右边)——这个过程发生在下面代码的第 3 阶段。

为了让您了解当前的输出,这里生成了一个有效和无效的匹配 - 注意热成像图像在左侧。我的目标是提高匹配过程的准确性。

有效匹配:

无效匹配:

代码如下:

    // for each candidate image specified on the command line, compare it against the query image
        Mat img1 = imread(argv[1], CV_LOAD_IMAGE_GRAYSCALE); // loading query image
        for(int candidateImage = 0; candidateImage < (argc - 2); candidateImage++) {
            Mat img2 = imread(argv[candidateImage + 2], CV_LOAD_IMAGE_GRAYSCALE); // loading candidate image
            if(img1.empty() || img2.empty())
            {
                printf("Can't read one of the images\n");
                return -1;
            }

            // detecting keypoints
            SiftFeatureDetector detector;
            vector<KeyPoint> keypoints1, keypoints2;
            detector.detect(img1, keypoints1);
            detector.detect(img2, keypoints2);

            // computing descriptors
            SiftDescriptorExtractor extractor;
            Mat descriptors1, descriptors2;
            extractor.compute(img1, keypoints1, descriptors1);
            extractor.compute(img2, keypoints2, descriptors2);

            // matching descriptors
            BFMatcher matcher(NORM_L1);
            vector< vector<DMatch> > matches_stage1;
            matcher.knnMatch(descriptors1, descriptors2, matches_stage1, 2);

            // use nndr to eliminate weak matches
            float nndrRatio = 0.80f;
            vector< DMatch > matches_stage2;
            for (size_t i = 0; i < matches_stage1.size(); ++i)
            {
                if (matches_stage1[i].size() < 2)
                    continue;
                const DMatch &m1 = matches_stage1[i][0];
                const DMatch &m2 = matches_stage1[i][3];
                if(m1.distance <= nndrRatio * m2.distance)
                    matches_stage2.push_back(m1);
            }

            // eliminate points which are too far away from each other
            vector<DMatch> matches_stage3;
            for(int i = 0; i < matches_stage2.size(); i++) {
                Point queryPt = keypoints1.at(matches_stage2.at(i).queryIdx).pt;
                Point trainPt = keypoints2.at(matches_stage2.at(i).trainIdx).pt;

                // determine the lowest number here
                int lowestXAxis;
                int greaterXAxis;
                if(queryPt.x <= trainPt.x) { lowestXAxis = queryPt.x; greaterXAxis = trainPt.x; }
                else { lowestXAxis = trainPt.x; greaterXAxis = queryPt.x; }

                int lowestYAxis;
                int greaterYAxis;
                if(queryPt.y <= trainPt.y) { lowestYAxis = queryPt.y; greaterYAxis = trainPt.y; }
                else { lowestYAxis = trainPt.y; greaterYAxis = queryPt.y; }

                // determine if these points are acceptable
                bool acceptable = true;
                if( (lowestXAxis + MARGIN) < greaterXAxis) { acceptable = false; }
                if( (lowestYAxis + MARGIN) < greaterYAxis) { acceptable = false; }
                if(acceptable == false) { continue; }

                //// it's acceptable -- provide details, perform input
                matches_stage3.push_back(matches_stage2.at(i));
            }

            // output how many individual matches were found for this training image
            cout << "good matches found for candidate image # " << (candidateImage+1) << " = " << matches_stage3.size() << endl;

我以this sites code 为例。我遇到的问题是特征检测不可靠,而且我似乎错过了 NNDR 比率的目的。我知道我正在为查询图像中的每个点找到 K 个可能的匹配项,并且我有 K = 2。但我不明白示例代码中这部分的目的:

vector< DMatch > matches_stage2;
for (size_t i = 0; i < matches_stage1.size(); ++i)
{
    if (matches_stage1[i].size() < 2)
        continue;
    const DMatch &m1 = matches_stage1[i][0];
    const DMatch &m2 = matches_stage1[i][1];
    if(m1.distance <= nndrRatio * m2.distance)
        matches_stage2.push_back(m1);
}

关于如何进一步改进这一点有什么想法吗?任何建议都将一如既往地受到赞赏。

【问题讨论】:

    标签: c++ opencv computer-vision


    【解决方案1】:

    您当前使用的验证

    第一阶段

    首先说一下代码中你看不懂的部分。这个想法是只保留“强匹配”。实际上,您对knnMatch 的调用会为每个描述符找到关于欧几里得距离“L2”(*) 的最佳两个对应关系。这并不意味着这些在现实中是很好的匹配,而只是那些特征点非常相似。

    现在让我尝试解释您的验证,仅考虑图像 A 中的一个特征点(它推广到所有特征点):

    • 您将此点的描述符与图像 B 匹配
    • 你得到两个关于欧几里得距离的最佳对应关系(即你得到图像 B 中两个最相似的点)
    • 如果从您的点到最佳对应关系的距离远小于从您的点到次佳对应关系的距离,那么您认为这是一个很好的匹配。换句话说,图像 B 中只有一个点与图像 A 中的点非常相似(即欧几里得距离很小)。
    • 如果两个匹配项过于相似(即!(m1.distance &lt;= nndrRatio * m2.distance)),那么您就无法真正区分它们并且您不会考虑匹配项。

    正如您可能观察到的那样,此验证有一些主要弱点:

    • 首先,如果您从 knnMatch 获得的最佳匹配都非常糟糕,那么无论如何都可能接受其中最好的。
    • 它不考虑几何。因此,图像最左侧的点可能与最右侧的点相似,尽管实际上它们显然不匹配。

    * 编辑:使用 SIFT,您可以使用浮点向量来描述图像中的每个特征点。通过计算两个向量之间的欧几里得距离,您可以知道它们的相似程度。如果两个向量完全相同,则距离为零。距离越小,点越相似。但这不是几何图形:图像左侧的点可能看起来与右侧的点相似。所以你首先找到看起来相似的点对(即“A中的这个点看起来与B中的这个点相似,因为它们的特征向量之间的欧几里得距离很小”)然后你需要验证这个匹配是一致的(即“这些相似的点可能实际上是相同的,因为它们都在我图像的左侧”或“它们看起来很相似,但这是不连贯的,因为我知道它们必须位于图像的同一侧而他们没有”)。

    第二阶段

    您在第二阶段所做的事情很有趣,因为它考虑了几何:知道两张图像是从同一点(或几乎同一点?)拍摄的,您可以消除两幅图像中不在同一区域的匹配项.

    我看到的问题是,如果两张图像不是在完全相同的位置以完全相同的角度拍摄的,那么它将无法工作。

    进一步改进验证的建议

    我会亲自在第二阶段工作。尽管两个图像不一定完全相同,但它们描述了相同的场景。你可以利用它的几何形状。

    这个想法是你应该能够找到从第一个图像到第二个图像的转换(即,一个点从图像 A 移动到图像 B 的方式实际上与所有点的移动方式相关联) .在您的情况下,我敢打赌,一个简单的 homography 已被改编。

    这是我的建议:

    • 使用 knnMatch 计算匹配,并保留第 1 阶段(您可能想稍后尝试删除它并观察后果)
    • 使用 cv::findHomography 计算这些匹配项之间的最佳单应变换(选择 RANSAC 算法)。
    • findHomography 有一个 mask 输出,它将为您提供内点(即用于计算单应变换的匹配项)。

    内点很可能是很好的匹配,因为它们在几何上是连贯的。

    编辑:我刚刚找到了一个使用 findHomography here 的示例。

    【讨论】:

    • 感谢您的出色回答——但是,“欧几里得距离”这个词仍然让我感到困惑。欧几里得距离是我可以用尺子测量两点之间的距离。当我们在第二张图像上找到这些特征点之一时,我们测量的距离是多少?在那一点之间和什么之间?我希望它将距离与发现的其他特征点进行比较,并将其与原始特征点进行比较——但我们不能对评估的第一个特征点执行此操作。你能在图表上显示这个“距离”代表什么吗?
    • 你在图像 A 上找到 N 个特征点,在图像 B 上独立找到 M 个特征点。然后你为每个点创建特征向量——在你的例子中是 SIFT 描述符。这些特征向量不代表图像中点的位置,但它们代表特征点本身。两个特征向量之间的欧式距离最小,点最相似。换句话说,这个距离只是告诉你你的特征有多相似(即有多少两点“看起来相同”)。
    【解决方案2】:

    尚未尝试使用红外/可见光摄影,但mutual information 指标通常可以在相似图像的直方图非常不同时发挥作用。

    根据您需要的速度以及有多少候选者,利用这一点的一种方法是使用互信息度量来注册图像,并找到最终误差最低的图像对。对图像进行下采样以加快处理速度并降低噪声敏感性可能是一个好主意。

    【讨论】:

    • 你有相关过程的例子吗?
    • 可能有点过头了,因为我找不到任何关于如何在 OpenCV 中进行注册的信息(我自己主要使用 ITK)。
    • 您可能想尝试模糊可见光图像,使边缘大致与红外图像一样清晰。目前似乎只找到一个“好”匹配(蓝线),这不足以比较图像。还有另一个 FLANN 匹配 here 的例子。
    【解决方案3】:

    在提取关键点、形成描述符和匹配后,使用一些异常值去除算法,如 RANSAC。 Opencv 为 RANSAC 提供了 findHomography 功能。你可以看到实现。我已经将它与 SURF 一起使用,它给了我相当好的结果。

    【讨论】:

      【解决方案4】:

      想法:

      a) 使用超分辨率模块改进您的输入 (OpenCV245)。

      b) 使用最大稳定的局部颜色区域作为匹配特征 (MSER)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多