使用OpenCV特征检测器匹配热成像/非热成像图像

时间:2013-05-16 20:56:28

标签: c++ opencv computer-vision

我目前正致力于构建可以使用热成像相机匹配从固定点拍摄的红外和非红外图像的软件。

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

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

为了让您了解当前输出,此处生成了有效且无效的匹配 - 请注意,热像图像位于左侧。我的目标是提高匹配过程的准确性。

有效匹配: Valid match

匹配无效: Invalid match

以下是代码:

    // 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);
}

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

4 个答案:

答案 0 :(得分:5)

您当前使用的验证

第一阶段

首先,让我们谈谈您不理解的代码部分。这个想法只是保持“强势匹配”。实际上,对knnMatch的调用,对于每个描述符,找到与欧几里德距离“L2”(*)相关的最佳两个对应关系。这并不意味着这些是现实中的良好匹配,而只是那些特征点非常相似。

让我现在尝试解释一下您的验证,只考虑图像A中的一个特征点(它概括为所有特征点):

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

这个验证有一些主要的缺点,正如您可能已经观察到的那样:

  • 首先,如果你从knnMatch获得的最佳匹配都非常糟糕,那么无论如何都可以接受最好的匹配。
  • 不考虑几何体。因此,图像左侧的一个点可能类似于右侧的一个点,即使实际上它们显然不匹配。

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

第二阶段

你在第二阶段所做的事情很有意思,因为它考虑了几何:知道两个图像都来自同一个点(或几乎相同的点?),你消除了两个图像中不在同一区域的匹配

我看到的问题是,如果两个图像都没有在相同角度的相同位置拍摄,那么它将不起作用。

进一步改进验证的提议

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

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

这是我的主张:

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

内点很可能是很好的匹配,因为几何上会有连贯的。

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

答案 1 :(得分:1)

没有尝试使用红外/可见光摄影,但是当您对类似图像的直方图非常不同时,mutual information指标通常会做一个合理的工作。

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

答案 2 :(得分:1)

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

答案 3 :(得分:0)

思路:

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

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