Улучшить сопоставление характерных точек с OpenCV

Я хочу сопоставить характерные точки в стереоизображениях. Я уже нашел и извлек характерные точки с помощью разных алгоритмов, и теперь мне нужно хорошее сопоставление. В этом случае я использую алгоритмы 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. де)

только несколько хороших совпадений
(источник: codemax. де)

Я думаю, что это не проблема программирования, а скорее соответствие. Насколько я понял, 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 )


person filla2003    schedule 31.07.2013    source источник
comment
Один из лучших способов, которые я нашел, — игнорировать функции, которые слишком похожи на другие функции, т. Е. Оставлять только самые уникальные из них.   -  person Phylliida    schedule 08.05.2017
comment
Кстати, вы рассматриваете возможность использования ORB? Он обладает инвариантностью к вращению и масштабной инвариантности, чего, по-видимому, не имеет FAST.   -  person ch271828n    schedule 13.07.2019


Ответы (3)


Альтернативным методом определения качественных совпадений признаков является тест отношения, предложенный Дэвидом Лоу в его статье на 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]);
    }
}
person Aurelius    schedule 31.07.2013
comment
Как вы можете видеть в моем коде, я уже сделал тест для удаления неверных совпадений (хотя это немного отличается от вашего предложения). Вот почему у меня есть только несколько совпадений на моем втором изображении. Мне нужна идея, как улучшить сопоставление, прежде чем они дадут мне плохие совпадения, с которыми я должен разобраться. - person filla2003; 01.08.2013
comment
@ 20steffi03 Какой дескриптор вы используете для сопоставления? Некоторые дескрипторы лучше других, но отфильтровывать плохие совпадения практически наверняка необходимо. Вы уже выполняете перебор, который даст максимальное качество доступных совпадений. - person Aurelius; 01.08.2013
comment
Я сравнил несколько алгоритмов обнаружения признаков. В моем случае детектор FAST в сочетании с экстрактором SIFT дает наилучшие результаты. Как вы можете видеть в моем посте редактирования, я рассмотрел локальную позицию совпадения, что, надеюсь, улучшит сопоставление. Теперь мои результаты намного лучше. Спасибо за вашу помощь! - person filla2003; 02.08.2013
comment
Пожалуйста, измените критерий на match[i][1].distance - person Leeor; 09.01.2014
comment
+1 за упоминание о том, что Лоу предложил использовать порог 0.8. Я собирался свернуть с ≈0.5, как рекомендуют многие другие блоги/ответы. - person rinogo; 16.04.2021

Сравнив все алгоритмы обнаружения признаков, я нашел хорошую комбинацию, которая дает мне гораздо больше совпадений. Теперь я использую FAST для обнаружения функций, SIFT для извлечения функций и BruteForce для сопоставления. В сочетании с проверкой, находятся ли совпадения внутри определенной области, я получаю много совпадений, см. изображение:

много хороших совпадений с FAST и SIFT
(источник: 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();
        }
    }
}
person filla2003    schedule 02.08.2013
comment
Для университетского проекта я изучаю применение openCV для расчета разницы в стереоизображениях. Судя по всему, вам удалось получить надежный алгоритм. Не могли бы вы поделиться своей реализацией, например. интерфейс с openCV и т.д.? - person cujo30227; 06.11.2016
comment
Два замечания: я не вижу DynamicAdaptedFeatureDetector в OpenCV 3 и, что более важно, SIFT и SURF являются запатентованными алгоритмами. - person Yuriy Chernyshov; 09.03.2017
comment
@YuriyChernyshov SIFT И SURF действительно являются запатентованными алгоритмами, но если вы используете их в исследовательских целях, взгляните только на их лицензию на авторские права, как будто память мне не изменяет, вы можете свободно использовать их в исследовательских целях при условии, что вы заявляете, что это не ваш алгоритм. . - person C.Radford; 11.07.2017

Помимо проверки соотношения, вы можете:

Используйте только симметричные совпадения:

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);
        }
    }
}
person András Kovács    schedule 04.08.2013
comment
какие типичные значения можно использовать для расстояния, достоверности, minInlierRatio? - person mkuse; 29.07.2015
comment
Но он использует совпадение Knn, которое возвращает вектор векторов совпадений. - person Mr.WorshipMe; 12.01.2016