OpenCV SIFT/SURF/ORB:drawMatch函数效果不佳

5
我使用Sift/Surf和ORB,但有时候在使用drawMatch函数时会出现问题。
以下是错误信息:
OpenCV错误:在drawMatches函数中断言失败(i2 >= 0 && i2 < static_cast(keypoints2.size())),文件/home/opencv-2.4.6.1/modules/features2d/src/draw.cpp,第208行
终止程序并抛出'cv::Exception'实例
具体代码如下:
drawMatchPoints(img1,keypoints_img1,img2,keypoints_img2,matches);

我尝试将img1、keypoints_img1与img2和keypoints_img2反转,如下所示:
drawMatchPoints(img2,keypoints_img2,img1,keypoints_img1,matches);

针对我正在执行单应性的功能:

void drawMatchPoints(cv::Mat image1,std::vector<KeyPoint> keypoints_img1,
                                      cv::Mat image2,std::vector<KeyPoint> keypoints_img2,std::vector<cv::DMatch> matches){

    cv::Mat img_matches;
    drawMatches( image1, keypoints_img1, image2, keypoints_img2,
                         matches, img_matches, Scalar::all(-1), Scalar::all(-1),
                         vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS );
            std::cout << "Number of good matching " << (int)matches.size() << "\n" << endl;



            //-- Localize the object
            std::vector<Point2f> obj;
            std::vector<Point2f> scene;

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

            Mat H = findHomography( obj, scene, CV_RANSAC );
            std::cout << "Size of homography " << *H.size << std::endl ;

            //-- Get the corners from the image_1 ( the object to be "detected" )
            std::vector<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<Point2f> scene_corners(4);


            perspectiveTransform( obj_corners, scene_corners, H);


            //-- Draw lines between the corners (the mapped object in the scene - image_2 )
            line( img_matches, scene_corners[0] + Point2f( image1.cols, 0), scene_corners[1] + Point2f( image1.cols, 0), Scalar(0, 255, 0), 4 );
            line( img_matches, scene_corners[1] + Point2f( image1.cols, 0), scene_corners[2] + Point2f( image1.cols, 0), Scalar( 0, 255, 0), 4 );
            line( img_matches, scene_corners[2] + Point2f( image1.cols, 0), scene_corners[3] + Point2f( image1.cols, 0), Scalar( 0, 255, 0), 4 );
            line( img_matches, scene_corners[3] + Point2f( image1.cols, 0), scene_corners[0] + Point2f( image1.cols, 0), Scalar( 0, 255, 0), 4 );

            //-- Show detected matches
            cv::imshow( "Good Matches & Object detection", img_matches );
            cv::waitKey(5000);

但我仍然出现了错误!

我注意到当我的keypoints_img1的大小小于keypoints_img2的大小时,就会出现错误:

keyPoint1的大小:244 - keyPoint2的大小:400

所以如果我交换两张图片的加载顺序,那么它可以工作,但我无法预先知道我的第一张图片是否比第二张图片具有更多的关键点...

我的代码(最重要的步骤)是为了创建特征:

init_Sift(400,5,0.04,25,1.6);
void init_Sift(int nf,int nOctaveL,double contrastThresh, double edgeThresh,double sigma){
this->nfeatureSift=nf;
this->nOctaveLayerSift=nOctaveL;
this->contrastThresholdSift=contrastThresh;
this->edgeThresholdSift=edgeThresh;
this->sigmaSift=sigma;}



 cv::FeatureDetector* detector=new SiftFeatureDetector(nfeatureSift,nOctaveLayerSift,contrastThresholdSift,edgeThresholdSift,sigmaSift);
cv::DescriptorExtractor* extractor=new SiftDescriptorExtractor

extractor->compute( image, keypoints, descriptors );

匹配部分:
    std::cout << "Type of matcher : " << type_of_matcher << std::endl;
if (type_of_matcher=="FLANN" || type_of_matcher=="BF"){
    std::vector<KeyPoint> keypoints_img1 = keyfeatures.compute_Keypoints(img1);
    std::vector<KeyPoint> keypoints_img2 = keyfeatures.compute_Keypoints(img2);

    cv::Mat descriptor_img1 = keyfeatures.compute_Descriptors(img1);
    cv::Mat descriptor_img2 = keyfeatures.compute_Descriptors(img2);

    std::cout << "Size keyPoint1 " << keypoints_img1.size() << "\n" << std::endl;
    std::cout << "Size keyPoint2 " << keypoints_img2.size() << "\n" << std::endl;

    //Flann with sift or surf
    if (type_of_matcher=="FLANN"){
        Debug::info("USING Matcher FLANN");
        fLmatcher.match(descriptor_img1,descriptor_img2,matches);

        double max_dist = 0; double min_dist = 100;

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

        std::vector< DMatch > good_matches;

          for( int i = 0; i < descriptor_img1.rows; i++ )
          { if( matches[i].distance <= max(2*min_dist, 0.02) )
            { good_matches.push_back( matches[i]); }
          }

          std::cout << "Size of good match : " <<  (int)good_matches.size() << std::endl;
          //-- Draw only "good" matches
          if (!good_matches.empty()){
              drawMatchPoints(img1,keypoints_img1,img2,keypoints_img2,good_matches);

          }
          else {
              Debug::error("Flann Matcher : Pas de match");
              cv::Mat img_matches;
              drawMatches( img1, keypoints_img1, img2, keypoints_img2,
                                matches, img_matches, Scalar::all(-1), Scalar::all(-1),
                                vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS );
              cv::imshow( "No match", img_matches );
              cv::waitKey(5000);
          }

    }
    //BruteForce with sift or surf
    else if (type_of_matcher=="BF"){
        Debug::info("USING Matcher Brute Force");

        bFmatcher.match(descriptor_img1,descriptor_img2,matches);
        if (!matches.empty()){
            std::nth_element(matches.begin(),//Initial position
                             matches.begin()+24, //Position  of the sorted element
                             matches.end());//End position
            matches.erase(matches.begin()+25,matches.end());

            drawMatchPoints(img1,keypoints_img1,img2,keypoints_img2,matches);
            //drawMatchPoints(img2,keypoints_img2,img1,keypoints_img1,matches);
        }
        else {
            Debug::error("Brute Force matcher  : Pas de match");
            cv::Mat img_matches;
            drawMatches( img1, keypoints_img1, img2, keypoints_img2,
                              matches, img_matches, Scalar::all(-1), Scalar::all(-1),
                              vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS );
            cv::imshow( "No match", img_matches );
            cv::waitKey(5000);

        }

}

你有任何建议或意见吗?

编辑:我解决了我的问题。 我有一个c++问题,因为我有两个类。一个是关于匹配的,另一个是关于查找关键特征点的。我在我的.h文件中写了std::vector和描述符。

class keyFeatures{

public:
...   
std::vector<keyPoint> keypoints;
...

我删除了这个属性,然后编写了一个函数,它以std::vector keypoints作为参数。

cv::Mat descriptor_img1 = keyfeatures.compute_Descriptors(img1,keypoints_img1);

代替
cv::Mat descriptor_img1 = keyfeatures.compute_Descriptors(img1);

我认为当我进行匹配时出现了冲突...但我不知道为什么我不必在我的.h文件中书写它,并在我的函数中使用局部参数。

谢谢!


关于不提前知道的部分 - 实际上,在调用cv :: drawMatches()之前,您确实知道每个图像的每个关键点向量的大小(否则您将无法调用它;))。作为另一种解决方案(尽管仍未解释手头的问题),您可以检查两个关键点向量的大小,并在必要时交换它们的位置。同样的问题也可以在http://answers.opencv.org/question/12048/drawmatches-bug/中看到,似乎交换应该已经解决了问题。这就是为什么我在第一个评论中还要求更多代码 - 特别是匹配过程。 - rbaleksandar
一个更好的输出提示:你可以使用findHomography()生成的掩码与RANSAC一起在drawMatches()中使用,以实际显示RANSAC点及其对应的匹配。显示结果应始终是最后一步,而不是第一步。 - rbaleksandar
我使用了ORB、SURF和SIFT,但我会保留SIFT。我已经编辑了我的帖子,以便向您展示我是如何做到的。关于你的提示,我没有完全理解,请再解释一遍好吗?你处理了吗?非常感谢! - lilouch
第二点 - 你能详细说明当matches为空时,你的代码背后的想法是什么吗?如果matches确实为空,为什么还要使用vector?第三点 - else块似乎完全错误。你在那里创建了img_matches并将其传递给cv::drawMatches(),但img_matches是空的!第四点 - 我刚注意到你将参数传递给函数的方式。尝试通过添加&(amp)来通过引用而不是值进行传递。没有必要传递副本。这里有一个问题:你尝试过调试你的代码吗?请这样做,并检查导致此错误的确切情况。 - rbaleksandar
还要注意你得到的错误:i2 >= 0 && i2 < static_cast(keypoints2.size())。这意味着,由于它是逻辑与,如果这两个术语中的任何一个或两个都为假,则整个过程失败。我认为>=0在某种程度上对你的问题负有责任,但我可能是错的。这就是为什么你需要调试并在你的问题中发布你的结果的原因。 - rbaleksandar
显示剩余14条评论
1个回答

6
对于像我这样搜寻但找不到解决方案的人来说,这篇文章很有帮助。
断言失败 (i2 >= 0 && i2 < static_cast(keypoints2.size()))。这意味着由于i2小于0或i2小于keypoints2大小而导致了断言失败。但是i2是什么?
从rbaleksandar提供的评论中的链接中可以看出,trainIdx在这里是keypoints2中的索引。检查i2 < static_cast(keypoints2.size())确保索引小于keypoints2.size()。
对我而言,它发生在我在计算描述符之后丢弃了一些关键点,然后调用drawMatches。这意味着通过描述符绘制匹配项时引用了旧的关键点,而我却改变了那些关键点。最终结果是一些关键点具有大的idx,但关键点大小很小,从而导致了错误。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接