C++ OpenCV Feature Detection and Description

Feature detection and description is a core technology in computer vision, used to extract keypoints from images and compute their descriptors. These keypoints and descriptors can be used for tasks such as image matching, object recognition, and 3D reconstruction.

Main Steps

  • Feature Detection: Detects keypoints in an image (such as corners, edges, etc.).

  • Feature Description: Computes a descriptor for each keypoint to represent the local features around the keypoint.

  • Feature Matching: Finds similar keypoints in different images by comparing descriptors.

  • Feature detection and description: Find similar keypoints in two images through feature matching, used for image stitching, panorama generation, etc.
  • Object Recognition: Extract features from the target image, match them with features in the database, and achieve object recognition.
  • 3D Reconstruction: Compute camera pose and reconstruct a 3D scene by matching feature points in multi-view images.
  • Real-time Tracking: Detect and track feature points in video sequences, used in applications such as SLAM (Simultaneous Localization and Mapping).

Common Feature Detection and Description Algorithms

OpenCV provides a variety of feature detection and description algorithms. The following are some common ones:

AlgorithmCharacteristicsApplicable Scenarios
SIFTScale-Invariant Feature Transform, robust to rotation, scaling, and brightness changes.Image matching, object recognition
SURFSpeeded-up version of SIFT, faster computation, but slightly less robust to rotation and scaling changes.Real-time image matching
ORBBased on FAST keypoint detection and BRIEF descriptors, fast and suitable for real-time applications.Real-time image matching, SLAM
FASTFast corner detection algorithm, only detects keypoints, does not generate descriptors.Real-time corner detection
BRIEFBinary descriptor, fast computation, but less robust to rotation and scaling changes.Real-time image matching
AKAZEFeature detection and description algorithm based on nonlinear scale space, robust to rotation and scaling changes.Image matching, object recognition

Corner Detection

Corners are points in an image where brightness changes sharply, usually located at object edges or texture-rich areas. Corner detection is the foundation of feature detection. Common corner detection algorithms include Harris corner detection and Shi-Tomasi corner detection.

Harris Corner Detection

Harris corner detection is a classic corner detection algorithm. It determines whether a pixel is a corner by computing the autocorrelation matrix for each pixel in the image. The eigenvalues of the autocorrelation matrix reflect the local structure of the pixel: if both eigenvalues are large, the pixel is likely a corner; if one eigenvalue is large and the other is small, the pixel may be an edge; if both eigenvalues are small, the pixel may be in a flat region.

In OpenCV, you can use thecv::cornerHarrisfunction to implement Harris corner detection:

Example

#include <opencv2/opencv.hpp>

int main() {
    cv::Mat src = cv::imread("image.jpg", cv::IMREAD_GRAYSCALE);
    cv::Mat dst, dst_norm;

    // Harris corner detection
    cv::cornerHarris(src, dst, 2, 3, 0.04);

    // Normalize and display the result
    cv::normalize(dst, dst_norm, 0, 255, cv::NORM_MINMAX, CV_32FC1, cv::Mat());
    cv::imshow("Harris Corners", dst_norm);
    cv::waitKey(0);

    return 0;
}

Shi-Tomasi Corner Detection

Shi-Tomasi corner detection is an improvement over Harris corner detection. It determines whether a pixel is a corner by computing the minimum eigenvalue for each pixel in the image. Compared with Harris corner detection, Shi-Tomasi corner detection is more stable and requires less computation.

In OpenCV, you can use thecv::goodFeaturesToTrackfunction to implement Shi-Tomasi corner detection:

Example

#include <opencv2/opencv.hpp>

int main() {
    cv::Mat src = cv::imread("image.jpg", cv::IMREAD_GRAYSCALE);
    std::vector<cv::Point2f> corners;

    // Shi-Tomasi corner detection
    cv::goodFeaturesToTrack(src, corners, 100, 0.01, 10);

    // Draw corners
    for (size_t i = 0; i < corners.size(); i++) {
        cv::circle(src, corners[i], 5, cv::Scalar(0, 0, 255), 2);
    }

    cv::imshow("Shi-Tomasi Corners", src);
    cv::waitKey(0);

    return 0;
}

Feature Point Detection

Feature point detection is the extraction of points with distinctive properties in an image. These points are usually invariant to rotation, scaling, and illumination. Common feature point detection algorithms include SIFT, SURF, and ORB.

SIFT Algorithm

SIFT (Scale-Invariant Feature Transform) is a feature point detection algorithm based on scale space. It is invariant to image rotation, scaling, and brightness changes. The SIFT algorithm detects extremum points in an image and computes gradient orientation histograms for these points to generate feature descriptors.

In OpenCV, you can use thecv::xfeatures2d::SIFTclass to implement SIFT feature point detection:

Example

#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>

int main() {
    cv::Mat src = cv::imread("image.jpg", cv::IMREAD_GRAYSCALE);
    std::vector<cv::KeyPoint> keypoints;
    cv::Mat descriptors;

    // SIFT feature point detection
    cv::Ptr<cv::xfeatures2d::SIFT> sift = cv::xfeatures2d::SIFT::create();
    sift->detectAndCompute(src, cv::noArray(), keypoints, descriptors);

    // Draw feature points
    cv::Mat output;
    cv::drawKeypoints(src, keypoints, output);
    cv::imshow("SIFT Keypoints", output);
    cv::waitKey(0);

    return 0;
}

SURF Algorithm

SURF (Speeded-Up Robust Features) is an improvement over the SIFT algorithm. It uses integral images and the Hessian matrix to speed up the feature point detection process. The SURF algorithm significantly improves computation speed while maintaining high detection accuracy.

In OpenCV, you can use thecv::xfeatures2d::SURFclass to implement SURF feature point detection:

Example

#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>

int main() {
    cv::Mat src = cv::imread("image.jpg", cv::IMREAD_GRAYSCALE);
    std::vector<cv::KeyPoint> keypoints;
    cv::Mat descriptors;

    // SURF feature point detection
    cv::Ptr<cv::xfeatures2d::SURF> surf = cv::xfeatures2d::SURF::create();
    surf->detectAndCompute(src, cv::noArray(), keypoints, descriptors);

    // Draw feature points
    cv::Mat output;
    cv::drawKeypoints(src, keypoints, output);
    cv::imshow("SURF Keypoints", output);
    cv::waitKey(0);

    return 0;
}

ORB Algorithm

ORB (Oriented FAST and Rotated BRIEF) is a feature point detection algorithm that combines FAST corner detection and the BRIEF descriptor. The ORB algorithm has high computational efficiency and is suitable for real-time applications.

In OpenCV, you can use thecv::ORBclass to implement ORB feature point detection:

Example

#include <opencv2/opencv.hpp>

int main() {
    cv::Mat src = cv::imread("image.jpg", cv::IMREAD_GRAYSCALE);
    std::vector<cv::KeyPoint> keypoints;
    cv::Mat descriptors;

    // ORB feature point detection
    cv::Ptr<cv::ORB> orb = cv::ORB::create();
    orb->detectAndCompute(src, cv::noArray(), keypoints, descriptors);

    // Draw feature points
    cv::Mat output;
    cv::drawKeypoints(src, keypoints, output);
    cv::imshow("ORB Keypoints", output);
    cv::waitKey(0);

    return 0;
}

Feature Matching

Feature matching is the process of matching feature points between two images. Common feature matching algorithms include BFMatcher (Brute-Force Matcher) and the FLANN matcher.

BFMatcher

BFMatcher (Brute-Force Matcher) is a simple feature matching algorithm that finds the best matching points by computing the Euclidean distance between feature descriptors. BFMatcher is suitable for cases with a small number of feature points.

In OpenCV, you can use thecv::BFMatcherclass to implement BFMatcher:

Example

#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>

int main() {
    cv::Mat src1 = cv::imread("image1.jpg", cv::IMREAD_GRAYSCALE);
    cv::Mat src2 = cv::imread("image2.jpg", cv::IMREAD_GRAYSCALE);
    std::vector<cv::KeyPoint> keypoints1, keypoints2;
    cv::Mat descriptors1, descriptors2;

    // SIFT feature point detection
    cv::Ptr<cv::xfeatures2d::SIFT> sift = cv::xfeatures2d::SIFT::create();
    sift->detectAndCompute(src1, cv::noArray(), keypoints1, descriptors1);
    sift->detectAndCompute(src2, cv::noArray(), keypoints2, descriptors2);

    // BFMatcher matching
    cv::BFMatcher matcher(cv::NORM_L2);
    std::vector<cv::DMatch> matches;
    matcher.match(descriptors1, descriptors2, matches);

    // Draw matching results
    cv::Mat output;
    cv::drawMatches(src1, keypoints1, src2, keypoints2, matches, output);
    cv::imshow("BFMatcher Matches", output);
    cv::waitKey(0);

    return 0;
}

FLANN Matcher

FLANN (Fast Library for Approximate Nearest Neighbors) is an approximate nearest neighbor search algorithm that speeds up the feature matching process by building KD-trees or K-means trees. The FLANN matcher is suitable for cases with a large number of feature points.

In OpenCV, you can use thecv::FlannBasedMatcherclass to implement the FLANN matcher:

Example

#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>

int main() {
    cv::Mat src1 = cv::imread("image1.jpg", cv::IMREAD_GRAYSCALE);
    cv::Mat src2 = cv::imread("image2.jpg", cv::IMREAD_GRAYSCALE);
    std::vector<cv::KeyPoint> keypoints1, keypoints2;
    cv::Mat descriptors1, descriptors2;

    // SIFT feature point detection
    cv::Ptr<cv::xfeatures2d::SIFT> sift = cv::xfeatures2d::SIFT::create();
    sift->detectAndCompute(src1, cv::noArray(), keypoints1, descriptors1);
    sift->detectAndCompute(src2, cv::noArray(), keypoints2, descriptors2);

    // FLANN matcher
    cv::FlannBasedMatcher matcher;
    std::vector<cv::DMatch> matches;
    matcher.match(descriptors1, descriptors2, matches);

    // Draw matching results
    cv::Mat output;
    cv::drawMatches(src1, keypoints1, src2, keypoints2, matches, output);
    cv::imshow("FLANN Matches", output);
    cv::waitKey(0);

    return 0;
}

Feature Point Matching and Filtering

In practical applications, the feature point matching results may contain some incorrect matching pairs. To improve matching accuracy, it is usually necessary to filter the matching results. Common filtering methods include distance-based filtering and geometric constraint-based filtering.

Distance-based Filtering

Distance-based filtering removes matching pairs with larger distances by setting a threshold. In OpenCV, you can use thecv::DescriptorMatcher::radiusMatchfunction to implement distance-based filtering.

Example

#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>

int main() {
    cv::Mat src1 = cv::imread("image1.jpg", cv::IMREAD_GRAYSCALE);
    cv::Mat src2 = cv::imread("image2.jpg", cv::IMREAD_GRAYSCALE);
    std::vector<cv::KeyPoint> keypoints1, keypoints2;
    cv::Mat descriptors1, descriptors2;

    // SIFT feature point detection
    cv::Ptr<cv::xfeatures2d::SIFT> sift = cv::xfeatures2d::SIFT::create();
    sift->detectAndCompute(src1, cv::noArray(), keypoints1, descriptors1);
    sift->detectAndCompute(src2, cv::noArray(), keypoints2, descriptors2);

    // BFMatcher matching
    cv::BFMatcher matcher(cv::NORM_L2);
    std::vector<std::vector<cv::DMatch>> matches;
    matcher.radiusMatch(descriptors1, descriptors2, matches, 50.0);

    // Draw matching results
    cv::Mat output;
    cv::drawMatches(src1, keypoints1, src2, keypoints2, matches, output);
    cv::imshow("Filtered Matches", output);
    cv::waitKey(0);

    return 0;
}

Geometric Constraint-based Filtering

Geometric constraint-based filtering removes matching pairs that do not satisfy geometric constraints by computing the fundamental matrix or homography matrix. In OpenCV, you can use thecv::findFundamentalMatorcv::findHomographyfunction to implement geometric constraint-based filtering.

Example

#include <opencv2/opencv.hpp>
#include <opencv2/xfeatures2d.hpp>

int main() {
    cv::Mat src1 = cv::imread("image1.jpg", cv::IMREAD_GRAYSCALE);
    cv::Mat src2 = cv::imread("image2.jpg", cv::IMREAD_GRAYSCALE);
    std::vector<cv::KeyPoint> keypoints1, keypoints2;
    cv::Mat descriptors1, descriptors2;

    // SIFT feature point detection
    cv::Ptr<cv::xfeatures2d::SIFT> sift = cv::xfeatures2d::SIFT::create();
    sift->detectAndCompute(src1, cv::noArray(), keypoints1, descriptors1);
    sift->detectAndCompute(src2, cv::noArray(), keypoints2, descriptors2);

    // BFMatcher matching
    cv::BFMatcher matcher(cv::NORM_L2);
    std::vector<cv::DMatch> matches;
    matcher.match(descriptors1, descriptors2, matches);

    // Geometric constraint-based filtering
    std::vector<cv::Point2f> points1, points2;
    for (size_t i = 0; i < matches.size(); i++) {
        points1.push_back(keypoints1[matches[i].queryIdx].pt);
        points2.push_back(keypoints2[matches[i].trainIdx].pt);
    }

    cv::Mat mask;
    cv::findHomography(points1, points2, cv::RANSAC, 3, mask);

    // Filter matching pairs
    std::vector<cv::DMatch> good_matches;
    for (size_t i = 0; i < matches.size(); i++) {
        if (mask.at<uchar>(i)) {
            good_matches.push_back(matches[i]);
        }
    }

    // Draw matching results
    cv::Mat output;
    cv::drawMatches(src1, keypoints1, src2, keypoints2, good_matches, output);
    cv::imshow("Filtered Matches", output);
    cv::waitKey(0);

    return 0;
}
Other Extensions