现在位置: 首页 > OpenCV 教程 > 正文

C++ OpenCV 特征检测与描述

特征检测与描述是计算机视觉中的核心技术,用于从图像中提取关键点(Keypoints)并计算其描述符(Descriptors)。这些关键点和描述符可以用于图像匹配、目标识别、3D 重建等任务。

主要步骤

  • 特征检测:检测图像中的关键点(如角点、边缘等)。

  • 特征描述:为每个关键点计算描述符,用于表示关键点周围的局部特征。

  • 特征匹配:通过比较描述符,找到不同图像中相似的关键点。

  • 特征检测与描述:通过特征匹配找到两幅图像中相似的关键点,用于图像拼接、全景图生成等。
  • 目标识别:提取目标图像的特征,与数据库中的特征进行匹配,实现目标识别。
  • 3D 重建:通过多视图图像中的特征点匹配,计算相机的位姿并重建 3D 场景。
  • 实时跟踪:在视频序列中检测和跟踪特征点,用于 SLAM(同步定位与地图构建)等应用。

常用特征检测与描述算法

OpenCV 提供了多种特征检测与描述算法,以下是常见的几种:

算法特点适用场景
SIFT尺度不变特征变换,对旋转、缩放、亮度变化具有鲁棒性。图像匹配、目标识别
SURF加速版的 SIFT,计算速度更快,但对旋转和缩放变化的鲁棒性稍差。实时图像匹配
ORB基于 FAST 关键点检测和 BRIEF 描述符,速度快,适合实时应用。实时图像匹配、SLAM
FAST快速角点检测算法,仅检测关键点,不生成描述符。实时角点检测
BRIEF二进制描述符,计算速度快,但对旋转和缩放变化的鲁棒性较差。实时图像匹配
AKAZE基于非线性尺度空间的特征检测与描述算法,对旋转和缩放变化具有鲁棒性。图像匹配、目标识别

角点检测

角点是图像中亮度变化剧烈的点,通常位于物体的边缘或纹理丰富的区域。角点检测是特征检测的基础,常用的角点检测算法有 Harris 角点检测和 Shi-Tomasi 角点检测。

Harris角点检测

Harris 角点检测是一种经典的角点检测算法,它通过计算图像中每个像素点的自相关矩阵来判断该点是否为角点。自相关矩阵的特征值可以反映该点的局部结构:如果两个特征值都很大,则该点很可能是角点;如果一个特征值很大,另一个很小,则该点可能是边缘;如果两个特征值都很小,则该点可能是平坦区域。

在 OpenCV 中,可以使用 cv::cornerHarris 函数来实现 Harris 角点检测:

实例

#include <opencv2/opencv.hpp>

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

    // Harris角点检测
    cv::cornerHarris(src, dst, 2, 3, 0.04);

    // 归一化并显示结果
    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 角点检测

Shi-Tomasi角点检测是对Harris角点检测的改进,它通过计算图像中每个像素点的最小特征值来判断该点是否为角点。与Harris角点检测相比,Shi-Tomasi角点检测更加稳定,且计算量较小。

在OpenCV中,可以使用cv::goodFeaturesToTrack函数来实现Shi-Tomasi角点检测:

实例

#include <opencv2/opencv.hpp>

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

    // Shi-Tomasi角点检测
    cv::goodFeaturesToTrack(src, corners, 100, 0.01, 10);

    // 绘制角点
    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;
}

特征点检测

特征点检测是提取图像中具有独特性质的点,这些点通常具有旋转、缩放、光照不变性。常用的特征点检测算法有SIFT、SURF和ORB。

SIFT 算法

SIFT(Scale-Invariant Feature Transform)是一种基于尺度空间的特征点检测算法,它对图像的旋转、缩放、亮度变化具有不变性。SIFT算法通过检测图像中的极值点,并计算这些点的梯度方向直方图来生成特征描述子。

在 OpenCV 中,可以使用cv::xfeatures2d::SIFT类来实现SIFT特征点检测:

实例

#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特征点检测
    cv::Ptr<cv::xfeatures2d::SIFT> sift = cv::xfeatures2d::SIFT::create();
    sift->detectAndCompute(src, cv::noArray(), keypoints, descriptors);

    // 绘制特征点
    cv::Mat output;
    cv::drawKeypoints(src, keypoints, output);
    cv::imshow("SIFT Keypoints", output);
    cv::waitKey(0);

    return 0;
}

SURF 算法

SURF(Speeded-Up Robust Features)是对SIFT算法的改进,它通过使用积分图像和Hessian矩阵来加速特征点检测过程。SURF算法在保持较高检测精度的同时,显著提高了计算速度。

在OpenCV中,可以使用cv::xfeatures2d::SURF类来实现SURF特征点检测:

实例

#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特征点检测
    cv::Ptr<cv::xfeatures2d::SURF> surf = cv::xfeatures2d::SURF::create();
    surf->detectAndCompute(src, cv::noArray(), keypoints, descriptors);

    // 绘制特征点
    cv::Mat output;
    cv::drawKeypoints(src, keypoints, output);
    cv::imshow("SURF Keypoints", output);
    cv::waitKey(0);

    return 0;
}

ORB 算法

ORB(Oriented FAST and Rotated BRIEF)是一种结合了FAST角点检测和BRIEF描述子的特征点检测算法。ORB算法具有较高的计算效率,适合实时应用。

在OpenCV中,可以使用cv::ORB类来实现ORB特征点检测:

实例

#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特征点检测
    cv::Ptr<cv::ORB> orb = cv::ORB::create();
    orb->detectAndCompute(src, cv::noArray(), keypoints, descriptors);

    // 绘制特征点
    cv::Mat output;
    cv::drawKeypoints(src, keypoints, output);
    cv::imshow("ORB Keypoints", output);
    cv::waitKey(0);

    return 0;
}

特征匹配

特征匹配是将两幅图像中的特征点进行匹配的过程。常用的特征匹配算法有BFMatcher(暴力匹配器)和FLANN匹配器。

BFMatcher

BFMatcher(Brute-Force Matcher)是一种简单的特征匹配算法,它通过计算特征描述子之间的欧氏距离来寻找最佳匹配点。BFMatcher适用于特征点数量较少的情况。

在OpenCV中,可以使用cv::BFMatcher类来实现BFMatcher:

实例

#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特征点检测
    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匹配
    cv::BFMatcher matcher(cv::NORM_L2);
    std::vector<cv::DMatch> matches;
    matcher.match(descriptors1, descriptors2, matches);

    // 绘制匹配结果
    cv::Mat output;
    cv::drawMatches(src1, keypoints1, src2, keypoints2, matches, output);
    cv::imshow("BFMatcher Matches", output);
    cv::waitKey(0);

    return 0;
}

FLANN 匹配器

FLANN(Fast Library for Approximate Nearest Neighbors)是一种近似最近邻搜索算法,它通过构建KD树或K-means树来加速特征匹配过程。FLANN匹配器适用于特征点数量较多的情况。

在OpenCV中,可以使用cv::FlannBasedMatcher类来实现FLANN匹配器:

实例

#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特征点检测
    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匹配器
    cv::FlannBasedMatcher matcher;
    std::vector<cv::DMatch> matches;
    matcher.match(descriptors1, descriptors2, matches);

    // 绘制匹配结果
    cv::Mat output;
    cv::drawMatches(src1, keypoints1, src2, keypoints2, matches, output);
    cv::imshow("FLANN Matches", output);
    cv::waitKey(0);

    return 0;
}

特征点匹配与筛选

在实际应用中,特征点匹配结果中可能存在一些错误的匹配对。为了提高匹配的准确性,通常需要对匹配结果进行筛选。常用的筛选方法有基于距离的筛选和基于几何约束的筛选。

基于距离的筛选

基于距离的筛选是通过设置一个阈值来过滤掉距离较大的匹配对。OpenCV中可以使用cv::DescriptorMatcher::radiusMatch函数来实现基于距离的筛选。

实例

#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特征点检测
    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匹配
    cv::BFMatcher matcher(cv::NORM_L2);
    std::vector<std::vector<cv::DMatch>> matches;
    matcher.radiusMatch(descriptors1, descriptors2, matches, 50.0);

    // 绘制匹配结果
    cv::Mat output;
    cv::drawMatches(src1, keypoints1, src2, keypoints2, matches, output);
    cv::imshow("Filtered Matches", output);
    cv::waitKey(0);

    return 0;
}

基于几何约束的筛选

基于几何约束的筛选是通过计算基础矩阵或单应性矩阵来过滤掉不符合几何约束的匹配对。OpenCV中可以使用cv::findFundamentalMatcv::findHomography函数来实现基于几何约束的筛选。

实例

#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特征点检测
    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匹配
    cv::BFMatcher matcher(cv::NORM_L2);
    std::vector<cv::DMatch> matches;
    matcher.match(descriptors1, descriptors2, matches);

    // 基于几何约束的筛选
    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);

    // 筛选匹配对
    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]);
        }
    }

    // 绘制匹配结果
    cv::Mat output;
    cv::drawMatches(src1, keypoints1, src2, keypoints2, good_matches, output);
    cv::imshow("Filtered Matches", output);
    cv::waitKey(0);

    return 0;
}