SURF C++代码 详细阅读(三)—— SURF描述符

阅读(二)获取了特征点,接着针对每个特征点,来构造一个SURF描述符。

3 SURF描述符

回顾主函数,1得到特征点后
使用图像和图像的所有特征点向量集合ipts2 构造SURF对象des
接着3 通过函数getDescriptors(upright)得到其描述符。

inline void surfDetDes(IplImage *img,    /* image to find Ipoints in */
                                             std::vector<Ipoint> &ipts, /* reference to vector of Ipoints */
                                             int single_mem_cpy,
                                             bool upright = false, /* run in rotation invariant mode? */
                                             int octaves = OCTAVES, /* number of octaves to calculate */
                                             int intervals = INTERVALS, /* number of intervals per octave */
                                             int init_sample = INIT_SAMPLE, /* initial sampling step */
                                             float thres = THRES /* blob response threshold */)
{
    // Create integral-image representation of the image
    IplImage *int_img = Integral(img);
    
    // Create Fast Hessian Object
    FastHessian fh(int_img, ipts, single_mem_cpy, octaves, intervals, init_sample, thres);

    // Extract interest points and store in vector ipts
    clock_t start = clock();
    //1得到特征点
    fh.getIpoints();
    clock_t end = clock();
    std::cout<< "Extract keypoints took: " << float(end - start) / CLOCKS_PER_SEC    << " seconds" << std::endl;

    // Create Surf Descriptor Object
    //2构造SURF对象des
    Surf des(int_img, ipts);
  
    // Extract the descriptors for the ipts
    start = clock();
    //3 通过函数getDescriptors(upright)得到其描述符
    des.getDescriptors(upright);
    end = clock();
    std::cout<< "Extract descriptor took: " << float(end - start) / CLOCKS_PER_SEC    << " seconds" << std::endl;

    // Deallocate the integral image
    cvReleaseImage(&int_img);
}

要构造SURF对象,先看SURF类

class Surf {
  
  public:
    
    //! Standard Constructor (img is an integral image)
    Surf(IplImage *img, std::vector<Ipoint> &ipts);

    //! Describe all features in the supplied vector
    void getDescriptors(bool bUpright = false);
  
  private:
    
    //---------------- Private Functions -----------------//

    //! Assign the current Ipoint an orientation
    void getOrientation(int index);
    
    //! Get the descriptor. See Agrawal ECCV 08
    void getDescriptor(bool bUpright = false, int index = 0);

    //! Calculate the value of the 2d gaussian at x,y
    inline float gaussian(int x, int y, float sig);
    inline float gaussian(float x, float y, float sig);

    //! Calculate Haar wavelet responses in x and y directions
    inline float haarX(int row, int column, int size);
    inline float haarY(int row, int column, int size);

    //! Get the angle from the +ve x-axis of the vector given by [X Y]
    float getAngle(float X, float Y);


    //---------------- Private Variables -----------------//

    //! Integral image where Ipoints have been detected
    IplImage *img;

    //! Ipoints vector
    IpVec &ipts;

    //! Index of current Ipoint in the vector
    // int index;
};

3.1 构造函数

其中图像参数使用了赋值运算符初始化,特征点使用了括号赋值初始化(C++特性)

Surf::Surf(IplImage *img, IpVec &ipts)
: ipts(ipts)
{
    this->img = img;
}

括号赋值是在给变量分配内存空间的同时对它进行初始化,而赋值运算符赋值则是先分配内存空间,然后再初始化,这里将特征点ipts(引用数据成员)在构造函数调入之后函数体执行之前就进行初始化。

3.2 获取描述符函数(主要)

getDescriptors(bool bUpright = false); 其中使用了SURF类中的高斯滤波、哈尔小波、获取角度函数。
在函数体中,大段描述的是不旋转的描述符,而SURF描述符是考虑旋转的,在得到描述符的邻域矩形计算过程时,要先计算主方向,这里直接执行else。
在这里插入图片描述
对所给图像中每一个特征点,得到其主方向getOrientation(i);,再得到其描述符getDescriptor(false, i);这里得到描述符的函数重载为两个参数,false的话是带旋转情况,i是每个特征点索引,便于对每个特征点找到主方向并进行旋转。

else
    {
        // Main SURF-64 loop assigns orientations and gets descriptors
        // #pragma acc parallel loop
        for (int i = 0; i < ipts_size; ++i)
        {
            // Set the Ipoint to be described
            // index = i;

            // Assign Orientations and extract rotation invariant descriptors
            getOrientation(i);
            getDescriptor(false, i);
        }
    }
3.2.1 获取主方向
//! Assign the supplied Ipoint an orientation
void Surf::getOrientation(int index)
{
    Ipoint *ipt = &ipts[index];
    float gauss = 0.f, scale = ipt->scale;
    const int s = fRound(scale), r = fRound(ipt->y), c = fRound(ipt->x);
    std::vector<float> resX(109), resY(109), Ang(109);//容量 半径为6的园空间内离散的像素点数109
    const int id[] = {6,5,4,3,2,1,0,1,2,3,4,5,6};

    int idx = 0;
    // calculate haar responses for points within radius of 6*scale
    
    for(int i = -6; i <= 6; ++i) 
    {
        for(int j = -6; j <= 6; ++j) 
        {
            if(i*i + j*j < 36) 
            {
                // gauss = static_cast<float>(gauss33[id[i+6]][id[j+6]]);
                gauss = gauss33[id[i+6]][id[j+6]];
                resX[idx] = gauss * haarX(r+j*s, c+i*s, 4*s);
                resY[idx] = gauss * haarY(r+j*s, c+i*s, 4*s);
                Ang[idx] = getAngle(resX[idx], resY[idx]);
                ++idx;
            }
        }
    }

    // calculate the dominant direction 
    float sumX=0.f, sumY=0.f;
    float max=0.f, orientation = 0.f;
    float ang1=0.f, ang2=0.f;
	//以特征点为中心,张角为π/3的扇形滑动,每次弧度增加0.15,
    // loop slides pi/3 window around feature point
    for(ang1 = 0; ang1 < 2*pi;    ang1+=0.15f) {//每次弧度增加0.15
        ang2 = ( ang1+pi/3.0f > 2*pi ? ang1-5.0f*pi/3.0f : ang1+pi/3.0f);//覆盖角度pi/3
        sumX = sumY = 0.f; 
        for(unsigned int k = 0; k < Ang.size(); ++k) 
        {
            // get angle from the x-axis of the sample point
            const float & ang = Ang[k];

            // determine whether the point is within the window
            if (ang1 < ang2 && ang1 < ang && ang < ang2) 
            {
                sumX+=resX[k];    
                sumY+=resY[k];
            } 
            else if (ang2 < ang1 && 
                ((ang > 0 && ang < ang2) || (ang > ang1 && ang < 2*pi) )) 
            {
                sumX+=resX[k];    
                sumY+=resY[k];
            }
        }
		//计算窗口内的Harr小波响应值dx、dy的累加,选取最大值作为主方向,通过反三角函数getAngle(sumX, sumY)得到主方向角度
        // if the vector produced from this window is longer than all 
        // previous vectors then this forms the new dominant direction
        if (sumX*sumX + sumY*sumY > max) 
        {
            // store largest orientation
            max = sumX*sumX + sumY*sumY;
            orientation = getAngle(sumX, sumY);
        }
    }

    // assign orientation of the dominant response vector
    ipt->orientation = orientation;
}

对每一个特征点,四舍五入fround,得到对应横纵坐标、尺度 r、c、s
接着在像素位置上,进行高斯模糊和哈尔小波运算,哈尔小波计算运用了积分图像。
这里举X方向哈尔小波HarrX例子来看

inline float Surf::haarX(int row, int column, int s)//积分图参数(r+j*s, c+i*s, 4*s)
{
    return BoxIntegral(img, row-s/2, column, s, s/2) 
        -1 * BoxIntegral(img, row-s/2, column-s/2, s, s/2);
}

得到尺度4s的X方向哈尔滤波器

在这里插入图片描述在经过X、Y方向的哈尔小波后,得到像素的差值dx、dy,接着以特征点为中心,张角为π/3的扇形滑动,每次弧度增加0.15,计算窗口内的Harr小波响应值dx、dy的累加,选取累加最大值方向作为主方向,通过反三角函数getAngle(sumX, sumY)得到角度,返回给ipt>orientation。在这里插入图片描述

在这里插入图片描述

3.2.1 计算描述符(旋转)
//! Get the modified descriptor. See Agrawal ECCV 08
//! Modified descriptor contributed by Pablo Fernandez
void Surf::getDescriptor(bool bUpright, int index)
{
    int y, x, sample_x, sample_y, count=0;
    int i = 0, ix = 0, j = 0, jx = 0, xs = 0, ys = 0;
    float scale, *desc, dx, dy, mdx, mdy, co, si;
    float gauss_s1 = 0.f, gauss_s2 = 0.f;
    float rx = 0.f, ry = 0.f, rrx = 0.f, rry = 0.f, len = 0.f;
    float cx = -0.5f, cy = 0.f; //Subregion centers for the 4x4 gaussian weighting

    Ipoint *ipt = &ipts[index];
    scale = ipt->scale;
    x = fRound(ipt->x);
    y = fRound(ipt->y);    
    desc = ipt->descriptor;

    if (bUpright)
    {
        co = 1;
        si = 0;
    }
    else
    {
        co = cos(ipt->orientation);
        si = sin(ipt->orientation);
    }

    i = -8;

    //Calculate descriptor for this interest point
    while(i < 12)
    {
        j = -8;
        i = i-4;

        cx += 1.f;
        cy = -0.5f;

        while(j < 12) 
        {
            dx=dy=mdx=mdy=0.f;
            cy += 1.f;

            j = j - 4;

            ix = i + 5;
            jx = j + 5;

            xs = fRound(x + ( -jx*scale*si + ix*scale*co));
            ys = fRound(y + ( jx*scale*co + ix*scale*si));

            for (int k = i; k < i + 9; ++k) 
            {
                for (int l = j; l < j + 9; ++l) 
                {
                    //Get coords of sample point on the rotated axis
                    sample_x = fRound(x + (-l*scale*si + k*scale*co));
                    sample_y = fRound(y + ( l*scale*co + k*scale*si));

                    //Get the gaussian weighted x and y responses
                    gauss_s1 = gaussian(xs-sample_x,ys-sample_y,2.5f*scale);
                    rx = haarX(sample_y, sample_x, 2*fRound(scale));
                    ry = haarY(sample_y, sample_x, 2*fRound(scale));

                    //Get the gaussian weighted x and y responses on rotated axis
                    rrx = gauss_s1*(-rx*si + ry*co);
                    rry = gauss_s1*(rx*co + ry*si);

                    dx += rrx;
                    dy += rry;
                    mdx += fabs(rrx);
                    mdy += fabs(rry);

                }
            }

            //Add the values to the descriptor vector
            gauss_s2 = gaussian(cx-2.0f,cy-2.0f,1.5f);

            desc[count++] = dx*gauss_s2;
            desc[count++] = dy*gauss_s2;
            desc[count++] = mdx*gauss_s2;
            desc[count++] = mdy*gauss_s2;

            len += (dx*dx + dy*dy + mdx*mdx + mdy*mdy) * gauss_s2*gauss_s2;

            j += 9;
        }
        i += 9;
    }

    //Convert to Unit Vector
    len = sqrt(len);
    for(int i = 0; i < 64; ++i)
        desc[i] /= len;

}

为了充分利用积分图计算,先使用水平和垂直的哈尔小波滤波器求得dx和dy,然后根据主方向旋转dx和dy与主方向操持一致。

以特征点为中心,在20s邻域矩形划分为4×4个子块,每个子块大小为20s20s,每个子块有5s×5s个像素
每个子块利用尺寸2s哈尔小波滤波器(积分图计算)和高斯滤波器,每个子块统计25个像素的水平方向和垂直方向的哈尔小波特征,得到∑dx、∑|dx|、∑dy、∑|dy|,4
4个小块具有444=64维数据,这就得到了SURF的64维描述符。
如下图2所示。图中,以特征点为中心,以20s为边长的矩形窗口为特征描述子计算使用的窗口,特征点到矩形边框的线段表示特征点的主方向。
在这里插入图片描述

像素点围绕另一个像素点旋转公式:

x1=(x-xb)cosθ - (y-yb)sinθ + xb

y1=(y-yb)cosθ + (x-xb)sinθ + yb

旋转原理可以参考https://guo-pu.blog.csdn.net/article/details/116196097
代码中每个子块中的像素(x-jxs,y-ixs)(图右)围绕中心点(x,y)旋转对应主方向的角度,后四舍五入到像素位,得到旋转后的坐标(xs,ys)(图左)。

xs = fRound(x + ( -jx*scale*si + ix*scale*co));
ys = fRound(y + ( jx*scale*co + ix*scale*si));

最后将特征值desc归一化,得到了SURF特征。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 在计算机科学中,Csurf(Curvature-Sensitive Scale-Invariant Feature Transform)是一种用于图像特征匹配的算法。它在SURF(Speeded Up Robust Features)算法的基础上,通过引入曲率敏感性来提高特征点的匹配精度。 Csurf算法首先通过SURF算法检测图像中的关键点,然后使用多尺度空间来计算这些关键点的特征描述符。接下来,Csurf算法通过计算特征点处的图像曲率,来对描述符进行调整。这种曲率调整使得特征点对于图像的细微变化更加敏感,从而提高了匹配的精度。 Csurf匹配精度的计算通常涉及两个步骤。首先,需要对参考图像和待匹配图像中的特征点进行比较,以找到最佳匹配。这可以通过计算描述符之间的相似度来完成。通常,使用欧氏距离或相关性来度量描述符之间的相似性。较小的距离或较大的相关性表示更好的匹配。 其次,需要计算匹配结果的准确率。准确率是指正确匹配的特征点数量与总匹配特征点数量的比例。可以使用混淆矩阵来计算准确率,其中真阳性是正确匹配的特征点数量,假阳性是错误匹配的特征点数量。 为了提高Csurf匹配精度,可以采取一些优化方法。例如,调整特征描述符的参数,或者应用其他图像增强技术,如滤波或光照补偿。此外,还可以使用更复杂的匹配算法,如RANSAC(Random Sample Consensus)来排除误匹配。 总之,Csurf是一种通过引入曲率敏感性来提高特征匹配精度的算法。通过计算描述符之间的相似度和准确率,可以评估Csurf匹配的质量。优化算法参数和采用其他图像增强技术可以进一步提高匹配精度。 ### 回答2: c surf是一种用于图像特征匹配的算法,其匹配精度可以通过以下几个因素来计算。 首先,c surf算法会提取图像中的特征点,这些特征点具有一定的鲁棒性,可以在图像中的不同视角、不同光照条件下保持较好的匹配精度。 其次,c surf算法会对特征点进行描述,生成特征向量,对特征点进行特征匹配。这个描述过程会考虑到特征点周围的图像信息,从而提高匹配的准确性。 另外,c surf算法使用了尺度不变性的方法,即可以在不同尺度下识别和匹配特征点。这样可以处理图像中物体的旋转、缩放等变换,提高了匹配的精度。 此外,c surf算法还使用了旋转不变性和仿射不变性的方法,可以处理图像中的旋转和仿射变换,提高了匹配的准确性。 最后,c surf算法还可以通过调整阈值参数来控制匹配精度。通过调整参数,可以平衡匹配的准确性和匹配的数量,从而得到更好的匹配精度。 综上所述,c surf算法通过提取特征点、描述特征点以及使用尺度、旋转和仿射不变性等方法,可以得到较高的匹配精度。但是具体的匹配精度还会受到图像质量、特征点分布等因素的影响。 ### 回答3: c surf是一种用于图像特征匹配的算法,其匹配精度可以通过计算得到。c surf算法是一种基于尺度不变特征变换(scale-invariant feature transform,SIFT)的快速版本。它通过在图像中找到关键点,并计算这些关键点的局部特征向量来进行匹配。 c surf匹配精度的计算通常涉及以下几个步骤: 1. 特征提取:c surf从输入图像中提取关键点,并计算这些关键点的局部特征向量。这些特征向量会用来描述图像的局部结构和纹理信息。 2. 特征匹配:使用提取到的特征向量,c surf会通过计算特征之间的距离或相似度来进行特征匹配。常用的匹配方法包括欧几里德距离、余弦相似度等。 3. 匹配筛选:得到初始的特征匹配后,需要进行筛选以获得更准确的匹配结果。常用的筛选方法包括最近邻筛选和低比值测试等。 4. 精度评估:匹配精度可以通过计算匹配点之间的误差来评估。通常使用的指标包括平均重投影误差、正确匹配率等。通过这些指标,可以评估c surf算法在匹配任务中的性能。 总的来说,c surf通过提取图像的局部特征,并计算特征之间的相似度来进行匹配,然后通过筛选和精度评估来提高匹配的准确性。匹配精度的计算是通过比较算法得到的匹配结果和真实结果之间的误差来评估的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值