SIFT学习笔记之二 特征提取
特征提取函数:
int _sift_features( IplImage* img, struct feature** feat, int intvls,
double sigma, double contr_thr, int curv_thr,
int img_dbl, int descr_width, int descr_hist_bins )
{
IplImage* init_img;
IplImage*** gauss_pyr, *** dog_pyr;
CvMemStorage* storage;
CvSeq* features;
int octvs, i, n = 0; /* check arguments */
if( ! img )
fatal_error( "NULL pointer error, %s, line %d", __FILE__, __LINE__ ); if( ! feat )
fatal_error( "NULL pointer error, %s, line %d", __FILE__, __LINE__ ); /* build scale space pyramid; smallest dimension of top level is ~4 pixels */
init_img = create_init_img( img, img_dbl, sigma );//创建初始图像,灰度,每位32F(单精度浮点)来存储。
octvs = log( MIN( init_img->width, init_img->height ) ) / log(2) - 2; //从原始图像构建金字塔的层数,-2 使得最小的一层图像有4个像素,而不是1个像素
gauss_pyr = build_gauss_pyr( init_img, octvs, intvls, sigma );//创建高斯金字塔,octvs层数(0层是原图,向上每层降采样一次),intvls每层中尺寸相同但是模糊程度不同,sigma模糊的初始参数,intvals+3
dog_pyr = build_dog_pyr( gauss_pyr, octvs, intvls );//差分金字塔,层数不变,每层中用相邻图像相减,故每层图像数intvls+2 storage = cvCreateMemStorage( 0 );
features = scale_space_extrema( dog_pyr, octvs, intvls, contr_thr,
curv_thr, storage );//检测极值点,在同一层中的图片,的一个像素点,如果是相邻另个模糊度图像对应3x3window中的max值(像素>0时),或者为min值(像素<0时),并且经过插值获得亚像素极值点的坐标,测试对比度,边缘影响,最后将该像素对应在原图中的坐标记录到feat中,将所有满足条件的像素的feat串联到featuresSeq中。
calc_feature_scales( features, sigma, intvls );
if( img_dbl )
adjust_for_img_dbl( features );
calc_feature_oris( features, gauss_pyr );
compute_descriptors( features, gauss_pyr, descr_width, descr_hist_bins ); /* sort features by decreasing scale and move from CvSeq to array */
cvSeqSort( features, (CvCmpFunc)feature_cmp, NULL );
n = features->total;
*feat = calloc( n, sizeof(struct feature) );
*feat = cvCvtSeqToArray( features, *feat, CV_WHOLE_SEQ );
for( i = 0; i < n; i++ )
{
free( (*feat)[i].feature_data );
(*feat)[i].feature_data = NULL;
} cvReleaseMemStorage( &storage );
cvReleaseImage( &init_img );
release_pyr( &gauss_pyr, octvs, intvls + 3 );
release_pyr( &dog_pyr, octvs, intvls + 2 );
return n;
}
这里的角度值是弧度值
通过计算特征向量的主曲率半径来判断特征是否是边缘which will导致不稳定,即去除边缘响应:
static int is_too_edge_like( IplImage* dog_img, int r, int c, int curv_thr )
{
double d, dxx, dyy, dxy, tr, det; /* principal curvatures are computed using the trace and det of Hessian */
d = pixval32f(dog_img, r, c);
dxx = pixval32f( dog_img, r, c+1 ) + pixval32f( dog_img, r, c-1 ) - 2 * d;
dyy = pixval32f( dog_img, r+1, c ) + pixval32f( dog_img, r-1, c ) - 2 * d;
dxy = ( pixval32f(dog_img, r+1, c+1) - pixval32f(dog_img, r+1, c-1) -
pixval32f(dog_img, r-1, c+1) + pixval32f(dog_img, r-1, c-1) ) / 4.0;
tr = dxx + dyy;
det = dxx * dyy - dxy * dxy; /* negative determinant -> curvatures have different signs; reject feature */
if( det <= 0 )
return 1; if( tr * tr / det < ( curv_thr + 1.0 )*( curv_thr + 1.0 ) / curv_thr )
return 0;
return 1;
}
calc_features_scales函数很简单,就是填充所有特征点对应的scl和scl_octv变量:
static void calc_feature_scales( CvSeq* features, double sigma, int intvls )
{
struct feature* feat;
struct detection_data* ddata;
double intvl;
int i, n; n = features->total;
for( i = 0; i < n; i++ )
{
feat = CV_GET_SEQ_ELEM( struct feature, features, i );
ddata = feat_detection_data( feat );
intvl = ddata->intvl + ddata->subintvl;
feat->scl = sigma * pow( 2.0, ddata->octv + intvl / intvls );
ddata->scl_octv = sigma * pow( 2.0, intvl / intvls );
}
}
函数adjust_for_img_dbl(),如果在开始的时候将图像dbl扩大了,这是就需要将特征点中的坐标值/2处理:
n = features->total;
for( i = 0; i < n; i++ )
{
feat = CV_GET_SEQ_ELEM( struct feature, features, i );
feat->x /= 2.0;
feat->y /= 2.0;
feat->scl /= 2.0;
feat->img_pt.x /= 2.0;
feat->img_pt.y /= 2.0;
}
在计算机中真是说不清‘2’这个数字有多重要,金字塔中也到处是它:)
下面是计算特征方向的函数:
static void calc_feature_oris( CvSeq* features, IplImage*** gauss_pyr )
{
struct feature* feat;
struct detection_data* ddata;
double* hist;
double omax;
int i, j, n = features->total; for( i = 0; i < n; i++ )
{
feat = malloc( sizeof( struct feature ) );
cvSeqPopFront( features, feat );
ddata = feat_detection_data( feat );
hist = ori_hist( gauss_pyr[ddata->octv][ddata->intvl],
ddata->r, ddata->c, SIFT_ORI_HIST_BINS,
cvRound( SIFT_ORI_RADIUS * ddata->scl_octv ),
SIFT_ORI_SIG_FCTR * ddata->scl_octv );
for( j = 0; j < SIFT_ORI_SMOOTH_PASSES; j++ )
smooth_ori_hist( hist, SIFT_ORI_HIST_BINS );
omax = dominant_ori( hist, SIFT_ORI_HIST_BINS );
add_good_ori_features( features, hist, SIFT_ORI_HIST_BINS,
omax * SIFT_ORI_PEAK_RATIO, feat );
free( ddata );
free( feat );
free( hist );
}
}
还是将每个特征取出来[函数cvSeqPopFront删除序列的头部元素],针对每个特征像素点统计其所在窗口图像中的方向梯度直方图,主要就是函数ori_hist():
aaarticlea/png;base64,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" alt="" />
直方图用一个double数组在存储,统计窗口图像中每个像素所在的dx dy ->梯度的角度和梯度的模,然后将角度转换为hist数组的index,累加值是梯度的模(距离加权后)。窗口的半径大小跟所在的尺度有关。
对得到的hist通过函数smooth_ori_hist()多次进行平滑,也是通过相加求平均的方法。
函数dominant_ori()用来求得直方图中值最大的那个方向的值,然后用这个值乘上一个系数作为阈值用于add_good_ori_fetures()函数:
static void add_good_ori_features( CvSeq* features, double* hist, int n,
double mag_thr, struct feature* feat )
{
struct feature* new_feat;
double bin, PI2 = CV_PI * 2.0;
int l, r, i; for( i = 0; i < n; i++ )
{
l = ( i == 0 )? n - 1 : i-1;
r = ( i + 1 ) % n; if( hist[i] > hist[l] && hist[i] > hist[r] && hist[i] >= mag_thr )
{
bin = i + interp_hist_peak( hist[l], hist[i], hist[r] );
bin = ( bin < 0 )? n + bin : ( bin >= n )? bin - n : bin;
new_feat = clone_feature( feat );
new_feat->ori = ( ( PI2 * bin ) / n ) - CV_PI;
cvSeqPush( features, new_feat );
free( new_feat );
}
}
}
该函数通过遍历整个hist直方图中的所有方向,然后找到局部极值点并且满足阈值的,填充ori变量,构成新特征,然后将此特征再添加会队列中。(可能有多个满足条件的点)
----
好了至此,所有特征点的坐标、所在尺度、其方向都已经得到,就剩下构成特征向量了:
static void compute_descriptors( CvSeq* features, IplImage*** gauss_pyr, int d, int n)
{
struct feature* feat;
struct detection_data* ddata;
double*** hist;
int i, k = features->total; for( i = 0; i < k; i++ )
{
feat = CV_GET_SEQ_ELEM( struct feature, features, i );
ddata = feat_detection_data( feat );
hist = descr_hist( gauss_pyr[ddata->octv][ddata->intvl], ddata->r,
ddata->c, feat->ori, ddata->scl_octv, d, n );
hist_to_descr( hist, d, n, feat );
release_descr_hist( &hist, d );
}
}
这里的descr_hist函数也是计算特征点所在窗口中的梯度方向分布->由于需要具有旋转不变性的特征,所以这里的梯度方向是相对于上面检测出来的特征像素的主方向的角度差来统计的:
函数里面有个坐标变换和求角度差的过程
static double*** descr_hist( IplImage* img, int r, int c, double ori,
double scl, int d, int n )
{
double*** hist;
double cos_t, sin_t, hist_width, exp_denom, r_rot, c_rot, grad_mag,
grad_ori, w, rbin, cbin, obin, bins_per_rad, PI2 = 2.0 * CV_PI;
int radius, i, j; hist = calloc( d, sizeof( double** ) );
for( i = 0; i < d; i++ )
{
hist[i] = calloc( d, sizeof( double* ) );
for( j = 0; j < d; j++ )
hist[i][j] = calloc( n, sizeof( double ) );
} cos_t = cos( ori );
sin_t = sin( ori );
bins_per_rad = n / PI2;
exp_denom = d * d * 0.5;
hist_width = SIFT_DESCR_SCL_FCTR * scl;
radius = hist_width * sqrt(2) * ( d + 1.0 ) * 0.5 + 0.5;
for( i = -radius; i <= radius; i++ )
for( j = -radius; j <= radius; j++ )
{
/*
Calculate sample's histogram array coords rotated relative to ori.
Subtract 0.5 so samples that fall e.g. in the center of row 1 (i.e.
r_rot = 1.5) have full weight placed in row 1 after interpolation.
*/
c_rot = ( j * cos_t - i * sin_t ) / hist_width;
r_rot = ( j * sin_t + i * cos_t ) / hist_width;
rbin = r_rot + d / 2 - 0.5;
cbin = c_rot + d / 2 - 0.5; if( rbin > -1.0 && rbin < d && cbin > -1.0 && cbin < d )
if( calc_grad_mag_ori( img, r + i, c + j, &grad_mag, &grad_ori ))
{
grad_ori -= ori;
while( grad_ori < 0.0 )
grad_ori += PI2;
while( grad_ori >= PI2 )
grad_ori -= PI2; obin = grad_ori * bins_per_rad;
w = exp( -(c_rot * c_rot + r_rot * r_rot) / exp_denom );
interp_hist_entry( hist, rbin, cbin, obin, grad_mag * w, d, n );
}
} return hist;
}
遍历窗口中的每一个像素,获得其梯度的新的相对角度,然后在相对坐标系中重新计算梯度方向直方图。这里涉及到一个3次插值,首先新坐标中的row、col、orientation都是浮点数,对
于离散化的图像来说,并不是简单的将其转换为整型数据,要利用精确数据的小数部分对两侧的数据进行加权然后累加。总而言之就是首先获得特征点窗口图像区域的梯度方向直方图,然后
计算新的相对坐标系中的点(浮点数),利用浮点数的小数部分对原梯度直方图中的数据(row、col、ori)进行加权累加到新的梯度直方图中,详见函数interp_hist_entry():
static void interp_hist_entry( double*** hist, double rbin, double cbin,
double obin, double mag, int d, int n )
{
double d_r, d_c, d_o, v_r, v_c, v_o;
double** row, * h;
int r0, c0, o0, rb, cb, ob, r, c, o; r0 = cvFloor( rbin );
c0 = cvFloor( cbin );
o0 = cvFloor( obin );
d_r = rbin - r0;
d_c = cbin - c0;
d_o = obin - o0; /*
The entry is distributed into up to 8 bins. Each entry into a bin
is multiplied by a weight of 1 - d for each dimension, where d is the
distance from the center value of the bin measured in bin units.
*/
for( r = 0; r <= 1; r++ )
{
rb = r0 + r;
if( rb >= 0 && rb < d )
{
v_r = mag * ( ( r == 0 )? 1.0 - d_r : d_r );
row = hist[rb];
for( c = 0; c <= 1; c++ )
{
cb = c0 + c;
if( cb >= 0 && cb < d )
{
v_c = v_r * ( ( c == 0 )? 1.0 - d_c : d_c );
h = row[cb];
for( o = 0; o <= 1; o++ )
{
ob = ( o0 + o ) % n;
v_o = v_c * ( ( o == 0 )? 1.0 - d_o : d_o );
h[ob] += v_o;
}
}
}
}
}
}
好了,至此得到了一个三维数组,row、col对应于窗口图像中点的坐标,ori对应于该位置上的直方图,该直方图的ori元素中的值是由相邻亚像素位置的梯度模值加权累加得到的,下面对
这个三维数组进行简单处理得到最终的特征描述descriptor:
static void hist_to_descr( double*** hist, int d, int n, struct feature* feat )
{
int int_val, i, r, c, o, k = 0; for( r = 0; r < d; r++ )
for( c = 0; c < d; c++ )
for( o = 0; o < n; o++ )
feat->descr[k++] = hist[r][c][o]; feat->d = k;
normalize_descr( feat );
for( i = 0; i < k; i++ )
if( feat->descr[i] > SIFT_DESCR_MAG_THR )
feat->descr[i] = SIFT_DESCR_MAG_THR;
normalize_descr( feat ); /* convert floating-point descriptor to integer valued descriptor */
for( i = 0; i < k; i++ )
{
int_val = SIFT_INT_DESCR_FCTR * feat->descr[i];
feat->descr[i] = MIN( 255, int_val );
}
}
首先简单的将三维存储的数据一维化,然后进行归一化,处理下较大的值,再次归一化,然后将浮点类型的数据展开成0~255的整型特征,这样该特征点对应的sift特征就提取完成了。
整个过程和HOG算法有很多类似的地方,尤其是三次线性插值的过程。
--------------------
至此将适当的处理下数据类型,释放相关的中间变量之后整个_sift_features()特征提取函数就完成了。在调用该函数的时候会传入feat特征的指针,会在_sift_featrues()函数内部进行
特征内存的分配。
SIFT学习笔记之二 特征提取的更多相关文章
- 《Java编程思想》学习笔记(二)——类加载及执行顺序
<Java编程思想>学习笔记(二)--类加载及执行顺序 (这是很久之前写的,保存在印象笔记上,今天写在博客上.) 今天看Java编程思想,看到这样一道代码 //: OrderOfIniti ...
- Hibernate学习笔记(二)
2016/4/22 23:19:44 Hibernate学习笔记(二) 1.1 Hibernate的持久化类状态 1.1.1 Hibernate的持久化类状态 持久化:就是一个实体类与数据库表建立了映 ...
- X-Cart 学习笔记(二)X-Cart框架1
目录 X-Cart 学习笔记(一)了解和安装X-Cart X-Cart 学习笔记(二)X-Cart框架1 X-Cart 学习笔记(三)X-Cart框架2 X-Cart 学习笔记(四)常见操作 四.X- ...
- C#可扩展编程之MEF学习笔记(二):MEF的导出(Export)和导入(Import)
上一篇学习完了MEF的基础知识,编写了一个简单的DEMO,接下来接着上篇的内容继续学习,如果没有看过上一篇的内容, 请阅读:http://www.cnblogs.com/yunfeifei/p/392 ...
- DuiLib学习笔记(二) 扩展CScrollbar属性
DuiLib学习笔记(二) 扩展CScrollbar属性 Duilib的滚动条滑块默认最小值为滚动条的高度(HScrollbar)或者宽度(VScrollbar).并且这个值默认为16.当采用系统样式 ...
- guava 学习笔记(二) 瓜娃(guava)的API快速熟悉使用
guava 学习笔记(二) 瓜娃(guava)的API快速熟悉使用 1,大纲 让我们来熟悉瓜娃,并体验下它的一些API,分成如下几个部分: Introduction Guava Collection ...
- Dynamic CRM 2013学习笔记(二十八)用JS动态设置字段的change事件、必填、禁用以及可见
我们知道通过界面设置字段的change事件,是否是必填,是否可见非常容易.但有时我们需要动态地根据某些条件来设置,这时有需要通过js来动态地控制了. 下面分别介绍如何用js来动态设置. 一.动态设 ...
- Dynamic CRM 2013学习笔记(二十七)无代码 复制/克隆方法
前面介绍过二种复制/克隆方法:<Dynamic CRM 2013学习笔记(十四)复制/克隆记录> 和<Dynamic CRM 2013学习笔记(二十五)JS调用web service ...
- .NET Remoting学习笔记(二)激活方式
目录 .NET Remoting学习笔记(一)概念 .NET Remoting学习笔记(二)激活方式 .NET Remoting学习笔记(三)信道 参考:百度百科 ♂风车车.Net 激活方式概念 在 ...
随机推荐
- C# GridView 的使用
1.GridView无代码分页排序: 1.AllowSorting设为True,aspx代码中是AllowSorting="True":2.默认1页10条,如果要修改每页条数,修改 ...
- Java中String为什么是不可变
什么是不可变对象? 众所周知, 在Java中, String类是不可变的.那么到底什么是不可变的对象呢? 可以这样认为:如果一个对象,在它创建完成之后,不能再改变它的状态,那么这个对象就是不可变的.不 ...
- 15分钟入门Markdown
一.标题一 标题三 标题六 # 一.标题一 ### 标题三 ###### 标题六 二.字体 1.普通字体 字体加粗 斜体 斜体加粗 删除线 1.普通字体 **字体加粗** *斜体* ***斜体加粗** ...
- 从入门到精通djang Django
http://docs.30c.org/djangobook2/ 推荐大家一本书 特别用用 中文版的 哦
- 003_linuxC++之_namespace使用
(一)引入namespace原因: 假如有很多跟人共同完成一项工程,工程中难免会有函数定义一样的名称,不可能一个一个的询问这个函数 你定义过了没有,所以引入namespace #include < ...
- Codeforces 1220 E Tourism
题面 可以发现一个边双必然是可以随意走的,所以我们就把原图求割边然后把边双缩成一个点,然后就是一个树上dp了. #include<bits/stdc++.h> #define ll lon ...
- 国庆集训Day1
T1 divide 题意: 有\(n\)个数 \(a_1, a_2,..., a_n\) 有m个数\(b_1, b_2,..., b_n\) 令\(a = a_1\times a_2\,\times ...
- java集合类-List接口
List接口包含List接口及List接口的所有实现类,List集合中的元素允许重复. List接口 List接口继承了Collection接口,包含Collection接口的所有方法,还定义了两个非 ...
- 第二章实战补充:Python操作Mysql
( 一) 导入pymysql 基础铺垫:pymysql与MySQLdb pymysql–支持py2.py3; MySQLdb–仅支持python3; django内部默认为MySQLdb,用Pytho ...
- elasticsearch 内部对象结构数据索引
内部对象 经常用于 嵌入一个实体或对象到其它对象中.例如,与其在 tweet 文档中包含 user_name 和 user_id 域,我们也可以这样写: { "tweet": &q ...