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

BSc*_*ker 14 c++ opencv computer-vision

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

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

我查看了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;
Run Code Online (Sandbox Code Playgroud)

以此网站代码为例.我遇到的问题是特征检测不可靠,我似乎错过了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);
}
Run Code Online (Sandbox Code Playgroud)

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

Jon*_*rin 5

您目前使用的验证

第一阶段

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

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

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

您可能已经观察到此验证存在一些主要缺点:

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

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

第二阶段

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

我看到的问题是,如果两个图像都没有在相同角度的完全相同的位置拍摄,那么它将无法工作.

提议进一步改进您的验证

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

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

这是我的主张:

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

内点很可能是很好的匹配,因为几何上将是连贯的.

编辑:我刚刚在findHomography 这里找到了一个例子.


smo*_*ing 1

还没有尝试过红外/可见光摄影,但当相似图像的直方图非常不同时,互信息度量通常会起到合理的作用。

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