先贴几个链接:

http://blog.csdn.net/abcjennifer/article/details/7639681  Rachel-Zhang的

http://blog.csdn.net/manji_lee/article/details/8922474

David G. Lowe的两篇标志性的文章分别叫 Object recognition from local scale-invariant features 和

Distinctive Image Features from Scale-Invariant Keypoints

其实我就是想在博客里保存下网上下载的纯MATLAB版本的sift代码

%% 功能:提取灰度图像的尺度不变特征(SIFT特征)
% 输入:
% im - 灰度图像,该图像的灰度值在0到1之间(注意:应首先对输入图像的灰度值进行归一化处理)
% octaves - 金字塔的组数:octaves (默认值为4).
% intervals - 该输入参数决定每组金字塔的层数(默认值为 2).
% object_mask - 确定图像中尺度不变特征点的搜索区域,如果没有特别指出,则算法将搜索整个图像
% contrast_threshold - 对比度阈值(默认值为0.03).
% curvature_threshold - 曲率阈值(默认值为10.0).
% interactive - 函数运行显示标志,将其设定为1,则显示算法运行时间和过程的相关信息;
% 如果将其设定为2,则仅显示最终运行结果(default = 1).
%
% 输出:
% pos - Nx2 矩阵,每一行包括尺度不变特征点的坐标(x,y)
% scale - Nx3 矩阵,每一行包括尺度不变特征点的尺度信息
% (第一列是尺度不变特征点所在的组,
% 第二列是其所在的层,
% 第三列是尺度不变特征点的sigma).
% orient - Nx1 向量,每个元素是特征点的主方向,其范围在 [-pi,pi)之间.
% desc - N x 128 矩阵,每一行包含特征点的特征向量. %% 步骤0:载入图像后归一化,并扩充成 2xN-1 大小
% 用于彻底搞懂SIFT的MATLAB版本代码的一个测试文件
clear all;clc;close all;
% im = rgb2gray(imread('nest.png'));
im = imread('nest.png');
im = scjMatMap(im,0,1); antialias_sigma = 0.5;
signal = im; [X Y] = meshgrid( 1:0.5:size(signal,2), 1:0.5:size(signal,1) );
signal = interp2( signal, X, Y, '*linear' ); % im被扩充成 2 x N - 1 了 subsample = [0.5]; % 降采样率
%% 步骤1:生成高斯和差分高斯(DOG)金字塔
% 这两个金字塔的数据分别存储在名为
% gauss_pyr{orient,interval}和DOG_pyr{orient,interval}
% 的元胞数组中。高斯金字塔含有s+3层,差分高斯金字塔含有s+2层。 preblur_sigma = sqrt(sqrt(2)^2 - (2*antialias_sigma)^2); g = gaussian_filter( preblur_sigma ); gauss_pyr{1,1} = conv2( g, g, signal, 'same' );
clear signal; % 完成了初始化工作 % 第一组第一层的sigma
initial_sigma = sqrt( (2*antialias_sigma)^2 + preblur_sigma^2 ); octaves = 4; % 有4层金字塔
intervals = 2; % 每一层有2张图片 s+3
% 之所以+3,是因为:生成DOG时会减少一个图片,在计算极值的时候
% 第一个和最后一个图片不能使用,所以要+3.
% 为了保证差分高斯金字塔DOG在每一层至少有一张图片可用来寻找极值点
% 必须让高斯金字塔有+3张图片
% 最后会有octaves层,每层intervals+3张图片 % 记录每一层和每一个尺度的sigma
absolute_sigma = zeros(octaves,intervals+3);
absolute_sigma(1,1) = initial_sigma * subsample(1); % 记录产生金字塔的滤波器的尺寸和标准差
filter_size = zeros(octaves,intervals+3);
filter_sigma = zeros(octaves,intervals+3); % 生成高斯和差分高斯金字塔
for octave = 1:octaves
sigma = initial_sigma;
g = gaussian_filter(sigma);
filter_size(octave,1) = length(g);
filter_sigma(octave,1) = sigma;
DOG_pyr{octave} = ...
zeros(size(gauss_pyr{octave,1},1),size(gauss_pyr{octave,1},2),intervals+2);
for interval = 2:(intervals+3)
sigma_f = sqrt(2^(2/intervals) - 1)*sigma;
g = gaussian_filter( sigma_f );
sigma = (2^(1/intervals))*sigma; % 记录sigma的值
absolute_sigma(octave,interval) = sigma * subsample(octave); % 记录滤波器的尺寸和标准差
filter_size(octave,interval) = length(g);
filter_sigma(octave,interval) = sigma; gauss_pyr{octave,interval} = conv2(g,g,gauss_pyr{octave,interval-1}, 'same' );
DOG_pyr{octave}(:,:,interval-1) = ...
gauss_pyr{octave,interval} - gauss_pyr{octave,interval-1};
end
if octave < octaves
sz = size(gauss_pyr{octave,intervals+1});
[X Y] = meshgrid( 1:2:sz(2), 1:2:sz(1) );
gauss_pyr{octave+1,1} = interp2(gauss_pyr{octave,intervals+1},X,Y,'*nearest');
absolute_sigma(octave+1,1) = absolute_sigma(octave,intervals+1);
subsample = [subsample subsample(end)*2];
end
end
%% 步骤2:查找差分高斯金字塔中的局部极值,并通过曲率和照度进行检验
contrast_threshold = 0.02;
curvature_threshold = 10.0; curvature_threshold = ((curvature_threshold + 1)^2)/curvature_threshold; % 二阶微分核
xx = [ 1 -2 1 ];
yy = xx';
xy = [1 0 -1;
0 0 0;
-1 0 1]/4; raw_keypoints = [];
contrast_keypoints = [];
curve_keypoints = []; object_mask = ones(size(im)); % 在高斯金字塔中查找局部极值
loc = cell(size(DOG_pyr));
for octave = 1:octaves
for interval = 2:(intervals+1)
keypoint_count = 0;
contrast_mask = abs(DOG_pyr{octave}(:,:,interval)) >= contrast_threshold;
loc{octave,interval} = zeros(size(DOG_pyr{octave}(:,:,interval))); edge = ceil(filter_size(octave,interval)/2); for y=(1+edge):(size(DOG_pyr{octave}(:,:,interval),1)-edge)
for x=(1+edge):(size(DOG_pyr{octave}(:,:,interval),2)-edge)
if object_mask(round(y*subsample(octave)),round(x*subsample(octave))) == 1
interactive = 1;
if( (interactive >= 2) | (contrast_mask(y,x) == 1) ) % 注意!
% 通过空间核尺度检测最大值和最小值
tmp = DOG_pyr{octave}((y-1):(y+1),(x-1):(x+1),(interval-1):(interval+1));
pt_val = tmp(2,2,2);
if( (pt_val == min(tmp(:))) | (pt_val == max(tmp(:))) )
% 存储对灰度大于对比度阈值的点的坐标
raw_keypoints = [raw_keypoints; x*subsample(octave) y*subsample(octave)];
if abs(DOG_pyr{octave}(y,x,interval)) >= contrast_threshold
contrast_keypoints = [contrast_keypoints; raw_keypoints(end,:)];
% 计算局部极值的Hessian矩阵
Dxx = sum(DOG_pyr{octave}(y,x-1:x+1,interval) .* xx);
Dyy = sum(DOG_pyr{octave}(y-1:y+1,x,interval) .* yy);
Dxy = sum(sum(DOG_pyr{octave}(y-1:y+1,x-1:x+1,interval) .* xy));
% 计算Hessian矩阵的直迹和行列式.
Tr_H = Dxx + Dyy;
Det_H = Dxx*Dyy - Dxy^2;
% 计算主曲率.
curvature_ratio = (Tr_H^2)/Det_H;
if ((Det_H >= 0) & (curvature_ratio < curvature_threshold))
% 存储主曲率小于阈值的的极值点的坐标(非边缘点)
curve_keypoints = [curve_keypoints; raw_keypoints(end,:)];
% 将该点的位置的坐标设为1,并计算点的数量.
loc{octave,interval}(y,x) = 1;
keypoint_count = keypoint_count + 1;
end
end
end
end
end
end
end fprintf( 2, '%d keypoints found on interval %d\n', keypoint_count, interval ); end
end
clear raw_keypoints contrast_keypoints curve_keypoints;
%% 步骤3:计算特征点的主方向.
% 在特征点的一个区域内计算其梯度直方图
g = gaussian_filter( 1.5 * absolute_sigma(1,intervals+3) / subsample(1) );
zero_pad = ceil( length(g) / 2 ); % 计算高斯金字塔图像的梯度方向和幅值
mag_thresh = zeros(size(gauss_pyr));
mag_pyr = cell(size(gauss_pyr));
grad_pyr = cell(size(gauss_pyr)); for octave = 1:octaves
for interval = 2:(intervals+1) % 计算x,y的差分
diff_x = 0.5*(gauss_pyr{octave,interval}(2:(end-1),3:(end))-gauss_pyr{octave,interval}(2:(end-1),1:(end-2)));
diff_y = 0.5*(gauss_pyr{octave,interval}(3:(end),2:(end-1))-gauss_pyr{octave,interval}(1:(end-2),2:(end-1))); % 计算梯度幅值
mag = zeros(size(gauss_pyr{octave,interval}));
mag(2:(end-1),2:(end-1)) = sqrt( diff_x .^ 2 + diff_y .^ 2 ); % 存储高斯金字塔梯度幅值
mag_pyr{octave,interval} = zeros(size(mag)+2*zero_pad);
mag_pyr{octave,interval}((zero_pad+1):(end-zero_pad),(zero_pad+1):(end-zero_pad)) = mag; % 计算梯度主方向
grad = zeros(size(gauss_pyr{octave,interval}));
grad(2:(end-1),2:(end-1)) = atan2( diff_y, diff_x );
grad(find(grad == pi)) = -pi; % 存储高斯金字塔梯度主方向
grad_pyr{octave,interval} = zeros(size(grad)+2*zero_pad);
grad_pyr{octave,interval}((zero_pad+1):(end-zero_pad),(zero_pad+1):(end-zero_pad)) = grad;
end
end clear mag grad
%% 步骤4:确定特征点的主方向
% 方法:通过寻找每个关键点的子区域内梯度直方图的峰值(注:每个关键点的主方向可以有不止一个) % 将灰度直方图分为36等分,每隔10度一份
num_bins = 36;
hist_step = 2*pi/num_bins; % 步进值
hist_orient = [-pi:hist_step:(pi-hist_step)]; % 步进方向 % 初始化关键点的位置、方向和尺度信息
pos = [];
orient = [];
scale = []; % 给关键点确定主方向
for octave = 1:octaves
for interval = 2:(intervals + 1)
keypoint_count = 0; % 构造高斯加权掩模
g = gaussian_filter( 1.5 * absolute_sigma(octave,interval)/subsample(octave) );
hf_sz = floor(length(g)/2);
g = g'*g; loc_pad = zeros(size(loc{octave,interval})+2*zero_pad);
loc_pad((zero_pad+1):(end-zero_pad),(zero_pad+1):(end-zero_pad)) = loc{octave,interval}; [iy ix]=find(loc_pad==1);
for k = 1:length(iy) x = ix(k);
y = iy(k);
wght = g.*mag_pyr{octave,interval}((y-hf_sz):(y+hf_sz),(x-hf_sz):(x+hf_sz));
grad_window = grad_pyr{octave,interval}((y-hf_sz):(y+hf_sz),(x-hf_sz):(x+hf_sz));
orient_hist=zeros(length(hist_orient),1);
for bin=1:length(hist_orient)
diff = mod( grad_window - hist_orient(bin) + pi, 2*pi ) - pi;
orient_hist(bin) = ...
orient_hist(bin)+sum(sum(wght.*max(1 - abs(diff)/hist_step,0)));
end % 运用非极大抑制法查找主方向直方图的峰值
peaks = orient_hist;
rot_right = [ peaks(end); peaks(1:end-1) ];
rot_left = [ peaks(2:end); peaks(1) ];
peaks( find(peaks < rot_right) ) = 0;
peaks( find(peaks < rot_left) ) = 0; % 提取最大峰值的值和其索引位置
[max_peak_val ipeak] = max(peaks); % 将大于等于最大峰值80% 的直方图的也确定为特征点的主方向
peak_val = max_peak_val;
while( peak_val > 0.8*max_peak_val ) % 最高峰值最近的三个柱值通过抛物线插值精确得到
A = [];
b = [];
for j = -1:1
A = [A; (hist_orient(ipeak)+hist_step*j).^2 (hist_orient(ipeak)+hist_step*j) 1];
bin = mod( ipeak + j + num_bins - 1, num_bins ) + 1;
b = [b; orient_hist(bin)];
end
c = pinv(A)*b;
max_orient = -c(2)/(2*c(1));
while( max_orient < -pi )
max_orient = max_orient + 2*pi;
end
while( max_orient >= pi )
max_orient = max_orient - 2*pi;
end % 存储关键点的位置、主方向和尺度信息
pos = [pos; [(x-zero_pad) (y-zero_pad)]*subsample(octave) ];
orient = [orient; max_orient];
scale = [scale; octave interval absolute_sigma(octave,interval)];
keypoint_count = keypoint_count + 1; peaks(ipeak) = 0;
[peak_val ipeak] = max(peaks);
end
end
fprintf( 2, '(%d keypoints)\n', keypoint_count );
end
end
clear loc loc_pad
%% 步骤5:特征向量生成
orient_bin_spacing = pi/4;
orient_angles = [-pi:orient_bin_spacing:(pi-orient_bin_spacing)]; grid_spacing = 4;
[x_coords y_coords] = meshgrid( [-6:grid_spacing:6] );
feat_grid = [x_coords(:) y_coords(:)]';
[x_coords y_coords] = meshgrid( [-(2*grid_spacing-0.5):(2*grid_spacing-0.5)] );
feat_samples = [x_coords(:) y_coords(:)]';
feat_window = 2*grid_spacing; desc = []; for k = 1:size(pos,1)
x = pos(k,1)/subsample(scale(k,1));
y = pos(k,2)/subsample(scale(k,1)); % 将坐标轴旋转为关键点的方向,以确保旋转不变性
M = [cos(orient(k)) -sin(orient(k)); sin(orient(k)) cos(orient(k))];
feat_rot_grid = M*feat_grid + repmat([x; y],1,size(feat_grid,2));
feat_rot_samples = M*feat_samples + repmat([x; y],1,size(feat_samples,2)); % 初始化特征向量.
feat_desc = zeros(1,128); for s = 1:size(feat_rot_samples,2)
x_sample = feat_rot_samples(1,s);
y_sample = feat_rot_samples(2,s); % 在采样位置进行梯度插值
[X Y] = meshgrid( (x_sample-1):(x_sample+1), (y_sample-1):(y_sample+1) );
G = interp2( gauss_pyr{scale(k,1),scale(k,2)}, X, Y, '*linear' ); % 耗时太长
G(find(isnan(G))) = 0;
diff_x = 0.5*(G(2,3) - G(2,1));
diff_y = 0.5*(G(3,2) - G(1,2));
mag_sample = sqrt( diff_x^2 + diff_y^2 );
grad_sample = atan2( diff_y, diff_x );
if grad_sample == pi
grad_sample = -pi;
end % 计算x、y方向上的权重
x_wght = max(1 - (abs(feat_rot_grid(1,:) - x_sample)/grid_spacing), 0);
y_wght = max(1 - (abs(feat_rot_grid(2,:) - y_sample)/grid_spacing), 0);
pos_wght = reshape(repmat(x_wght.*y_wght,8,1),1,128); diff = mod( grad_sample - orient(k) - orient_angles + pi, 2*pi ) - pi;
orient_wght = max(1 - abs(diff)/orient_bin_spacing,0);
orient_wght = repmat(orient_wght,1,16); % 计算高斯权重
g = exp(-((x_sample-x)^2+(y_sample-y)^2)/(2*feat_window^2))/(2*pi*feat_window^2); feat_desc = feat_desc + pos_wght.*orient_wght*g*mag_sample;
end % 将特征向量的长度归一化,则可以进一步去除光照变化的影响.
feat_desc = feat_desc / norm(feat_desc); feat_desc( find(feat_desc > 0.2) ) = 0.2;
feat_desc = feat_desc / norm(feat_desc); % 存储特征向量.
desc = [desc; feat_desc];
tmp = mod(k,25);
if ( tmp == 0 )
fprintf( 2, '.' );
end
end % 调整采样偏差
sample_offset = -(subsample - 1);
for k = 1:size(pos,1)
pos(k,:) = pos(k,:) + sample_offset(scale(k,1));
end if size(pos,1) > 0
scale = scale(:,3);
end hh = display_keypoints( pos, scale, orient);

调用了

function g = gaussian_filter(sigma)

% 功能:生成一维高斯滤波器
% 输入:
% sigma – 高斯滤波器的标准差
% 输出:
% g – 高斯滤波器 sample = 7.0/2.0;
n = 2*round(sample * sigma)+1;
x = 1:n;
x = x - ceil(n/2); g = exp(-(x.^2)/(2*sigma^2))/(sigma*sqrt(2*pi));

原作者是Xiaochuan ZHAO 邮箱:zhaoxch@bit.edu.cn

我去除了他的代码中一些无关紧要的部分。

纯MATLAB版本 SIFT代码的更多相关文章

  1. RobHess的SIFT代码解析步骤三

    平台:win10 x64 +VS 2015专业版 +opencv-2.4.11 + gtk_-bundle_2.24.10_win32 主要参考:1.代码:RobHess的SIFT源码 2.书:王永明 ...

  2. k-means算法MATLAB和opencv代码

    上一篇博客写了k-means聚类算法和改进的k-means算法.这篇博客就贴出相应的MATLAB和C++代码. 下面是MATLAB代码,实现用k-means进行切割: %%%%%%%%%%%%%%%% ...

  3. MATLAB版本(2012b 64bit),在尝试调用svmtrain函数时报错

    问题:MATLAB版本(2012b 64bit),在尝试调用svmtrain函数时报错: 解决方案:参照https://blog.csdn.net/TIME_LEAF/article/details/ ...

  4. 一道在CF上WA了9次才AC的A题题目与10个版本的代码代码

    题目(题目链接:https://codeforces.com/problemset/problem/733/A):   A. Grasshopper And the String time limit ...

  5. 球体的双目视觉定位(matlab,附代码)

    球体的双目视觉定位(matlab,附代码) 标签(空格分隔): 机器视觉 引言 双目视觉定位是我们的一个课程设计,最近刚做完,拿出来与大家分享一下,实验的目的是在拍摄的照片中识别球体,并求出该球体到相 ...

  6. RobHess的SIFT代码解析步骤四

    平台:win10 x64 +VS 2015专业版 +opencv-2.4.11 + gtk_-bundle_2.24.10_win32 主要参考:1.代码:RobHess的SIFT源码 2.书:王永明 ...

  7. H5+ 重写在线升级版本比较代码

    重写h5+在线升级版本比较代码 hello h5+版本在线升级提供了如下的版本比较方法,逻辑比较繁琐,相关判断多余,非常不宜读. 先判断新旧版本有无, 接着分割为数组比较数组项大小,而且还只取了前四项 ...

  8. sift代码实现详解

    1.创建高斯金字塔第-1组 1.1.将源图片转成灰度图 void ConvertToGray(const Mat& src, Mat& dst) { cv::Size size = s ...

  9. ie使用已安装的最高版本ie代码,及ie下不要在结尾处写多余的逗号“,”

    ie使用已安装的最高版本ie代码 <meta http-equiv = "X-UA-Compatible" content = "IE=7,IE=9" & ...

随机推荐

  1. Object Relational Mapping(ORM)

    Object Relational Mapping(ORM) ORM介绍 ORM概念 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据 ...

  2. 试用 openresty/lua-resty-shell

    openresty/lua-resty-shell 是当前最新rc 版本内置的shell 功能,我们可以用来执行一个脚本,以及命令 还是比较方便的. 测试集成了一个oreilly电子书下载的功能 环境 ...

  3. Python中字典get方法的使用

    Python中字典get方法的使用 说起来,这个功能是否需要学习还真是有待于讨论.其实,知道了字典这种数据结构以后,通过最基本的Python功能也能够实现一个类似的功能.不过,既然内置了这个功能那么我 ...

  4. setsockopt调用IP_ADD_MEMBERSHIP出错errno:19 no such device

    if (setsockopt(fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq)) < 0) {                 pri ...

  5. 如何对 PHP 代码加密?

    这几天有同事问如何对 PHP 代码进行加密? 在我印象中 PHP 加密还是比较麻烦的,之前只知道 Zend Guard . 刚刚找了一圈发现现在还是有很多可行的方案,先记录一下,以后使用. 使用混淆 ...

  6. linux下常见软件安装

    读者还可以参考文档:https://download.csdn.net/download/qq_27799563/10482900 Mysql的安装过程: 解压MySQL安装包: tar -xvf M ...

  7. 两个简单的API限流实现方案

    1, Ngnix限流 Nginx在架构中起到请求转发与负载均衡器的作用.外部req首先到Nginx监听的80端口,然后Nginx将req交给到监听8080端口的APP服务器处理.处理结果再经由Ngin ...

  8. vue-cli 项目优化之3种方法对比:本地静态库资源(推荐)、cdn、DllPlugin

    vue-cli 项目优化之3种方法对比:本地静态库资源(推荐).cdn.DllPlugin 事项 本地静态库资源 cdn DllPlugin 依赖 依赖cdn网站资源(有种完善方法:如果cdn引入不成 ...

  9. TP5模型belongsTo和hasOne的区别

    在使用tp5模型的ORM的时候出现belongsTo和hasOne都有表示一对一的关系,但是二者并不相同.以下举例说明两者的区别: 首先有user表 字段 id name password字段 然后有 ...

  10. sql查询:存在A表而不在B表中的数据

    A.B两表,找出ID字段中,存在A表,但是不存在B表的数据. 方法一:使用 not inselect distinct A.ID from A where A.ID not in (select ID ...