OpenCV的RobustMatch匹配算法

简介: OpenCV的RobustMatch匹配算法

今天在网上看到一个比较健壮的图像特征匹配算法,遂拿出来与大家分享

1.首先看看类定义:

class RobustMatcher 
{
private:
  // pointer to the feature point detector object
  cv::Ptr<cv::FeatureDetector> detector;
  // pointer to the feature descriptor extractor object
  cv::Ptr<cv::DescriptorExtractor> extractor;
  // pointer to the matcher object
  cv::Ptr<cv::DescriptorMatcher > matcher;
  float ratio; // max ratio between 1st and 2nd NN
  bool refineF; // if true will refine the F matrix
  double distance; // min distance to epipolar
  double confidence; // confidence level (probability)
public:
  RobustMatcher() : ratio(0.65f), refineF(true),
    confidence(0.99), distance(3.0) 
  {
      // ORB is the default feature
      detector= new cv::OrbFeatureDetector();
      extractor= new cv::OrbDescriptorExtractor();
      matcher= new cv::BruteForceMatcher<cv::HammingLUT>;
  }
  // Set the feature detector
  void setFeatureDetector(cv::Ptr<cv::FeatureDetector>& detect) 
  {
      detector= detect;
  }
  // Set the descriptor extractor
  void setDescriptorExtractor(cv::Ptr<cv::DescriptorExtractor>& desc) 
  {
      extractor= desc;
  }
  // Set the matcher
  void setDescriptorMatcher(cv::Ptr<cv::DescriptorMatcher>& match) 
  {
      matcher= match;
  }
  // Set confidence level
  void setConfidenceLevel(double conf) 
  {
      confidence= conf;
  }
  //Set MinDistanceToEpipolar
  void setMinDistanceToEpipolar(double dist) 
  {
    distance= dist;
  }
  //Set ratio
  void setRatio(float rat) 
  {
    ratio= rat;
  }
  cv::Mat match(cv::Mat& image1, cv::Mat& image2, // input images
    // output matches and keypoints
    std::vector<cv::DMatch>& matches,
    std::vector<cv::KeyPoint>& keypoints1,
    std::vector<cv::KeyPoint>& keypoints2);
  cv::Mat ransacTest(
    const std::vector<cv::DMatch>& matches,
    const std::vector<cv::KeyPoint>& keypoints1,
    const std::vector<cv::KeyPoint>& keypoints2,
    std::vector<cv::DMatch>& outMatches);
  void symmetryTest(
    const std::vector<std::vector<cv::DMatch> >& matches1,
    const std::vector<std::vector<cv::DMatch> >& matches2,
    std::vector<cv::DMatch>& symMatches);
  int ratioTest(std::vector<std::vector<cv::DMatch>>& matches);
};

2.cpp中的内容为:

int RobustMatcher::ratioTest(std::vector<std::vector<cv::DMatch> >
  &matches) 
{
    int removed=0;
    // for all matches
    for (std::vector<std::vector<cv::DMatch> >::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;//返回被删除的点数量
}
// Insert symmetrical matches in symMatches vector
void RobustMatcher::symmetryTest(
  const std::vector<std::vector<cv::DMatch> >& matches1,
  const std::vector<std::vector<cv::DMatch> >& matches2,
  std::vector<cv::DMatch>& symMatches)
{
    // for all matches image 1 -> image 2
    for (std::vector<std::vector<cv::DMatch> >::
      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<cv::DMatch> >::
          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
          }
        }
    }
}
// Identify good matches using RANSAC
// Return fundemental matrix
cv::Mat RobustMatcher::ransacTest(
  const std::vector<cv::DMatch>& matches,
  const std::vector<cv::KeyPoint>& keypoints1,
  const std::vector<cv::KeyPoint>& keypoints2,
  std::vector<cv::DMatch>& outMatches) 
{
    // Convert keypoints into Point2f
    std::vector<cv::Point2f> points1, points2;
    cv::Mat fundemental;
    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);
    if (points1.size()>0&&points2.size()>0)
    {
      cv::Mat fundemental= cv::findFundamentalMat(
        cv::Mat(points1),cv::Mat(points2), // matching points
        inliers,       // match status (inlier or outlier)
        CV_FM_RANSAC, // RANSAC method
        distance,      // distance to epipolar line
        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
          outMatches.push_back(*itM);
        }
      }
      if (refineF) 
      {
        // The F matrix will be recomputed with
        // all accepted matches
        // Convert keypoints into Point2f
        // for final F computation
        points1.clear();
        points2.clear();
        for (std::vector<cv::DMatch>::
          const_iterator it= outMatches.begin();
          it!= outMatches.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 8-point F from all accepted matches
        if (points1.size()>0&&points2.size()>0)
        {
          fundemental= cv::findFundamentalMat(
            cv::Mat(points1),cv::Mat(points2), // matches
            CV_FM_8POINT); // 8-point method
        }
      }
    }
  return fundemental;
}
// Match feature points using symmetry test and RANSAC
// returns fundemental matrix
cv::Mat RobustMatcher:: match(cv::Mat& image1,
  cv::Mat& image2, // input images
  // output matches and keypoints
  std::vector<cv::DMatch>& matches,
  std::vector<cv::KeyPoint>& keypoints1,
  std::vector<cv::KeyPoint>& keypoints2) 
{
  // 1a. Detection of the ORB features
  detector->detect(image1,keypoints1);
  detector->detect(image2,keypoints2);
  // 1b. Extraction of the ORB descriptors
  cv::Mat descriptors1, descriptors2;
  extractor->compute(image1,keypoints1,descriptors1);
  extractor->compute(image2,keypoints2,descriptors2);
  // 2. Match the two image descriptors
  // Construction of the matcher
  //cv::BruteForceMatcher<cv::L2<float>> matcher;
  // from image 1 to image 2
  // based on k nearest neighbours (with k=2)
  std::vector<std::vector<cv::DMatch> > matches1;
  matcher->knnMatch(descriptors1,descriptors2,
    matches1, // vector of matches (up to 2 per entry)
    2);        // return 2 nearest neighbours
  // from image 2 to image 1
  // based on k nearest neighbours (with k=2)
  std::vector<std::vector<cv::DMatch> > matches2;
  matcher->knnMatch(descriptors2,descriptors1,
    matches2, // vector of matches (up to 2 per entry)
    2);        // return 2 nearest neighbours
  // 3. Remove matches for which NN ratio is
  // > than threshold
  // clean image 1 -> image 2 matches
  int removed= ratioTest(matches1);
  // clean image 2 -> image 1 matches
  removed= ratioTest(matches2);
  // 4. Remove non-symmetrical matches
  std::vector<cv::DMatch> symMatches;
  symmetryTest(matches1,matches2,symMatches);
  //=========================================测试代码
  cv::Mat img_matches;
  cv::drawMatches( image1, keypoints1, image2, keypoints2, 
    symMatches, img_matches, cv::Scalar::all(-1), cv::Scalar::all(-1), 
    std::vector<char>(), cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS ); 
  /*imshow("Test",img_matches);*/
  //cvWaitKey(0);
  //=========================================测试代码
  // 5. Validate matches using RANSAC
  cv::Mat fundemental= ransacTest(symMatches,
    keypoints1, keypoints2, matches);
  //=========================================测试代码
  std::vector<cv::Point2f> obj;
  std::vector<cv::Point2f> scene;
  for( int i = 0; i < symMatches.size(); i++ )
  {
    //-- Get the keypoints from the good matches
    obj.push_back( keypoints1[ symMatches[i].queryIdx ].pt );
    scene.push_back( keypoints2[ symMatches[i].trainIdx ].pt ); 
  }
  cv::Mat H = cv::findHomography( obj, scene, CV_RANSAC ,2);
  //-- Get the corners from the image_1 ( the object to be "detected" )
  std::vector<cv::Point2f> obj_corners(4);
  obj_corners[0]=cvPoint(0,0);
  obj_corners[1]=cvPoint(image1.cols, 0 );
  obj_corners[2]=cvPoint(image1.cols,image1.rows);
  obj_corners[3]=cvPoint(0,image1.rows);
  std::vector<cv::Point2f> scene_corners(4);
  cv::perspectiveTransform( obj_corners, scene_corners, H);
  for( int i = 0; i < 4; i++ )
  {
    scene_corners[i].x+=image1.cols;
  }
  line( img_matches, scene_corners[0], scene_corners[1], cv::Scalar(0, 255, 0), 2 );
  line( img_matches, scene_corners[1], scene_corners[2], cv::Scalar( 0, 255, 0), 2 );
  line( img_matches, scene_corners[2], scene_corners[3], cv::Scalar( 0, 255, 0), 2 );
  line( img_matches, scene_corners[3], scene_corners[0], cv::Scalar( 0, 255, 0), 2 );
  imshow("Test",img_matches);
  cvWaitKey(0);
  //=========================================测试代码
  // return the found fundemental matrix
  return fundemental;
}

剔除低质量匹配点

ratioTest用来剔除距离比例相差过大的配对点,配对点之间的距离相差越大,能匹配上的概率也就越小。这里使用一个参数ratio来控制剔除距离相差在一定范围之外的特征点。

symmetryTest用来判断两个图像间的特征点匹配是否是一一映射,对于不是的点则剔除掉

3.具体调用的例子:

void main()
{
  // set parameters
  int numKeyPoints = 1500;
  //Instantiate robust matcher
  RobustMatcher rmatcher;
  //instantiate detector, extractor, matcher
  cv::Ptr<cv::FeatureDetector> detector = new cv::OrbFeatureDetector(numKeyPoints);
  cv::Ptr<cv::DescriptorExtractor> extractor = new cv::OrbDescriptorExtractor;
  cv::Ptr<cv::DescriptorMatcher> matcher = new cv::BruteForceMatcher<cv::HammingLUT>;
  rmatcher.setFeatureDetector(detector);
  rmatcher.setDescriptorExtractor(extractor);
  rmatcher.setDescriptorMatcher(matcher);
  //Load input image detect keypoints
  cv::Mat img1;
  std::vector<cv::KeyPoint> img1_keypoints;
  cv::Mat img1_descriptors;
  cv::Mat img2;
  std::vector<cv::KeyPoint> img2_keypoints;
  cv::Mat img2_descriptors;
  std::vector<cv::DMatch>  matches;
  img1 = cv::imread("C:\\temp\\PyramidPattern.jpg", CV_LOAD_IMAGE_GRAYSCALE);
  /*img2 = cv::imread("C:\\temp\\PyramidPatternTest.bmp", CV_LOAD_IMAGE_GRAYSCALE);*/
  //img2 = cv::imread("C:\\temp\\test1.jpg", CV_LOAD_IMAGE_GRAYSCALE);
  img2 = cv::imread("C:\\temp\\test2.jpg", CV_LOAD_IMAGE_GRAYSCALE);
  rmatcher.match(img1, img2, matches, img1_keypoints, img2_keypoints);
}

最后,对于OpenCV中的所有特征匹配算法都可以用这个办法来做,比如SIFT, SURF等等。只需要简单的替换第一步中的extractor和detector就可以了。

匹配效果还不错,上个图


但是缺点也是存在的,如果完整匹配下来,大约一帧耗时100+ms,不适合实时性要求较高的应用


image.png

image.png

目录
相关文章
|
6月前
|
算法 C++
OpenCV-白平衡(完美反射算法)
OpenCV-白平衡(完美反射算法)
168 0
|
6月前
|
算法 C++
OpenCV-白平衡(灰度世界算法)
OpenCV-白平衡(灰度世界算法)
140 0
|
1月前
|
算法 C++ 计算机视觉
Opencv(C++)学习系列---Laplacian拉普拉斯边缘检测算法
Opencv(C++)学习系列---Laplacian拉普拉斯边缘检测算法
|
1月前
|
算法 C++ 计算机视觉
Opencv(C++)学习系列---Canny边缘检测算法
Opencv(C++)学习系列---Canny边缘检测算法
|
4月前
|
算法 数据挖掘 计算机视觉
OpenCV中应用尺度不变特征变换SIFT算法讲解及实战(附源码)
OpenCV中应用尺度不变特征变换SIFT算法讲解及实战(附源码)
32 0
|
4月前
|
算法 计算机视觉
OpenCV4-图像分割-watershed(分水岭算法)
1.分水岭概念 分水岭法是根据像素灰度值之间的差值寻找相同区域以实现分割的算法。我们可以将灰度值理解成像素的高度,这样一张图像可以看成崎岖不平的地面或者山区。向地面低洼的地方倾倒一定量的水,水将会掩盖低于某个高度的区域。
76 0
|
8月前
|
算法 安全 机器人
Baumer工业相机堡盟工业相机如何联合BGAPISDK和OpenCV实现图像的伽马变换校正算法增强(C++)
Baumer工业相机堡盟工业相机如何联合BGAPISDK和OpenCV实现图像的伽马变换校正算法增强(C++)
79 0
|
8月前
|
算法 安全 机器人
Baumer工业相机堡盟工业相机如何联合BGAPISDK和OpenCV实现图像的直方图算法增强(C++)
Baumer工业相机堡盟工业相机如何联合BGAPISDK和OpenCV实现图像的直方图算法增强(C++)
42 0
|
10月前
|
算法 数据挖掘 API
python opencv图像处理算法之GrabCut算法
python opencv图像处理算法之GrabCut算法
320 0
|
算法 计算机视觉 Python
CV8 OpenCV环境下实现大津算法
大津算法最终的目的,就是求一个标准的全局阈值区分前景和背景(小于该阈值的为前景。大于该阈值的为背景),使得前景和背景像素的灰度值方差之和最大。因为方差越大,相关性越小,黑白越分明。
129 0