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,iVBORw0KGgoAAAANSUhEUgAAAeUAAAEACAIAAAAC2/kEAAAgAElEQVR4nO2dy7WEKBCGiYuAiMMQ2BmFWwMwhTnHxRiCZ2JgFgoUT/GNff/v3EVfGwGx+++yKAqmsgim+nwJAAAAj8De7gAAAIAioNeKseYrf28PFQDgTaDX+/WaD5NSSo1io2S3uJJ6caaShrFG9GslL44SAOB1oNeV63UrJ1vJa2MEAKgA6PV9/pCsXpf+Qa8BACsX6nUvGrEzmOSqU0rqmWS3SJ+Pa/AuzJJ7sjtLOa5vid32te3cNPC4fd05/e87Ktaa0bzbsHaIXswGvWB7B/xCkrfgfg580qqq/5kmHmtlYfnwd8XNPSkXLzJ2rEkNSlavZ8nb7Bdss8AGz9yAXjSMdVJ2sh84a7icnbcZa5gYE6cFn6dp4Dv9IUG9XaDXkUGcZJvQ67GLi3Uv2EJ2KNZ3SwpfyMYtyIzDE5z9GL9d/zNNXN/KR/X6gc/q2LEuIkl/Q6+VUusg85jO2U/MatUaOXE/T/rdvf5rY1M71dJK3ArXYq6Jbfwhs2yb2K/LOg4b+ksLPG5rp2/By0Cv32nlo3r9BLNs28CoUZ5eG0ViDZfzJFuqPOTf1R71CpBRIO6EyPfTETp9Cjmo5YgOq3ndi0YITzk3mt6yryP6S13PgRv68HzjemGhRkedIVH/9Tzw5LNSKMdCW9FcTtECqcK9f+IkOTMQ907sMDlo291hX69jIvXHy/ksmt8qZ5Q2v/aZz4/3MaYsH6ZWyvWR6Or6lf8pSv4SH24iMpi5cbpooCbHo9g41qhxCRbduOflwsFRvbV5176Ofw5LPsPpIVJKTUPLh/BWEb0ehR26UYgx/XM6S7703ClAx8h8Knrh3w73XVOPc3B5nboB9A55Jekpk2zphzPtv6Yy6o7dJFvrv/Zv7IV6HfsWxe3r5IPS2pQjweafXqzKmdFr/c8kufM6sETsQVMtrY1WO0nOyaBn/NehXjdczu6jCSmzHne+G8V6Tb8v4ceY4n6jSvW6vP5dT9ZnmqAD+MRAOWXozTpy416Ri+WMLvYjWvI5LPgMp4ZItxL9phO9XsecfD+92xP+0EX1mup+iPNu/JS+W4Yo+YPZk5J8mJx6XM3M2StmCIrs63v1OtZdbbMc12syUKuy5uzrjdfG5GZs1XO/BX3QpezJM2JfB98BMiDOF2mvDAVf0Q0ZKtHTQ/XH5eDaSwgHM3851w1U1I7ee+PelYvYk64zjMnLKfgMp4ZIX/mWXttL1X2gt2cULByOCvTaKZlvOkGZ//pOvfafjyK3dnmYOqPXgcTueU2N9Whl8YPlQK/vaeItvV6/Sv5NXH0C7+n1brlwJdW3kc/odWKIzJVv6PU0cPPJWYMgzHOQd21Gu2mB1NOKNyJeneEp9jV5FLK/Ft5dCR9wGL2KIvs68Z1x40Nuta9jXwL9QaHPO3v91+T1pj9kS69D3wn1ljhOEschcot9vQxOdA6g75rIB31LhppoN2N6fWX9ibt/fRMxvb51oKiQhf6Q8htXjVwsvzRxv1zkcnbaHKE/ZNt/TX1QnjOdPMqwhrFOmFEgBUq9+PYXi9RDT6EjGJTs6eqTyFSD40kr0opYR2Px1/f6Q9LPkrZjs8rFh/RuiF7ov1BEd8PCG9ptqxPCHLQzi5y72h20vMFOvQ4mgsxoj6KJTaxvuCwdW8cS0etL61f+p0iMVzeR1Ou7B4rMz7dUjHbeuDflwu+qY0fnP4dFn+HUEKnC+JC/SVZz6/pTSqlk/LXHQcfEQXoy9fgwTjz7PPBjK4kKubv+Z5p4rJVyauvPfpzP4UmOxV//DY4LaO7eeME6F+q1Klvf+IBeU0P6wd8GpYLhta66LvYUeR131/9ME4+1Uk5t/Skk9Tk8w+H1jX+D7+k1AOBPAr3+nD/kB6lzfJ6v/5kmHmvl6x/sCr/j0GvoNQDgG0CvAQDgG8T0uu8a1sRiMwEAALxGqNffD6wBAIBfJNTr/IpnAAAA7wC9BgCAb+DrdTYdPgAAgNegej12LL6EHwAAwOvAvgYAgG8A/zUAAHwD6DUAAHwDxF8DAMA3wPpGAAD4BsgfAgAA3+BJvXb2vjxxSkk9k+xS252UbM8KAADV8Xt63YuGsU7KTvYDt3udW1J6vbl7dMn20gAAcBe/p9dKKXdL2+h7R+xr6DUA4E3u12tnA2i6Nf16MLJpsbtJs9D7smv9zW/EXmpfS72xsS6R2/ZY9O7+XnoX+rZo61sAALiAu/W6F1Yxtc7OkjsHl9cpvaaC7pWkp0yypdK86b9e6gq3nW/lpLc69td5BvY19BoA8CA36/UoiONBy6tzUPXdIoxJ+7onJfkwOfW4G2aVLaRPaHSo1+ufEWj4QwAAb/IxvXZKuvXs6dOWXiulpoGTnwJijEOvAQDvcLNez5I7PuvAH2JfE8+JKemLeOgPsTb1NPDL7GvC4sjmctbOclpg7FiDwEAAwDPcPt9o7dROROcJqeAGJXsRcXfE5xtZafDJtl57k42mYXvczjdiFSgA4CGwvvEEfYfcswCAx4BeH2cUCA4BADwH9BoAAL7Bj65vBLeDNCyvk1lkAH4T6PXz/EZcYG16nYjGd0LpbXwSOXj6EibJGUX0SvWC+Z/RyKFEFWuxXrj/O3VtLOIFvwn0GhyjTr3utNSFmQZMV2fJ9etFzROJZoroBWOc/vROknM5qV4Eh9NyrU8yVep/chqfTZKjVC8UY4oxxWWy85wpxhQTyQKgMn4vf0jYtpMqxI/J860tYva6ptm2NjlLbEw9OmpbGmnYtq+J3Cynd73pz0ZEijPcJl6dDJC+XjEaS59zp3zxNdIxibSbTtWSGquS+5W/8LU/JGrefytz1n58VSY4Euz9t1HYqnRKrzft696qsGAq2rLk6/FeKJvWASkWquZX84cskFQhWiByKxXXMl3vvS76Vodh3ctr9/egTK+J3edHhWefLKjFtba7lF9VT4wxw3PpSbgaKCA5Jql286laUq+L71dy/J1rz92+tH3t/1hHK8iqMH1zQ679skaks/Z1of+6T1TAjVndWxscel03H1uPvjN/SF5D40vPY8ZsgV67xu8ii8Rb6qyK3KE+fcd4J3jD5bzYVNt9cP/oD2MwTBkfwuY1knOT7Ral0yJjVXa/ciMWPsr5l2nR9/24cy0vw/bdjBluijJD1NI+SC+S7g6q13CJfISP6fXO/CEF3/9Va4IyL+v1UkMrRLsaxaLLeitJHxJlCvU6a861Ob2OtPu8Xpc7PajtH6HIvt4S4lVut1U3Jfzn9FrynBBH7WtQN7+dP6RYr/X6ct8fsmqQ+4Xvu9hmxOVa4+p1vDa79p3L2YiHFZdcHyK/e1l/SLg0P0FyTFLt5pf+l/hGYmO4Pf5bb523rE1NknvzjaoXztSh6AtE9wa95pGZxr4jwyCFWp8vpfVfIyVO3fx2/pCi779tg7fEPxtkEdHHR9G0UaMsP9+Y0OvN2kRvXttKkmf506eN6DfnG3Mj6HcqMSbRdnO/B9n5xvz9Slx7Qq8j8XyBH+2kcMfi+Uzzwj+SqiLU60w833aX1uCQ5U9X7ei1LcOVk9IdKXHqBesbC3Ce9+eBXzghc6y2q/pQW0xeCdeOP3BBSpy6gV6nSK2qGDs+XLc64VhtV/Uh59gtcd++wbXj/zDGZE7Z4y+DlDiVA70GAIBvAL0GAIBv8MB6mQOPe1/0qwIAwL28al/Pksfjx1J6XRS5nKDs3PMJJQAA4B6+pdcAAPB3ecwfEmZuIrG8vjKn8gRF1lxEcgnFKc7aQe1rpFMAAFTDk3pNM+LphY45+zrclTzU6zBPRYoyf4izhA96DQCoiEfta7uyqtVSXOAPIUuo82uaS/JL7NRrAACohs/oNVlJDL0GAPxF3tbreLRfPk/Qll7n8gFtZFkK9BrpbwAAtfCqXnvZTC2pPERFep3NB1SWZcnxXyP9DQCgCn5vfWNJPqB0GS/+GulvAADV8Ht6XZIPKF4mSO+J9DcAgIr4Pb0GAIDfBHoNAADfAHoNAADfAHoNAADfAHoNAADfAHoNAADfAHoNAADfAHoNAADfAHoNAADfAHoNAADfAHoNAADfAHoNAADfAHoNAADfAHq9n75rsInBTWBsAUhzt16PIvLtix58kl4wvYsCi25wk2Ee+E29p1s6nGDZoLjhutau2dgy+EC70VPK68nu2tN3zXZGXAD+Iq/odZqSzQYu4IRe176Dgd27Ryml1Nht7+/+vF4vHUtu3PP67zkAdQK93idV1UuJI5qzbAt+XV7RazXLNr4lm6r/RxGAd3hCr0XXsKZhjXnMtaLX67dY08p5lq3+9/CmiZPkbIXLyfmfGW2O6XUvgmJhbRmrcBRNw5pODq17sUnWvSsbxprFIl71btlC0vyte5OtXg7m7gZs97lc9M3W2coN142zR6a7QSap0FVhZzdO0Xn9ISWjvfWYhjY1RnTbzGlotx8RAPgTPKDX2lSy6qH1mqrf2Ilxe2NF5y8iRb1YdHV57Vl7k+S+TOsXk+T0xPV1WNumXi8yvWp3ztgche3/KMSoIvbpKBjRx9ju8sb10QtX9Jfz07015ckp2xVSvaaC7pWM760cDkFmMDvoNQABT/pDzBOwPjgP3NO1k/6QqHuDGM4spdfuiet/kdq27evevk4+7itlLFDagKvXVD39/eKZGF3FV6rvWCD66d465+pTtiuMKrLqu8WIJvWEvY11okivAQCaV/XalmnYYoaftK9Dhe0FcW+EbpD39No0ZRXN8ycQs9RV0ujBd/XaKRntbawT0GsAdvGmXk9Dayyv1Zl5MljOc2uI3tFcq91Zf4h5HdaWm29M63Xfhf74aeDk2rljnzreBt06ObKeSzXdvqZKmh7NWXLHZx34Q5wKqZkf6nXUHxL0NiTjv3bmG7PBfwD8JWrwhzSssSHC6wzkZfONxB0iRNK+dk9MzzdmQheSej2KmKVNZuRYu1YdnW9cbVVaPjatF5sezMWH2FY6EZ0npIIblFwfC5JNR3vrD0AyPsT5UaRzjwD8bbC+cS97HwEeClFMUBJ//Qppj4e3XgaxfQBooNf72bdmeuzeXa23vb7xedIujmBsR1Fb5wF4Deg1AAB8g6/rdfXrDS9gkp2s28Ssv4cA/ALQ65rpRcNYJ2Un+4GzJrXwxKM8mvA89fcQgN8Ben0nvVCMKcYUl8kynCnGFBPJAqPIrequgfp7CMBPcL9ek6C9ZeJtGloypTQKu4Y7zDSSYOx01hEyOxU0lKyTnK5DD/ySfWFPcvRWhQVTUYeB5ErH8qlI2Nu29TrpdCXkclzr1RkrL+LQyXZiq1oDN0g6l6Q5XGsPsYod/CKvxF+bbxONDyaZRhIhy6RO866pPxXoHWQvmQfunB4tGeY8cShLZkJPSLzLjVndp23wtHd4UTo/Lo6oobPiP4wQb/QaJS/zSTD4YeaAynsIvQa/yM167ZtONpR2MWCJ9ep4NnI5653IXZI6KtJQTMTdwF8d3UtLpl4fpRdJdwfV64xLJIVz1fRHSP/ryGWohuHrxWLVVdEFTTm9/nIPAfgO9+t1YlXE8myb0evkIomUXkcaukuvd9jXkueEuMi+zuIKlm+BnlHDVWrDMj/XQwA+woP5VGnCkHXZnecPoSqZ9odQH4VdeBFtKKbX1B9iX99jX/PITGPfEXGXQumkJTH/9Q7I71/gbVgdA4fUcFHSDX9IhT1E1hHwgzw638h0mqfEfGPEbRKFzDt1Vk+DhlLec1oy8WtxhV5Pcg0OWf5M4iiq17YMV/udrd5UnmulkqmC9Gxe3ttgd5No22N6/VoPkXUE/CL1xPPVHZn3AyzWaM2TcBf2EFlHwC9Ss147FjcmlA7hRbxVOHq39BBZR8BPUo9eAwAAyAG9BgCAb/CAXvfHp9TiuUAr8XQXBpBV0tuF5/uMOA0ALuN+vRZMMaaOfGOrzbW/8MWA31f6jM0YAbiGu/V6UvxgsFpmL6s6+FN67WwzVvzWSmbrLwBAOTfrtSAByMvKEScq2ei4lnUpdMnMtluXexh0SO9g4snSiYRSC6CTCSs2ezvJlrHd+e0e7vMpvc5trQsAKOYZ+9r4Q3qSO5S+1sWssmceok+vEfdxlc5TMLpwLpmc6JBer5vS+jntyi7k4T6f02u4RAC4gmf1eskHbb7d1rUduk1263URdt9uqpJuCqH1WMwmTSa7yJD+dWnP7MnyTJ9X29/9E1tvRSqEXgNwnt/R69P2NdG+RCKhdeX0FXq9NHvavn6sz7CvAXifqvwhVK/f8F972hcmEkomJ1LpwLWb/dcP9Rn+awDe52G93ppvJJKVjg+5X6/TiYQSyYkyCYZuir9+uM+IDwHgfWpe31h5/LXLFxMMPdRnOEMAuIaa9Tq1vrFGvphg6JE+Y30jAJdRt14DAADQfFSvL89Jkp3gfJTiJYi1dHjhlW7DeAd/i2/q9c/mJFHfXOau3us2nOPgD/FFvf7hnCTqT+o1gk8AKOKDen1HThJnsclV3Jnio6jD9WUmiXcbwd0AFPFBvb4lJ4n6WlqSZIeXlmvNTBLtNhZPAlDE9/X6mjXu6mtpSZIdrjszCe02kpMAsA/otX33U2lJkh1eWq41M0m027CvASji+3p9TU4S9bW0JCUdrjAzSbTb8F8DUMQP6PUlOUnU19KS3NHht7qN+BAAiviiXp8hHX89D7zCyOxMio86O7zwXLfhDAF/iL+m1/H1jctkWoWR2akUH9V2eOGpbmN9I/hb/D29BgCAbwK9LqPvvrjoEACw8hNfYeh1FJJPSqmLchT1IhZj9zhLNzaD5L7Ad9LtBtw0VxyjrrxgFxIdw6yLrO+aj4cSQa8Xeie2xMsndU1ef+j1SbwBTE0dL5GFiBnZwyjWePxTK60qaTo3Bf3g7+QtQK9D/EDAi+4x9PpKqk/dVbLSp5KGerF+LCfZsmcH9ZamcyGeX9wHigC9XiD2tZ9PKh8xtsgfXS7oraU2Au3pdXhiGmODRBZtczkIbZ6kmqYr5vNru2nfhBz4Up4P06Rf666m24r3No3TN1NPeHV0APPbMRv7el3FI3WcOB9mGzNu7uq6gDOIIk91182HlR7DzIWX2ABL5xu/t7saKs0LZkST2hOjaDqhQ+/5MPfktVLKSzVjZdDJXnCsaeem6Jpn2ephb7o+M4aZJVS5lQEfAHq9QP0h3nqcjF5vmcyrEi1fJ1p4j60dqSSWZMN7nKRnJWtIjobQMm3ElA9TstsXtFVydbT1zE0J9brhw2xEhA+zUyaTtSqOnw8rfV1n9Lrcq7NpX5f8NngfTmFGY9XKeeDO66BCe3AUtttHm54H7lSyvJ5lS3+3MpXnPh6fDtiHXi8c0uvFkAw/M3FjlnwuUydutxIqmque0aaTNSRHI6uhusVL2nKHYslwIsbo1R3W61CLyRrO3VmrYvmwLIWZqraEzOnVmYYK2tIqOQ18/V1cf/vpiYnXzqOJzgJWJKnZpt3bq30Ynp8Dev13uU6vV29AKHb363Wi6dWVca1epy7zr+s17e0J+3pbrwsbKmlLMCLFzCac2dJrJxaDHCzX61TT0OsE0OuFjF5nPhcx/wAVoAv9IavJU6DXYdPJGpKjUarXz7TllDziv87pdSZrVd/FJPNqvY63co8/JNrWKMg9oregQK+dOQCTBUyXpK93NU39IfZ1sV5n/NeYb/wJcnqdvceRaUObtrTlCb2On5jCzvU5j70Rf0ii6VQNydEo0dCL2tqab4zrdSY+ZKdep7NWUUes091L9TrRiumw/js231jWFrll5iGJl/hDbCKwrjMH7Xi2LT/YtDNp6cw3Fuh1Jj6kxKFeM9DrEqpZcjCaYIkvcG9vb9g6eTRboKmn8mnd34pt4fEMYfYWPdl02uOB9TJ/hdsXs7rmtmOcejNLV0X1Zlo8Q7S397R1zfpGb3dKksL7ie/2va24CbaeuSI6ng8PpvJ86g5Yjw4AAOAxoNcuq0PuwzPIAIBfBXpNqXkLAADAXwd6Tfl2bCYA4LeBXlOg1wCAeoFeW6pP+QYA+NNArxfGDimTAQB1A722wL4GANQM9JoC/zUAoF6g1xToNQCgXqDXFMRfAwDqBXrtgvWNAIBagV4DAMA3gF7/JX4iRRnYBjf6R4Fe/x32Z/GOJyz1cr5PsqMZrs9lhPf76FWeuoh0Fk1T0cD5O1MTL6XI/36uZxACvf4z7N4JaXNDgF40jHVSdrIfeOn2ZqXsrTwf27O5Cct9vLalydf3UgEh0Ou/wt5vb+nqIWeH1k126uaeyjO7QDn7uj7Ne7L58b0KQQj0+jaswWf3rstsBLrNJDlb4XLSB8h2pevxXjAhhFNyf2h5fkPbzu4a45vAeXnK6vWyHVemcqdAQHpw+47lhItsFbhUMA2tu/P3cnwUTSc6p2SStavuNitBQ8k6yelmdxivZL/Zk2lozU2fhvby7dPA40Cvb8MEc4+d/lLFTUC7cW1u+91eWGmeJF91eJKccTmpSXLzdi8Yfb2U3KvXmfJn/Nd7NvOOVJ6vP9XnXuT2kKQV2huk9Y0+ZozCUc9Mvhn6rqk/2hCp0/xE0t3BbVVeyeCs/IBAr38C6PV9rN/JvmvluGj3KA5/ZXptMGscu9ra0cpVdiPYaf2125NTH3G5Xpe8623qmNm8cdN7sFuvN2YaHUOY2rNrLD6xXp2mc/N5Tkf0WfGGYiLuXof2amzvVl44IOC7QK9vZBpaMS4aPcu2lePAY1+fIvvaEWG3Fcnzei1WtbjJvt717l77eleBaJ8n2WbnKtMXOg1tXq+TPpaUXkcagl6DHUCv78R6QtYv/4npH+rlUJPk6+vVfvb8IdT4XpV857RXif/6wLt36nXUfz1LvnHZ1Mux/MQuh7uYP4SqZNofQgfPhhpGG4rpNfWH2Nf79dr5SdkOeQT1A72+FfKtdpyShyDzjYt0p+cbI26TvdEC6fiQSvU6OjmwMdO4nmmnARdNS883RtwmUZafZ9Y0rOtsh4OGUt5zWjLxa7Gt184bdO4RfBbo9e+Rcp3sXS+zGX99A5sZt5IF4s/+l4Zdfyqg2fOvI7bvJ4Be/x5pV/feZcrx9Y13wbfs1nSBZx72Q712LG4/eu9Fght9YqYbVAT0GgAAvgH0GtwDUg79EribdQC9vo+ou/NTPtB9UD/M/txSPuFKGa+1w76aL96CChJaIYFUBUCv76MqXXCCS3hWCY9C9PrU7FZJpqc35kINzliuMTiRSYP0PIJfhY3N9IN6CIcTWs2yJe51myPB8bab1e0sHcVU1Qf6bwK9vo+qPt52CbtygrUvxOrTBVeezfR05072WxGHJmxSs46sjXQ3hzODTG8HPTWn8UcTWjnnESvZDQo0P3+Zn0IEmbwN9Po+oql8nGwSpcmD8rh5oDyd0FrgC4SzAtIP1vZW3Cyvo2mkotHe+bWRDWs6qcOTYxe+aV9nnS2RTEmzbFs5mFxRZyLEfVUmOCPs/bdR2I53Tq+PJrRydd5JQxYdh3QsCRJIvQ30+j6i6YGoXm+k7CnOA2W++b3wH83NayoQ5BSdL8o9ntJrfdT+Jritl+o1XX6Sks6M/zqTCyWaKWmWbRNZIB4nq9dZFfYeYbJOp9TjTlavDya0onqdWq5JepZbWIMEUi8Dvb6PaHqgfLa2/cS+4EZEiSpQh6lrfkf+S9rX+qCuOJqqpMC+7u3rAxeerj+eecMb3hMZqfIybN/NmOGmaHQ64Yheb800Ov7rVDoU20JOhJGQ5GWg1/cRTQ+0Q6+P54FaLTaqGkZLHIsaeh1w2B+yvp2YfPRICf8Bvd5KaJW0BvxxKLCYodcvA72+j2h6oKvta8+hQZ+rBf3ue/4Q6tkIfSPuBFhGrxNpt9OKeIlep/3X8UxJF+r1Yhi7UtsL7/dvW66P6fXBhFZFet13zbYUI4HU20Cv7yOaHuhqvVaRfWeUWqcByVc/mA0zb5PTqR7rQyJnX8dLZgIJLtHrbHxIJFPSlXqtVDyez56eDMjzqgj1Oh/PdzihVfQ8N57PSUSVzAqABFKvA70Gd3B+vUyeV+Ov3+GBhFZZkECqAqDX4B7uXsH8bC6qowTbAhVZ3yFvOx+QQKoOoNcAAPANoNf38WD+kLixWU8Ck13mYT3dBqAuoNf38ZjEpJy5VWkccb/aHX6jK4Cq6jYAFQG9vo+HdOfoxl1PY8MUZikGHULYhuENN3ebM8WYYuK+FgC4Cej1fTyTP2RjY9yLOrAu/4utoytJCbLUEQkfjoWjne72LHk8q59Skiu9AlHZ6EQsrQbfAHp9H8/kD9lYTHimA0oZ30VmBV15SpCgq6NgkcQXp7ud0WtuzOpecbm+hF6DjwC9vo9H8ods6PWpDvQdi+lp2ErhEhja1Um20Z+fC7qdg+o1XCLga0Cv7+OR/CF79HpvB5Z3yuzrI3qdWG9xRbdTRO1rAD4C9Po+HskfsuW/vqgD2/7rbb1Op2++uNs5/7VQeqbT+q9fX40CQBnQ6/t4KH9INj7kgQ6U6rXbRiar3OluZ/R6koot8SFc2WwqSIUBvgH0+gf4RDKNilNxIhUG+AjQ65+g9mQaVTsckAoDfAXoNQAAfAPoNQAAfAPo9X2cylukJ92WR/UDVel9wk9tvX4JmZ1zAQA7gF7fx848GPPAiR/VPfloSg0vx/zT9KJhrJOyk/3As0HcAIBtoNf3Ub9eF+x9dZ5RJAK3AQD7gF7fRz5v0bJlx/LXynmWrf63c9bzFVSVToF0k17b2DyzniWezWnLvvZ73vtXQYaFbiro7DeoxwlpQMCvA72+j2hmIi2yNBx57MS4aV8fSoG0X6+LFsGbro6d1tbMipuM/9rreTaRkzOGpi0yTtBr8OtAr+8juhJPH1wtRKJKxf6QHSmQ4nq9Li53/3YZ2msrfdfKcen2sSBmb+l57LWeN7WmtLPypq4c3wDcCvT6PrJ6bcvoJ/09el2aAukm+1qpaWjFuGj0LNtWjh9gCD4AAAPySURBVAM/snpxS6+ddTaxRxPoNfhLQK/vI6fX09Aab+zq+XWdAGm93pMC6b75RusJUdPQOs7lPbVs6zXxGjH7aKJLOoNW9SpKAM4Dvb6PEn8IibE2M5Dx+cZDKZBujA8hTuR54MkcqpuVbPhD7Kxs15mDk97LhrWt1WukbQK/DvS6Uq55zn85/vp+aKorpG0Cvw70ulLc9Y1HKqhmfePl0CA/6kFCcAj4caDXAADwDaDXC6s1itkqAEC1QK8JmLACAFQM9JpS8R4oAIA/D/SaAr0GANQL9NqBZqYAAICqgF77zLJtYGUDAOoDeu0A+xoAUC3Qawr81wCAeoFeU6DXAIB6gV4TEH8NAKgY6PUC1jcCAGoHeg0AAN8Aeg0AAN8Aeg0AAN8Aeg0AAN8Aeg0AAN8Aeg0AAN8Aeg0AAN8Aeg0AAN8Aeg0AAN8Aeg0AAN8Aeg0AAN8Aeg0AAN8Aeg0AAN/gj+j1KCKp96IHAQCgUurR614xphhX0x2V75TmeeDtcEtHAADgKNDrGNBrAEB93K3Xk+JMMa6kUIwpLpVSWpr1n5x0Mf0nel1GkPKLlIcV6pKSkwo9RtF0omtY07Cm4cNsDi4i3uu3WNPKeZat/nfZbWYaWgb9BgC8zDN6rf+MvK7Crd/tVWBf5/U6qHCVaXoWZRRNI5advuaBrzKt9Zru2jh2Ygzsa+g1AKACnrKvjdj1wjGufakt1GvqNkmVpFB/yCzbVs7k4Dxwby8w+EMAAPXxkl6v9jXlPb22ZRq2mOHQawBAfTyu18Z9YZRSLK+9klR579XraWiFdodMQ8uHmfhM1tM7bMULAHib5/Va+T5oo91mwlD0zr/r393+kIY1jXFTrzOQdr7ReLgBAOAd6onnq5i+awTkGgDwMtDrbUYBZzYA4H2g1wAA8A2oXo8d8+LaAAAA1EJgX784tzYPbbsuPay2RgAAeIvQH0JX+7nMQ9to7pD0g+o6dk3qtGiNmfJjF72wuy8cAAAK2KPXTqEbhOt9+3r7sm65cAAAKCAy3ziKZolPTlOgg8Qm1UXnoW3b1juo1NgZ4zVu+JIC3ejV1I05CY22aMpTs7kdZtJO8uLgYAEAvEY8PmSWbZP1imzYmPPQ2iJG48hR85KWjIqhU5V2ZfgHM3odtKjLx1rLWs8lFw4AALex174uMzCpyWztYnquVkaqkClvM9HImNZu2Nd+i6S87aY9UFIVAAC8wEH/9QZxcYupJz22y76+Qq8NsV8PAACoi7167chnDuocbow/JFTPWMGgRzH/9Vm9jra7NhX/sYGWAwDepKb4awAAAGl+Zn1j4DKHNQwA+C3YP//+hz/84Q9/+Kv/73+jcCijN7D4ewAAAABJRU5ErkJggg==" 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 激活方式概念 在 ...
随机推荐
- Sort a list(tuple,dict)
FROM:https://www.pythoncentral.io/how-to-sort-python-dictionaries-by-key-or-value/ AND https://www.p ...
- 洛谷1546 最短网络Agri-Net【最小生成树】【prim】
[内含最小生成树Prim模板] 题目:https://www.luogu.org/problemnew/show/P1546 题意:给定一个邻接矩阵.求最小生成树. 思路:点少边多用Prim. Pri ...
- 001_linux基础命令
开局日常吹牛一小时,今天更新的是linux的基础命令.现在是2018/5/30,晴,心情挺好的. 回归正题,linux基础命令只是一些初学者常用的命令,如果其他更多高级的命令等我学我再发上来,因为这个 ...
- 快速了解AMD、CMD、CommonJS、ESM
1.ES6 Module javascript在ES2015(ES6)中出现了语言层面的模块(module). ES6的模块既可以用于浏览器端,也可以用于服务器端(nodeJS). ES6模块是静态化 ...
- Mac下Maven的删除和安装
一 删除maven 找到当前的maven路劲:使用mvn -v查看当前maven的安装目录在哪 删掉sudo rm -rf [maven的路径] 二 安装maven 1.下载maven压缩包 mac下 ...
- 分页——为Mybatis配置PageHelper
1.pom.xml追加 pagehelper : 4.1.4 2.mappers.xml中追加 <plugins> <plugin interceptor="com.git ...
- json 文件读写
#coding=utf- import json data ={","version":"0.0.0","desc":{" ...
- CF1217A
CF1217A 题意: 配exp点经验给力量str和智力int,求有多少种分配情况使str比int高 解法: 打表找规律,没了. CODE; #include <bits/stdc++.h> ...
- Maven依赖传递
依赖传递原则: 1.路径最短原则 2.路径相同是先申明者优先(dependency申明顺序先的优先)
- Django中常用的基本命令
django-admin startproject 项目的名称 创建一个项目 python manage.py startapp 应用的名称 创建一个应用 python manage.py ru ...