iOS上的OpenCV:与SurfFeatureDetector和FlannBasedMatcher的错误匹配

时间:2012-12-09 19:00:35

标签: ios opencv feature-detection surf

我正在尝试使用OpenCV的特征检测工具来判断较大的场景图像中是否存在小样本图像。
我使用here中的代码作为参考(没有单应性部分)。

UIImage *sceneImage, *objectImage1;
cv::Mat sceneImageMat, objectImageMat1;
cv::vector<cv::KeyPoint> sceneKeypoints, objectKeypoints1;
cv::Mat sceneDescriptors, objectDescriptors1;
cv::SurfFeatureDetector *surfDetector;
cv::FlannBasedMatcher flannMatcher;
cv::vector<cv::DMatch> matches;
int minHessian;
double minDistMultiplier;

minHessian = 400;
minDistMultiplier= 3;
surfDetector = new cv::SurfFeatureDetector(minHessian);

sceneImage = [UIImage imageNamed:@"twitter_scene.png"];
objectImage1 = [UIImage imageNamed:@"twitter.png"];

sceneImageMat = cv::Mat(sceneImage.size.height, sceneImage.size.width, CV_8UC1);
objectImageMat1 = cv::Mat(objectImage1.size.height, objectImage1.size.width, CV_8UC1);

cv::cvtColor([sceneImage CVMat], sceneImageMat, CV_RGB2GRAY);
cv::cvtColor([objectImage1 CVMat], objectImageMat1, CV_RGB2GRAY);

if (!sceneImageMat.data || !objectImageMat1.data) {
    NSLog(@"NO DATA");
}

surfDetector->detect(sceneImageMat, sceneKeypoints);
surfDetector->detect(objectImageMat1, objectKeypoints1);

surfExtractor.compute(sceneImageMat, sceneKeypoints, sceneDescriptors);
surfExtractor.compute(objectImageMat1, objectKeypoints1, objectDescriptors1);

flannMatcher.match(objectDescriptors1, sceneDescriptors, matches);

double max_dist = 0; double min_dist = 100;

for( int i = 0; i < objectDescriptors1.rows; i++ )
{ 
    double dist = matches[i].distance;
    if( dist < min_dist ) min_dist = dist;
    if( dist > max_dist ) max_dist = dist;
}

cv::vector<cv::DMatch> goodMatches;
for( int i = 0; i < objectDescriptors1.rows; i++ )
{ 
    if( matches[i].distance < minDistMultiplier*min_dist )
    { 
        goodMatches.push_back( matches[i]);
    }
}
NSLog(@"Good matches found: %lu", goodMatches.size());

cv::Mat imageMatches;
cv::drawMatches(objectImageMat1, objectKeypoints1, sceneImageMat, sceneKeypoints, goodMatches, imageMatches, cv::Scalar::all(-1), cv::Scalar::all(-1),
                cv::vector<char>(), cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);

for( int i = 0; i < goodMatches.size(); i++ )
{
    //-- Get the keypoints from the good matches
    obj.push_back( objectKeypoints1[ goodMatches[i].queryIdx ].pt );
    scn.push_back( objectKeypoints1[ goodMatches[i].trainIdx ].pt );
}

cv::vector<uchar> outputMask;
cv::Mat homography = cv::findHomography(obj, scn, CV_RANSAC, 3, outputMask);
int inlierCounter = 0;
for (int i = 0; i < outputMask.size(); i++) {
    if (outputMask[i] == 1) {
        inlierCounter++;
    }
}
NSLog(@"Inliers percentage: %d", (int)(((float)inlierCounter / (float)outputMask.size()) * 100));

cv::vector<cv::Point2f> objCorners(4);
objCorners[0] = cv::Point(0,0);
objCorners[1] = cv::Point( objectImageMat1.cols, 0 );
objCorners[2] = cv::Point( objectImageMat1.cols, objectImageMat1.rows );
objCorners[3] = cv::Point( 0, objectImageMat1.rows );

cv::vector<cv::Point2f> scnCorners(4);

cv::perspectiveTransform(objCorners, scnCorners, homography);

cv::line( imageMatches, scnCorners[0] + cv::Point2f( objectImageMat1.cols, 0), scnCorners[1] + cv::Point2f( objectImageMat1.cols, 0), cv::Scalar(0, 255, 0), 4);
cv::line( imageMatches, scnCorners[1] + cv::Point2f( objectImageMat1.cols, 0), scnCorners[2] + cv::Point2f( objectImageMat1.cols, 0), cv::Scalar( 0, 255, 0), 4);
cv::line( imageMatches, scnCorners[2] + cv::Point2f( objectImageMat1.cols, 0), scnCorners[3] + cv::Point2f( objectImageMat1.cols, 0), cv::Scalar( 0, 255, 0), 4);
cv::line( imageMatches, scnCorners[3] + cv::Point2f( objectImageMat1.cols, 0), scnCorners[0] + cv::Point2f( objectImageMat1.cols, 0), cv::Scalar( 0, 255, 0), 4);

[self.mainImageView setImage:[UIImage imageWithCVMat:imageMatches]];

这很有效,但即使小图像不是较大图像的一部分,我也会继续获得大量的匹配。
这是一个良好输出的例子:
Good Output
这是输出错误的一个例子:
Bad Output
两个输出都是相同代码的结果。唯一的区别是小样本图像。
有了这样的结果,我不可能知道样本图像何时不在较大的图像中 在进行研究时,我发现了this stackoverflow问题。我按照那里给出的答案,并尝试了“OpenCV 2计算机视觉应用程序设计手册”一书中建议的步骤,但我无法使其适用于不同大小的图像(似乎是cv的限制:: findFundamentalMat函数)。

我错过了什么?有没有办法使用SurfFeatureDetector和FlannBasedMatcher来了解一个样本图像何时是较大图像的一部分,而另一个样本图像不是?是否有一种不同的方法可以更好地实现这一目的?
更新:
我更新了上面的代码,包括我使用的完整功能,包括尝试实际绘制单应性。另外,这里有3个图像 - 1个场景,以及我想在场景中找到的两个小物体。我的爪子图标的内部百分比越来越好,而不是实际在场景中的推特图标。另外,由于某种原因,不会出现单应性:
Twitter Icon
Paw Icon
Scene

2 个答案:

答案 0 :(得分:3)

您的匹配器将始终将较小描述符列表中的每个点与较大列表中的一个匹配。然后你必须自己寻找这些匹配中哪些是有意义的,哪些不是。您可以通过丢弃超过最大允许描述符距离的每个匹配来执行此操作,或者您可以尝试查找转换矩阵(例如,使用findHomography)并检查是否有足够的匹配对应。

答案 1 :(得分:0)

这是一个老帖子,但是我必须为课堂做类似的任务。删除错误输出的一种方法是检查大多数匹配行是否相互平行(相对),并删除指向错误方向的匹配。