14

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

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

我查看了 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;

我以这个网站的代码为例。我遇到的问题是特征检测不可靠,而且我似乎错过了 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);
}

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

4

4 回答 4

5

您当前使用的验证

第一阶段

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

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

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

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

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

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

第二阶段

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

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

进一步改进验证的建议

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

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

这是我的提议:

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

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

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

于 2013-05-21T07:23:13.897 回答
1

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

于 2013-05-21T10:27:37.943 回答
1

尚未尝试使用红外/可见光摄影,但当您对相似图像有非常不同的直方图时,互信息度量通常会做得很合理。

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

于 2013-05-16T21:38:17.507 回答
0

想法:

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

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

于 2013-05-20T11:05:41.993 回答