web-dev-qa-db-fra.com

Améliorez la correspondance des points de fonctionnalité avec OpenCV

Je souhaite faire correspondre les points de fonctionnalité des images stéréo. J'ai déjà trouvé et extrait les points de fonctionnalité avec différents algorithmes et maintenant j'ai besoin d'une bonne correspondance. Dans ce cas, j'utilise les algorithmes FAST pour la détection et l'extraction et le BruteForceMatcher pour faire correspondre les points de fonctionnalité.

Le code correspondant:

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]);     
}

Parce qu'il y a beaucoup de fausses correspondances, j'ai calu la distance min et max et supprimé toutes les correspondances qui sont trop mauvaises:

//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]); 
    }
}

Le problème est que j'obtiens beaucoup de fausses correspondances ou seulement quelques bonnes (voir les images ci-dessous).

many matches with bad results
(source: codemax.de )

only a few good matches
(source: codemax.de )

Je pense que ce n'est pas un problème de programmation mais plutôt une chose correspondante. Pour autant que je sache, le BruteForceMatcher ne concerne que la distance visuelle des points caractéristiques (qui est stockée dans le FeatureExtractor), pas la distance locale (position x & y), ce qui est dans mon cas important, aussi. Quelqu'un at-il des expériences avec ce problème ou une bonne idée pour améliorer les résultats correspondants?

[~ # ~] modifier [~ # ~]

J'ai changé le code, ça me donne les 50 meilleures correspondances. Après cela, je passe par le premier match pour vérifier si c'est dans une zone spécifiée. Si ce n'est pas le cas, je prends le match suivant jusqu'à ce que j'aie trouvé un match à l'intérieur de la zone donnée.

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();
    }
}

Je pense que je n'obtiens pas plus de correspondances, mais avec cela je peux supprimer plus de fausses correspondances:

less but better features
(source: codemax.de )

27
filla2003

En comparant tous les algorithmes de détection de fonctionnalités, j'ai trouvé une bonne combinaison, ce qui me donne beaucoup plus de correspondances. Maintenant, j'utilise FAST pour la détection des fonctionnalités, SIFT pour l'extraction des fonctionnalités et BruteForce pour la correspondance. Combiné avec la vérification, si les correspondances sont à l'intérieur d'une région définie, j'obtiens beaucoup de correspondances, voir l'image:

a lot of good matches with FAST and SIFT
(source: codemax.de )

Le code correspondant:

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();
        }
    }
}
24
filla2003

Une autre méthode pour déterminer les correspondances de fonctionnalités de haute qualité est le test de rapport proposé par David Lowe dans son article sur SIFT (page 20 pour une explication). Ce test rejette les mauvaises correspondances en calculant le rapport entre la meilleure et la deuxième meilleure correspondance. Si le rapport est inférieur à un certain seuil, la correspondance est rejetée comme étant de faible qualité.

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]);
    }
}
27
Aurelius

Outre le test de rapport, vous pouvez:

Utilisez uniquement des correspondances symétriques:

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;
            }
        }
    }
}

et puisque c'est une image stéréo, utilisez le test 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);
        }
    }
}
7
András Kovács