《sift程序详解》PPT课件.ppt
SIFTSIFT特征提取过程中特征提取过程中的核心函数的核心函数PART ONE.物理架构opencv_contrib-3.1.0(版本号)/modules/xfeature2d/opencv_xfeature2d project/src/sift.cppcv namespacexfeature2d namespaceFeature2DFeature2D父类/基类SIFTSIFT类类feature2d的子类父类/基类SIFT_ImplSIFT_Impl类类SIFT的子类PART TWO.运行架构主函数int main()/创建SIFT_Impl类指针 Ptr f2d=xfeatures2d:SIFT:create();Mat img_1=imread(1.jpg);/读入图片 vector keypoints_1;/用于存放检测到的关键点的容器 f2d-detect(img_1,keypoints_1);/检测出关键点 Mat descriptors_1;f2d-compute(img_1,keypoints_1,descriptors_1);/计算描述子 waitKey(0);/等待任意按键按下Feature2D:detect(InputArray image,CV_OUT std:vector&keypoints,InputArray mask=noArray()SIFT_Impl:detectAndCompute(image,mask,keypoints,noArray(),false);SIFT_Impl:detectAndCompute(image,noArray(),keypoints,descriptors,true);Feature2D:compute(InputArray image,std:vector&keypoints,OutputArray descriptors)调用了 检测关键点函数调用了 计算描述子函数createInitialImage()图像初始化函数buildGaussianPyramid()构建高斯金字塔函数buildDoGPyramid()构建DOG金字塔函数SIFT_Impl:detectAndCompute(image,mask,keypoints,noArray(),false);findScaleSpaceExtrema()检测尺度空间极值函数 adjustLocalExtrema()调整极值函数calcOrientationHist()计算关键点主方向函数函数功能:检测图片中的关键点参数image:输入图片参数mask:无掩模参数keypoints:关键点容器参数noArray():无描述子参数false:不使用给定的关键点PART TREE.检测关键点部分图像尺度空间的构建1.1图像初始化函数函数功能:将输入图像转化为灰度图,并设置图像初始尺度c+:static Mat createInitialImage(const Mat&img,bool double ImageSize,float sigma)入口参数:参数const Mat&img:输入图像参数bool double ImageSize:是否将初始图像扩大两倍,若值为1,则图像扩大为原来两倍参数float sigma:设定图像的初始尺度返回值:无说明:一个图像的尺度空间L(x,y,),定义为原始图像I(x,y)与一个可变尺度的2维高斯函数G(x,y,)卷积产生:其中:lowe 把初始图像的尺度I(x,y)设置为0.5即:I(x,y)=I(x,y,0.5),那么由L(x,y,1)得到L(x,y,2),即由尺度为1的图像生成尺度为2的图像的公式为:图像尺度空间的构建1.2构建图像金字塔函数:函数功能:将输入图像作为金字塔初始图像,通过不断进行高斯模糊和降采样后的图片存放在Mat类容器中c+:void SIFT_Impl:buildGaussianPyramid(const Mat&base,std:vector&pyr,int nOctaves)const入口参数:参数const Mat&base:输入图像参数std:vector&pyr:用于存储金字塔图像的容器参数int nOctaves:金字塔的组数返回值:无图像尺度空间的构建1.3构建图像DOG金字塔函数:函数功能:将构建好的高斯金字塔相邻层相减后构成DOG金字塔存放在Mat类容器中void SIFT_Impl:buildDoGPyramid(const std:vector&gpyr,std:vector&dogpyr)const入口参数:参数:vector&gpyr:存放高斯金字塔图片的容器参数:vector&dogpyr:存放DOG金字塔图片的容器返回值:无关键点定位关键点定位并确定主方向函数函数功能:(1)在DOG空间检测极值点 (2)精确定位关键点位置和尺度并进行插值处理 (3)去除边缘不稳定的点 (4)为关键点分配主方向void SIFT_Impl:findScaleSpaceExtrema(const std:vector&gauss_pyr,const std:vector&dog_pyr,std:vector&keypoints)const入口参数:参数const std:vector&gauss_pyr:高斯金字塔容器参数const std:vector&dog_pyr:DOG金字塔容器参数std:vector&keypoints:存放关键点容器返回值:无调整局部极值函数函数功能:精确定位关键点位置和尺度并进行插值处理,去除边缘不稳定的点static bool adjustLocalExtrema(const std:vector&dog_pyr,KeyPoint&kpt,int octv,int&layer,int&r,int&c,int nOctaveLayers,float contrastThreshold,float edgeThreshold,float sigma)入口参数:参数const std:vector&dog_pyr:DOG尺度空间金字塔参数KeyPoint&kpt:定义了一个KeyPoint类对象用来存放筛选通过的关键点参数int octv:该像素点所在的组数参数int&layer:该像素点所在的层数参数int&r:该像素点所在的行参数int&c:该像素点所在的列参数int nOctaveLayers:金字塔每组的层数S参数float contrastThreshold:对比度阈值0.04参数float edgeThreshold:边缘响应阈值参数float sigma:此处的sigma为初始尺度sigma0返回值:若极值调整成功返回1,调整失败返回0说明:其中令 得:令 解得 :接下来判断偏移量在每个维度的偏差是否大于0.5,若不大于直接保留,若大于进行迭代插值处理。在设置的迭代次数内修正成功则保留,否则舍弃。设f(i,j)是y轴为i、x轴为j的图像像素值,则在(i,j)点处的一阶、二阶及二阶混合偏导为:再对 进行筛选去除对比度低的点,在opencv中,使用下面的公式来判断其是否为不稳定的极值:去除边缘响应较强的点,具体在程序中的计算同上:计算关键点主方向函数函数功能:为特征点分配主方向static float calcOrientationHist(const Mat&img,Point pt,int radius,float sigma,float*hist,int n)入口参数:参数const Mat&img:当前特征点所在的图像参数Point pt:关键点位置参数int radius:指统计在以关键点为中心,以radius=3*1.5sigma为半径的区域为作为该关键点的邻域,以统计直方图的形式来确定主方向参数float sigma:这里的sigma是指相对于当前组第一层图像的尺度来说的参数float*hist:定义了hist指针用于存放直方图参数int n:直方图的条数返回值:直方图的主峰值说明:直方图平滑处理;opencv使用的平滑公式为:由于直方图代表的只是一个角度范围,想要得到更精确的方向角度值,需要对离散的梯度方向直方图进行插值拟合处理,拟合公式为:SIFT_Impl:detectAndCompute(image,noArray(),keypoints,descriptors,true)函数功能:计算图片中的关键点描述子参数image:输入图片参数noArray():无掩模参数keypoints:关键点容器参数descriptors:描述子参数true:使用给定的关键点unpackOctave()calcDescriptors()计算关键点描述子函数calcSIFTDescriptorPART FOUR.关键点描述子createInitialImage()图像初始化函数buildGaussianPyramid()构建高斯金字塔函数buildDoGPyramid()构建DOG金字塔函数unpackOctave(const KeyPoint&kpt,int&octave,int&layer,float&scale)计算sift特征描述子入口参数:参数const KeyPoint&kpt:关键点参数int&octave:关键点所在层参数int&layer:关键点所在组参数float&scale:关键点所在尺度的比例因子返回值:无函数功能:去除关键点所在高斯金字塔的层数,组数,以及尺度比例因子以方便后面计算描述子计算sift特征描述子3.2计算sift特征描述子函数功能:计算特征点描述符 calcDescriptors函数static void calcDescriptors(const std:vector&gpyr,const std:vector&keypoints,Mat&descriptors,int nOctaveLayers,int firstOctave)入口参数:参数const std:vector&gpyr:存放高斯金字塔图片的容器参数const std:vector&keypoints:存放关键点的容器参数Mat&descriptors:描述子矩阵参数int nOctaveLayers:高斯金字塔层数参数int firstOctave:如果原图像扩大两倍,则为-1,如果没有,则为0返回值:无说明:计算sift特征描述子要用高斯金字塔,dog金字塔只用来求取极值点int d=SIFT_DESCR_WIDTH=4,n=SIFT_DESCR_HIST_BINS=8float size=kpt.size*scale计算特征点的特征矢量calcSIFTDescriptor(img,ptf,angle,size*0.5f,d,n,descriptors.ptr(int)i)函数功能:计算sift特征描述子static void calcSIFTDescriptor(const Mat&img,Point2f ptf,float ori,float scl,int d,int n,float*dst)3.3计算sift特征点的描述矢量:入口参数:参数const Mat&img:输入图像参数Point2f ptf:输入检测到的特征点,float型参数float ori:特征点的幅角,角度,float型参数float scl:输入特征点的尺度参数int d:特征点周围邻域的宽度参数int n:设定直方图的条目数,之前统计主方向时设n=36,现在计算特征矢量n=8参数float*dst:输出特征点的描述矢量,float型返回值:无关键点描述子1.首先要明确,我们前面的步骤分别得到了关键点的位置(层,组,所在图像的横纵坐标),尺度,主方向的信息,我们的特征描述子是回到了高斯金字塔上去做的。所以我们定位关键点在高斯金塔的那一张图片上。2.然后确定用描述周围多大的区域去描述这个关键点,首先我们把这块区域分成d*d个小区域,sift中d=4,如图中蓝色区域所示,每一格的宽度为3,这里的是指相对于当前组第一层图片来说的。注意:opencv中计算的区域是最外面的橘色的矩形区域,面积为(2Rradius+1)*(2Rradius+1)其中:3.根据公式来计算橘色矩形内每个像素点的幅值和幅角。4.接下来将橘色矩形内的每个像素的幅角按照旋转关键点主方向的角度,使得关键点主方向为0(或统一为多少度)5.建立三维直方图,如图所示,三维直方图是由(opencv中)(d+2)*(d+2)(n+2)个长宽高均为1的单位立方体组成的三维直方图,高对应邻域像素幅角的大小,把360度分成8等分,立方体的底就是特征点的邻域区域。该区域被分为4*4个子区域(opencv中划分了(d+2)*(d+2)个区域),邻域内的像素根据坐标位置,把他们归属到这16个子区域的一个,再根据邻域像素的幅角的大小,把他们归属到这8等分(opencv中为n+2)中的一份,这样每个像素点都能对应到其中的一个立方体里,三维直方图建立起来了。(这里分别把每个像素的横纵坐标和幅角都进行了归一化处理)。横纵坐标+d/2横纵坐标减0.56.三线性插值的计算-坐标平移6.三线性插值的计算xy正方体的中点同时是立方体的顶点显然,正方体的中心应该代表着该正方体,但落入正方体的邻域像素不可能都在中心,因此,根据它对中心点位置的贡献大小进行加权处理,即在正方体内,根据像素点相对于正方体的距离,对梯度幅值做加权处理。在实际应用中,我们需要经过坐标平移,把中心点平移到正方体的顶点上,这样只要计算出正方体内的点对正方体的8个顶点的贡献大小即可。根据三线性插值法,对某点的贡献值是以该顶点和正方体内的点为对角线的两个顶点,所构成的立方体的体积。6.三线性插值的计算-程序实现根据三线性插值法,对某点的贡献是以该顶点和正方体内的点为对角线的两个顶点,所构成的立方体的体积:6.描述子归一化处理经过上述处理后我们得出128维描述子,在opencv中,一共对描述子进行了两次归一化处理。第一次使用一下公式对描述子进行归一化处理并对大于0.2的描述子进行截断处理:假设经过第一次归一化处理后描述子用 表示,第二次归一化操作是:其中分母要大于FLT_EPSILON#define FLT_EPSILON 1.192092896e-07F