前两个月闲的蛋疼做了一个图片匹配的东东,原本以为直接用OpenCV就可以用图像特征的方式来做了,结果发现OpenCV只是封装了比较低层的特征识别算法,并没有高级的端口。这里把特征匹配的实现思路记录如下:
1. 计算出descriptor:
1 2 3 4 5 | Mat img = imread(filename); cv::Feature2D *detector = new cv::OrbFeatureDetector(); cv::Feature2D *extractor = new cv::OrbDescriptorExtractor(); detector->detect(img, kpImg); extractor->compute(img, kpImg, descImg); |
其中的detector负责提取图片中的特征点,并由extractor提取出descriptor,最终保存到descImg中。
2. 两张图片的descriptor进行对比
1 2 3 4 5 6 7 8 | BruteForceMatcher< L2 > matcher; matcher.knnMatch(desc1, desc2, matches1, 2); matcher.knnMatch(desc2, desc1, matches2, 2); ratioTest(matches1); ratioTest(matches2); symmetryTest(matches1, matches2, symMatches); |
首先会使用BruteForceMatcher找出两个descriptor到对方的配对点,然后使用ratioTest和SymmetryTest来剔除不靠谱的匹配特征点,也就是下面所要说的剔除匹配点的操作。
3. 剔除低质量匹配点
ratioTest用来剔除距离比例相差过大的配对点,配对点之间的距离相差越大,能匹配上的概率也就越小。这里使用一个参数ratio来控制剔除距离相差在一定范围之外的特征点。
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 | int ratioTest( VVecMatch &matches ) { float ratio = 0.8f; int removed=0; // for all matches for (std::vector<std::vector>::iterator matchIterator= matches.begin(); matchIterator!= matches.end(); ++matchIterator) { // if 2 NN has been identified if (matchIterator->size() > 1) { // check distance ratio if ((*matchIterator)[0].distance/ (*matchIterator)[1].distance > ratio) { matchIterator->clear(); // remove match removed++; } } else { // does not have 2 neighbours matchIterator->clear(); // remove match removed++; } } return removed; } |
symmetryTest用来判断两个图像间的特征点匹配是否是一一映射,对于不是的点则剔除掉。
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 | void symmetryTest( const VVecMatch matches1, const VVecMatch matches2, VecMatch& symMatches ) { // for all matches image 1 -> image 2 for (std::vector<std::vector>::const_iterator matchIterator1= matches1.begin(); matchIterator1!= matches1.end(); ++matchIterator1) { // ignore deleted matches if (matchIterator1->size() < 2) continue; // for all matches image 2 -> image 1 for (std::vector<std::vector>::const_iterator matchIterator2= matches2.begin(); matchIterator2!= matches2.end(); ++matchIterator2) { // ignore deleted matches if (matchIterator2->size() < 2) continue; // Match symmetry test if ((*matchIterator1)[0].queryIdx == (*matchIterator2)[0].trainIdx && (*matchIterator2)[0].queryIdx == (*matchIterator1)[0].trainIdx) { // add symmetrical match symMatches.push_back( cv::DMatch((*matchIterator1)[0].queryIdx, (*matchIterator1)[0].trainIdx, (*matchIterator1)[0].distance)); break; // next match in image 1 -> image 2 } } } } |
4. 判断是否匹配上
经过上面的特征匹配和剔除,剩下来的应该都是比较靠谱的匹配点了。如果用一个图片来和一个模板集合来进行比对,那么通过简单的比较能够匹配到的特征点数目就能够判断出是否匹配上了。
PS:对于OpenCV中的所有特征匹配算法都可以用这个办法来做,比如SIFT, SURF等等。只需要简单的替换第一步中的extractor和detector就可以了



