【问题标题】:Improve matching of feature points with OpenCV使用 OpenCV 改进特征点的匹配
【发布时间】:2013-07-31 20:02:33
【问题描述】:

我想匹配立体图像中的特征点。我已经用不同的算法找到并提取了特征点,现在我需要一个好的匹配。在这种情况下,我使用 FAST 算法进行检测和提取,使用 BruteForceMatcher 匹配特征点。

匹配码:

vector< vector<DMatch> > matches;
//using either FLANN or BruteForce
Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create(algorithmName);
matcher->knnMatch( descriptors_1, descriptors_2, matches, 1 );

//just some temporarily code to have the right data structure
vector< DMatch > good_matches2;
good_matches2.reserve(matches.size());  
for (size_t i = 0; i < matches.size(); ++i)
{ 
    good_matches2.push_back(matches[i][0]);     
}

因为有很多错误匹配,我计算了最小和最大距离并删除了所有太糟糕的匹配:

//calculation of max and min distances between keypoints
double max_dist = 0; double min_dist = 100;
for( int i = 0; i < descriptors_1.rows; i++ )
{
    double dist = good_matches2[i].distance;
    if( dist < min_dist ) min_dist = dist;
    if( dist > max_dist ) max_dist = dist;
}

//find the "good" matches
vector< DMatch > good_matches;
for( int i = 0; i < descriptors_1.rows; i++ )
{
    if( good_matches2[i].distance <= 5*min_dist )
    {
        good_matches.push_back( good_matches2[i]); 
    }
}

问题是,我要么得到很多错误匹配,要么只有少数正确匹配(见下图)。


(来源:codemax.de


(来源:codemax.de

我认为这不是编程问题,而是匹配的问题。据我了解BruteForceMatcher 仅考虑特征点的视觉距离(存储在FeatureExtractor 中),而不是局部距离(x&y 位置),这在我的情况下也很重要。有没有人遇到过这个问题或改进匹配结果的好主意?

编辑

我更改了代码,它为我提供了 50 个最佳匹配项。在此之后,我通过第一场比赛来检查它是否在指定区域。如果不是,我会进行下一场比赛,直到在给定区域内找到比赛为止。

vector< vector<DMatch> > matches;
Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create(algorithmName);
matcher->knnMatch( descriptors_1, descriptors_2, matches, 50 );

//look if the match is inside a defined area of the image
double tresholdDist = 0.25 * sqrt(double(leftImageGrey.size().height*leftImageGrey.size().height + leftImageGrey.size().width*leftImageGrey.size().width));

vector< DMatch > good_matches2;
good_matches2.reserve(matches.size());  
for (size_t i = 0; i < matches.size(); ++i)
{ 
    for (int j = 0; j < matches[i].size(); j++)
    {
    //calculate local distance for each possible match
    Point2f from = keypoints_1[matches[i][j].queryIdx].pt;
    Point2f to = keypoints_2[matches[i][j].trainIdx].pt;        
    double dist = sqrt((from.x - to.x) * (from.x - to.x) + (from.y - to.y) * (from.y - to.y));
    //save as best match if local distance is in specified area
    if (dist < tresholdDist)
    {
        good_matches2.push_back(matches[i][j]);
        j = matches[i].size();
    }
}

我认为我没有得到更多匹配项,但这样我可以删除更多错误匹配项:


(来源:codemax.de

【问题讨论】:

  • 我发现的最好方法之一是忽略与其他功能过于相似的功能,即只保留最独特的功能
  • 对了,你考虑用ORB吗?它具有旋转不变性和尺度不变性,而 FAST 似乎没有

标签: c++ opencv matching feature-detection


【解决方案1】:

确定高质量特征匹配的另一种方法是 David Lowe 在他关于 SIFT 的论文中提出的比率测试(第 20 页有解释)。此测试通过计算最佳匹配和次佳匹配之间的比率来拒绝不良匹配。如果该比率低于某个阈值,则匹配被视为低质量而被丢弃。

std::vector<std::vector<cv::DMatch>> matches;
cv::BFMatcher matcher;
matcher.knnMatch(descriptors_1, descriptors_2, matches, 2);  // Find two nearest matches
vector<cv::DMatch> good_matches;
for (int i = 0; i < matches.size(); ++i)
{
    const float ratio = 0.8; // As in Lowe's paper; can be tuned
    if (matches[i][0].distance < ratio * matches[i][1].distance)
    {
        good_matches.push_back(matches[i][0]);
    }
}

【讨论】:

  • 正如您在我的代码中看到的那样,我已经进行了删除错误匹配的测试(尽管它与您的建议略有不同)。这就是为什么我的第二张图片中只有几场比赛。我需要知道如何在他们给我必须解决的糟糕匹配之前改进匹配。
  • @20steffi03 你用什么描述符来做匹配?一些描述符比其他描述符更好,但过滤掉“坏匹配”几乎肯定是必要的。您已经在进行蛮力搜索,这将提供最高质量的可用匹配器
  • 我比较了几种特征检测算法。在我的例子中,结合 SIFT 提取器的 FAST 检测器给了我最好的结果。正如您在我的编辑帖子中看到的那样,我已经考虑了匹配的本地位置,这有望使匹配更好。我现在的结果现在好多了。感谢您的帮助!
  • 请将条件改为matches[i][1].distance
  • +1 提到 Lowe 提议使用0.8 的阈值。正如许多其他博客/答案所推荐的那样,我正准备使用 ≈0.5
【解决方案2】:

通过比较所有特征检测算法,我找到了一个很好的组合,这给了我更多的匹配。现在我使用 FAST 进行特征检测,使用 SIFT 进行特征提取,使用 BruteForce 进行匹配。结合检查,匹配项是否在定义的区域内,我得到了很多匹配项,见图:


(来源:codemax.de

相关代码:

Ptr<FeatureDetector> detector;
detector = new DynamicAdaptedFeatureDetector ( new FastAdjuster(10,true), 5000, 10000, 10);
detector->detect(leftImageGrey, keypoints_1);
detector->detect(rightImageGrey, keypoints_2);

Ptr<DescriptorExtractor> extractor = DescriptorExtractor::create("SIFT");
extractor->compute( leftImageGrey, keypoints_1, descriptors_1 );
extractor->compute( rightImageGrey, keypoints_2, descriptors_2 );

vector< vector<DMatch> > matches;
Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce");
matcher->knnMatch( descriptors_1, descriptors_2, matches, 500 );

//look whether the match is inside a defined area of the image
//only 25% of maximum of possible distance
double tresholdDist = 0.25 * sqrt(double(leftImageGrey.size().height*leftImageGrey.size().height + leftImageGrey.size().width*leftImageGrey.size().width));

vector< DMatch > good_matches2;
good_matches2.reserve(matches.size());  
for (size_t i = 0; i < matches.size(); ++i)
{ 
    for (int j = 0; j < matches[i].size(); j++)
    {
        Point2f from = keypoints_1[matches[i][j].queryIdx].pt;
        Point2f to = keypoints_2[matches[i][j].trainIdx].pt;

        //calculate local distance for each possible match
        double dist = sqrt((from.x - to.x) * (from.x - to.x) + (from.y - to.y) * (from.y - to.y));

        //save as best match if local distance is in specified area and on same height
        if (dist < tresholdDist && abs(from.y-to.y)<5)
        {
            good_matches2.push_back(matches[i][j]);
            j = matches[i].size();
        }
    }
}

【讨论】:

  • 对于一个大学项目,我正在研究openCV在计算立体图像差异方面的应用。显然你设法得到了一个健壮的算法。你介意分享更多你的实现吗,例如与openCV等的接口?
  • 两个注意事项 - 我在 OpenCV 3 中没有看到 DynamicAdaptedFeatureDetector,更重要的是 SIFT 和 SURF 是专利算法。
  • @YuriyChernyshov SIFT 和 SURF 确实是专利算法,但如果您将其用于研究目的,只需查看其版权许可,就好像记忆对我有用,您可以免费将其用于研究目的你说这不是你的算法。
【解决方案3】:

除了比率测试,您还可以:

只使用对称匹配:

void symmetryTest(const std::vector<cv::DMatch> &matches1,const std::vector<cv::DMatch> &matches2,std::vector<cv::DMatch>& symMatches)
{
    symMatches.clear();
    for (vector<DMatch>::const_iterator matchIterator1= matches1.begin();matchIterator1!= matches1.end(); ++matchIterator1)
    {
        for (vector<DMatch>::const_iterator matchIterator2= matches2.begin();matchIterator2!= matches2.end();++matchIterator2)
        {
            if ((*matchIterator1).queryIdx ==(*matchIterator2).trainIdx &&(*matchIterator2).queryIdx ==(*matchIterator1).trainIdx)
            {
                symMatches.push_back(DMatch((*matchIterator1).queryIdx,(*matchIterator1).trainIdx,(*matchIterator1).distance));
                break;
            }
        }
    }
}

因为它是立体图像,所以使用 ransac 测试:

void ransacTest(const std::vector<cv::DMatch> matches,const std::vector<cv::KeyPoint>&keypoints1,const std::vector<cv::KeyPoint>& keypoints2,std::vector<cv::DMatch>& goodMatches,double distance,double confidence,double minInlierRatio)
{
    goodMatches.clear();
    // Convert keypoints into Point2f
    std::vector<cv::Point2f> points1, points2;
    for (std::vector<cv::DMatch>::const_iterator it= matches.begin();it!= matches.end(); ++it)
    {
        // Get the position of left keypoints
        float x= keypoints1[it->queryIdx].pt.x;
        float y= keypoints1[it->queryIdx].pt.y;
        points1.push_back(cv::Point2f(x,y));
        // Get the position of right keypoints
        x= keypoints2[it->trainIdx].pt.x;
        y= keypoints2[it->trainIdx].pt.y;
        points2.push_back(cv::Point2f(x,y));
    }
    // Compute F matrix using RANSAC
    std::vector<uchar> inliers(points1.size(),0);
    cv::Mat fundemental= cv::findFundamentalMat(cv::Mat(points1),cv::Mat(points2),inliers,CV_FM_RANSAC,distance,confidence); // confidence probability
    // extract the surviving (inliers) matches
    std::vector<uchar>::const_iterator
    itIn= inliers.begin();
    std::vector<cv::DMatch>::const_iterator
    itM= matches.begin();
    // for all matches
    for ( ;itIn!= inliers.end(); ++itIn, ++itM)
    {
        if (*itIn)
        { // it is a valid match
            goodMatches.push_back(*itM);
        }
    }
}

【讨论】:

  • 距离、置信度、minInlierRatio 有哪些典型值?
  • 但他使用的是 Knn 匹配,它返回匹配向量的向量。
猜你喜欢
  • 1970-01-01
  • 2018-04-27
  • 2013-11-15
  • 2022-10-14
  • 2018-09-12
  • 1970-01-01
  • 2015-09-10
  • 2018-05-08
  • 1970-01-01
相关资源
最近更新 更多