OpenCV Sift/Surf/Orb: функция drawMatch не работает должным образом

Я использую Sift/Surf и ORB, но иногда у меня возникают проблемы с функцией drawMatch.

Здесь ошибка:

Ошибка OpenCV: утверждение не выполнено (i2 >= 0 && i2 ‹ static_cast(keypoints2.size())) в drawMatches, файл /home/opencv-2.4.6.1/modules/features2d/src/draw.cpp, строка 208 завершена после вызова создание экземпляра 'cv::Exception' what(): /home/opencv-2.4.6.1/modules/features2d/src/draw.cpp:208: error: (-215) i2 >= 0 && i2 ‹ static_cast( keypoints2.size()) в функции drawMatche

Код :

drawMatchPoints(img1,keypoints_img1,img2,keypoints_img2,matches);

Я попытался инвертировать img 1, 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);

        }

}

Есть ли у вас какие-либо предложения или советы?

EDIT: я решил свою проблему. У меня была проблема с С++, потому что у меня было два класса. Один о сопоставлении, а другой о поиске keyFeature. Я написал на своем .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 и использовать локальный параметр в моей функции.

Спасибо !


person lilouch    schedule 03.07.2014    source источник
comment
Насколько я знаю, нет такой вещи, как cv::drawMatchPoints(). Однако есть cv::drawMatches(). Можете ли вы предоставить больше информации и код? Разница в количестве ключевых точек на обоих изображениях не должна создавать проблем, поскольку cv::drawMatches() использует совпадающие данные для отображения фактических совпадений. Все остальные ключевые точки просто рисуются с помощью cv::drawKeypoints(), как видно из исходного кода по адресу github.com/Itseez/opencv/blob/master/modules/features2d/src/ и #L189.   -  person rbaleksandar    schedule 03.07.2014
comment
Что касается незнания заранее части - вы действительно знаете размер каждого вектора ключевой точки для каждого изображения ДО того, как вы вызовете cv::drawMatches() (иначе вы не сможете его вызвать;)). В качестве альтернативного решения (хотя все еще не объясняющего проблему) вы можете проверить размеры обоих векторов ключевых точек и при необходимости поменять их местами. Ту же проблему можно увидеть на странице answers.opencv.org/question/12048/drawmatches- ошибка и кажется, что замена должна была решить проблему. Вот почему я также попросил больше кода в первом комментарии - в частности, процедуру сопоставления   -  person rbaleksandar    schedule 03.07.2014
comment
Да, извините, я отредактировал свой пост. На самом деле drawMatchPoint — это созданная мной функция, которая содержит функцию cv::drawMatches(). Но я попытался поменять два параметра, но это не сработало. У меня такая же ошибка. Что касается ссылки на answer.opencv, я уже посмотрел... Спасибо!   -  person lilouch    schedule 03.07.2014
comment
Хм, а какой матчер ты используешь? Также вы можете показать точный код, в котором вы создаете свой вектор совпадений? Я только что проверил свой собственный код, где я использую ORB для сшивания аэрофотоснимков вместе, и я выполняю перекрестное сопоставление каждого изображения со всеми остальными. Я настроил свой ORB, чтобы попытаться обнаружить 600 функций на изображении. Кто-то вернул 600, а кто-то меньше (например, 569), что автоматически попадает в вашу ситуацию, однако таких ошибок не было и все прошло по плану.   -  person rbaleksandar    schedule 03.07.2014
comment
Совет для лучшего вывода: вы можете использовать маску, сгенерированную findHomography() с RANSAC в drawMatches(), чтобы фактически показать точки RANSAC и соответствующие им совпадения. Отображение результатов ВСЕГДА должно быть вашим последним шагом, а не первым.   -  person rbaleksandar    schedule 03.07.2014
comment
Я использовал ORB, SURF и SIFT, но оставлю SIFT. Я отредактировал свой пост, чтобы показать вам, как я это делаю. Для вашего совета, я не очень хорошо понял, не могли бы вы объяснить мне еще раз, пожалуйста? А вы это обрабатываете? Большое спасибо !   -  person lilouch    schedule 04.07.2014
comment
Еще нужна соответствующая деталь. Извлечение признаков в данном случае не важно, так как ваша проблема как-то связана с их разницей в количестве, а не в самих признаках. Что касается моего совета: findHomography(), а также findFundamentalMat() имеют последний параметр, называемый mask, который имеет значение по умолчанию cv::noArray(). Если вы передаете свою собственную матрицу, информация, хранящаяся в ней, может быть использована cv::drawMatches() (эта временная маска находится на одну позицию перед последним параметром, то есть DrawMatchesFlag) для рисования линий только между ключевыми точками RANSACed (это не единственное приложение, хотя).   -  person rbaleksandar    schedule 04.07.2014
comment
Извините, хорошо, я положил соответствующую часть. Итак, я должен сделать что-то подобное: cv::Mat arrayRansac; Mat H = findHomography(obj, scene, CV_RANSAC,3,arrayRansac); А после использования функции drawMatch путем замены параметра vector‹char›() ? Спасибо   -  person lilouch    schedule 04.07.2014
comment
Да, это правильно. cv::Mat де-факто является вектором векторов некоторого типа (в данном случае — char). Что касается вашего кода, я вижу некоторые ошибки, и ошибка может исходить оттуда. Во-первых, всегда будьте осторожны, когда вы выполняете дополнительную фильтрацию своих совпадений, ОСОБЕННО, когда вы применяете к ним RANSAC позже. Принцип работы RANSAC заключается в том, что он требует как ошибочных данных, так и правильных. Если вы отфильтруете большинство (или даже все) плохих совпадений (в вашем случае с использованием минимального/максимального расстояния), RANSAC получит набор в основном хороших совпадений => нестабильные результаты или даже сбой при вычислении гомографии.   -  person rbaleksandar    schedule 04.07.2014
comment
Во-вторых, можете ли вы уточнить, в чем именно заключается ваша идея кода, когда совпадения пусты? Если спички действительно пусты, зачем вообще мыслитель с вектором? В-третьих, блок else кажется совершенно неправильным. Вы создаете там img_matches и передаете его cv::drawMatches() в следующей строке, но img_matches пуст! В-четвертых, только что заметил, как вы передаете свои аргументы в свою функцию. Попробуйте передать по ссылке, а не по значению, добавив & (amp). Нет необходимости передавать копию. Вот вопрос: вы вообще пробовали отлаживать свой код? Пожалуйста, сделайте это и проверьте, какие именно обстоятельства вызывают эту ошибку.   -  person rbaleksandar    schedule 04.07.2014
comment
Также обратите внимание на полученную ошибку: i2 ›= 0 && i2 ‹ static_cast(keypoints2.size()). Это означает, что, поскольку это логическое И, если одно или оба этих условия ложны, все терпит неудачу. Мне кажется, что ›=0 как-то отвечает за вашу проблему, но я могу ошибаться. Вот почему вам нужно отладить и опубликовать свои результаты в своем вопросе.   -  person rbaleksandar    schedule 04.07.2014
comment
Еще одна вещь о хороших совпадениях - stackoverflow.com/questions/24456788/ Вы можете использовать маску для получения хороших совпадений (в моем случае меня интересовали точки), поскольку каждая ячейка внутри маски, отмеченная цифрой 1, представляет собой хорошее совпадение между двумя точками, прошедшими процедуру RANSAC. Вы можете использовать индекс этой ячейки и извлечь соответствующее хорошее совпадение из вашего вектора совпадений.   -  person rbaleksandar    schedule 05.07.2014
comment
Спасибо за полный ответ! На самом деле я начинаю с openCv, поэтому, возможно, я делаю большую ошибку. Я посмотрю и научусь отлаживать, чтобы увидеть, в чем проблема (возможно, у вас есть предложения по этому поводу)! Однако я напечатал все параметры hte, прежде чем передавать их функциям drawMatche, но они не пусты. непосредственно перед функцией drawMatch Размер keypoints_img1: 84 Размер keypoints_img2: 400 Размер совпадений: 400 относительно того, пусты ли совпадения, я сделал это только для того, чтобы показать два изображения вместе без совпадений... (Так что нет линий между две картинки)   -  person lilouch    schedule 07.07.2014
comment
Хорошо, все еще остается дело об отладке. ;) Насчет глупых ошибок - их нет, если на них не учиться. Для части отладки OpenCV — это просто библиотека, поэтому вы можете использовать свои знания для предыдущих программ C/C++, которые вы написали и отладили, чтобы увидеть, что происходит под поверхностью. Извините, больше пока не могу. Я сам недоумеваю, почему это происходит, особенно учитывая, что я проверил теорию с разными ключевыми точками в своем собственном коде, и она работает просто отлично.   -  person rbaleksandar    schedule 07.07.2014
comment
я решил свою проблему... Если вы знаете, почему, можете ли вы объяснить? смотрите в конце моего поста!   -  person lilouch    schedule 07.07.2014
comment
Вау, я только что заметил, что вы используете пользовательские функции для всего — определение ключевых точек, вычисление дескрипторов. Это очень ценная информация, и я пропустил ее, мой плохой. Я не могу сказать вам, почему это решило вашу проблему, так как я не знаю, как в вашем случае работает detect_Keypoints() и calculate_Descriptors(). Однако один небольшой совет - то, как вы делаете что-то в своем коде, очень запутанно и трудно читать. Проблема с использованием таких пользовательских подпрограмм в вашем приложении OpenCV заключается в том, что кто-то, знакомый с OpenCV, сделает определенное предположение о том, что делают ваши функции, что может быть неверным.   -  person rbaleksandar    schedule 07.07.2014
comment
да, я сделал таможенную функцию, потому что, когда я реализовал ORB, SURF и SIFT с сопоставлением различий, я хотел попытаться сделать что-то хорошее, создав два класса... Если у вас есть время, могу я отправить вам свой код (не так долго ) и подскажите, что я могу улучшить? Потому что теперь, если бы вы сказали мне, что я не должен выполнять таможенные функции, это будет немного беспорядок, не так ли?   -  person lilouch    schedule 08.07.2014
comment
Извините, сейчас очень занят. Однако мой совет здесь таков: если вы пишете пользовательские функции (нередкая практика в зависимости от поставленной задачи), убедитесь, что вы включили подробную информацию о них, когда публикуете вопрос. В противном случае люди, пытающиеся помочь вам, понятия не имеют, на что они смотрят. Это особенно актуально, ЕСЛИ вы выполняете дополнительную обработку в этих функциях (в вашем случае вы ТАКЖЕ фильтруете хорошие совпадения!). Как мы видели здесь, проблема заключается в пользовательской функции, которую вы закодировали. Пишите подробно, НО используйте только простые примеры, которые показывают, как работает ваш код.   -  person rbaleksandar    schedule 08.07.2014
comment
Окей я понял! Большое спасибо за ваше терпение и вашу помощь!   -  person lilouch    schedule 09.07.2014


Ответы (1)


Для кого-то вроде меня, который искал это, но не смог найти решение.

Ошибка утверждения (i2 >= 0 && i2 ‹ static_cast(keypoints2.size()))

Это означает, что утверждение не выполнено из-за того, что i2 меньше 0 или i2 меньше размера keypoints2. Но что такое i2?

По ссылке, предоставленной rbaleksandar в комментарии

int i2 = match1to2[m].trainIdx;

trainIdx здесь — индекс в keypoints2. Проверка i2 ‹ static_cast(keypoints2.size()) гарантирует, что индекс меньше, чем keypoints2.size().

Для меня это произошло потому, что я отбросил некоторые ключевые точки перед вызовом drawMatches, но после того, как были вычислены дескрипторы, т.е. был вызван DescriptorExtractor#compute. Это означало, что drawMatches ссылались на старые ключевые точки через дескрипторы, в то время как я изменял эти ключевые точки. Конечным результатом было то, что у некоторых ключевых точек был большой idx, но размер ключевых точек был маленьким, отсюда и ошибка.

person str7    schedule 14.03.2016