OpenCV SIFT源码讲解——代码逻辑宏观窥探

一、暴露在外的接口:SIFT

一般来说,我们在OpenCV中使用SIFT算法的方式为:

//该表达式涉及到多态,详解在第2节
cv::Ptr<SiftFeatureDetector>  sift = SIFT::create();

那么,SiftFeatureDetector是什么呢?在 features2d.hpp中清晰的写出来:

typedef SIFT SiftFeatureDetector;

同样在该文件中,可以看到:

class CV_EXPORTS_W SIFT : public Feature2D
{
...
}

CV_EXPORTS_W 的本质如下,表明SIFT 是一个暴露给用户的接口。

define CV_EXPORTS_W  CV_EXPORTS 
//下面一行宏定义只能在源码内看到,不对外暴露
//若在程序中使用编译好的OpenCV,只能追踪到上一行宏定义
define CV_EXPORTS __declspec(dllexport)

二、隐藏在SIFT背后的本质:SIFT_Impl

在编译好的OpenCV内,对于SIFT,只提供了features2d.hpp文件,我们无法看到任何有关与他的具体实现。因此,想要探究其本质,现在进入其源码内:sift.dispatch.cpp

在该cpp文件中,定义了一个SIFT的派生类SIFT_Impl 。该派生类不对外暴露。

class SIFT_Impl : public SIFT{
};

同时,在该CPP文件中,也定义了SIFT接口的create函数:

Ptr<SIFT> SIFT::create( int _nfeatures, int _nOctaveLayers,
                     double _contrastThreshold, double _edgeThreshold, double _sigma, int _descriptorType )
{
	...
	/*
	*	返回指针
	*/
    return makePtr<SIFT_Impl>(_nfeatures, _nOctaveLayers, _contrastThreshold, _edgeThreshold, _sigma, _descriptorType);
}

因此,到目前为止,已经知道了SIFT背后的逻辑:

//定义SIFT类型的指针
//在create函数内定义 派生类 SIFT_Impl对象
//SIFT_Impl指针向上转型,赋值给sift。(多态)
cv::Ptr<SiftFeatureDetector>  sift = SIFT::create();

之后,sift通过箭头描述符调用的任何函数,都将通过虚指针指向派生类的具体实现。也即SIFT_Impl才是sift的本质。

三、使用sift算法全流程

//1.读取图像
cv::Mat matL = cv::imread(imgL);
cv::Mat matR = cv::imread(imgR);

//2.定义关键点和描述符
std::vector<cv::KeyPoint> kpL,kpR;
cv::Mat descriptorL,descriptorR;

//3.创建SIFT对象
cv::Ptr<SiftFeatureDetector>  siftL = SIFT::create();
cv::Ptr<SiftFeatureDetector>  siftR = SIFT::create();

//4.探测关键点
siftL->detect(matL, kpL);
siftR->detect(matR, kpR);

//5.计算描述符
siftL->compute(matL, kpL, descriptorL);
siftR->compute(matR, kpR, descriptorR);

//6.描述符匹配
...

对于第3步,创建SIFT对象,上两节已经讲过,那么现在只剩下detect和compute两个步骤了。这两个步骤是个重难点,我将分多篇文章细讲,争取能给大家讲明白。(描述符匹配属于特征匹配通用的部分,不属于SIFT专有,在这里就不讲了)

回到features2d.hpp,可以看SIFT接口内并没有声明detectcompute两个函数,这是因为这两个函数是继承其基类Feature2D的(可以在该hpp文件内找到)。
进入features2d.cpp内,可以找到detectcompute的定义:

void Feature2D::detect( InputArray image,
                        std::vector<KeyPoint>& keypoints,
                        InputArray mask )
{
	...
    detectAndCompute(image, mask, keypoints, noArray(), false);
}

void Feature2D::compute( InputArray image,
                         std::vector<KeyPoint>& keypoints,
                         OutputArray descriptors )
{
	...
    detectAndCompute(image, noArray(), keypoints, descriptors, true);
}

可以看到,这两个函数最终都将调用detectAndCompute,该函数是被SIFT_Impl重写(detect和compute还有一个重载函数,但是最后都会调用detectAndCompute)。
我将单独写一篇文章介绍detectAndCompute函数

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值