【OpenCV】SIFT原理与源码分析:关键点搜索与定位
《SIFT原理与源码分析》系列文章索引:http://www.cnblogs.com/tianyalu/p/5467813.html
由前一步《DoG尺度空间构造》,我们得到了DoG高斯差分金字塔:

如上图的金字塔,高斯尺度空间金字塔中每组有五层不同尺度图像,相邻两层相减得到四层DoG结果。关键点搜索就在这四层DoG图像上寻找局部极值点。
DoG局部极值点
寻找DoG极值点时,每一个像素点和它所有的相邻点比较,当其大于(或小于)它的图像域和尺度域的所有相邻点时,即为极值点。如下图所示,比较的范围是个3×3的立方体:中间的检测点和它同尺度的8个相邻点,以及和上下相邻尺度对应的9×2个点——共26个点比较,以确保在尺度空间和二维图像空间都检测到极值点。

在一组中,搜索从每组的第二层开始,以第二层为当前层,第一层和第三层分别作为立方体的的上下层;搜索完成后再以第三层为当前层做同样的搜索。所以每层的点搜索两次。通常我们将组Octaves索引以-1开始,则在比较时牺牲了-1组的第0层和第N组的最高层

高斯金字塔,DoG图像及极值计算的相互关系如上图所示。
关键点精确定位



// Detects features at extrema in DoG scale space. Bad features are discarded
// based on contrast and ratio of principal curvatures.
// 在DoG尺度空间寻特征点(极值点)
void SIFT::findScaleSpaceExtrema( const vector<Mat>& gauss_pyr, const vector<Mat>& dog_pyr,
vector<KeyPoint>& keypoints ) const
{
int nOctaves = (int)gauss_pyr.size()/(nOctaveLayers + ); // The contrast threshold used to filter out weak features in semi-uniform
// (low-contrast) regions. The larger the threshold, the less features are produced by the detector.
// 过滤掉弱特征的阈值 contrastThreshold默认为0.04
int threshold = cvFloor(0.5 * contrastThreshold / nOctaveLayers * * SIFT_FIXPT_SCALE);
const int n = SIFT_ORI_HIST_BINS; //
float hist[n];
KeyPoint kpt; keypoints.clear(); for( int o = ; o < nOctaves; o++ )
for( int i = ; i <= nOctaveLayers; i++ )
{
int idx = o*(nOctaveLayers+)+i;
const Mat& img = dog_pyr[idx];
const Mat& prev = dog_pyr[idx-];
const Mat& next = dog_pyr[idx+];
int step = (int)img.step1();
int rows = img.rows, cols = img.cols; for( int r = SIFT_IMG_BORDER; r < rows-SIFT_IMG_BORDER; r++)
{
const short* currptr = img.ptr<short>(r);
const short* prevptr = prev.ptr<short>(r);
const short* nextptr = next.ptr<short>(r); for( int c = SIFT_IMG_BORDER; c < cols-SIFT_IMG_BORDER; c++)
{
int val = currptr[c]; // find local extrema with pixel accuracy
// 寻找局部极值点,DoG中每个点与其所在的立方体周围的26个点比较
// if (val比所有都大 或者 val比所有都小)
if( std::abs(val) > threshold &&
((val > && val >= currptr[c-] && val >= currptr[c+] &&
val >= currptr[c-step-] && val >= currptr[c-step] &&
val >= currptr[c-step+] && val >= currptr[c+step-] &&
val >= currptr[c+step] && val >= currptr[c+step+] &&
val >= nextptr[c] && val >= nextptr[c-] &&
val >= nextptr[c+] && val >= nextptr[c-step-] &&
val >= nextptr[c-step] && val >= nextptr[c-step+] &&
val >= nextptr[c+step-] && val >= nextptr[c+step] &&
val >= nextptr[c+step+] && val >= prevptr[c] &&
val >= prevptr[c-] && val >= prevptr[c+] &&
val >= prevptr[c-step-] && val >= prevptr[c-step] &&
val >= prevptr[c-step+] && val >= prevptr[c+step-] &&
val >= prevptr[c+step] && val >= prevptr[c+step+]) ||
(val < && val <= currptr[c-] && val <= currptr[c+] &&
val <= currptr[c-step-] && val <= currptr[c-step] &&
val <= currptr[c-step+] && val <= currptr[c+step-] &&
val <= currptr[c+step] && val <= currptr[c+step+] &&
val <= nextptr[c] && val <= nextptr[c-] &&
val <= nextptr[c+] && val <= nextptr[c-step-] &&
val <= nextptr[c-step] && val <= nextptr[c-step+] &&
val <= nextptr[c+step-] && val <= nextptr[c+step] &&
val <= nextptr[c+step+] && val <= prevptr[c] &&
val <= prevptr[c-] && val <= prevptr[c+] &&
val <= prevptr[c-step-] && val <= prevptr[c-step] &&
val <= prevptr[c-step+] && val <= prevptr[c+step-] &&
val <= prevptr[c+step] && val <= prevptr[c+step+])))
{
int r1 = r, c1 = c, layer = i; // 关键点精确定位
if( !adjustLocalExtrema(dog_pyr, kpt, o, layer, r1, c1,
nOctaveLayers, (float)contrastThreshold,
(float)edgeThreshold, (float)sigma) )
continue; float scl_octv = kpt.size*0.5f/( << o);
// 计算梯度直方图
float omax = calcOrientationHist(
gauss_pyr[o*(nOctaveLayers+) + layer],
Point(c1, r1),
cvRound(SIFT_ORI_RADIUS * scl_octv),
SIFT_ORI_SIG_FCTR * scl_octv,
hist, n);
float mag_thr = (float)(omax * SIFT_ORI_PEAK_RATIO);
for( int j = ; j < n; j++ )
{
int l = j > ? j - : n - ;
int r2 = j < n- ? j + : ; if( hist[j] > hist[l] && hist[j] > hist[r2] && hist[j] >= mag_thr )
{
float bin = j + 0.5f * (hist[l]-hist[r2]) /
(hist[l] - *hist[j] + hist[r2]);
bin = bin < ? n + bin : bin >= n ? bin - n : bin;
kpt.angle = (float)((.f/n) * bin);
keypoints.push_back(kpt);
}
}
}
}
}
}
}
删除边缘效应

为最大特征值,
为最小特征值,那么:
表示最大特征值与最小特征值的比值,则有:
也会增加。我们只需要去掉比率大于一定值的特征点。Lowe论文中去掉r=10的点。// Interpolates a scale-space extremum's location and scale to subpixel
// accuracy to form an image feature. Rejects features with low contrast.
// Based on Section 4 of Lowe's paper.
// 特征点精确定位
static bool adjustLocalExtrema( const vector<Mat>& dog_pyr, KeyPoint& kpt, int octv,
int& layer, int& r, int& c, int nOctaveLayers,
float contrastThreshold, float edgeThreshold, float sigma )
{
const float img_scale = .f/(*SIFT_FIXPT_SCALE);
const float deriv_scale = img_scale*0.5f;
const float second_deriv_scale = img_scale;
const float cross_deriv_scale = img_scale*0.25f; float xi=, xr=, xc=, contr;
int i = ; //三维子像元插值
for( ; i < SIFT_MAX_INTERP_STEPS; i++ )
{
int idx = octv*(nOctaveLayers+) + layer;
const Mat& img = dog_pyr[idx];
const Mat& prev = dog_pyr[idx-];
const Mat& next = dog_pyr[idx+]; Vec3f dD((img.at<short>(r, c+) - img.at<short>(r, c-))*deriv_scale,
(img.at<short>(r+, c) - img.at<short>(r-, c))*deriv_scale,
(next.at<short>(r, c) - prev.at<short>(r, c))*deriv_scale); float v2 = (float)img.at<short>(r, c)*;
float dxx = (img.at<short>(r, c+) +
img.at<short>(r, c-) - v2)*second_deriv_scale;
float dyy = (img.at<short>(r+, c) +
img.at<short>(r-, c) - v2)*second_deriv_scale;
float dss = (next.at<short>(r, c) +
prev.at<short>(r, c) - v2)*second_deriv_scale;
float dxy = (img.at<short>(r+, c+) -
img.at<short>(r+, c-) - img.at<short>(r-, c+) +
img.at<short>(r-, c-))*cross_deriv_scale;
float dxs = (next.at<short>(r, c+) -
next.at<short>(r, c-) - prev.at<short>(r, c+) +
prev.at<short>(r, c-))*cross_deriv_scale;
float dys = (next.at<short>(r+, c) -
next.at<short>(r-, c) - prev.at<short>(r+, c) +
prev.at<short>(r-, c))*cross_deriv_scale; Matx33f H(dxx, dxy, dxs,
dxy, dyy, dys,
dxs, dys, dss); Vec3f X = H.solve(dD, DECOMP_LU); xi = -X[];
xr = -X[];
xc = -X[]; if( std::abs( xi ) < 0.5f && std::abs( xr ) < 0.5f && std::abs( xc ) < 0.5f )
break; //将找到的极值点对应成像素(整数)
c += cvRound( xc );
r += cvRound( xr );
layer += cvRound( xi ); if( layer < || layer > nOctaveLayers ||
c < SIFT_IMG_BORDER || c >= img.cols - SIFT_IMG_BORDER ||
r < SIFT_IMG_BORDER || r >= img.rows - SIFT_IMG_BORDER )
return false;
} /* ensure convergence of interpolation */
// SIFT_MAX_INTERP_STEPS:插值最大步数,避免插值不收敛,程序中默认为5
if( i >= SIFT_MAX_INTERP_STEPS )
return false; {
int idx = octv*(nOctaveLayers+) + layer;
const Mat& img = dog_pyr[idx];
const Mat& prev = dog_pyr[idx-];
const Mat& next = dog_pyr[idx+];
Matx31f dD((img.at<short>(r, c+) - img.at<short>(r, c-))*deriv_scale,
(img.at<short>(r+, c) - img.at<short>(r-, c))*deriv_scale,
(next.at<short>(r, c) - prev.at<short>(r, c))*deriv_scale);
float t = dD.dot(Matx31f(xc, xr, xi)); contr = img.at<short>(r, c)*img_scale + t * 0.5f;
if( std::abs( contr ) * nOctaveLayers < contrastThreshold )
return false; /* principal curvatures are computed using the trace and det of Hessian */
//利用Hessian矩阵的迹和行列式计算主曲率的比值
float v2 = img.at<short>(r, c)*.f;
float dxx = (img.at<short>(r, c+) +
img.at<short>(r, c-) - v2)*second_deriv_scale;
float dyy = (img.at<short>(r+, c) +
img.at<short>(r-, c) - v2)*second_deriv_scale;
float dxy = (img.at<short>(r+, c+) -
img.at<short>(r+, c-) - img.at<short>(r-, c+) +
img.at<short>(r-, c-)) * cross_deriv_scale;
float tr = dxx + dyy;
float det = dxx * dyy - dxy * dxy; //这里edgeThreshold可以在调用SIFT()时输入;
//其实代码中定义了 static const float SIFT_CURV_THR = 10.f 可以直接使用
if( det <= || tr*tr*edgeThreshold >= (edgeThreshold + )*(edgeThreshold + )*det )
return false;
} kpt.pt.x = (c + xc) * ( << octv);
kpt.pt.y = (r + xr) * ( << octv);
kpt.octave = octv + (layer << ) + (cvRound((xi + 0.5)*) << );
kpt.size = sigma*powf(.f, (layer + xi) / nOctaveLayers)*( << octv)*; return true;
}
至此,SIFT第二步就完成了。参见《SIFT原理与源码分析》
本文转自:http://blog.csdn.net/xiaowei_cqu/article/details/8087239
【OpenCV】SIFT原理与源码分析:关键点搜索与定位的更多相关文章
- OpenCV SIFT原理与源码分析
http://blog.csdn.net/xiaowei_cqu/article/details/8069548 SIFT简介 Scale Invariant Feature Transform,尺度 ...
- 【OpenCV】SIFT原理与源码分析:关键点描述
<SIFT原理与源码分析>系列文章索引:http://www.cnblogs.com/tianyalu/p/5467813.html 由前一篇<方向赋值>,为找到的关键点即SI ...
- 【OpenCV】SIFT原理与源码分析:DoG尺度空间构造
原文地址:http://blog.csdn.net/xiaowei_cqu/article/details/8067881 尺度空间理论 自然界中的物体随着观测尺度不同有不同的表现形态.例如我们形 ...
- 【OpenCV】SIFT原理与源码分析:方向赋值
<SIFT原理与源码分析>系列文章索引:http://www.cnblogs.com/tianyalu/p/5467813.html 由前一篇<关键点搜索与定位>,我们已经找到 ...
- 【OpenCV】SIFT原理与源码分析
SIFT简介 Scale Invariant Feature Transform,尺度不变特征变换匹配算法,是由David G. Lowe在1999年(<Object Recognition f ...
- OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波
http://blog.csdn.net/chenyusiyuan/article/details/8710462 OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波 201 ...
- ConcurrentHashMap实现原理及源码分析
ConcurrentHashMap实现原理 ConcurrentHashMap源码分析 总结 ConcurrentHashMap是Java并发包中提供的一个线程安全且高效的HashMap实现(若对Ha ...
- HashMap和ConcurrentHashMap实现原理及源码分析
HashMap实现原理及源码分析 哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表, ...
- (转)ReentrantLock实现原理及源码分析
背景:ReetrantLock底层是基于AQS实现的(CAS+CHL),有公平和非公平两种区别. 这种底层机制,很有必要通过跟踪源码来进行分析. 参考 ReentrantLock实现原理及源码分析 源 ...
随机推荐
- Linux文件归档和解压缩
1.tar tar命令相当于归档,不做压缩,解压同样也是把归档文件释放出来(归档通俗上可以理解为把文件分类,把一些文件放到一个包中归类,方便用户管理) 解包:tar -zxvf file.tar #解 ...
- openstack-r版(rocky)搭建基于centos7.4 的openstack swift对象存储服务 二
openstack-r版(rocky)搭建基于centos7.4 的openstack swift对象存储服务 一 openstack-r版(rocky)搭建基于centos7.4 的openstac ...
- mysql 5.5 zip配置安装
1.解压2.创建option文件 --defaults-file=../my.ini [mysql] # 设置mysql客户端默认字符集 default-character-set=utf8 [mys ...
- 基于C#的机器学习--模糊逻辑-穿越障碍
模糊逻辑-穿越障碍 模糊逻辑.另一个我们经常听到的术语.但它的真正含义是什么?它是否意味着不止一件事?我们马上就会知道答案. 我们将使用模糊逻辑来帮助引导一辆自动驾驶汽车绕过障碍,如果我们做得正确,我 ...
- 【Pthon入门学习】多级菜单小例子
menu_list = { '北京':{ '昌平':{ '回龙观':{ '和谐家园':{}, '矩阵小区':{}, '北店家园':{} }, '沙河':{ '北街家园1区':{}, '北街家园2区': ...
- 互评Alpha版本 - Hello World团队项目空天猎
在测评该项目时,我找到了Hello World!团队的git,并下载了相关文件以及阅读了程序运行说明. 如图所示,我下载了一个名为 SkyHunter1.0.rar 的压缩文件包,文件包内容如下: 根 ...
- python apply()函数
python apply函数的具体的含义: apply(func [, args [, kwargs ]]) 函数用于当函数参数已经存在于一个元组或字典中时,间接地调用函数.args是一个包含将要提供 ...
- HDU 5655 CA Loves Stick 水题
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5656 CA Loves Stick Accepts: 381 Submissions: 3204 ...
- C#中委托的理解
请注意,这只是个人关于C#中委托的一点点理解,参考了一些博客,如有不周之处,请指出,谢谢! 委托是一种函数指针,委托是方法的抽象,方法是委托的实例.委托是C#语言的一道坎,明白了委托才能算是C#真正入 ...
- lintcode-442-实现 Trie
442-实现 Trie 实现一个 Trie,包含 insert, search, 和 startsWith 这三个方法. 注意事项 你可以假设所有的输入都是小写字母a-z. 样例 insert(&qu ...