1999年的SIFT(ICCV 1999,并改进发表于IJCV 2004,本文描述);参考描述:图像特征点描述。

参考原文:SURF特征提取分析 本文有大量删除,如有疑义,请参考原文。

SURF对SIFT的改进:

引用Wiki百科中对SURF描述为:“ SURF (Speeded Up Robust Features) is a robust local feature detector, first presented by Herbert Bay et al. in 2006, that can be used in computer vision tasks likeobject recognition or 3D reconstruction. It is partly inspired by the SIFT descriptor.The standard version of SURF is several times faster than SIFT and claimed by its authors to be more robust against different image transformations than SIFT. SURF is based on sums of2D Haar wavelet responses and makes an efficient use ofintegral images.It uses an integer approximation to the determinant of Hessian blob detector, which can be computed extremely quickly with an integral image (3 integer operations). For features, it uses the sum of the Haar wavelet response around the point of interest. Again, these can be computed with the aid of the integral image".

从上述对SURF描述,可知:第一、SURF算法是对SIFT算法加强版,同时加速的具有鲁棒性的特征。第二、标准的SURF算子比SIFT算子快好几倍,并且在多幅图片下具有更好的鲁棒性。SURF最大的特征在于采用了harr特征以及积分图像integral image的概念,这大大加快了程序的运行时间。

算法描述:

为了实现尺度不变性的特征点检测与匹配,SURF算法则先利用Hessian矩阵确定候选点,然后进行非极大抑制,计算复杂度降低多了。整个算法由以下几个部分组成。

OpenCV源代码Nonfree包含了SURF的源代码,并添加了GPU实现:

1.使用Hessian矩阵构建,进行特征댠提取

void SURF::operator()(InputArray _img, InputArray _mask,CV_OUT vector<KeyPoint>& keypoints,OutputArray _descriptors,bool useProvidedKeypoints) const
{Mat img = _img.getMat(), mask = _mask.getMat(), mask1, sum, msum;bool doDescriptors = _descriptors.needed();CV_Assert(!img.empty() && img.depth() == CV_8U);if( img.channels() > 1 )cvtColor(img, img, COLOR_BGR2GRAY);CV_Assert(mask.empty() || (mask.type() == CV_8U && mask.size() == img.size()));CV_Assert(hessianThreshold >= 0);CV_Assert(nOctaves > 0);CV_Assert(nOctaveLayers > 0);integral(img, sum, CV_32S);// 1. 使用Hessian矩阵寻找候选点集合:// Compute keypoints only if we are not asked for evaluating the descriptors are some given locations:if( !useProvidedKeypoints ){if( !mask.empty() ){cv::min(mask, 1, mask1);integral(mask1, msum, CV_32S);}fastHessianDetector( sum, msum, keypoints, nOctaves, nOctaveLayers, (float)hessianThreshold );}int i, j, N = (int)keypoints.size();if( N > 0 ){Mat descriptors;bool _1d = false;int dcols = extended ? 128 : 64;size_t dsize = dcols*sizeof(float);if( doDescriptors ){_1d = _descriptors.kind() == _InputArray::STD_VECTOR && _descriptors.type() == CV_32F;if( _1d ){_descriptors.create(N*dcols, 1, CV_32F);descriptors = _descriptors.getMat().reshape(1, N);}else{_descriptors.create(N, dcols, CV_32F);descriptors = _descriptors.getMat();}}//2.使用SURFInvoker计算特征描述子// we call SURFInvoker in any case, even if we do not need descriptors,// since it computes orientation of each feature.parallel_for_(Range(0, N), SURFInvoker(img, sum, keypoints, descriptors, extended, upright) );// remove keypoints that were marked for deletionfor( i = j = 0; i < N; i++ ){if( keypoints[i].size > 0 ){if( i > j ){keypoints[j] = keypoints[i];if( doDescriptors )memcpy( descriptors.ptr(j), descriptors.ptr(i), dsize);}j++;}}if( N > j ){N = j;keypoints.resize(N);if( doDescriptors ){Mat d = descriptors.rowRange(0, N);if( _1d )d = d.reshape(1, N*dcols);d.copyTo(_descriptors);}}}
}

2.寻找多尺度空间的描述子

在进行高斯模糊时,SIFT的高斯模板大小是始终不变的,只是在不同的octave之间改变图片的大小。而在SURF中,图片的大小是一直不变的,不同的octave层得到的待检测图片是改变高斯模糊尺寸大小得到的,当然了,同一个octave中个的图片用到的高斯模板尺度也不同。算法允许尺度空间多层图像同时被处理,不需对图像进行二次抽样,从而提高算法性能

SURF算法使原始图像保持不变而只改变滤波器大小。SURF采用这种方法节省了降采样过程,其处理速度自然也就提上去了?

Invoke的代码:

// Multi-threaded search of the scale-space pyramid for keypoints
// 2.多线程寻找多尺度的特征点描述子
struct SURFFindInvoker : ParallelLoopBody
{SURFFindInvoker( const Mat& _sum, const Mat& _mask_sum,const vector<Mat>& _dets, const vector<Mat>& _traces,const vector<int>& _sizes, const vector<int>& _sampleSteps,const vector<int>& _middleIndices, vector<KeyPoint>& _keypoints,int _nOctaveLayers, float _hessianThreshold ){sum = &_sum;mask_sum = &_mask_sum;dets = &_dets;traces = &_traces;sizes = &_sizes;sampleSteps = &_sampleSteps;middleIndices = &_middleIndices;keypoints = &_keypoints;nOctaveLayers = _nOctaveLayers;hessianThreshold = _hessianThreshold;}static void findMaximaInLayer( const Mat& sum, const Mat& mask_sum,const vector<Mat>& dets, const vector<Mat>& traces,const vector<int>& sizes, vector<KeyPoint>& keypoints,int octave, int layer, float hessianThreshold, int sampleStep );void operator()(const Range& range) const{for( int i=range.start; i<range.end; i++ ){int layer = (*middleIndices)[i];int octave = i / nOctaveLayers;findMaximaInLayer( *sum, *mask_sum, *dets, *traces, *sizes,*keypoints, octave, layer, hessianThreshold,(*sampleSteps)[layer] );}}const Mat *sum;const Mat *mask_sum;const vector<Mat>* dets;const vector<Mat>* traces;const vector<int>* sizes;const vector<int>* sampleSteps;const vector<int>* middleIndices;vector<KeyPoint>* keypoints;int nOctaveLayers;float hessianThreshold;static Mutex findMaximaInLayer_m;
};

3.计算特征描述子:

采用3维线性插值法得到亚像素级的特征点,同时也去掉那些值小于一定阈值的点,增加极值使检测到的特征点数量减少,最终只有几个特征最强点会被检测出来。

4. 选取特征点主方向确定

为了保证旋转不变性,在SURF中,不统计其梯度直方图,而是统计特征点领域内的Harr小波特征。即以特征点为中心,计算半径为6s(S为特征点所在的尺度值)的邻域内,统计60度扇形内所有点在x(水平)和y(垂直)方向的Haar小波响应总和(Haar小波边长取4s),并给这些响应值赋高斯权重系数,使得靠近特征点的响应贡献大,而远离特征点的响应贡献小,然后60度范围内的响应相加以形成新的矢量,遍历整个圆形区域,选择最长矢量的方向为该特征点的主方向。这样,通过特征点逐个进行计算,得到每一个特征点的主方向

5. 构造SURF特征点描述算子

在SURF中,也是在特征点周围取一个正方形框,框的边长为20s(s是所检测到该特征点所在的尺度)。该框带方向,方向当然就是第4步检测出来的主方向了。然后把该框分为16个子区域,每个子区域统计25个像素的水平方向和垂直方向的haar小波特征,这里的x(水平)和y(垂直)方向都是相对主方向而言的。该haar小波特征为x(水平)方向值之和,水平方向绝对值之和,垂直方向之和,垂直方向绝对值之和。

/** Find the maxima in the determinant of the Hessian in a layer of the* scale-space pyramid*/
void SURFFindInvoker::findMaximaInLayer( const Mat& sum, const Mat& mask_sum,const vector<Mat>& dets, const vector<Mat>& traces,const vector<int>& sizes, vector<KeyPoint>& keypoints,int octave, int layer, float hessianThreshold, int sampleStep )
{// Wavelet Dataconst int NM=1;const int dm[NM][5] = { {0, 0, 9, 9, 1} };SurfHF Dm;int size = sizes[layer];// The integral image 'sum' is one pixel bigger than the source imageint layer_rows = (sum.rows-1)/sampleStep;int layer_cols = (sum.cols-1)/sampleStep;// Ignore pixels without a 3x3x3 neighbourhood in the layer aboveint margin = (sizes[layer+1]/2)/sampleStep+1;if( !mask_sum.empty() )resizeHaarPattern( dm, &Dm, NM, 9, size, mask_sum.cols );int step = (int)(dets[layer].step/dets[layer].elemSize());for( int i = margin; i < layer_rows - margin; i++ ){const float* det_ptr = dets[layer].ptr<float>(i);const float* trace_ptr = traces[layer].ptr<float>(i);for( int j = margin; j < layer_cols-margin; j++ ){float val0 = det_ptr[j];if( val0 > hessianThreshold ){/* Coordinates for the start of the wavelet in the sum image. Thereis some integer division involved, so don't try to simplify this(cancel out sampleStep) without checking the result is the same */int sum_i = sampleStep*(i-(size/2)/sampleStep);int sum_j = sampleStep*(j-(size/2)/sampleStep);/* The 3x3x3 neighbouring samples around the maxima.The maxima is included at N9[1][4] */const float *det1 = &dets[layer-1].at<float>(i, j);const float *det2 = &dets[layer].at<float>(i, j);const float *det3 = &dets[layer+1].at<float>(i, j);float N9[3][9] = { { det1[-step-1], det1[-step], det1[-step+1],det1[-1]  , det1[0] , det1[1],det1[step-1] , det1[step] , det1[step+1]  },{ det2[-step-1], det2[-step], det2[-step+1],det2[-1]  , det2[0] , det2[1],det2[step-1] , det2[step] , det2[step+1]  },{ det3[-step-1], det3[-step], det3[-step+1],det3[-1]  , det3[0] , det3[1],det3[step-1] , det3[step] , det3[step+1]  } };/* Check the mask - why not just check the mask at the center of the wavelet? */if( !mask_sum.empty() ){const int* mask_ptr = &mask_sum.at<int>(sum_i, sum_j);float mval = calcHaarPattern( mask_ptr, &Dm, 1 );if( mval < 0.5 )continue;}//使用非极大值抑制/* Non-maxima suppression. val0 is at N9[1][4]*/if( val0 > N9[0][0] && val0 > N9[0][1] && val0 > N9[0][2] &&val0 > N9[0][3] && val0 > N9[0][4] && val0 > N9[0][5] &&val0 > N9[0][6] && val0 > N9[0][7] && val0 > N9[0][8] &&val0 > N9[1][0] && val0 > N9[1][1] && val0 > N9[1][2] &&val0 > N9[1][3]                    && val0 > N9[1][5] &&val0 > N9[1][6] && val0 > N9[1][7] && val0 > N9[1][8] &&val0 > N9[2][0] && val0 > N9[2][1] && val0 > N9[2][2] &&val0 > N9[2][3] && val0 > N9[2][4] && val0 > N9[2][5] &&val0 > N9[2][6] && val0 > N9[2][7] && val0 > N9[2][8] ){/* Calculate the wavelet center coordinates for the maxima */float center_i = sum_i + (size-1)*0.5f;float center_j = sum_j + (size-1)*0.5f;KeyPoint kpt( center_j, center_i, (float)sizes[layer],-1, val0, octave, CV_SIGN(trace_ptr[j]) );//对最显著点位置进行3*3插值/* Interpolate maxima location within the 3x3x3 neighbourhood  */int ds = size - sizes[layer-1];int interp_ok = interpolateKeypoint( N9, sampleStep, sampleStep, ds, kpt );/* Sometimes the interpolation step gives a negative size etc. */if( interp_ok  ){/*printf( "KeyPoint %f %f %d\n", point.pt.x, point.pt.y, point.size );*/cv::AutoLock lock(findMaximaInLayer_m);keypoints.push_back(kpt);}}}}}
}
                        //使用非极大值抑制/* Non-maxima suppression. val0 is at N9[1][4]*/if( val0 > N9[0][0] && val0 > N9[0][1] && val0 > N9[0][2] &&val0 > N9[0][3] && val0 > N9[0][4] && val0 > N9[0][5] &&val0 > N9[0][6] && val0 > N9[0][7] && val0 > N9[0][8] &&val0 > N9[1][0] && val0 > N9[1][1] && val0 > N9[1][2] &&val0 > N9[1][3]                    && val0 > N9[1][5] &&val0 > N9[1][6] && val0 > N9[1][7] && val0 > N9[1][8] &&val0 > N9[2][0] && val0 > N9[2][1] && val0 > N9[2][2] &&val0 > N9[2][3] && val0 > N9[2][4] && val0 > N9[2][5] &&val0 > N9[2][6] && val0 > N9[2][7] && val0 > N9[2][8] ){/* Calculate the wavelet center coordinates for the maxima */float center_i = sum_i + (size-1)*0.5f;float center_j = sum_j + (size-1)*0.5f;KeyPoint kpt( center_j, center_i, (float)sizes[layer],-1, val0, octave, CV_SIGN(trace_ptr[j]) );//对最显著点位置进行3*3插值/* Interpolate maxima location within the 3x3x3 neighbourhood  */int ds = size - sizes[layer-1];int interp_ok = interpolateKeypoint( N9, sampleStep, sampleStep, ds, kpt );/* Sometimes the interpolation step gives a negative size etc. */if( interp_ok  ){/*printf( "KeyPoint %f %f %d\n", point.pt.x, point.pt.y, point.size );*/cv::AutoLock lock(findMaximaInLayer_m);keypoints.push_back(kpt);}}}}}
}

改进之处的可靠性?

1. 为什么选用高斯金字塔来作特征提取?

首先,采用DOG的金字塔原因是:因为它接近LOG,而LOG的极值点提供了最稳定的特征,而且DOG方便计算(只要做减法).而LOG的极值点提供的特征最稳定。其次,我们从直观理解:特征明显的点经过不同尺度的高斯滤波器进行滤波后,差别较大,所以用到的是DOG。但是直观上怎么理解呢. 如果相邻Octave的sigma不是两倍关系还好理解:如果两幅图像只是缩放的关系,那么假设第一个Octave找到了小一倍图像的极值点,那么大一倍图像的极值点会在下一个Octave找到相似的。但是现在,如果把大一倍图像进行一次下采样(这样和小的图像就完全一样了),进行Gauss滤波时,两个图像滤波系数(sigma)是不一样的,不就找不到一样的极值点了么.

2.Hessian矩阵为什么能用来筛选极值点?

SIFT先利用非极大抑制,再用到Hessian矩阵进行滤除。SURF先用Hessian矩阵,再进行非极大抑制。SURF的顺序可以加快筛选速度么(Hessian矩阵滤除的点更多?)至于SURF先用Hessian矩阵,再进行非极大抑制的原因,是不管先极大值抑制还是判断Hessian矩阵的行列式,金字塔上的点的行列式都是要计算出来的。先判断是否大于0只要进行1次判断,而判断是否是极大值点或者极小值点要与周围26个点比较,只比较1次肯定快。而在SIFT中,构建的高斯金字塔只有一座(不想SURF是有3座),要进行非极大抑制可以直接用金字塔的结果进行比较。而如果计算Hessian矩阵的行列式,还要再计算Dxx、Dxy、Dyy。因此先进行非极大抑制。这两个步骤的先后与SIFT/SURF的实际计算情况有关的,都是当前算法下的最佳顺序,而不是说哪种先计算一定更好。

3、为什么采用梯度特征作为局部不变特征?

这与人的视觉神经相关。采用梯度作为描述子的原因是,人的视觉皮层上的神经元对特定方向和空间频率的梯度相应很敏感,经过SIFT作者的一些实验验证,用梯度的方法进行匹配效果很好。

4、为什么可以采用某些特征点的局部不变特征进行整幅图像的匹配?

从直观的人类视觉印象来看,人类视觉对物体的描述也是局部化的,基于局部不变特征的图像识别方法十分接近于人类视觉机理,通过局部化的特征组合,形成对目标物体的整体印象,这就为局部不变特征提取方法提供了生物学上的解释,因此局部不变特征也得到了广泛应用。图像中的每个局部区域的重要性和影响范围并非同等重要,即特征不是同等显著的,其主要理论来源是Marr的计算机视觉理论和Treisman的特征整合理论,一般也称为“原子论”。该理论认为视觉的过程开始于对物体的特征性质和简单组成部分的分析,是从局部性质到大范围性质。SIFT/SURF都是对特征点的局部区域的描述,这些特征点应该是影响重要的点,对这些点的分析更加重要。所以在局部不变特征的提取和描述时也遵循与人眼视觉注意选择原理相类似的机制,所以SIFT/SURF用于匹配有效果。

5. 为什么Hessian矩阵可以用来判断极大值/极小值?

在x0点上,hessian矩阵是正定的,且各分量的一阶偏导数为0,则x0为极小值点。在x0点上,hessian矩阵是负定的,且各分量的一阶偏导数为0,则x0为极大值点。

对于某个局部区域,若hessian矩阵是半正定的,则这个区域是凸的(反之依然成立);若负定,则这个区域是凹的(反之依然成立)。而对于正定和负定来说,Hessian矩阵的行列式总是大于等于0的。反过来就是说:某个点若是极大值/极小值,hessian矩阵的行列式必然要大于等于0,而大于等于0如果是满足的,这个点不一定是极大值/极小值(还要判断一阶导数)。所以后面还要进行极大值抑制.

参考资料:

1.SURF原始论文:Herbert Bay, Andreas Ess, Tinne Tuytelaars, Luc Van Gool, "SURF: Speeded Up Robust Features", Computer Vision and Image Understanding (CVIU), Vol. 110, No. 3, pp. 346--359, 2008.

2.Wiki百科: Speeded Up Robust Features(SURF) ,Wikipedia, the free encyclopedia .

3. The Website of SURF: Speeded Up Robust Features.

图像局部显著性—点特征(SURF)相关推荐

  1. 图像局部显著性—点特征(SIFT为例)

    基于古老的Marr视觉理论,视觉识别和场景重建的基础即第一阶段为局部显著性探测.探测到的主要特征为直觉上可刺激底层视觉的局部显著性--特征点.特征线.特征块. SalientDetection 已经好 ...

  2. 图像局部显著性—点特征(GLOH)

    基于古老的Marr视觉理论,视觉识别和场景重建的基础即第一阶段为局部显著性探测.探测到的主要特征为直觉上可刺激底层视觉的局部显著性--特征点.特征线.特征块. 相关介绍:局部特征显著性-点特征(SIF ...

  3. 图像局部显著性—点特征(Fast)

    fast作为几乎最快的角点检测算法,一般说明不附带描述子.参考综述:图像的显著性检测--点特征 详细内容,请拜访原=文章:Fast特征点检测算法 在局部特征点检测快速发展的时候,人们对于特征的认识也越 ...

  4. 图像局部显著性—点特征(FREAK)

    参考文章:Freak特征提取算法  圆形区域分割 一.Brisk特征的计算过程(参考对比): 1.建立尺度空间:产生8层Octive层. 2.特征点检测:对这8张图进行FAST9-16角点检测,得到具 ...

  5. 图像局部显著性—线特征

    线检测--快速几何形状检测 参考: [OpenCV入门指南]第七篇 线段检测与圆检测 (一).边缘检测 一维显著特征常见表示为边缘.边缘检测的预处理常用 高斯模糊:主要数学运算为计算一阶和二阶导数,寻 ...

  6. 图像局部显著性—点特征(SiftGPU)

    SIFT的计算复杂度较高. SiftGpu的主页:SiftGPU: A GPU Implementation of ScaleInvariant Feature Transform (SIFT) 对S ...

  7. 图像特征检测描述:SIFT、SURF、ORB、HOG、LBP特征的原理概述

    版权声明:本文为博主原创文章,转载请标明原始博文地址: https://blog.csdn.net/yuanlulu/article/details/82148429 </div>< ...

  8. 图像特征提取算法:加速鲁棒特征SURF

    1.原理: Sift算法的优点是特征稳定,对旋转.尺度变换.亮度保持不变性,对视角变换.噪声也有一定程度的稳定性:缺点是实时性不高,并且对于边缘光滑目标的特征点提取能力较弱. Surf(Speeded ...

  9. 四种简单的图像显著性区域特征提取方法-----AC/HC/LC/FT。

    四种简单的图像显著性区域特征提取方法-----> AC/HC/LC/FT. 分类: 图像处理 2014-08-03 12:40 4088人阅读 评论(4) 收藏 举报 salient regio ...

最新文章

  1. 杂谈 | 当前知识蒸馏与迁移学习有哪些可用的开源工具?
  2. C++ string中的几个小陷阱,你掉进过吗?
  3. 微软的 Android 计划:邪恶的天才计划或只是邪恶?
  4. 【职场】从算法工程师到主管的转变,需要改变的是什么
  5. CPU 的 ring0、ring1、ring2、ring3
  6. iframe_demo实例:消息发送(PHP版本)
  7. cocos2d-x游戏开发(二)开始菜单续
  8. Javascript基础之-原型(prototype)
  9. Linux系统:Centos7下搭建PostgreSQL关系型数据库
  10. windows7-SQLyog 安装图解
  11. extjs的Ext.grid.EditorGridPanel中ComboBox显示值的问题
  12. Cisco路由器密码重置
  13. 计算机桌面推流,OBS推流PPT电脑桌面投屏
  14. 如何去除word文档中向下的箭头
  15. Metasploit攻击winserver2008实例
  16. 文章详情页面评论功能添加及实现原理
  17. 大数据下的数据分析平台架构
  18. 对 Go2 错误处理提案的批判
  19. UE4 实现C++蓝图接口
  20. Netty 通过 WebSocket 编程实现服务器和客户端全双工长连接<2021SC@SDUSC>

热门文章

  1. 这是一个测试:测试博客在浏览器中是否可以显示数学内容以及代码格式
  2. 网站前端和后台性能优化24
  3. 学习Java的三十三个建议
  4. SharePoint 2010无法使用外部asp.net web应用程序调试的解决办法
  5. linux C 快速排序法
  6. citrix POC环境准备
  7. 基于用例的工作量估计
  8. DECRIBE / EXPLAIN
  9. jupyter安装出现问题:安装后无法打开
  10. Mac 下安装mysqldb 问题:一条命令解决mysql_config not found