« View All Resources

find-object: Simple Qt interface to try OpenCV implementations of feature detectors and descriptors (by Mathieu Labbé)

Computer Vision Central - Posted on January 5, 2012 at 11:39 am.

  • Links: http://code.google.com/p/find-object/
  • Details:
  • Simple Qt interface to try OpenCV implementations of SIFT, SURF, FAST, BRIEF and other feature detectors and descriptors

    Description

    Using a webcam:

    1. Go "Edit" -> "Add object...",
    2. Present an object,
    3. Select the features extracted from the object, return to main screen,
    4. Play ("Edit" -> "Start") and
    5. See highlighted features corresponding to the object.

    Features:

    • You can change any parameters at runtime, make it easier to test feature detectors and descriptors without always recompiling. Note that camera's parameters (except imageRate) take effect only when the camera is restarted ("Edit" -> "Stop" then "Start"). To change camera selection, modify "View->Parameters->Camera->deviceId" parameter (default is 0).

    • Detectors/descriptors supported (from OpenCV): BRIEF, Dense, FAST, GoodFeaturesToTrack, MSER, ORB, SIFT, STAR, SURF.
    • Mainly inspired from the sample find_obj.cpp from OpenCV! Sample code with the OpenCV C++ interface below...
    • For an example of an application using SURF descriptors: see my project RTAB-Map (an appearance-based loop closure detector for SLAM).

    Screenshots

    • Add object:

    • Enjoy detections:

    • Change algorithms' parameters (mapping OpenCV names), here using STAR detector and BRIEF descriptors:

    Code example

    • Here an example of features extraction (with OpenCV C++ interface) and nearest neighbor search with Flann library. This code can be found in this sample application main.cpp. Sample images from OpenCV : box.png and box_in_scene.png. Example results:
    • ./find_object-example box.png box_in_scene.png
      Object: 884 keypoints detected in 185 ms
      Scene: 1159 keypoints detected in 101 ms
      Object: 884 descriptors extracted in 86 ms
      Scene: 1159 descriptors extracted in 132 ms
      Time creating FLANN index = 7 ms
      Time nearest neighbor search = 10 ms
      Time finding homography = 11 ms
      Inliers=40 Outliers=68

    // OpenCV stuff
    #include <opencv2/core/core.hpp>
    #include <opencv2/highgui/highgui.hpp>
    #include <opencv2/features2d/features2d.hpp>
    #include <opencv2/calib3d/calib3d.hpp> // for homography

    //...

    // LOAD IMAGES (as grayscale)
    IplImage * objectImg = cvLoadImage(argv[1], CV_LOAD_IMAGE_GRAYSCALE);
    IplImage * sceneImg = cvLoadImage(argv[2], CV_LOAD_IMAGE_GRAYSCALE);

    std
    ::vector<cv::KeyPoint> objectKeypoints;
    std
    ::vector<cv::KeyPoint> sceneKeypoints;
    cv
    ::Mat objectDescriptors;
    cv
    ::Mat sceneDescriptors;

    ////////////////////////////
    // EXTRACT KEYPOINTS
    ////////////////////////////
    // The detector can be any of (see OpenCV features2d.hpp):
    // cv::FeatureDetector * detector = new cv::OrbFeatureDetector();
    // cv::FeatureDetector * detector = new cv::FastFeatureDetector();
    // cv::FeatureDetector * detector = new cv::MserFeatureDetector();
    // cv::FeatureDetector * detector = new cv::SiftFeatureDetector();
    // cv::FeatureDetector * detector = new cv::SurfFeatureDetector();
    // cv::FeatureDetector * detector = new cv::StarFeatureDetector();
    cv
    ::FeatureDetector * detector = new cv::SurfFeatureDetector();
    detector
    ->detect(objectImg, objectKeypoints);
    detector
    ->detect(sceneImg, sceneKeypoints);

    ////////////////////////////
    // EXTRACT DESCRIPTORS
    ////////////////////////////
    // The extractor can be any of (see OpenCV features2d.hpp):
    // cv::DescriptorExtractor * extractor = new cv::BriefDescriptorExtractor();
    // cv::DescriptorExtractor * extractor = new cv::OrbDescriptorExtractor();
    // cv::DescriptorExtractor * extractor = new cv::SiftDescriptorExtractor();
    // cv::DescriptorExtractor * extractor = new cv::SurfDescriptorExtractor();
    cv
    ::DescriptorExtractor * extractor = new cv::SurfDescriptorExtractor();
    extractor
    ->compute(objectImg, objectKeypoints, objectDescriptors);
    extractor
    ->compute(sceneImg, sceneKeypoints, sceneDescriptors);

    ////////////////////////////
    // NEAREST NEIGHBOR MATCHING USING FLANN LIBRARY (included in OpenCV)
    ////////////////////////////
    // Format descriptors for Flann
    cv
    ::Mat objectData;
    cv
    ::Mat sceneData;
    if(objectDescriptors.type()!=CV_32F) {
            objectDescriptors
    .convertTo(objectData, CV_32F); // make sure it's CV_32F
    }
    else {
            objectData
    = objectDescriptors;
    }
    if(sceneDescriptors.type()!=CV_32F) {
            sceneDescriptors
    .convertTo(sceneData, CV_32F); // make sure it's CV_32F
    }
    else {
            sceneData
    = sceneDescriptors;
    }

    // Create Flann index
    cv
    ::flann::Index treeFlannIndex(sceneData, cv::flann::KDTreeIndexParams());

    // search (nearest neighbor)
    int k=2; // find the 2 nearest neighbors
    cv
    ::Mat results(objectData.rows, k, CV_32SC1); // Results index
    cv
    ::Mat dists(objectData.rows, k, CV_32FC1); // Distance results are CV_32FC1
    treeFlannIndex
    .knnSearch(objectData, results, dists, k, cv::flann::SearchParams() );

    ////////////////////////////
    // PROCESS NEAREST NEIGHBOR RESULTS
    ////////////////////////////
    // Find correspondences by NNDR (Nearest Neighbor Distance Ratio)
    float nndrRatio = 0.6;
    std
    ::vector<cv::Point2f> mpts_1, mpts_2; // Used for homography
    std
    ::vector<int> indexes_1, indexes_2; // Used for homography
    std
    ::vector<uchar> outlier_mask;  // Used for homography
    for(unsigned int i=0; i<objectData.rows; ++i)
    {
           
    // Check if this descriptor matches with those of the objects
           
    // Apply NNDR
           
    if(dists.at<float>(i,0) <= nndrRatio * dists.at<float>(i,1))
           
    {
                    mpts_1
    .push_back(objectKeypoints.at(i).pt);
                    indexes_1
    .push_back(i);

                    mpts_2
    .push_back(sceneKeypoints.at(results.at<int>(i,0)).pt);
                    indexes_2
    .push_back(results.at<int>(i,0));
           
    }
    }

    // FIND HOMOGRAPHY
    int nbMatches = 8;
    if(mpts_1.size() >= nbMatches)
    {
            cv
    ::Mat H = findHomography(mpts_1,
                            mpts_2
    ,
                            cv
    ::RANSAC,
                           
    1.0,
                            outlier_mask
    );
           
    // Do what you want with the homography (like showing a rectangle)
           
    // The "outlier_mask" contains a mask representing the inliers and outliers.
           
    // ...
    }

    ////////////////////////////
    //Cleanup
    ////////////////////////////
    delete detector;
    delete extractor;
    cvReleaseImage
    (&objectImg);
    cvReleaseImage
    (&sceneImg);


    k();} ?>