怎么样才能让皮肤变白不让手机耗电快

SURF算法与源码分析、下
我的图书馆
SURF算法与源码分析、下
16:20 by ☆Ronny丶, 1441 阅读, 6 评论, 收藏,
上一篇文章
中主要分析的是SURF特征点定位的算法原理与相关OpenCV中的源码分析,这篇文章接着上篇文章对已经定位到的SURF特征点进行特征描述。这一步至关重要,这是SURF特征点匹配的基础。总体来说算法思路和SIFT相似,只是每一步都做了不同程度的近似与简化,提高了效率。
1. SURF特征点方向分配
为了保证特征矢量具有旋转不变性,与SIFT特征一样,需要对每个特征点分配一个主方向。为些,我们需要以特征点为中心,以6s(s=1.2?L/9为特征点的尺度)为半径的圆形区域,对图像进行Haar小波响应运算。这样做实际就是对图像进行梯度运算只不过是我们需要利用积分图像,提高计算图像梯度的效率。在SIFT特征描述子中我们在求取特征点主方向时,以是特征点为中心,在以4.5σ为半径的邻域内计算梯度方向直方图。事实上,两种方法在求取特征点主方向时,考虑到Haar小波的模板带宽,实际计算梯度的图像区域是相同的。用于计算梯度的Harr小波的尺度为4s。
与SIFT类似,使用σ=2s的高斯加权函数对Harr小波的响应值进行高斯加权。为了求取主方向值,需要设计一个以特征点为中心,张角为π/3的扇形滑动窗口。以步长为0.2弧度左右,旋转这个滑动窗口,并对滑动窗口内的图像Harr小波响应值dx、dy进行累加,得到一个矢量(mw,θw):
mw=∑wdx+∑wdy
θw=arctan(∑wdx/∑wdy)
主方向为最大Harr响应累加值所对应的方向,也就是最长矢量所对应的方向,即
θ=θw|max{mw}
可以依照SIFT求方方向时策略,当存在另一个相当于主峰值80%能量的峰值时,则将这个方向认为是该特征点的辅方向。一个特征点可能会被指定具有多个方向(一个主方向,一个以上辅方向),这可以增强匹配的鲁棒性。和SIFT的描述子类似,如果在mw中出现另一个大于主峰能量max{mw}80时的次峰,可以将该特征点复制成两个特征点。一个主的方向为最大响应能量所对应的方向,另一个主方向为次大响应能量所对应的方向。
图 1& 求取主方向时扇形滑动窗口围绕特征点转动,统计Haar小波响应值,并计算方向角
2. 特征点特征矢量生成
生成特征点描述子与确定特征点方向有些类似,它需要计算图像的Haar小波响应。不过,与主方向的确定不同的是,这次我们不是使用一个圆形区域,而是在一个矩形区域来计算Haar小波响应。以特征点为中心,沿上一节讨论得到的主方向,沿主方向将s20s×20s的图像划分为4×4个子块,每个子块利用尺寸2s的Harr模板进行响应值进行响应值计算,然后对响应值进行统计∑dx、∑|dx|、∑dy、∑|dy|形成特征矢量。如下图2所示。图中,以特征点为中心,以20s为边长的矩形窗口为特征描述子计算使用的窗口,特征点到矩形边框的线段表示特征点的主方向。
图2 特征描述子表示
将20s的窗口划分成4×4子窗口,每个子窗口有5s×5s个像素。使用尺寸为2s的Harr小波对子窗口图像进行其响应值计算,共进行25次采样,分别得到沿主方向的dy和垂直于主方向的dx。然后,以特征点为中心,对dy和dx进行高斯加权计算,高斯核的参数为σ=3.3s(即20s/6)。最后,分别对每个子块的响应值进行统计,得到每个子块的矢量:
V子块=[∑dx,∑|dx|,∑dy,∑|dy|]
由于共有4×4个子块,因此,特征描述子共由4×4×4=64维特征矢量组成。SURF描述子不仅具有尺度和旋转不变性,而且对光照的变化也具有不变性。使小波响应本身就具有亮度不变性,而对比度的不变性则是通过将特征矢量进行归一化来实现。图3 给出了三种不同图像模式的子块得到的不同结果。对于实际图像的描述子,我们可以认为它们是由这三种不同模式图像的描述子组合而成的。
图3 不同的图像密度模式得到的不同的描述子结果
为了充分利用积分图像进行Haar小波的响应计算,我们并不直接旋转Haar小波模板求得其响应值,而是在积图像上先使用水平和垂直的Haar模板求得响应值dy和dx,然后根据主方向旋转dx和dy与主方向操持一致,如下图4所示。为了求得旋转后Haar小波响应值,首先要得到旋转前图像的位置。旋转前后图偈的位置关系,可以通过点的旋转公式得到:
x=x0–j×scale×sin(θ)+i×scale×cos(θ)
y=y0–j×scale×cos(θ)+i×scale×sin(θ)
在得到点(j,i)在旋转前对应积分图像的位置(x,y)后,利用积分图像与水平、垂直Harr小波,求得水平与垂直两个方向的响应值dx和dy。对dx和dy进行高斯加权处理,并根据主方向的角度,对dx和dy进行旋转变换,从而,得到旋转后的dx’和dy’。其计算公式如下:
dx′=w(?dx×sin(θ)+dy×cos(θ))
dy′=w(?dx×cos(θ)+dy×sin(θ))
图4 利用积分图像进行Haar小波响应计算示意图,左边为旋转后的图像,右边为旋转前的图像
3. 特征描述子的维数
一般而言,特征矢量的长度越长,特征矢量所承载的信息量就越大,特征描述子的独特性就越好,但匹配时所付出的时间代价就越大。对于SURF描述子,可以将它扩展到用128维矢量来表示。具体方法是在求∑dx、∑|dx|时区分dy&0和dy≥0情况。同时,在求取∑dy、∑|dy|时区分dx&0和dx≥0情况。这样,每个子块就产生了8个梯度统计值,从而使描述子特征矢量的长度增加到8×4×4=128维。
为了实现快速匹配,SURF在特征矢量中增加了一个新的变量,即特征点的拉普拉斯响应正负号。在特征点检测时,将Hessian矩阵的迹的正负号记录下来,作为特征矢量中的一个变量。这样做并不增加运算量,因为特征点检测进已经对Hessian矩阵的迹进行了计算。在特征匹配时,这个变量可以有效地节省搜索的时间,因为只有两个具有相同正负号的特征点才有可能匹配,对于正负号不同的特征点就不进行相似性计算。
简单地说,我们可以根据特征点的响应值符号,将特征点分成两组,一组是具有拉普拉斯正响应的特征点,一组是具有拉普拉斯负响应的特征点,匹配时,只有符号相同组中的特征点才能进行相互匹配。显然,这样可以节省特征点匹配的时间。如下图5所示。
图5 黑背景下的亮斑和白背景下的黑斑 因为它们的拉普拉斯响应正负号不同,不会对它们进行匹配
4. 源码解析
特征点描述子的生成这一部分的代码主要是通过SURFInvoker这个类来实现。在主流程中,通过一个parallel_for_()函数来并发计算。
struct SURFInvoker
enum{ORI_RADIUS = 6, ORI_WIN = 60, PATCH_SZ = 20};
// Parameters
const Mat*
const Mat*
vector&KeyPoint&*
// Pre-calculated values
vector&Point& // 特征点周围用于描述方向的邻域的点
vector&float& // 描述 方向时的 高斯 权
vector&float& DW;
SURFInvoker(const Mat& _img, const Mat& _sum,
vector&KeyPoint&& _keypoints, Mat& _descriptors,
bool _extended, bool _upright)
keypoints = &_
descriptors = &_
extended = _
upright = _
// 用于描述特征点的 方向的 邻域大小: 12*sigma+1 (sigma =1.2) 因为高斯加权的核的参数为2sigma
// nOriSampleBound为 矩形框内点的个数
const int nOriSampleBound = (2 * ORI_RADIUS + 1)*(2 * ORI_RADIUS + 1); // 这里把s近似为1 ORI_DADIUS = 6s
// 分配大小
apt.resize(nOriSampleBound);
aptw.resize(nOriSampleBound);
DW.resize(PATCH_SZ*PATCH_SZ); // PATHC_SZ为特征描述子的 区域大小 20s(s 这里初始为1了)
/* 计算特征点方向用的 高斯分布 权值与坐标 */
Mat G_ori = getGaussianKernel(2 * ORI_RADIUS + 1, SURF_ORI_SIGMA, CV_32F); // SURF_ORI_SIGMA = 1.2 *2 =2.5
nOriSamples = 0;
for (int i = -ORI_RADIUS; i &= ORI_RADIUS; i++)
for (int j = -ORI_RADIUS; j &= ORI_RADIUS; j++)
if (i*i + j*j &= ORI_RADIUS*ORI_RADIUS) // 限制在圆形区域内
apt[nOriSamples] = cvPoint(i, j);
// 下面这里有个坐标转换,因为i,j都是从-ORI_RADIUS开始的。
aptw[nOriSamples++] = G_ori.at&float&(i + ORI_RADIUS, 0) * G_ori.at&float&(j + ORI_RADIUS, 0);
CV_Assert(nOriSamples &= nOriSampleBound); // nOriSamples为圆形区域内的点,nOriSampleBound是正方形区域的点
/* 用于特征点描述子的高斯 权值 */
Mat G_desc = getGaussianKernel(PATCH_SZ, SURF_DESC_SIGMA, CV_32F); // 用于生成特征描述子的 高斯加权 sigma = 3.3s (s初取1)
for (int i = 0; i & PATCH_SZ; i++)
for (int j = 0; j & PATCH_SZ; j++)
DW[i*PATCH_SZ + j] = G_desc.at&float&(i, 0) * G_desc.at&float&(j, 0);
/* x与y方向上的 Harr小波,参数为4s */
const int NX = 2, NY = 2;
const int dx_s[NX][5] = { { 0, 0, 2, 4, -1 }, { 2, 0, 4, 4, 1 } };
const int dy_s[NY][5] = { { 0, 0, 4, 2, 1 }, { 0, 2, 4, 4, -1 } };
float X[nOriSampleBound], Y[nOriSampleBound], angle[nOriSampleBound]; // 用于计算特生点主方向
uchar PATCH[PATCH_SZ + 1][PATCH_SZ + 1];
float DX[PATCH_SZ][PATCH_SZ], DY[PATCH_SZ][PATCH_SZ]; // 20s * 20s区域的 梯度值
CvMat matX = cvMat(1, nOriSampleBound, CV_32F, X);
CvMat matY = cvMat(1, nOriSampleBound, CV_32F, Y);
CvMat _angle = cvMat(1, nOriSampleBound, CV_32F, angle);
Mat _patch(PATCH_SZ + 1, PATCH_SZ + 1, CV_8U, PATCH);
int dsize = extended ? 128 : 64;
int k, k1 = 0, k2 = (int)(*keypoints).size();// k2为Harr小波的 模板尺寸
float maxSize = 0;
for (k = k1; k & k2; k++)
maxSize = std::max(maxSize, (*keypoints)[k].size);
// maxSize*1.2/9 表示最大的尺度 s
int imaxSize = std::max(cvCeil((PATCH_SZ + 1)*maxSize*1.2f / 9.0f), 1);
Ptr&CvMat& winbuf = cvCreateMat(1, imaxSize*imaxSize, CV_8U);
for (k = k1; k & k2; k++)
int i, j, kk,
SurfHF dx_t[NX], dy_t[NY];
KeyPoint& kp = (*keypoints)[k];
float size = kp.
Point2f center = kp.
/* s是当前层的尺度参数 1.2是第一层的参数,9是第一层的模板大小*/
float s = size*1.2f / 9.0f;
/* grad_wav_size是 harr梯度模板的大小 边长为 4s */
int grad_wav_size = 2 * cvRound(2 * s);
if (sum-&rows & grad_wav_size || sum-&cols & grad_wav_size)
/* when grad_wav_size is too big,
* the sampling of gradient will be meaningless
* mark keypoint for deletion. */
kp.size = -1;
float descriptor_dir = 360.f - 90.f;
if (upright == 0)
// 这一步 是计算梯度值,先将harr模板放大,再根据积分图计算,与前面求D_x,D_y一致类似
resizeHaarPattern(dx_s, dx_t, NX, 4, grad_wav_size, sum-&cols);
resizeHaarPattern(dy_s, dy_t, NY, 4, grad_wav_size, sum-&cols);
for (kk = 0, nangle = 0; kk & nOriS kk++)
int x = cvRound(center.x + apt[kk].x*s - (float)(grad_wav_size - 1) / 2);
int y = cvRound(center.y + apt[kk].y*s - (float)(grad_wav_size - 1) / 2);
if (y & 0 || y &= sum-&rows - grad_wav_size ||
x & 0 || x &= sum-&cols - grad_wav_size)
const int* ptr = &sum-&at&int&(y, x);
float vx = calcHaarPattern(ptr, dx_t, 2);
float vy = calcHaarPattern(ptr, dy_t, 2);
X[nangle] = vx*aptw[kk];
Y[nangle] = vy*aptw[kk];
if (nangle == 0)
// No gradient could be sampled because the keypoint is too
// near too one or more of the sides of the image. As we
// therefore cannot find a dominant direction, we skip this
// keypoint and mark it for later deletion from the sequence.
kp.size = -1;
matX.cols = matY.cols = _angle.cols =
// 计算邻域内每个点的 梯度角度
cvCartToPolar(&matX, &matY, 0, &_angle, 1);
float bestx = 0, besty = 0, descriptor_mod = 0;
for (i = 0; i & 360; i += SURF_ORI_SEARCH_INC) // SURF_ORI_SEARCH_INC 为扇形区域扫描的步长
float sumx = 0, sumy = 0, temp_
for (j = 0; j & j++)
// d是 分析到的那个点与 现在主方向的偏度
int d = std::abs(cvRound(angle[j]) - i);
if (d & ORI_WIN / 2 || d & 360 - ORI_WIN / 2)
sumx += X[j];
sumy += Y[j];
temp_mod = sumx*sumx + sumy*
// descriptor_mod 是最大峰值
if (temp_mod & descriptor_mod)
descriptor_mod = temp_
descriptor_dir = fastAtan2(-besty, bestx);
kp.angle = descriptor_
if (!descriptors || !descriptors-&data)
/* 用特征点周围20*s为边长的邻域 计算特征描述子 */
int win_size = (int)((PATCH_SZ + 1)*s);
CV_Assert(winbuf-&cols &= win_size*win_size);
Mat win(win_size, win_size, CV_8U, winbuf-&data.ptr);
if (!upright)
descriptor_dir *= (float)(CV_PI / 180); // 特征点的主方向 弧度值
float sin_dir = -std::sin(descriptor_dir); //
float cos_dir = std::cos(descriptor_dir);
float win_offset = -(float)(win_size - 1) / 2;
float start_x = center.x + win_offset*cos_dir + win_offset*sin_
float start_y = center.y - win_offset*sin_dir + win_offset*cos_
uchar* WIN = win.
int ncols1 = img-&cols - 1, nrows1 = img-&rows - 1;
size_t imgstep = img-&
for (i = 0; i & win_ i++, start_x += sin_dir, start_y += cos_dir)
double pixel_x = start_x;
double pixel_y = start_y;
for (j = 0; j & win_ j++, pixel_x += cos_dir, pixel_y -= sin_dir)
int ix = cvFloor(pixel_x), iy = cvFloor(pixel_y);
if ((unsigned)ix & (unsigned)ncols1 &&
(unsigned)iy & (unsigned)nrows1)
float a = (float)(pixel_x - ix), b = (float)(pixel_y - iy);
const uchar* imgptr = &img-&at&uchar&(iy, ix);
WIN[i*win_size + j] = (uchar)
cvRound(imgptr[0] * (1.f - a)*(1.f - b) +
imgptr[1] * a*(1.f - b) +
imgptr[imgstep] * (1.f - a)*b +
imgptr[imgstep + 1] * a*b);
int x = std::min(std::max(cvRound(pixel_x), 0), ncols1);
int y = std::min(std::max(cvRound(pixel_y), 0), nrows1);
WIN[i*win_size + j] = img-&at&uchar&(y, x);
float win_offset = -(float)(win_size - 1) / 2;
int start_x = cvRound(center.x + win_offset);
int start_y = cvRound(center.y - win_offset);
uchar* WIN = win.
for (i = 0; i & win_ i++, start_x++)
int pixel_x = start_x;
int pixel_y = start_y;
for (j = 0; j & win_ j++, pixel_y--)
int x = MAX(pixel_x, 0);
int y = MAX(pixel_y, 0);
x = MIN(x, img-&cols - 1);
y = MIN(y, img-&rows - 1);
WIN[i*win_size + j] = img-&at&uchar&(y, x);
// Scale the window to size PATCH_SZ so each pixel's size is s. This
// makes calculating the gradients with wavelets of size 2s easy
resize(win, _patch, _patch.size(), 0, 0, INTER_AREA);
// Calculate gradients in x and y with wavelets of size 2s
for (i = 0; i & PATCH_SZ; i++)
for (j = 0; j & PATCH_SZ; j++)
float dw = DW[i*PATCH_SZ + j]; // 高斯加权系数
float vx = (PATCH[i][j + 1] - PATCH[i][j] + PATCH[i + 1][j + 1] - PATCH[i + 1][j])*
float vy = (PATCH[i + 1][j] - PATCH[i][j] + PATCH[i + 1][j + 1] - PATCH[i][j + 1])*
DX[i][j] =
DY[i][j] =
// Construct the descriptor
vec = descriptors-&ptr&float&(k);
for (kk = 0; kk & kk++)
vec[kk] = 0;
double square_mag = 0;
if (extended)
// 128维描述子,考虑dx与dy的正负号
for (i = 0; i & 4; i++)
for (j = 0; j & 4; j++)
// 每个方块内是一个5s * 5s的区域,每个方法由8个特征描述
for (int y = i * 5; y & i * 5 + 5; y++)
for (int x = j * 5; x & j * 5 + 5; x++)
float tx = DX[y][x], ty = DY[y][x];
if (ty &= 0)
vec[1] += (float)fabs(tx);
vec[3] += (float)fabs(tx);
if (tx &= 0)
vec[5] += (float)fabs(ty);
vec[7] += (float)fabs(ty);
for (kk = 0; kk & 8; kk++)
square_mag += vec[kk] * vec[kk];
// 64位描述子
for (i = 0; i & 4; i++)
for (j = 0; j & 4; j++)
for (int y = i * 5; y & i * 5 + 5; y++)
for (int x = j * 5; x & j * 5 + 5; x++)
float tx = DX[y][x], ty = DY[y][x];
vec[0] += vec[1] +=
vec[2] += (float)fabs(tx); vec[3] += (float)fabs(ty);
for (kk = 0; kk & 4; kk++)
square_mag += vec[kk] * vec[kk];
// 归一化 描述子 以满足 光照不变性
vec = descriptors-&ptr&float&(k);
float scale = (float)(1. / (sqrt(square_mag) + DBL_EPSILON));
for (kk = 0; kk & kk++)
vec[kk] *=
实际上有文献指出,SURF比SIFT工作更出色。他们认为主要是因为SURF在求取描述子特征矢量时,是对一个子块的梯度信息进行求和,而SIFT则是依靠单个像素梯度的方向。
(请您对文章做出评价)
上一篇: 下一篇:
分类: 标签: ,
Add your comment
博主,你好,这个SURF的源码能发我一份嘛,最近在做Android的上的图像识别,是毕业设计,能发我邮箱吗?谢谢。支持(0)反对(0)
您好,再次打扰。在主方向定位上,我没有在SURF原文中看到辅方向这个概念,刚刚看了SIFT,貌似也没有提及关于出现一个占主峰能量80%的次方向就定义成主方向的相关论述。您的这篇文章中关于这块的见解从何而来?感谢。。支持(0)反对(0)
#3楼[楼主]
夜幕夜幕这个在很多文献中应该都能看到,我的参考书是《图像局部不变性特征与描述》。感觉你对原文抠的太死了,SIFT的伟大在于提出了一多层次DoG特征点以及一种非常优秀的特征描述子,在这个思想下实现方式是灵活的。支持(0)反对(0)
☆Ronny丶好的,我去看看这本书!十分感谢指点!支持(0)反对(0)
您好,我就是想不明白,旋转的公式为什么是这个dx′=w(?dx×sin(θ)+dy×cos(θ))dy′=w(?dx×cos(θ)+dy×sin(θ)),我推导的总是dy和dx颠倒,您这个公式是如何计算得来的呢
馆藏&22469
TA的最新馆藏【转】&关于2006_CVPR_Beyond&Bags&of&Features&Spatial&Pyramid&
对这篇paper及对应code研究好久了,也困惑了好久,现在终于有点明白是怎么回事了,赶紧记下来
对应代码的整个算法过程如下(代码是丕子的):
Step1&用均匀网格划分图像。程序中采用像素,即。比如的图像可画出()个(一个网格用黑色块表示)。
Step&2&计算特征向量。程序中采用的(一个用紫色表示)计算一个描述子(即一个特征向量),这样一个包括个。计算时,一个划分为()个,每个为个(一个用红色表示),每个计算一次(维),这样一个就计算次,共维。然后计算下一个,即上一个向右移动一个(移动后为浅色边框部分),以此类推,指导移动到图像的边缘,然后向下移动,这样共有()()个。每个对应一个维的描述子,最终,这幅图像可以用个维的向量表示()。
&<img ALT="" src="/blog7style/images/common/sg_trans.gif" real_src ="http://img.my.csdn.net/uploads//_3773.jpg" STYLE="border-style: max-width: 100%;"
TITLE="【转】&关于2006_CVPR_Beyond&Bags&of&Features&Spatial&Pyramid&" />
Step&3&计算词典。采用方法构造单词表,即用每幅图像的描述子进行运算,代码中计算的词典每个单词为维向量。
Step&4&计算每幅图像的直方图。每幅图像的描述子(即特征向量)用词典量化,这样一幅图像就可以用个词来表示,即的矩阵。()。
Step&5&计算金字塔。中的三层金字塔计算如下。
由计算公式算出每幅图像可用维的向量表示。式中为词汇数(),为金字塔的层数()。
<img ALT="" src="/blog7style/images/common/sg_trans.gif" real_src ="http://img.my.csdn.net/uploads//_7543.jpg" STYLE="border-style: max-width: 100%;"
TITLE="【转】&关于2006_CVPR_Beyond&Bags&of&Features&Spatial&Pyramid&" />
Step&6&金字塔匹配()。主要参考下面这个公式:
<img ALT="" src="/blog7style/images/common/sg_trans.gif" real_src ="http://img.my.csdn.net/uploads//_4291.jpg" STYLE="border-style: max-width: 100%;"
TITLE="【转】&关于2006_CVPR_Beyond&Bags&of&Features&Spatial&Pyramid&" />
Xm,Ym分别为两幅图像中第个的描述子()集合,都是二维的(分别是描述子的横坐标和纵坐标)。一个即一种(码字),对每一个做金字塔匹配,最后求和。
附:本人小硕,阅历尚浅,文中有偏差的地方忘各位高手不吝赐教。。
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。【特征匹配】BRIEF特征描述子原理及源码解析
C++中的const关键字的用法非常灵活,而使用const将大大改善程序的健壮性,本人根据各方面查到的资料进行总结如下,期望对朋友们有所帮助。Const 是C++中常用的
转载请注明出处:&
& 传统的特征点描述子如SIFT,SURF描述子,每个特征点采用128维(SIFT)或者64维(SURF)向量去描述,每个维度上占用4字节,SIFT需要128×4=512字节内存,SURF则需要256字节。如果对于内存资源有限的情况下,这种描述子方法显然不适应。同时,在形成描述子的过程中,也比较耗时。后来有人提出采用PCA降维的方法,但没有解决计算描述子耗时的问题。
& &鉴于上述的缺点Michael Calonder等人在论文提出BRIEF描述特征点的方法(BRIEF:Binary Robust Independent Elementary Features)。BRIEF描述子采用二进制码串(每一位非1即0)作为描述子向量,论文中考虑长度有128,256,512几种,同时形成描述子算法的过程简单,由于采用二进制码串,匹配上采用汉明距离,(一个串变成另一个串所需要的最小替换次数)。但由于BRIEF描述子不具有方向性,大角度旋转会对匹配上有很大的影响。
& &BRIRF只提出了描述特征点的方法,所以特征点的检测部分必须结合其他的方法,如SIFT,SURF等,但论文中建议与Fast结合,因为会更能体现出Brirf速度快等优点。
--------------------------------------------------------------------------------------------------
BRIEF描述子原理简要为三个步骤,长度为N的二进制码串作为描述子(占用内存N/8):
& &1.以特征点P为中心,取一个S×S大小的Patch邻域;
& &2.在这个邻域内随机取N对点,然后对这2×N点分别做高斯平滑。定义τ测试,比较N对像素点的灰度&#20540;的大小;
& & & & & & & & & & & & & & & & & & &
& &3.最后把步骤2得到的N个二进制码串组成一个N维向量即可;
& & & & & & & & & & & & & & & & & & &
-----------------------------------------------------------------------------------------------------
原理解析:
__1.关于做τ测试前,需要对随机点做高斯平滑,由于采用单个的像素灰度&#20540;做比较,会对噪声很敏感;采用高斯平滑图像,会降低噪声的影响,使得 & & & & 描述子更加稳定。论文中建议采用9×9的kernal。
__2.论文中对随机取N对点采用了5中不同的方法做测试,论文中建议采用G II的方法:
& & & & & & & & & & & &
& & & & & & &G I :(X,Y)~(-S/2,S/2)分布,X,Y即均匀分布;
& & & & & & &G II:&,X,Y均服从高斯分布;
& & & & & & &G III:&,先随机取X点,再以X点为中心,取Y点;
& & & & & & &G IV: 在量化极坐标系下,随机取2N个点;
& & & & & & &G V: X固定在中心,在Patch内,Y在极坐标系中尽可能取所有可能的&#20540;;
__3.最后汉明距离的计算,直接比较两二进制码串的距离,距离定义为:其中一个串变成另一个串所需要的最少操作。因而比欧氏距离运算速度快.
& & & 如果取N=128,即每个特征点需要128/8=16个字节内存大小作为其描述子。
& & & & & & & & & & & & & & & & & & & &&
OPENCV源码解析:
#include &stdio.h&
#include &iostream&
#include &cv.h&
#include &opencv2/highgui/highgui.hpp&
#include &opencv2/core/core.hpp&
#include &opencv2/features2d/features2d.hpp&
#include &opencv2/nonfree/nonfree.hpp&
int main( int argc, char** argv )
Mat img_1 = imread( &F:\\Picture\\book.jpg&, CV_LOAD_IMAGE_GRAYSCALE );
Mat img_2 = imread( &F:\\Picture\\book_2.jpg&, CV_LOAD_IMAGE_GRAYSCALE );
if( !img_1.data || !img_2.data )
return -1; }
//-- Step 1: Detect the keypoints using SURF Detector
int minHessian = 400;
SurfFeatureDetector detector( minHessian);
//采用Surf特征点检测
std::vector&KeyPoint& keypoints_1, keypoints_2;
detector.detect( img_1, keypoints_1 );
detector.detect( img_2, keypoints_2 );
//-- Step 2: Calculate descriptors (feature vectors)
BriefDescriptorExtractor
extractor(64);
//参数表示字节数,采用长度为64&#215;8=512的向量表示,见下方分析
Mat descriptors_1, descriptors_2;
pute( img_1, keypoints_1, descriptors_1 );
pute( img_2, keypoints_2, descriptors_2 );
//-- Step 3: Matching descriptor vectors with a brute force matcher
matcher(NORM_HAMMING);
//汉明距离匹配特征点
std::vector& DMatch &
matcher.match( descriptors_1, descriptors_2, matches );
//-- Draw matches
drawMatches( img_1, keypoints_1, img_2, keypoints_2, matches, img_matches );
////-- Show detected matches
imshow(&Matches&, img_matches );
waitKey(0);
Brief描述子的类定义:
你最喜欢的

我要回帖

更多关于 怎么样才能让牙齿变白 的文章

 

随机推荐