SIFT算法实现理解及注释详解(基于RobHess源码)_第1页
SIFT算法实现理解及注释详解(基于RobHess源码)_第2页
SIFT算法实现理解及注释详解(基于RobHess源码)_第3页
SIFT算法实现理解及注释详解(基于RobHess源码)_第4页
SIFT算法实现理解及注释详解(基于RobHess源码)_第5页
已阅读5页,还剩21页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

RobHess的SIFT算法实现理解及注释

SIFT算法不用我多解释了,这是一个很强大的算法,主要用于图像配准和物体识别等领域,但是其计算量相比也比较大,性价比比较高的算法包括PCA-SIFT和SURF其中OpenCV提供了SURF算法,但是为了方便理解。这里给出了RobHess所实现的SIFT算法的实现以及注释,结合我自己的理解,如果,您有关于SIFT算法不理解的地方咱们可以一起交流一下。或者您认为不详细的地方提出来。

SIFT算法的主要实现在sift.c这个文件,其主要流程为:(1)首先创建初始图像,即通过将图像转换为32位的灰度图,然后将图像使用三次插值来方大,之后通过高斯模糊处理(2)在此基础上进行高斯金字塔的构建以及高斯差分金字塔的构建(3)对图像进行极值点检测(4)计算特征向量的尺度(5)调整图像大小(6)计算特征的方向(7)计算描述子,其中包括计算二维方向直方图并转换直方图为特征描述子首先给出sift算法的整体框架代码:输入参数:img为输入图像;feat为所要提取的特征指针;intvl指的是高斯金字塔和差分金字塔的层数;sigma指的是图像初始化过程中高斯模糊所使用的参数;

contr_thr是归一化之后的去除不稳定特征的阈值;curv_thr指的是去除边缘的特征的主曲率阈值;img_dbl是是否将图像放大为之前的两倍;descr_with用来计算特征描述子的方向直方图的宽度;descr_hist_bins是直方图中的条数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

*/

/*

构建高斯尺度空间金字塔,顶层最小的为4像素

*/

init_img

=

create_init_img(

img,

img_dbl,

sigma

);

octvs

=

log(

double

MIN(

init_img->width,

init_img->height

)

)

/

log(2.0)

-

2;

//构建高斯金字塔和高斯差分金字塔

gauss_pyr

=

build_gauss_pyr(

init_img,

octvs,

intvls,

sigma

);

dog_pyr

=

build_dog_pyr(

gauss_pyr,

octvs,

intvls

);

storage

=

cvCreateMemStorage(

0

);

//尺度空间极值点检测

features

=

scale_space_extrema(

dog_pyr,

octvs,

intvls,

contr_thr,

curv_thr,

storage

);

//画出去除低对比度的极值点

//draw_extrempoint(img

,

features);

//计算特征向量的尺度

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

=

static_cast<feature

*>(

calloc(

n,

sizeof(struct

feature)

)

);

*feat

=

static_cast<feature

*>(

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;

}

(1)初始化图像输入参数:这里不需要解释了该函数主要用来初始化图像,转换图像为32位灰度图以及进行高斯模糊。static

IplImage*

create_init_img(

IplImage*

img,

int

img_dbl,

double

sigma

)

{

IplImage*

gray,

*

dbl;

float

sig_diff;

gray

=

convert_to_gray32(

img

);

if(

img_dbl

)

{

sig_diff

=

sqrt(

sigma

*

sigma

-

SIFT_INIT_SIGMA

*

SIFT_INIT_SIGMA

*

4

);

dbl

=

cvCreateImage(

cvSize(

img->width*2,

img->height*2

),

IPL_DEPTH_32F,

1

);

cvResize(

gray,

dbl,

CV_INTER_CUBIC

);

cvSmooth(

dbl,

dbl,

CV_GAUSSIAN,

0,

0,

sig_diff,

sig_diff

);

cvReleaseImage(

&gray

);

return

dbl;

}

else

{

sig_diff

=

sqrt(

sigma

*

sigma

-

SIFT_INIT_SIGMA

*

SIFT_INIT_SIGMA

);

cvSmooth(

gray,

gray,

CV_GAUSSIAN,

0,

0,

sig_diff,

sig_diff

);

return

gray;

}

}

(2)构建高斯金字塔输入参数:octvs是高斯金字塔的组invls是高斯金字塔的层数sigma是初始的高斯模糊参数,后续也通过它计算每一层所使用的sigma<span

style="font-size:13px;">static

IplImage***

build_gauss_pyr(

IplImage*

base,

int

octvs,int

intvls,

double

sigma

)

{

IplImage***

gauss_pyr;

double*

sig

=

static_cast<double

*>(

calloc(

intvls

+

3,

sizeof(double))

);

double

sig_total,

sig_prev,

k;

int

i,

o;

gauss_pyr

=

static_cast<IplImage

***>(

calloc(

octvs,

sizeof(

IplImage**

)

)

);

for(

i

=

0;

i

<

octvs;

i++

)

gauss_pyr[i]

=

static_cast<IplImage

**>(

calloc(

intvls

+

3,

sizeof(

IplImage*

)

)

);

/*

precompute

Gaussian

sigmas

using

the

following

formula:

预计算每次高斯模糊的sigma

\sigma_{total}^2

=

\sigma_{i}^2

+

\sigma_{i-1}^2

*/

sig[0]

=

sigma;

k

=

pow(

2.0,

1.0

/

intvls

);

for(

i

=

1;

i

<

intvls

+

3;

i++

)

{

sig_prev

=

pow(

k,

i

-

1

)

*

sigma;

sig_total

=

sig_prev

*

k;

sig[i]

=

sqrt(

sig_total

*

sig_total

-

sig_prev

*

sig_prev

);

}

for(

o

=

0;

o

<

octvs;

o++

)

for(

i

=

0;

i

<

intvls

+

3;

i++

)

{

//对每一层进行降采样,形成高斯金字塔的每一层

if(

o

==

0

&&

i

==

0

)

gauss_pyr[o][i]

=

cvCloneImage(base);

/*

base

of

new

octvave

is

halved

image

from

end

of

previous

octave

*/

//每一组的第一层都是通过对前面一组的最上面一层的降采样实现的

else

if(

i

==

0

)

gauss_pyr[o][i]

=

downsample(

gauss_pyr[o-1][intvls]

);

/*

blur

the

current

octave's

last

image

to

create

the

next

one

*/

//每一组的其他层则使通过使用不同sigma的高斯模糊来进行处理

else

{

gauss_pyr[o][i]

=

cvCreateImage(

cvGetSize(gauss_pyr[o][i-1]),

IPL_DEPTH_32F,

1

);

cvSmooth(

gauss_pyr[o][i-1],

gauss_pyr[o][i],

CV_GAUSSIAN,

0,

0,

sig[i],

sig[i]

);

}

}

free(

sig

);

return

gauss_pyr;

}</span>

降采样处理输入参数:不解释这就是降采样,其实就是将图像通过最近邻算法缩小为原来的一半static

IplImage*

downsample(

IplImage*

img

)

{

IplImage*

smaller

=

cvCreateImage(

cvSize(img->width

/

2,

img->height

/

2),

img->depth,

img->nChannels

);

cvResize(

img,

smaller,

CV_INTER_NN

);

return

smaller;

}

(3)构建高斯差分金字塔输入参数:不解释了参见上面的说明即可实际上差分金字塔的构成是通过对相邻层的图像进行相减获得的<span

style="font-size:16px;">static

IplImage***

build_dog_pyr(

IplImage***

gauss_pyr,

int

octvs,

int

intvls

)

{

IplImage***

dog_pyr;

int

i,

o;

dog_pyr

=

static_cast<IplImage

***>(

calloc(

octvs,

sizeof(

IplImage**

)

)

);

for(

i

=

0;

i

<

octvs;

i++

)

dog_pyr[i]

=

static_cast<IplImage

**>(

calloc(

intvls

+

2,

sizeof(IplImage*)

)

);

for(

o

=

0;

o

<

octvs;

o++

)

for(

i

=

0;

i

<

intvls

+

2;

i++

)

{

dog_pyr[o][i]

=

cvCreateImage(

cvGetSize(gauss_pyr[o][i]),

IPL_DEPTH_32F,

1

);

cvSub(

gauss_pyr[o][i+1],

gauss_pyr[o][i],

dog_pyr[o][i],

NULL

);

}

return

dog_pyr;

}</span>

(4)极值点检测输入参数:contr_thr是去除对比度低的点所采用的阈值curv_thr是去除边缘特征的阈值static

CvSeq*

scale_space_extrema(

IplImage***

dog_pyr,

int

octvs,

int

intvls,

double

contr_thr,

int

curv_thr,

CvMemStorage*

storage

)

{

CvSeq*

features;

double

prelim_contr_thr

=

0.5

*

contr_thr

/

intvls;

struct

feature*

feat;

struct

detection_data*

ddata;

int

o,

i,

r,

c;

features

=

cvCreateSeq(

0,

sizeof(CvSeq),

sizeof(struct

feature),

storage

);

for(

o

=

0;

o

<

octvs;

o++

)

for(

i

=

1;

i

<=

intvls;

i++

)

for(r

=

SIFT_IMG_BORDER;

r

<

dog_pyr[o][0]->height-SIFT_IMG_BORDER;

r++)

for(c

=

SIFT_IMG_BORDER;

c

<

dog_pyr[o][0]->width-SIFT_IMG_BORDER;

c++)

/*

perform

preliminary

check

on

contrast

*/

if(

ABS(

pixval32f(

dog_pyr[o][i],

r,

c

)

)

>

prelim_contr_thr

)

if(

is_extremum(

dog_pyr,

o,

i,

r,

c

)

)

{

feat

=

interp_extremum(dog_pyr,

o,

i,

r,

c,

intvls,

contr_thr);

if(

feat

)

{

ddata

=

feat_detection_data(

feat

);

if(

!

is_too_edge_like(

dog_pyr[ddata->octv][ddata->intvl],

ddata->r,

ddata->c,

curv_thr

)

)

{

cvSeqPush(

features,

feat

);

}

else

free(

ddata

);

free(

feat

);

}

}

return

features;

}

SIFT_IMG_BORDER是预定义的图像边缘;通过和对比度阈值比较去掉低对比度的点;而通过is_extremum来判断是否为极值点,如果是则通过极值点插值的方式获取亚像素的极值点的位置。然后通过is_too_eage_like和所给的主曲率阈值判断是否为边缘点*判断是否为极值点其原理为:通过和高斯金字塔的上一层的9个像素+本层的除了本像素自己的其他的8个像素和下一层的9个像素进行比较看是否为这26个像素中最小的一个或者是否为最大的一个,如果是则为极值点。static

int

is_extremum(

IplImage***

dog_pyr,

int

octv,

int

intvl,

int

r,

int

c

)

{

float

val

=

pixval32f(

dog_pyr[octv][intvl],

r,

c

);

int

i,

j,

k;

/*

check

for

maximum

*/

if(

val

>

0

)

{

for(

i

=

-1;

i

<=

1;

i++

)

for(

j

=

-1;

j

<=

1;

j++

)

for(

k

=

-1;

k

<=

1;

k++

)

if(

val

<

pixval32f(

dog_pyr[octv][intvl+i],

r

+

j,

c

+

k

)

)

return

0;

}

/*

check

for

minimum

*/

else

{

for(

i

=

-1;

i

<=

1;

i++

)

for(

j

=

-1;

j

<=

1;

j++

)

for(

k

=

-1;

k

<=

1;

k++

)

if(

val

>

pixval32f(

dog_pyr[octv][intvl+i],

r

+

j,

c

+

k

)

)

return

0;

}

return

1;

}

*获取亚像素的极值点的位置static

struct

feature*

interp_extremum(

IplImage***

dog_pyr,

int

octv,

int

intvl,

int

r,

int

c,

int

intvls,

double

contr_thr

)

{

struct

feature*

feat;

struct

detection_data*

ddata;

double

xi,

xr,

xc,

contr;//分别为亚像素的intval,row,col的偏移offset,和对比度

int

i

=

0;

while(

i

<

SIFT_MAX_INTERP_STEPS

)//重新确定极值点并重新定位的操作只能循环

5次

{

interp_step(

dog_pyr,

octv,

intvl,

r,

c,

&xi,

&xr,

&xc

);

if(

ABS(

xi

)

<

0.5

&&

ABS(

xr

)

<

0.5

&&

ABS(

xc

)

<

0.5

)//如果满足条件就停止寻找

break;

//否则继续寻找极值点

c

+=

cvRound(

xc

);

r

+=

cvRound(

xr

);

intvl

+=

cvRound(

xi

);

if(

intvl

<

1

||

intvl

>

intvls

||

c

<

SIFT_IMG_BORDER

||

r

<

SIFT_IMG_BORDER

||

c

>=

dog_pyr[octv][0]->width

-

SIFT_IMG_BORDER

||

r

>=

dog_pyr[octv][0]->height

-

SIFT_IMG_BORDER

)

{

return

NULL;

}

i++;

}

//确保极值点是经过最大5步找到的

/*

ensure

convergence

of

interpolation

*/

if(

i

>=

SIFT_MAX_INTERP_STEPS

)

return

NULL;

//获取找到的极值点的对比度

contr

=

interp_contr(

dog_pyr,

octv,

intvl,

r,

c,

xi,

xr,

xc

);

//判断极值点是否小于某一个阈值

if(

ABS(

contr

)

<

contr_thr

/

intvls

)

return

NULL;

//若小于,则认为是极值点

feat

=

new_feature();

ddata

=

feat_detection_data(

feat

);

feat->img_pt.x

=

feat->x

=

(

c

+

xc

)

*

pow(

2.0,

octv

);

feat->img_pt.y

=

feat->y

=

(

r

+

xr

)

*

pow(

2.0,

octv

);

ddata->r

=

r;

ddata->c

=

c;

ddata->octv

=

octv;

ddata->intvl

=

intvl;

ddata->subintvl

=

xi;

return

feat;

}

*获取亚像素位置中所用到的函数static

void

interp_step(

IplImage***

dog_pyr,

int

octv,

int

intvl,

int

r,

int

c,

double*

xi,

double*

xr,

double*

xc

)

{

CvMat*

dD,

*

H,

*

H_inv,

X;

double

x[3]

=

{

0

};

//计算三维偏导数

dD

=

deriv_3D(

dog_pyr,

octv,

intvl,

r,

c

);

//计算三维海森矩阵

H

=

hessian_3D(

dog_pyr,

octv,

intvl,

r,

c

);

H_inv

=

cvCreateMat(

3,

3,

CV_64FC1

);

cvInvert(

H,

H_inv,

CV_SVD

);

cvInitMatHeader(

&X,

3,

1,

CV_64FC1,

x,

CV_AUTOSTEP

);

cvGEMM(

H_inv,

dD,

-1,

NULL,

0,

&X,

0

);

cvReleaseMat(

&dD

);

cvReleaseMat(

&H

);

cvReleaseMat(

&H_inv

);

*xi

=

x[2];

*xr

=

x[1];

*xc

=

x[0];

}

*计算三维偏导数计算在x和y方向上的偏导数,高斯差分尺度空间金字塔中像素的尺度实际上在离散数据中计算偏导数是通过相邻像素的相减来计算的比如说计算x方向的偏导数dx,则通过该向所的x方向的后一个减去前一个然后除以2即可求的dxstatic

CvMat*

deriv_3D(

IplImage***

dog_pyr,

int

octv,

int

intvl,

int

r,

int

c

)

{

CvMat*

dI;

double

dx,

dy,

ds;

dx

=

(

pixval32f(

dog_pyr[octv][intvl],

r,

c+1

)

-

pixval32f(

dog_pyr[octv][intvl],

r,

c-1

)

)

/

2.0;

dy

=

(

pixval32f(

dog_pyr[octv][intvl],

r+1,

c

)

-

pixval32f(

dog_pyr[octv][intvl],

r-1,

c

)

)

/

2.0;

ds

=

(

pixval32f(

dog_pyr[octv][intvl+1],

r,

c

)

-

pixval32f(

dog_pyr[octv][intvl-1],

r,

c

)

)

/

2.0;

dI

=

cvCreateMat(

3,

1,

CV_64FC1

);

cvmSet(

dI,

0,

0,

dx

);

cvmSet(

dI,

1,

0,

dy

);

cvmSet(

dI,

2,

0,

ds

);

return

dI;

}

*计算三维海森矩阵不需要讲什么,其实就是计算二次导数,计算方法也和一次导数的计算如出一辙。然后将结果放入到一个矩阵中去。static

CvMat*

hessian_3D(

IplImage***

dog_pyr,

int

octv,

int

intvl,

int

r,

int

c

)

{

CvMat*

H;

double

v,

dxx,

dyy,

dss,

dxy,

dxs,

dys;

v

=

pixval32f(

dog_pyr[octv][intvl],

r,

c

);

dxx

=

(

pixval32f(

dog_pyr[octv][intvl],

r,

c+1

)

+

pixval32f(

dog_pyr[octv][intvl],

r,

c-1

)

-

2

*

v

);

dyy

=

(

pixval32f(

dog_pyr[octv][intvl],

r+1,

c

)

+

pixval32f(

dog_pyr[octv][intvl],

r-1,

c

)

-

2

*

v

);

dss

=

(

pixval32f(

dog_pyr[octv][intvl+1],

r,

c

)

+

pixval32f(

dog_pyr[octv][intvl-1],

r,

c

)

-

2

*

v

);

dxy

=

(

pixval32f(

dog_pyr[octv][intvl],

r+1,

c+1

)

-

pixval32f(

dog_pyr[octv][intvl],

r+1,

c-1

)

-

pixval32f(

dog_pyr[octv][intvl],

r-1,

c+1

)

+

pixval32f(

dog_pyr[octv][intvl],

r-1,

c-1

)

)

/

4.0;

dxs

=

(

pixval32f(

dog_pyr[octv][intvl+1],

r,

c+1

)

-

pixval32f(

dog_pyr[octv][intvl+1],

r,

c-1

)

-

pixval32f(

dog_pyr[octv][intvl-1],

r,

c+1

)

+

pixval32f(

dog_pyr[octv][intvl-1],

r,

c-1

)

)

/

4.0;

dys

=

(

pixval32f(

dog_pyr[octv][intvl+1],

r+1,

c

)

-

pixval32f(

dog_pyr[octv][intvl+1],

r-1,

c

)

-

pixval32f(

dog_pyr[octv][intvl-1],

r+1,

c

)

+

pixval32f(

dog_pyr[octv][intvl-1],

r-1,

c

)

)

/

4.0;

H

=

cvCreateMat(

3,

3,

CV_64FC1

);

cvmSet(

H,

0,

0,

dxx

);

cvmSet(

H,

0,

1,

dxy

);

cvmSet(

H,

0,

2,

dxs

);

cvmSet(

H,

1,

0,

dxy

);

cvmSet(

H,

1,

1,

dyy

);

cvmSet(

H,

1,

2,

dys

);

cvmSet(

H,

2,

0,

dxs

);

cvmSet(

H,

2,

1,

dys

);

cvmSet(

H,

2,

2,

dss

);

return

H;

}

*计算插入像素的对比度static

double

interp_contr(

IplImage***

dog_pyr,

int

octv,

int

intvl,

int

r,

int

c,

double

xi,

double

xr,

double

xc

)

{

CvMat*

dD,

X,

T;

double

t[1],

x[3]

=

{

xc,

xr,

xi

};

cvInitMatHeader(

&X,

3,

1,

CV_64FC1,

x,

CV_AUTOSTEP

);

cvInitMatHeader(

&T,

1,

1,

CV_64FC1,

t,

CV_AUTOSTEP

);

dD

=

deriv_3D(

dog_pyr,

octv,

intvl,

r,

c

);

cvGEMM(

dD,

&X,

1,

NULL,

0,

&T,

CV_GEMM_A_T

);

cvReleaseMat(

&dD

);

return

pixval32f(

dog_pyr[octv][intvl],

r,

c

)

+

t[0]

*

0.5;

}

其中cvGEMM是矩阵的通用计算函数,至于CV_GEMM_A_T是计算dD的转置矩阵放入T中*去除边缘相应

通过计算所在特征向量的主曲率半径来判断特征是边缘的从而导致不稳定

即去除边缘响应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;

}

(4)计算特征向量的尺度实际上是通过最初的sigma来获得每一层每一组的尺度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

);

}

}

(5)调整图像特征坐标、尺度、点的坐标大小为原来的一半static

void

adjust_for_img_dbl(

CvSeq*

features

)

{

struct

feature*

feat;

int

i,

n;

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;

}

}

(6)给每一个图像特征向量计算规范化的方向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

=

static_cast<feature

*>(

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

);

}

}

*对所给像素计算灰度方向直方图

以关键点为中心的邻域窗口内采样,并用直方图统计邻域像素的梯度

方向。梯度直方图的范围是0~360度,其中每10度一个柱,总共36个柱static

double*

ori_hist(

IplImage*

img,

int

r,

int

c,

int

n,

int

rad,

double

sigma)

{

double*

hist;

double

mag,

ori,

w,

exp_denom,

PI2

=

CV_PI

*

2.0;

int

bin,

i,

j;

hist

=

static_cast<double

*>(

calloc(

n,

sizeof(

double

)

)

);

exp_denom

=

2.0

*

sigma

*

sigma;

for(

i

=

-rad;

i

<=

rad;

i++

)

for(

j

=

-rad;

j

<=

rad;

j++

)

if(

calc_grad_mag_ori(

img,

r

+

i,

c

+

j,

&mag,

&ori

)

)

{

w

=

exp(

-(

i*i

+

j*j

)

/

exp_denom

);

bin

=

cvRound(

n

*

(

ori

+

CV_PI

)

/

PI2

);

bin

=

(

bin

<

n

)?

bin

:

0;

hist[bin]

+=

w

*

mag;

}

return

hist;

}

*计算所给像素的梯度大小和方向

每一个小格都代表了特征点邻域所在的尺度空间的一个像素,箭头方向代表了像素梯

度方向,箭头长度代表该像素的幅值也就是梯度的值static

int

calc_grad_mag_ori(

IplImage*

img,

int

r,

int

c,

double*

mag,

double*

ori

)

{

double

dx,

dy;

if(

r

>

0

&&

r

<

img->height

-

1

&&

c

>

0

&&

c

<

img->width

-

1

)

{

dx

=

pixval32f(

img,

r,

c+1

)

-

pixval32f(

img,

r,

c-1

);

dy

=

pixval32f(

img,

r-1,

c

)

-

pixval32f(

img,

r+1,

c

);

*mag

=

sqrt(

dx*dx

+

dy*dy

);

*ori

=

atan2(

dy,

dx

);

return

1;

}

else

return

0;

}

*对方向直方图进行高斯模糊

使用高斯函数对直方图进行平滑,减少突变的影响。static

void

smooth_ori_hist(

double*

hist,

int

n

)

{

double

prev,

tmp,

h0

=

hist[0];

int

i;

prev

=

hist[n-1];

for(

i

=

0;

i

<

n;

i++

)

{

tmp

=

hist[i];

hist[i]

=

0.25

*

prev

+

0.5

*

hist[i]

+

0.25

*

(

(

i+1

==

n

)?

h0

:

hist[i+1]

);

prev

=

tmp;

}

}

*在直方图中找到主方向的梯度

利用关键点邻域像素的梯度方向分布特性为每个关键点指定方向参数,使算子具备

旋转不变性。static

double

dominant_ori(

double*

hist,

int

n

)

{

double

omax;

int

maxbin,

i;

omax

=

hist[0];

maxbin

=

0;

for(

i

=

1;

i

<

n;

i++

)

if(

hist[i]

>

omax

)

{

omax

=

hist[i];

maxbin

=

i;

}

return

omax;

}

*将大于某一个梯度大小阈值的特征向量加入到直方图中去n为方向的个数<span

style="font-size:18px;">mag_thr描述子向量门限一般取0.2</span>

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;

//描述子向量门限化,一般门限取0.2

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

);

}

}

}

(7)计算特征描述子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

);

}

}

*计算二维方向直方图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

=

static_cast<double

***>(

calloc(

d,

sizeof(

double**

)

)

);

for(

i

=

0;

i

<

d;

i++

)

{

hist[i]

=static_cast<double

**>(

calloc(

d,

sizeof(

double*

)

)

);

for(

j

=

0;

j

<

d;

j++

)

hist[i][j]

=

static_cast<double

*>(

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.0)

*

(

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;

}

*插入一个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

);

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论