SIFT算法的特征点筛选和DMatch、Keypoint描述

SIFT算法描述

SIFT(Scale-invariant feature transform)是一种检测局部特征的算法,该算法通过求一幅图中的特征点(interest points,or corner points)及其有关scaleorientation 的描述子得到特征并进行图像特征点匹配
这个算法具有比较良好的尺度不变性和旋转不变形

KeyPoint

KeyPoint类的成员变量和描述

class KeyPoint
{
Point2f  pt;     //特征点坐标
float  size;     //特征点邻域直径
float  angle;    //特征点的方向,值为0~360,负值表示不使用
float  response; //特征点的响应强度,代表了该点是特征点的稳健度,可以用于后续处理中特征点排序
int  octave;     //特征点所在的图像金字塔的层组
int  class_id;   //用于聚类的id
}

特征点主要保存了特征点的位置、领域直径、方向、响应强度、金字塔的层组、聚类ID

DMatch

DMatch,相信用过FAST、SURF、ORB等特征点匹配和识别算法的都遇到过这个类型,这个类型最主要的就是存储了两个特征点在各自keyPoints中的下标值,然后通过drawMatches的API实现两张图片的特征点连线。

对于如下的代码:

Mat input1 = imread("img2.jpg", 0);
Mat input2 = imread("img1.jpg", 0);
SiftFeatureDetector detector;
vector<KeyPoint> keypoints1, keypoints2;
detector.detect(input1, keypoints1);
detector.detect(input2, keypoints2);

SiftDescriptorExtractor extractor;
Mat descriptor1, descriptor2;

BruteForceMatcher<L2<float>> matcher;
vector<DMatch> matches;

extractor.compute(input1, keypoints1, descriptor1);
extractor.compute(input2, keypoints2, descriptor2);
matcher.match(descriptor1, descriptor2, matches);

我们获得了两张图的特征点,然后通过BruteForceMatcher<L2<float>>::match函数获得了matches

这个matchs里面存储的就是相对于keypoints1keypoints2这两个特征点组对应点的下标值和其欧拉距离

其实现如下:

struct CV_EXPORTS DMatch
{
    DMatch() : queryIdx(-1), trainIdx(-1), imgIdx(-1), distance(std::numeric_limits<float>::max()) {}
    DMatch( int _queryIdx, int _trainIdx, float _distance ) :
            queryIdx(_queryIdx), trainIdx(_trainIdx), imgIdx(-1), distance(_distance) {}
    DMatch( int _queryIdx, int _trainIdx, int _imgIdx, float _distance ) :
            queryIdx(_queryIdx), trainIdx(_trainIdx), imgIdx(_imgIdx), distance(_distance) {}

    int queryIdx; // query descriptor index
    int trainIdx; // train descriptor index
    int imgIdx;   // train image index
ss
    float distance;

    // less is better
    bool operator<( const DMatch &m ) const
    {
        return distance < m.distance;
    }
};

成员变量意义如下:(针对如上的示例代码)

  • queryIdx :特征点在keypoints1中的下标号
  • trainIdx :特征点在keypoints2中的下标号
  • imgIdx :当前匹配点对应训练图像(如果有若干个)的索引,
    如果只有一个训练图像跟查询图像配对,即两两配对,则imgIdx=0
  • distance:对应特征点之间的欧氏距离,越小表明匹配度越高

通过其operator <重载函数可以看出,distance越小,代表DMatch的匹配率越高,同时代表DMatch中的两个对应特征点匹配度越高

所以,基于这个原理,我们可以得出一个通过SIFT算法(任意方法得到的KeyPoints都可以)得到的KeyPoints进行优化筛选的方案 —— 通过比较DMatch的欧式距离的方式去筛选出欧式距离较小的DMatch
这样筛选出来的的DMatch就代表了更好的匹配性,我们可以为此做一个测试

不进行欧拉距离比对筛选的特征点匹配

这里我们直接使用SIFT算法找出keyPoints然后直接进行比对

代码如下

int main()
{
    Mat input1 = imread("img2.png", 1);
    Mat input2 = imread("img1.png", 1);
    Mat imgGray1, imgGray2;
    //转换灰度图
    cvtColor(input1, imgGray1, CV_BGR2GRAY);
    cvtColor(input2, imgGray2, CV_BGR2GRAY);
    SiftFeatureDetector detector;
    vector<KeyPoint> keypoint1, keypoint2;
    detector.detect(imgGray1, keypoint1);
    detector.detect(imgGray2, keypoint2);

    SiftDescriptorExtractor extractor;
    Mat descriptor1, descriptor2;

    BruteForceMatcher<L2<float>> matcher;
    vector<DMatch> matches;

    extractor.compute(imgGray1, keypoint1, descriptor1);
    extractor.compute(imgGray2, keypoint2, descriptor2);

    matcher.match(descriptor1, descriptor2, matches);
    Mat img_matches;
    drawMatches(input1, keypoint1, input2, keypoint2, matches, img_matches, DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
    imshow("SIFT_Match_Image", img_matches);
    waitKey();
    return 0;
}

效果如图
这里写图片描述

可以发现,这个效果是非常的差的,所有的能够识别到的特征点都被进行了匹配,这样势必导致了大量的错配点和重复点的情况,这并不是我们想要的特征点比对。我们要得是很精准的,少量的特征点

这时候我们就需要用上面我们想到的利用欧拉距离的比对进行筛选的当时选出最优的几个特征点进行匹配

使用欧拉距离进行筛选的匹配

代码如下:


int main()
{
    Mat input1 = imread("img2.png", 1);
    Mat input2 = imread("img1.png", 1);
    Mat imgGray1, imgGray2;
    //转换灰度图
    cvtColor(input1, imgGray1, CV_BGR2GRAY);
    cvtColor(input2, imgGray2, CV_BGR2GRAY);
    SiftFeatureDetector detector;
    vector<KeyPoint> keypoint1, keypoint2;
    detector.detect(imgGray1, keypoint1);
    detector.detect(imgGray2, keypoint2);

    SiftDescriptorExtractor extractor;
    Mat descriptor1, descriptor2;

    BruteForceMatcher<L2<float>> matcher;
    vector<DMatch> matches;

    extractor.compute(imgGray1, keypoint1, descriptor1);
    extractor.compute(imgGray2, keypoint2, descriptor2);

    matcher.match(descriptor1, descriptor2, matches);
    //特征点排序
    sort(matches.begin(), matches.end());
    //获取排名前10个的匹配度高的匹配点集
    vector<KeyPoint> goodImagePoints1, goodImagePoints2;

    vector<DMatch> matchesVoted;

    for (int i = 0; i<10; i++)
    {
        DMatch dmatch;
        dmatch.queryIdx = i;
        dmatch.trainIdx = i;

        matchesVoted.push_back(dmatch);
        goodImagePoints1.push_back(keypoint1[matches[i].queryIdx]);
        goodImagePoints2.push_back(keypoint2[matches[i].trainIdx]);
    }

    Mat img_matches;
    std::vector< DMatch > emptyVec;
    drawMatches(input1, goodImagePoints1, input2, goodImagePoints2, matchesVoted, img_matches, DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
    imshow("SIFT_Match_Image", img_matches);
    waitKey();
    return 0;
}

我们这次并不是仅仅找出了所有的keyPoints,还根据匹配得到的dmatch数组,进行欧拉距离的排序,然后选出排名前n的数据,这样得到的匹配项,就是我们想要的比较好的特征点匹配

效果如图:
这里写图片描述
可以看到,效果非常的不错

想一想,通过读DMatchkeyPoint的源码,我们就可以知道如何优化和筛选SIFT算法得到的大量keyPoint,并达到非常好的匹配效果
所以编程不能总是只顾着埋头实现功能,自己真正的竞争力还是要自己去看原理得到

好的,下面是一个基于 SIFT 算法的局部特征匹配的 C++ OpenCV 实现。 首先,我们需要导入必要的头文件和命名空间: ```cpp #include <opencv2/opencv.hpp> using namespace cv; ``` 然后,我们可以定义一个函数来执行 SIFT 特征提取和匹配: ```cpp void siftMatch(const Mat &img1, const Mat &img2, std::vector<Point2f> &pts1, std::vector<Point2f> &pts2) { // 创建 SIFT 特征点检测器和描述符提取器 Ptr<Feature2D> detector = SIFT::create(); // 检测关键点,并计算描述符 std::vector<KeyPoint> keypoints1, keypoints2; Mat descriptors1, descriptors2; detector->detectAndCompute(img1, Mat(), keypoints1, descriptors1); detector->detectAndCompute(img2, Mat(), keypoints2, descriptors2); // 创建暴力匹配器 BFMatcher matcher(NORM_L2); // 匹配描述符 std::vector<DMatch> matches; matcher.match(descriptors1, descriptors2, matches); // 选择最佳匹配 double minDist = 100.0; for (int i = 0; i < matches.size(); i++) { if (matches[i].distance < minDist) { minDist = matches[i].distance; } } // 选取最佳匹配的点对 for (int i = 0; i < matches.size(); i++) { if (matches[i].distance < 3 * minDist) { pts1.push_back(keypoints1[matches[i].queryIdx].pt); pts2.push_back(keypoints2[matches[i].trainIdx].pt); } } } ``` 在这个函数中,我们首先创建了 SIFT 特征点检测器和描述符提取器,然后使用它们检测关键点并计算描述符。接下来,我们创建了一个暴力匹配器,并使用它来匹配描述符。然后我们选择最佳匹配,并选取最佳匹配的点对。 最后,我们可以在主函数中调用这个函数,如下所示: ```cpp int main() { // 读取图像 Mat img1 = imread("img1.jpg"); Mat img2 = imread("img2.jpg"); // 提取关键点并匹配 std::vector<Point2f> pts1, pts2; siftMatch(img1, img2, pts1, pts2); // 绘制匹配结果 Mat imgMatches; drawMatches(img1, keypoints1, img2, keypoints2, matches, imgMatches); imshow("Matches", imgMatches); waitKey(0); return 0; } ``` 在这个示例中,我们首先读取了两张图像。然后,我们调用 `siftMatch` 函数来提取关键点并匹配。最后,我们使用 OpenCV 的 `drawMatches` 函数来绘制匹配结果,并显示在窗口中。 注意,在实际使用中,我们可能需要对提取的关键点进行筛选和优化,以提高匹配的准确性。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值