Matching thermographic / non-thermographic images with OpenCV feature detectors
Ia€?m目前正在开发建筑软件,该软件可以匹配使用热像仪从固定点拍摄的红外和非红外图像。
使用情况如下:使用红外热像仪和标准相机,使用固定点的三脚架拍摄照片。拍摄照片后,摄影师希望匹配每个相机的图像。在某些情况下,仅使用一个摄像机拍摄图像,而不必使用其他图像类型。是的,可能可以使用时间戳来匹配图像,但是最终用户需要使用计算机视觉来匹配它们。
我已经看过StackOverflow上的其他图像匹配帖子-他们通常专注于使用直方图匹配和特征检测器。直方图匹配在这里不是选项,因为我们不能匹配两种图像类型之间的颜色。结果,我开发了一个进行特征检测的应用程序。除了标准特征检测外,Ia€?还添加了一些逻辑,该逻辑说两个关键点如果不在彼此的一定范围内,则无法匹配(查询图像最左侧的关键点不能与上的关键点匹配)。候选图像的最右边)-此过程在下面的代码的第3阶段中进行。
为使您了解当前的输出,以下是产生的有效和无效匹配-请注意,热像图在左侧。我的目标是提高匹配过程的准确性。
有效匹配:
无效匹配:
这是代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | // 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\ "); 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。但是我不理解示例代码中该部分的目的:
1 2 3 4 5 6 7 8 9 10 | 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); } |
关于如何进一步改善这一点的任何想法?任何建议将一如既往。
您当前使用的验证
第一阶段
首先,让我们讨论一下您不了解的代码部分。这个想法是只保留"强匹配项"。实际上,您调用
现在让我尝试解释您的验证,只考虑图像A中的一个特征点(将其概括为所有特征点):
- 您将此匹配点的描述符与图片B进行了匹配
- 您获得了关于欧几里德距离的两个最佳对应关系(即,您在图像B中获得了两个最相似的点)
- 如果从您的点到最佳对应点的距离比从您的点到次佳对应点的距离小得多,那么您就认为这是一个很好的匹配。换句话说,图像B中只有一个点与图像A中的点真正相似(即欧几里得距离很小)。
-
如果两个匹配都太相似(即
!(m1.distance <= nndrRatio * m2.distance) ),那么您就不能真正区分它们,也不会考虑匹配。
您可能已经观察到,此验证存在一些主要弱点:
-
首先,如果您从
knnMatch 获得的最佳匹配都非常糟糕,那么无论如何都可以接受其中的最佳匹配。 - 它不考虑几何形状。因此,尽管实际上它们显然不匹配,但图像左侧的一个点可能类似于右侧的一个点。
*编辑:使用SIFT,您可以使用浮点矢量描述图像中的每个特征点。通过计算两个向量之间的欧几里得距离,您可以知道它们之间的相似程度。如果两个向量完全相同,则距离为零。距离越小,点越相似。但这不是几何形状:图像左侧的点可能看起来与右侧的点相似。因此,您首先要找到看起来相似的点对(即" A中的该点与B中的该点类似,因为它们的特征向量之间的欧几里得距离很小"),然后您需要验证此匹配是否一致(即"这些相似的点可能实际上是相同的,因为它们都在我图像的左侧"或"它们看起来相似,但这是不连贯的,因为我知道它们必须位于图像的同一面,但它们不")。
第二阶段
您在第二阶段所做的事情很有趣,因为它考虑了几何形状:知道两个图像都是从同一点(或几乎相同的点?)拍摄的,您可以消除两个图像中不在同一区域的匹配项。
我看到的问题是,如果两个图像都没有以相同的angular在完全相同的位置拍摄,那么它将无法正常工作。
建议进一步改善您的验证
我会亲自进行第二阶段的工作。即使两个图像不一定完全相同,但它们描述的是同一场景。您可以利用它的几何形状。
这个想法是,您应该能够找到从第一个图像到第二个图像的转换(即,从图像A到图像B的点实际上与所有点的移动方式相关联) 。在您的情况下,我敢打赌,采用了简单的单应性。
这是我的主张:
- 使用knnMatch计算匹配项,并保留第1阶段(您可能希望稍后尝试将其删除并观察结果)
-
使用
cv::findHomography (选择RANSAC算法)在这些匹配之间计算最佳的单应变换。 -
findHomography 具有一个mask 输出,它将为您提供inliers(即用于计算单应变换的匹配项)。
由于几何形状上的连贯性,因此内线很可能会很好地匹配。
编辑:我刚刚在这里找到了使用
在提取关键点,形成描述符并进行匹配之后,请使用一些离群值消除算法,例如RANSAC。 Opencv为RANSAC提供了findHomography函数。您可以看到实现。我将它与SURF一起使用,它给了我相当不错的效果。
Haven并没有尝试使用红外/可见光摄影,但是当您为相似图像使用非常不同的直方图时,相互信息度量通常可以做得很合理。
取决于所需的速度和候选的数量,一种利用此方法的方法是使用互信息量度来注册图像,并找到最终出错率最低的图像对。对图像进行降采样以加快处理速度并降低噪声敏感性可能是一个好主意。
想法:
a)使用超分辨率模块来改善您的输入(OpenCV245)。
b)使用最大稳定的局部颜色区域作为匹配特征(MSER)。