精华内容
下载资源
问答
  • SURF算法原理与源码分析
    2018-11-01 20:30:23

    如果说SIFT算法中使用DOG对LOG进行了简化,提高了搜索特征点的速度,那么SURF算法则是对DoH的简化与近似。虽然SIFT算法已经被认为是最有效的,也是最常用的特征点提取的算法,但如果不借助于硬件的加速和专用图像处理器的配合,SIFT算法以现有的计算机仍然很难达到实时的程度。对于需要实时运算的场合,如基于特征点匹配的实时目标跟踪系统,每秒要处理8-24帧的图像,需要在毫秒级内完成特征点的搜索、特征矢量生成、特征矢量匹配、目标锁定等工作,这样SIFT算法就很难适应这种需求了。SURF借鉴了SIFT中简化近似的思想,把DoH中的高斯二阶微分模板进行了简化,使得模板对图像的滤波只需要进行几个简单的加减法运算,并且,这种运算与滤波器的尺度无关。实验证明,SURF算法较SIFT在运算速度上要快3倍左右。

    1. 积分图像

    SURF算法中要用到积分图像的概念。借助积分图像,图像与高斯二阶微分模板的滤波转化为对积分图像的加减运算。

    积分图像中任意一点(i,j)

    的值ii(i,j),为原图像左上角到点(i,j)

    相应的对角线区域灰度值的总和,即

     

    ii(i,j)=∑r≤i, c≤jp(r,c)

     

    式中,p(r,c)

    表示图像中点(r,c)的灰度值,ii(i,j)

    可以用下面两式迭代计算得到

     

    S(i,j)=S(i,j−1)+p(i,j)

     

     

    ii(i,j)=ii(i−1,j)+S(i,j)

     

    式中,S(i,j)

    表示一列的积分,且S(i,−1)=0,ii(−1,j)=0

    。求积分图像,只需要对原图像所有像素进行一遍扫描。

    OpenCV中提供了用于计算积分图像的接口

    /*
    * src :输入图像,大小为M*N
    * sum: 输出的积分图像,大小为(M+1)*(N+1)
    * sdepth:用于指定sum的类型,-1表示与src类型一致
    */
    void integral(InputArray src, OutputArray sum, int sdepth = -1);

    值得注意的是OpenCV里的积分图大小比原图像多一行一列,那是因为OpenCV中积分图的计算公式为:

     

    ii(i,j)=∑r<i, c<jp(r,c)

     

    image

    一旦积分图计算好了,计算图像内任何矩形区域的像素值的和只需要三个加法,如上图所示。

    2. DoH近似

    斑点检测这篇文章中已经提到过,我们可以利用Hessian矩阵行列式的极大值检测斑点。下面我们给出Hessian矩阵的定义。

    给定图像I

    中的一个点x(i,j),在点x处,尺度为σ的Hessian矩阵H(x,σ)

    定义如下:

     

    H(x,σ)=[Lxx(x,σ)Lxy(x,σ)Lxy(x,σ)Lyy(x,σ)]

     

    式中,Lxx(x,σ)

    是高斯二阶微分∂2g(σ)∂x2在点x处与图像I的卷积,Lx,y(x,σ)和Lyy(x,σ)

    具有类似的含义。

    下面显示的是上面三种高斯微分算子的图形。

    image

    但是利用Hessian行列式进行图像斑点检测时,有一个缺点。由于二阶高斯微分被离散化和裁剪的原因,导致了图像在旋转奇数倍的π/4

    时,即转换到模板的对角线方向时,特征点检测的重复性降低(也就是说,原来特征点的地方,可能检测不到特征点了)。而在π/2

    时,特征点检测的重现率真最高。但这一小小的不足不影响我们使用Hessian矩阵进行特征点的检测。

    为了将模板与图产像的卷积转换为盒子滤波运算,我们需要对高斯二阶微分模板进行简化,使得简化后的模板只是由几个矩形区域组成,矩形区域内填充同一值,如下图所示,在简化模板中白色区域的值为正数,黑色区域的值为负数,灰度区域的值为0。

    image

    对于σ=1.2

    的高斯二阶微分滤波器,我们设定模板的尺寸为9×9的大小,并用它作为最小尺度空间值对图像进行滤波和斑点检测。我们使用Dxx、Dxy和Dyy

    表示模板与图像进行卷积的结果。这样,便可以将Hessian矩阵的行列式作如下的简化。

     

    Det(H)=LxxLyy–LxyLxy=DxxLxxDxxDyyLyyDyy−DxyLxyDxyDxyLxyDxy≈DxxDyy–(wDxy)2

     

    滤波器响应的相关权重w

    是为了平衡Hessian行列式的表示式。这是为了保持高斯核与近似高斯核的一致性。

     

    w=|Lxy(σ)|F|Dxx(σ)F||Lxx(σ)|F|Dxy(σ)F|=0.912

     

    其中|X|F

    为Frobenius范数。理论上来说对于不同的σ的值和对应尺寸的模板尺寸,w

    值是不同的,但为了简化起见,可以认为它是同一个常数。

    使用近似的Hessian矩阵行列式来表示图像中某一点x

    处的斑点响应值,遍历图像中所有的像元点,便形成了在某一尺度下琉璃点检测的响应图像。使用不同的模板尺寸,便形成了多尺度斑点响应的金字塔图像,利用这一金字塔图像,就可以进行斑点响应极值点的搜索,其过程完全与SIFT算法类同。

    3. 尺度空间表示

    通常想要获取不同尺度的斑点,必须建立图像的尺度空间金字塔。一般的方法是通过不同σ

    的高斯函数,对图像进行平滑滤波,然后重采样图像以获得更高一层的金字塔图像。SIFT特征检测算法中就是通过相邻两层图像金字塔相减得到DoG图像,然后再在DoG图像上进行斑点和边缘检测工作的。

    由于采用了盒子滤波和积分图像,所以,我们并不需要像SIFT算法那样去直接建立图像金字塔,而是采用不断增大盒子滤波模板的尺寸的间接方法。通过不同尺寸盒子滤波模板与积分图像求取Hessian矩阵行列式的响应图像。然后在响应图像上采用3D非最大值抑制,求取各种不同尺度的斑点。

    如前所述,我们使用9×9

    的模板对图像进行滤波,其结果作为最初始的尺度空间层(此时,尺度值为s=1.2,近似σ=1.2

    的高斯微分),后续的层将通过逐步放大滤波模板尺寸,以及放大后的模板不断与图像进行滤波得到。由于采用盒子滤波和积分图像,滤波过程并不随着滤波模板尺寸的增加而使运算工作量增加。

    与SIFT算法类似,我们需要将尺度空间划分为若干组(Octaves)。一个组代表了逐步放大的滤波模板对同一输入图像进行滤波的一系列响应图。每个组又由若干固定的层组成。由于积分图像离散化的原因,两个层之间的最小尺度变化量是由高斯二阶微分滤波器在微分方向上对正负斑点响应长度l0

    决定的,它是盒子滤波器模板尺寸的1/3。对于9×9的模板,它的l0=3。一下层的响应长度至少应该在l0的基础上增加2个像素,以保证一边一个像素,即l0=5。这样模板的尺寸就为15×15。以此类推,我们可以得到一个尺寸增大模板序列,它们的尺寸分别为:9×9,15×15,21×21,27×27

    ,黑色、白色区域的长度增加偶数个像素,以保证一个中心像素的存在。

    image        image

    采用类似的方法来处理其他几组的模板序列。其方法是将滤波器尺寸增加量翻倍(6,12,24,38)。这样,可以得到第二组的滤波器尺寸,它们分别为15,27,39,51。第三组的滤波器尺寸为27,51,75,99。如果原始图像的尺寸仍然大于对应的滤波器尺寸,尺度空间的分析还可以进行第四组,其对应的模板尺寸分别为51,99,147和195。下图显示了第一组至第三组的滤波器尺寸变化。

    image

    在通常尺度分析情况下,随着尺度的增大,被检测到的斑点数量迅速衰减。所以一般进行3-4组就可以了,与此同时,为了减少运算量,提高计算的速度,可以考虑在滤波时,将采样间隔设为2。

    对于尺寸为L的模板,当用它与积分图运算来近似二维高斯核的滤波时,对应的二维高斯核的参数σ=1.2×L9

    ,这一点至关重要,尤其是在后面计算描述子时,用于计算邻域的半径时。

    4. 兴趣点的定位

    为了在图像及不同尺寸中定位兴趣点,我们用了3×3×3

    邻域非最大值抑制。具体的步骤基本与SIFT一致,而且Hessian矩阵行列式的最大值在尺度和图像空间被插值。

    下面显示了我们用的快速Hessian检测子检测到的兴趣点。

    5. SURF源码解析

    这份源码来自OpenCV nonfree模块。

    这里先介绍SURF特征点定位这一块,关于特征点的描述下一篇文章再介绍。

    5.1 主干函数 fastHessianDetector

    特征点定位的主干函数为fastHessianDetector,该函数接受一个积分图像,以及尺寸相关的参数,组数与每组的层数,检测到的特征点保存在vector<KeyPoint>类型的结构中。

    static void fastHessianDetector(const Mat& sum, const Mat& msum, vector<KeyPoint>& keypoints,
        int nOctaves, int nOctaveLayers, float hessianThreshold)
    {
        /*first Octave图像采样的步长,第二组的时候加倍,以此内推
        增加这个值,将会加快特征点检测的速度,但是会让特征点的提取变得不稳定*/
        const int SAMPLE_STEP0 = 1;
    
        int nTotalLayers = (nOctaveLayers + 2)*nOctaves; // 尺度空间的总图像数
        int nMiddleLayers = nOctaveLayers*nOctaves; // 用于检测特征点的层的 总数,也就是中间层的总数
    
        vector<Mat> dets(nTotalLayers); // 每一层图像 对应的 Hessian行列式的值
        vector<Mat> traces(nTotalLayers); // 每一层图像 对应的 Hessian矩阵的迹的值
        vector<int> sizes(nTotalLayers); // 每一层用的 Harr模板的大小
        vector<int> sampleSteps(nTotalLayers); // 每一层用的采样步长 
        vector<int> middleIndices(nMiddleLayers); // 中间层的索引值
    
        keypoints.clear();
    
        // 为上面的对象分配空间,并赋予合适的值
        int index = 0, middleIndex = 0, step = SAMPLE_STEP0;
    
        for (int octave = 0; octave < nOctaves; octave++)
        {
            for (int layer = 0; layer < nOctaveLayers + 2; layer++)
            {
                /*这里sum.rows - 1是因为 sum是积分图,它的大小是原图像大小加1*/
                dets[index].create((sum.rows - 1) / step, (sum.cols - 1) / step, CV_32F); // 这里面有除以遍历图像用的步长
                traces[index].create((sum.rows - 1) / step, (sum.cols - 1) / step, CV_32F);
                sizes[index] = (SURF_HAAR_SIZE0 + SURF_HAAR_SIZE_INC*layer) << octave;
                sampleSteps[index] = step;
    
                if (0 < layer && layer <= nOctaveLayers)
                    middleIndices[middleIndex++] = index;
                index++;
            }
            step *= 2;
        }
        // Calculate hessian determinant and trace samples in each layer
        for (int i = 0; i < nTotalLayers; i++)
        {
            calcLayerDetAndTrace(sum, sizes[i], sampleSteps[i], dets[i], traces[i]);
        }
    
        // Find maxima in the determinant of the hessian
        for (int i = 0; i < nMiddleLayers; i++)
        {
            int layer = middleIndices[i];
            int octave = i / nOctaveLayers;
            findMaximaInLayer(sum, msum, dets, traces, sizes, keypoints, octave, layer, hessianThreshold, sampleSteps[layer]);
        }
    
        std::sort(keypoints.begin(), keypoints.end(), KeypointGreater());
    }

    5.2 计算Hessian矩阵的行列式与迹calcLayerDetAndTrace

    这个函数首先定义了尺寸为9的第一层图像的三个模板。模板分别为一个3×5

    、3×5、4×5

    的二维数组表示,数组的每一行表示一个黑白块的位置参数。函数里只初始化了第一层图像的模板参数,后面其他组其他层的Harr模板参数都是用resizeHaarPattern这个函数来计算的。这个函数返回的是一个SurfHF的结构体,这个结构体由两个点及一个权重构成。

    struct SurfHF
    {
        int p0, p1, p2, p3;
        float w;
    
        SurfHF() : p0(0), p1(0), p2(0), p3(0), w(0) {}
    };

    resizeHaarPattern这个函数非常的巧妙,它把模板中的点坐标。转换到在积分图中的相对(模板左上角点)坐标。

    static void
    resizeHaarPattern(const int src[][5], SurfHF* dst, int n, int oldSize, int newSize, int widthStep)
    {
        float ratio = (float)newSize / oldSize;
        for (int k = 0; k < n; k++)
        {
            int dx1 = cvRound(ratio*src[k][0]);
            int dy1 = cvRound(ratio*src[k][1]);
            int dx2 = cvRound(ratio*src[k][2]);
            int dy2 = cvRound(ratio*src[k][3]);
            /*巧妙的坐标转换*/
            dst[k].p0 = dy1*widthStep + dx1; // 转换为一个相对距离,距离模板左上角点的  在积分图中的距离 !!important!!
            dst[k].p1 = dy2*widthStep + dx1; 
            dst[k].p2 = dy1*widthStep + dx2;
            dst[k].p3 = dy2*widthStep + dx2;
            dst[k].w = src[k][4] / ((float)(dx2 - dx1)*(dy2 - dy1));// 原来的+1,+2用 覆盖的所有像素点平均。
        }
    }

    在用积分图计算近似卷积时,用的是calcHaarPattern函数。这个函数比较简单,只用知道左上与右下角坐标即可。

    inline float calcHaarPattern(const int* origin, const SurfHF* f, int n)
    {
        /*orgin即为积分图,n为模板中 黑白 块的个数 */
        double d = 0;
        for (int k = 0; k < n; k++)
            d += (origin[f[k].p0] + origin[f[k].p3] - origin[f[k].p1] - origin[f[k].p2])*f[k].w;
        return (float)d;
    }

    最终我们可以看到了整个calcLayerDetAndTrack的代码

    static void calcLayerDetAndTrace(const Mat& sum, int size, int sampleStep,
        Mat& det, Mat& trace)
    {
        const int NX = 3, NY = 3, NXY = 4;
        const int dx_s[NX][5] = { { 0, 2, 3, 7, 1 }, { 3, 2, 6, 7, -2 }, { 6, 2, 9, 7, 1 } };
        const int dy_s[NY][5] = { { 2, 0, 7, 3, 1 }, { 2, 3, 7, 6, -2 }, { 2, 6, 7, 9, 1 } };
        const int dxy_s[NXY][5] = { { 1, 1, 4, 4, 1 }, { 5, 1, 8, 4, -1 }, { 1, 5, 4, 8, -1 }, { 5, 5, 8, 8, 1 } };
    
        SurfHF Dx[NX], Dy[NY], Dxy[NXY];
    
        if (size > sum.rows - 1 || size > sum.cols - 1)
            return;
        resizeHaarPattern(dx_s, Dx, NX, 9, size, sum.cols);
        resizeHaarPattern(dy_s, Dy, NY, 9, size, sum.cols);
        resizeHaarPattern(dxy_s, Dxy, NXY, 9, size, sum.cols);
    
        /* The integral image 'sum' is one pixel bigger than the source image */
        int samples_i = 1 + (sum.rows - 1 - size) / sampleStep; // 最大能遍历到的 行坐标,因为要减掉一个模板的尺寸
        int samples_j = 1 + (sum.cols - 1 - size) / sampleStep; // 最大能遍历到的 列坐标
    
        /* Ignore pixels where some of the kernel is outside the image */
        int margin = (size / 2) / sampleStep;
    
        for (int i = 0; i < samples_i; i++)
        {
            /*坐标为(i,j)的点是模板左上角的点,所以实际现在模板分析是的i+margin,j+margin点处的响应*/
            const int* sum_ptr = sum.ptr<int>(i*sampleStep);
            float* det_ptr = &det.at<float>(i + margin, margin); // 左边空隙为 margin
            float* trace_ptr = &trace.at<float>(i + margin, margin);
            for (int j = 0; j < samples_j; j++)
            {
                float dx = calcHaarPattern(sum_ptr, Dx, 3);
                float dy = calcHaarPattern(sum_ptr, Dy, 3);
                float dxy = calcHaarPattern(sum_ptr, Dxy, 4);
                sum_ptr += sampleStep;
                det_ptr[j] = dx*dy - 0.81f*dxy*dxy;
                trace_ptr[j] = dx + dy;
            }
        }
    }

    5.3 局部最大值搜索findMaximaInLayer

    这里算法思路很简单,值得注意的是里面的一些坐标的转换很巧妙,里面比较重的函数就是interpolateKeypoint函数,通过插值计算最大值点。

    /*
    * Maxima location interpolation as described in "Invariant Features from
    * Interest Point Groups" by Matthew Brown and David Lowe. This is performed by
    * fitting a 3D quadratic to a set of neighbouring samples.
    *
    * The gradient vector and Hessian matrix at the initial keypoint location are
    * approximated using central differences. The linear system Ax = b is then
    * solved, where A is the Hessian, b is the negative gradient, and x is the
    * offset of the interpolated maxima coordinates from the initial estimate.
    * This is equivalent to an iteration of Netwon's optimisation algorithm.
    *
    * N9 contains the samples in the 3x3x3 neighbourhood of the maxima
    * dx is the sampling step in x
    * dy is the sampling step in y
    * ds is the sampling step in size
    * point contains the keypoint coordinates and scale to be modified
    *
    * Return value is 1 if interpolation was successful, 0 on failure.
    */
    
    static int
    interpolateKeypoint(float N9[3][9], int dx, int dy, int ds, KeyPoint& kpt)
    {
        Vec3f b(-(N9[1][5] - N9[1][3]) / 2,  // Negative 1st deriv with respect to x
            -(N9[1][7] - N9[1][1]) / 2,  // Negative 1st deriv with respect to y
            -(N9[2][4] - N9[0][4]) / 2); // Negative 1st deriv with respect to s
    
        Matx33f A(
            N9[1][3] - 2 * N9[1][4] + N9[1][5],            // 2nd deriv x, x
            (N9[1][8] - N9[1][6] - N9[1][2] + N9[1][0]) / 4, // 2nd deriv x, y
            (N9[2][5] - N9[2][3] - N9[0][5] + N9[0][3]) / 4, // 2nd deriv x, s
            (N9[1][8] - N9[1][6] - N9[1][2] + N9[1][0]) / 4, // 2nd deriv x, y
            N9[1][1] - 2 * N9[1][4] + N9[1][7],            // 2nd deriv y, y
            (N9[2][7] - N9[2][1] - N9[0][7] + N9[0][1]) / 4, // 2nd deriv y, s
            (N9[2][5] - N9[2][3] - N9[0][5] + N9[0][3]) / 4, // 2nd deriv x, s
            (N9[2][7] - N9[2][1] - N9[0][7] + N9[0][1]) / 4, // 2nd deriv y, s
            N9[0][4] - 2 * N9[1][4] + N9[2][4]);           // 2nd deriv s, s
    
        Vec3f x = A.solve(b, DECOMP_LU);
    
        bool ok = (x[0] != 0 || x[1] != 0 || x[2] != 0) &&
            std::abs(x[0]) <= 1 && std::abs(x[1]) <= 1 && std::abs(x[2]) <= 1;
    
        if (ok)
        {
            kpt.pt.x += x[0] * dx;
            kpt.pt.y += x[1] * dy;
            kpt.size = (float)cvRound(kpt.size + x[2] * ds);
        }
        return ok;
    }
    
    static void findMaximaInLayer(const Mat& sum, const Mat& mask_sum,
        const vector<Mat>& dets, const vector<Mat>& traces,
        const vector<int>& sizes, vector<KeyPoint>& keypoints,
        int octave, int layer, float hessianThreshold, int sampleStep)
    {
        // Wavelet Data
        const int NM = 1;
        const int dm[NM][5] = { { 0, 0, 9, 9, 1 } };
        SurfHF Dm;
    
        int size = sizes[layer];
    
        // 当前层图像的大小
        int layer_rows = (sum.rows - 1) / sampleStep;
        int layer_cols = (sum.cols - 1) / sampleStep;
    
        // 边界区域大小,考虑的下一层的模板大小
        int margin = (sizes[layer + 1] / 2) / sampleStep + 1;
    
        if (!mask_sum.empty())
            resizeHaarPattern(dm, &Dm, NM, 9, size, mask_sum.cols);
    
        int step = (int)(dets[layer].step / dets[layer].elemSize());
    
        for (int i = margin; i < layer_rows - margin; i++)
        {
            const float* det_ptr = dets[layer].ptr<float>(i);
            const float* trace_ptr = traces[layer].ptr<float>(i);
            for (int j = margin; j < layer_cols - margin; j++)
            {
                float val0 = det_ptr[j]; // 中心点的值
                if (val0 > hessianThreshold)
                {
                    // 模板左上角的坐标
                    int sum_i = sampleStep*(i - (size / 2) / sampleStep);
                    int sum_j = sampleStep*(j - (size / 2) / sampleStep);
    
                    /* The 3x3x3 neighbouring samples around the maxima.
                    The maxima is included at N9[1][4] */
    
                    const float *det1 = &dets[layer - 1].at<float>(i, j);
                    const float *det2 = &dets[layer].at<float>(i, j);
                    const float *det3 = &dets[layer + 1].at<float>(i, j);
                    float N9[3][9] = { { det1[-step - 1], det1[-step], det1[-step + 1],
                        det1[-1], det1[0], det1[1],
                        det1[step - 1], det1[step], det1[step + 1] },
                        { det2[-step - 1], det2[-step], det2[-step + 1],
                        det2[-1], det2[0], det2[1],
                        det2[step - 1], det2[step], det2[step + 1] },
                        { det3[-step - 1], det3[-step], det3[-step + 1],
                        det3[-1], det3[0], det3[1],
                        det3[step - 1], det3[step], det3[step + 1] } };
    
                    /* Check the mask - why not just check the mask at the center of the wavelet? */
                    if (!mask_sum.empty())
                    {
                        const int* mask_ptr = &mask_sum.at<int>(sum_i, sum_j);
                        float mval = calcHaarPattern(mask_ptr, &Dm, 1);
                        if (mval < 0.5)
                            continue;
                    }
    
                    /* 检测val0,是否在N9里极大值,??为什么不检测极小值呢*/
                    if (val0 > N9[0][0] && val0 > N9[0][1] && val0 > N9[0][2] &&
                        val0 > N9[0][3] && val0 > N9[0][4] && val0 > N9[0][5] &&
                        val0 > N9[0][6] && val0 > N9[0][7] && val0 > N9[0][8] &&
                        val0 > N9[1][0] && val0 > N9[1][1] && val0 > N9[1][2] &&
                        val0 > N9[1][3] && val0 > N9[1][5] &&
                        val0 > N9[1][6] && val0 > N9[1][7] && val0 > N9[1][8] &&
                        val0 > N9[2][0] && val0 > N9[2][1] && val0 > N9[2][2] &&
                        val0 > N9[2][3] && val0 > N9[2][4] && val0 > N9[2][5] &&
                        val0 > N9[2][6] && val0 > N9[2][7] && val0 > N9[2][8])
                    {
                        /* Calculate the wavelet center coordinates for the maxima */
                        float center_i = sum_i + (size - 1)*0.5f;
                        float center_j = sum_j + (size - 1)*0.5f;
    
                        KeyPoint kpt(center_j, center_i, (float)sizes[layer],
                            -1, val0, octave, CV_SIGN(trace_ptr[j]));
    
                        /* 局部极大值插值,用Hessian,类似于SIFT里的插值,里面没有迭代5次,只进行了一次查找,why?  */
                        int ds = size - sizes[layer - 1];
                        int interp_ok = interpolateKeypoint(N9, sampleStep, sampleStep, ds, kpt);
    
                        /* Sometimes the interpolation step gives a negative size etc. */
                        if (interp_ok)
                        {
                            /*printf( "KeyPoint %f %f %d\n", point.pt.x, point.pt.y, point.size );*/
                            keypoints.push_back(kpt);
                        }
                    }
                }
            }
        }
    }

    6. 总结

    总体来说,如果理解了SIFT算法,再来看SURF算法会发现思路非常简单。尤其是局部最大值查找方面,基本一致。关键还是一个用积分图来简化卷积的思路,以及怎么用不同的模板来近似原来尺度空间中的高斯滤波器。

    这一篇主要讨论分析的是SURF的定位问题,下面还有SURF特征点的方向计算与描述子的生成,将在下一篇文章中详细描述。

    更多相关内容
  • surf算法原理(1)构建Hessian矩阵(2)尺度空间生成(3)初步确定特征点(4)精确定位特征点(5)选取特征点主方向确定(6)构造surf特征点描述算子surf算法特征点提取extract_surf.cpp data =read_png_u8_gray读出灰度图记做I...
  • Surf算法原理

    千次阅读 2018-03-13 21:07:15
    Surf算法原理:参考资料: Surf算法论文及实现源码作为尺度不变特征变换算法(Sift算法)的加速版,Surf算法在适中的条件下完成两幅图像中物体的匹配基本实现了实时处理,其快速的基础实际上只有一个——积分图像.....
    Surf算法是对Sift算法的一种改进,主要是在算法的执行效率上,比Sift算法来讲运行更快!
    1、算法原理;2、源码简析;3、OpenCV中Demo分析;4、一些关于Surf算法的剖析。

    Surf算法原理:

    作为尺度不变特征变换算法(Sift算法)的加速版,Surf算法在适中的条件下完成两幅图像中物体的匹配基本实现了实时处理,其快速的基础实际上只有一个——积分图像haar求导。我们先来看介绍Sift算法的基本过程,然后再介绍Surf算法。

    1、Sift算法简介

    Sift算法是David Lowe于1999年提出的局部特征描述子,并于2004年进行了更深入的发展和完善。Sift特征匹配算法可以处理两幅图像之间发生平移、旋转、仿射变换情况下的匹配问题,具有很强的匹配能力。总体来说,Sift算子具有以下特性:
    (1) 、Sift特征是图像的局部特征,对平移、旋转、尺度缩放、亮度变化、遮挡和噪声等具有良好的不变性,对视觉变化、仿射变换也保持一定程度的稳定性。
    (2) 、独特性好,信息量丰富,适用于在海量特征数据库中进行快速、准确的匹配。
    (3) 、多量性,即使少数的几个物体也可以产生大量Sift特征向量。
    (4) 、速度相对较快,经优化的Sift匹配算法甚至可以达到实时的要求。
    (5) 、可扩展性强,可以很方便的与其他形式的特征向量进行联合。
    其 Sift算法的三大工序为,(1)提取关键点;(2)对关键点附加详细的信息(局部特征)也就是所谓的描述器;(3)通过两方特征点(附带上特征向量的关键点)的两两比较找出相互匹配的若干对特征点,也就建立了景物间的对应关系。  提取关键点和  对关键点附加详细的信息(局部特征)也就是所谓的描述器 可以称做是Sift特征的生成,即从多幅图像中提取对尺度缩放、旋转、亮度变化无关的特征向量,Sift特征的生成一般包括以下几个步骤:
    (1) 、  构建尺度空间,检测极值点,获得尺度不变性;
    (2) 、  特征点过滤并进行精确定位;
    (3) 、  为特征点分配方向值;
    (4) 、  生成特征描述子。
    以特征点为中心取16*16的邻域作为采样窗口,将采样点与特征点的相对方向通过高斯加权后归入包含8个bin的方向直方图,最后获得4*4*8的128维特征描述子。示意图如下:
    当两幅图像的Sift特征向量生成以后,下一步就可以采用关键点特征向量的欧式距离来作为两幅图像中关键点的相似性判定度量。取图1的某个关键点,通过遍历找到图像2中的距离最近的两个关键点。在这两个关键点中,如果次近距离除以最近距离小于某个阙值,则判定为一对匹配点。

    2、Surf算法原理

    (1)、构建Hessian矩阵
    Hessian矩阵是Surf算法的核心,为了方便运算,假设函数f(z,y),Hessian矩阵H是由函数,偏导数组成:
    H矩阵判别式为:
    判别式的值是H矩阵的特征值,可以利用判定结果的符号将所有点分类,根据判别式取值正负,来判别该点是或不是极值点。在SURF算法中,用图像像素l(x,y)代替函数值f(x,y),选用二阶标准高斯函数作为滤波器,通过特定核间的卷积计算二阶偏导数,这样便能计算出H矩阵的三个矩阵元素L。、L。、k,从而计算出H矩阵:
    L。(X,£)是一幅图像在不同解析度下的表示,可以利用高斯核G(£)与图像函数,(X)在点X一(z,y)的卷积来实现,核函数G(£)具体表示如式(5),g(£)为高斯函数,t为高斯方差,L。与L。同理。通过这种方法可以为图像中每个像素计算出其H行列式的决定值,并用这个值来判别特征点。为方便应用,Herbert Bay提出用近似值现代替L。为平衡准确值与近似值间的误差引入权值叫,权值硼随尺度变化,则H矩阵判别式可表示为:
    (2)、构建尺度空间
    图像的尺度空间是这幅图像在不同解析度下的表示,由式(4)知,一幅图像j(X)在不同解析度下的表示可以利用高斯核G(£)的卷积来实现,图像的尺度大小一般用高斯标准差来表示[6]。在计算视觉领域,尺度空间被象征性的表述为一个图像金字塔,其中,输入图像函数反复与高斯函数的核卷积并反复对其进行二次抽样,这种方法主要用于Sift算法的实现,但每层图像依赖于前一层图像,并且图像需要重设尺寸,因此,这种计算方法运算量较大,而SURF算法申请增加图像核的尺寸,这也是SIFT算法与SURF算法在使用金字塔原理方面的不同。算法允许尺度空间多层图像同时被处理,不需对图像进行二次抽样,从而提高算法性能。图1(a)是传统方式建立一个如图所示的金字塔结构,图像的寸是变化的,并且运(1) 算会反复使用高斯函数对子层进行平滑处理,图1(b)说明Surf算法使原始图像保持不变而只改变滤波器大小。
    (3)、精确定位特征点
    所有小于预设极值的取值都被丢弃,增加极值使检测到的特征点数量减少,最终只有几个特征最强点会被检测出来。检测过程中使用与该尺度层图像解析度相对应大小的滤波器进行检测,以3×3的滤波器为例,该尺度层图像中9个像素点之一图2检测特征点与自身尺度层中其余8个点和在其之上及之下的两个尺度层9个点进行比较,共26个点,图2中标记‘x’的像素点的特征值若大于周围像素则可确定该点为该区域的特征点。
    (4)、主方向确定
    为保证旋转不变性[8I,首先以特征点为中心,计算半径为6s(S为特征点所在的尺度值)的邻域内的点在z、y
    方向的Haar小波(Haar小波边长取4s)响应,并给这些响应值赋高斯权重系数,使得靠近特征点的响应贡献大,而远离特征点的响应贡献小,其次将60。范围内的响应相加以形成新的矢量,遍历整个圆形区域,选择最长矢量的方向为该特征点的主方向。这样,通过特征点逐个进行计算,得到每一个特征点的主方向。

    (5)特征点描述子生成 
    首先将坐标轴旋转为关键点的方向,以确保旋转不变性。

    接下来以关键点为中心取8×8的窗口。图左部分的中央黑点为当前关键点的位置,每个小格代表关键点邻域所在尺度空间的一个像素,利用公式求得每个像素的梯度幅值与梯度方向,箭头方向代表该像素的梯度方向,箭头长度代表梯度模值,然后用高斯窗口对其进行加权运算,每个像素对应一个向量,长度为,为该像素点的高斯权值,方向为, 图中蓝色的圈代表高斯加权的范围(越靠近关键点的像素梯度方向信息贡献越大)。然后在每4×4的小块上计算8个方向的梯度方向直方图,绘制每个梯度方向的累加值,即可形成一个种子点,如图右部分示。此图中一个关键点由2×2共4个种子点组成,每个种子点有8个方向向量信息。这种邻域方向性信息联合的思想增强了算法抗噪声的能力,同时对于含有定位误差的特征匹配也提供了较好的容错性。

    3、结束语

    Sift/Surf采用Henssian矩阵获取图像局部最值还是十分稳定的,但是在求主方向阶段太过于依赖局部区域像素的梯度方向,有可能使得找到的主 方向不准确,后面的特征向量提取以及匹配都严重依赖于主方向,即使不大偏差角度也可以造成后面特征匹配的放大误差,从而匹配不成功;另外图像金字塔的层取 得不足够紧密也会使得尺度有误差,后面的特征向量提取同样依赖相应的尺度,发明者在这个问题上的折中解决方法是取适量的层然后进行插值。Sift是一种只 利用到灰度性质的算法,忽略了色彩信息,后面又出现了几种据说比Surf更稳定的描述器其中一些利用到了色彩信息,让我们拭目以待吧。

    展开全文
  • surf算法原理

    千次阅读 2017-07-06 11:17:01
    SURF算法简介:  SURF (Speeded Up Robust Features, 加速稳健特征) 是一个稳健的图像识别和描述算法,首先于2006年发表在ECCV大会上。这个算法可被用于计算机视觉任务,如物件识别和3D重构。他部分的灵感来自于 ...

    转自坚持奋斗的李洛克的博客

    SURF算法简介: 
    SURF (Speeded Up Robust Features, 加速稳健特征) 是一个稳健的图像识别和描述算法,首先于2006年发表在ECCV大会上。这个算法可被用于计算机视觉任务,如物件识别和3D重构。他部分的灵感来自于 SIFT 算法。SURF标准的版本比SIFT要快数倍,并且其作者声称在不同图像变换方面比SIFT更加稳健。

    SURF使用海森矩阵(Hessian)的行列式值作特征点响应侦测并用积分图加速运算;SURF 的描述子基于 2D 离散小波变换响应Harr小波并且有效地利用了积分图。

    SURF算法的概念及步骤均建立在SIFT之上,但详细的流程略有不同。SURF算法包含以下三个步骤:特征点侦测、特征邻近描述、描述子配对。

    1.特征点侦测:

    SIFT对原图像不断地进行Gauss平滑+降采样。得到金字塔图像后,有进一步得到了DoG图,边和斑状结构就是通过DoG图得到其在原图的位置。而高斯核的尺度因子即sigma是不同的。

    SURF使用了方型滤波器取代SIFT中的高斯滤波器,借此达到高斯糢糊的近似。图像大小保持不变,改变的是滤波器的大小。其滤波器可表示为:

    这里写图片描述

    此外使用方型滤波器可利用积分图大幅提高运算速度,仅需计算位于滤波器方型的四个角落値即可。

    SURF使用了斑点侦测的海森矩阵来侦测特征点,其行列式值代表相素点周围的变化量,因此特征点需取行列式值为极大、极小值。除此之外,为了达到尺度上的不变,SURF还使用了尺度σ的行列式值作特征点的侦测,给定图形中的一点x=(x, y),在尺度σ的海森矩阵为H(x, σ): 
    这里写图片描述

    使用近似的Hessian矩阵行列式来表示图像中某一点x处的斑点响应值,遍历图像中所有的像元点,便形成了在某一尺度下特征点检测的响应图像。使用不同的模板尺寸,便形成了多尺度特征点响应的金字塔图像,利用这一金字塔图像,就可以进行特征点响应极值点的搜索,其过程完全与SIFT算法类同。 
    作者为了加速卷积的速度,采用了方型滤波器(box filter)对高斯滤波器二阶偏导进行近似。 
    这里写图片描述这里写图片描述这里写图片描述 
    上图分别为X方向,Y方向,XY方向的近似模板。 
    这里写图片描述
    由SURF原作者得出w的值近似为0.9。

    2.尺度空间描述与特征点定位:

    1.特征点定位

    因为影像会在不同的尺度下作比较,特征点往往会在不同尺度下被侦测到。为特征点具有尺度不变性,在SIFT中,由于连续的高斯糢糊处理以及影像的降采样,在尺度上会形成类似金字塔的概念,例如高斯金字塔。

    而SURF中尺度是由方型滤波器的大小决定的,并不是连续的斯糢糊及降采样处理。其中最底层的尺度(初始尺度)的方型滤波器大小为9*9,近似于σ=1.2的高斯滤波器。越往上层的尺度滤波器的大小也就跟着增加,例如15*15、21*21、27*27……。

    这里写图片描述 
    即第一层的每组相隔6,第二层的每组相隔12,第三层的每组相隔24。。。 
    在通常尺度分析情况下,随着尺度的增大,被检测到的斑点数量迅速衰减。所以一般进行3-4组就可以了,与此同时,为了减少运算量,提高计算的速度,可以考虑在滤波时,将采样间隔设为2。 
    与SIFT相同,SURF会使用特征点海森矩阵的行列式值作邻近资料插补来定位特征点。

    为了在图像及不同尺寸中定位兴趣点,我们用了3×3×3邻域非最大值抑制:所有小于预设极值的取值都被丢弃,增加极值使检测到的特征点数量减少,最终只有几个特征最强点会被检测出来。该尺度层图像中9个像素点之一作为检测特征点与自身尺度层中其余8个点和在其之上及之下的两个尺度层9个点进行比较,共26个点,图中标记‘x’的像素点的特征值(hessian矩阵行列式)若大于周围像素则可确定该点为该区域的特征点。 
    这里写图片描述

    2.特征点描述

    为了特征点具有旋转不变的特性,需要赋予特征点一个描述子,使其能保有其不变性且能够轻易地被区分。大多数的描述子建立的方法为描述特征点与其相邻的相素点间的变化,因此描述子往往都是区域性的。

    同时描述子的维度也是描述子重要的考量之一,一个维度不足的描述子可能会使能特征点不易区分,然而维度过大的描述子要耗费的计算也就越复杂。SURF的描述子使用了哈尔小波转换的概念,并利用积分图简化描述子的计算。 
    方位定向: 
    以特征点为中心,计算半径为6s(S为特征点所在的尺度值)的圆形邻域内的点在x、y方向的Haar小波(Haar小波边长取4s)响应,Harr小波模板如图所示:

    这里写图片描述 
    分别为X,Y方向响应 
    计算出图像在哈尔小波的x和y方向上的响应值之后,对两个值进行因子为2S的高斯加权,加权后的值分别表示在水平和垂直方向上的方向分量。Harr特征值反应了图像灰度变化的情况,那么这个主方向就是描述那些灰度变化特别剧烈的区域方向。接着,以特征点为中心,张角为π/3的扇形滑动,计算窗口内的Harr小波响应值dx、dy的累加: 
    这里写图片描述 
    这里写图片描述 
    在所有的向量当中最长的(即x、y分量最大的)即为此特征点的方向。

    特征点特征矢量的生成 
    选定了特征点的方向后,其周围相素点需要以此方向为基准来建立描述子。以特征点为中心,构造一个20*20的正方形窗口(为特征点所在窗口的尺度值)。其后将将该窗口划分为更小的4X4个子区域,在每个子区域内按照5X5的大小进行取样,计算子区域内的x、y方向(此时以平行特征点方向为x、垂直特征点方向为y)的哈尔小波转换总合sum dx、sum dx与其向量长度总合sum |dx|、sum |dy|共四个量值,共可产生一个4X4X4=64维资料的描述子。

    3.匹配

    为了加速匹配过程,作者借助Laplacian(比如Hessian矩阵的迹)的符号使匹配过程索引加快。

    相关知识点简介附: 
    对于OPENCV,opencvsurf和opencv中的surf是不同的,这点要清楚。否则网上有很多surf源码分析,让人摸不着头脑。 
    opencv中的surf类继承结构如下: 
    这里写图片描述
    积分图解释如下: 
    这里写图片描述

    展开全文
  • opencv之SURF算法原理及关键点检测

    万次阅读 多人点赞 2017-04-18 10:51:54
    基于特征匹配的方法有很多种如:FAST、HARRIS、SIFT、SURF、SUSAN等。其中SIFT算法由D.G.Lowe于1999年提出,2004年完善总结。SIFT是一种鲁棒性好的尺度不变特征描述方法,但SIFT算法计算数据量大、时间复杂度高、...

    1.概述

    在基础篇里面讲模板匹配的时候已经介绍过,图像匹配主要有基于灰度和基于特征两种方法。基于特征匹配的方法有很多种如:FAST、HARRIS、SIFT、SURF、SUSAN等。其中SIFT算法由D.G.Lowe于1999年提出,2004年完善总结。SIFT是一种鲁棒性好的尺度不变特征描述方法,但SIFT算法计算数据量大、时间复杂度高、算法耗时长。针对上述缺点许多研究者对SIFT算法做了不同的改进,Yanke等人提出用PCA-SIFT方法对特征描述进行数据降维,但在没有任何先验知识的情况下反而增加了计算量;Delpont等人提出用SVD方法进行特征匹配,但匹配过程计算复杂,且不能用于宽基线匹配;Grabner等人用积分图像虽提高了SIFT的计算速度,但是降低了SIFT方法的优越性。
    Herbert Bay等人于2006年提出了SIFT算法的改进算法SURF算法,其性能超过了SIFT算法且能够获得更快的速度。SURF在光照变化和视角变化不变性方面的性能接近SIFT算法,尤其对图像严重模糊和旋转处理得非常好。且标准的SURF算子比SIFT算子快好几倍,SURF算法最大的特征在于采用了harr特征以及积分图像的概念,这大大加快了程序的运行速度。
    SURF算法和SIFT算法在opencv中是一种很高级的算法,opencv提供了SURF算法的API接口。需要说明的是SURF和SIFT算法在OpenCV提供的nonfree中,而且在OpenCV3.x后不再集成到OpenCV发行版中,同意存放到opencv_contrib中,需要去opencv github页面手动下载编译。点击跳转,编译方法网上有很多教程。

    2.Surf原理

    特征点的提取基于尺度空间理论,我们通过检测图像局部极值点来锁定特征点坐标,即局部的最亮点或最暗点。

    2.1构建Hessian矩阵

    SURF算法检测特征点是基于Hessian矩阵实现的,Hessian矩阵是SURF算法的核心。设f(x,y)为二阶可微函数,Hessian矩阵H是函数、偏导数组成如下:
    这里写图片描述
    Hessian矩阵判别式为:
    这里写图片描述
    判别式的值是H矩阵的特征值,可以利用判别式的符号确定是否是极值,若det⁡(H)<0则可判断(x, y)不是局部极值点,若det⁡(H)>0则可判断点(x, y)为局部极值点。
    将上述方法应用到图像中,给出图像中的一个点,其像素可表示为I(x, y),在尺度为σ其Hessian矩阵定义如下:
    这里写图片描述
    式中:L_xx是高斯滤波二阶导这里写图片描述同I=(x,y)卷积的结果,其中这里写图片描述L_xy、L_yy的含义类似。
    空间尺度理论中高斯是最优化的分析方法。Bay等人指出高斯分析需要对图像进行离散化和裁剪,即使使用高斯滤波对图像进行采样也会出现走样的情况。所以可以使用方框滤波来代替高斯滤波,使用积分图像来加快卷积以提高计算速度。
    在原始图像上,使用方框滤波器的效果反映在掩膜版尺寸上。如图所示为9×9方框滤波掩膜版,其中灰色部分掩膜版值为0.对应二阶高斯滤波系数σ=1.2,方框滤波模板同图像卷积运算后的值记分别记为D_xx,D_yy,D_xy。
    这里写图片描述
    为平衡准确值与近似值间的误差引入权值,权值随尺度变化,H矩阵判别式可以表示为:
    这里写图片描述
    由于高斯滤波与近似高斯滤波的差异性,我们用根据公式这里写图片描述计算滤波响应的相对权重系数进一步平衡Hessian矩阵行列式的相对权重,其中|��|��是Frobenius范数,这样就保证了Frobenius范数能够适用于任何尺寸的滤波器模板。在实际应用中,用常量0.9表示其相对权重系数不会对结果产生较大的影响。

    2.2尺度不变性

    为了使图像具有尺度不变性以适应不同的图像中目标尺度的变化,我们需要构建尺度空间进行SURF特征点的提取。图像金字塔是图像多尺度表达的一种方式,为了获取图像在不同尺度下通过Hessian矩阵判别式得到极值点,用类似SIFT的方法构建尺度图像金字塔,将尺度空间分为若干阶(octave),每一阶存储了不同尺寸的方框滤波对输入图像进行滤波后得到的模糊程度不同的图片。但SURF算法中图片大小是一直不变的,只是不同阶中方框滤波模板大小不相同。在每一阶中选择4层的尺度图像,构建参数如图所示:
    这里写图片描述
    灰色底的数字表示方框滤波模板的大小。如果图像尺寸远大于模板大小,还可以继续增加阶数。若模板尺寸为N×N,则该模板对应的尺度为σ=1.2×9/N。通过Hessian矩阵求出个尺度极值后,在3×3×3的立体邻域内进行非极大值比较,若该极值点仍为最大值或最小值,则该极值点为候选特征点,然后在尺度空间和图像空间中进行插值运算,得到稳定的特征点位置及所在的尺度值。

    2.3特征点主方向选取

    SIFT算法选取特征点主方向是采用在特征点领域内统计其梯度直方图,取直方图bin值最大的以及超过最大bin值80%的那些方向作为特征点的主方向。而SURF算法是通过统计特征点领域内的Haar小波特征确定其主方向。
    这里写图片描述
    为保证旋转不变性,以特征点为中心,计算特征点邻域(如半径为6s的圆,s为该点所在尺度)内的点在x,y方向的Haar小波响应,Haar小波边长取4s,这样一个扇形得到了一个值。然后60°扇形以一定间隔进行旋转,将60°范围内的响应相加以形成新的矢量,遍历整个圆形区域,选择最长矢量的方向为该特征点的主方向。

    2.4特征点描述算子

    SURF算法中,以特征点为中心,将坐标轴旋转到主方向以确保旋转不变性。按照主方向选取边长为20s的正方形区域,然后将该区域划分为4×4的子区域,每个子区域计算5s×5s范围内的小波响应。
    这里写图片描述
    相对于主方向的水平、垂直方向的Haar小波响应分别记做d_x 、d_y,同样赋予响应值以权值系数,以增加对集合变换的鲁棒性;之后将每个子区域的响应及其绝对值相加形成这里写图片描述,这样在每个子区域形成四维分量的矢量这里写图片描述,因此对每一个特征点,则形成64维的描述向量,再进行向量的归一化,从而对光照具有一定的鲁棒性。

    3.OpenCV API

    在opencv中可以看到如下定义

    typedef SURF    cv::xfeatures2d::SurfDescriptorExtractor
    
    typedef SURF    cv::xfeatures2d::SurfFeatureDetector
    

    也就是说在我们实际使用中是根据不同的功能分别调用SurfDescriptorExtractor和SurfFeatureDetector两个函数的,同样SIFT算法也有类似定义如下:

    typedef SIFT    cv::xfeatures2d::SiftDescriptorExtractor
    
    typedef SIFT    cv::xfeatures2d::SiftFeatureDetector
    

    有需要使用SIFT算法的同学可以参考下。重点说一下SURF算法的两个函数。
    SURF算法作为一个大类,其继承关系可参照下图:
    这里写图片描述
    其成员函数有很多,如下:

    virtual bool    getExtended () const =0
    
    virtual double  getHessianThreshold () const =0
    
    virtual int     getNOctaveLayers () const =0
    
    virtual int     getNOctaves () const =0
    
    virtual bool    getUpright () const =0
    
    virtual void    setExtended (bool extended)=0
    
    virtual void    setHessianThreshold (double hessianThreshold)=0
    
    virtual void    setNOctaveLayers (int nOctaveLayers)=0
    
    virtual void    setNOctaves (int nOctaves)=0
    
    virtual void    setUpright (bool upright)=0
    

    函数详细含义可以查询OpenCV文档得知。这里介绍一下特征点的检测。
    利用SURF算法进行特征点检测可以使用SurfFeatureDetector及它的子函数detect(位于其父类Feature2D中)来实现检测过程,使用drawKeypoints函数绘制检测到的关键点。
    drawKeypoints

    void cv::drawKeypoints  (   InputArray  image,
                                const std::vector< KeyPoint > &     keypoints,
                                InputOutputArray    outImage,
                                const Scalar &  color = Scalar::all(-1),
                                int     flags = DrawMatchesFlags::DEFAULT 
                            )
    

    image:输入图像
    **keyPoint:**SURF算法检测到的特征点
    outImage:输出图像,其内容取决于第五个参数标识符
    color:绘制特征点颜色,有默认值Scalar::all(-1)
    flags:绘制特征点的特征标识符,有默认值,有如下方式:

    enum    { 
                DEFAULT = 0, //创建输出图像矩阵,使用现存的输出图像绘制匹配对和特征点,对每一个关键点只绘制中间点
                DRAW_OVER_OUTIMG = 1,//不创建输出图像矩阵,而是在输出图像上绘制匹配对 
                NOT_DRAW_SINGLE_POINTS = 2, //单点特征点不被绘制 
                DRAW_RICH_KEYPOINTS = 4 //对每一个特征点绘制带大小和方向的关键点图形。
            }
    

    这里有必要说一下KeyPoint()类,是一个为特征点检测而形成的数据结构,用于表示特征点,其类结构如下:

    cv::KeyPoint::KeyPoint  (   Point2f     _pt,
                                float   _size,
                                float   _angle = -1,
                                float   _response = 0,
                                int     _octave = 0,
                                int     _class_id = -1 
                            )   
    

    _pt:特征点坐标
    _size特征点直径
    _angle:特征点方向,范围为[0,360),负值表示不使用
    _response:关键点检测器对于关键点的响应程度,也就是关键点强度
    _octave:特征点所在金字塔的层
    _class_id:用于聚类的id
    函数还有另外一种定义形式如下:

    cv::KeyPoint::KeyPoint  (   float   x,
                                float   y,
                                float   _size,
                                float   _angle = -1,
                                float   _response = 0,
                                int     _octave = 0,
                                int     _class_id = -1 
                            )
    

    4.示例代码

    #include <iostream>
    #include <opencv2/core/core.hpp>
    #include <opencv2/highgui/highgui.hpp>
    #include <opencv2/nonfree/nonfree.hpp>
    #include <opencv2/imgproc/imgproc.hpp>
    
    using namespace std;
    using namespace cv;
    
    int main()
    {
        Mat srcImage = imread("Surf_test_image.jpg");
    
        //判断图像是否读取成功
        if (srcImage.empty())
        {
            cout << "图像加载失败" << endl;
            return -1;
        } 
        else
        {
            cout << "图像加载成功" << endl << endl;
        }
    
        namedWindow("原图像",WINDOW_AUTOSIZE);
        imshow("原图像",srcImage);
    
        Mat imageMid;   //定义滤波后图像
        //GaussianBlur(srcImage,imageMid,Size(9, 9),0,0);       //kernel尺寸为3x3的高斯滤波
        //namedWindow("高斯滤波后图像",WINDOW_AUTOSIZE);
        //imshow("高斯滤波后图像",imageMid);
    
        int minHessian = 700;                           //定义Hessian矩阵阈值特征点检测算子
        SurfFeatureDetector detector(minHessian);       //定义SURF检测器
    
        vector<KeyPoint> keypoints;                     //定义KeyPoint类型的矢量容器vector存储检测到的特征点
        detector.detect(srcImage,keypoints);            //调用detect检测特征点
    
        //绘制检测到的特征点
        Mat dstImage;
        //drawKeypoints(imageMid,keypoints,dstImage,Scalar::all(-1),DrawMatchesFlags::DEFAULT); //高斯滤波后关键点检测
        drawKeypoints(srcImage, keypoints, dstImage, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
    
        namedWindow("特征点检测",WINDOW_AUTOSIZE);
        imshow("特征点检测",dstImage);
    
    
        waitKey(0);
    
        return 0;
    }

    运行结果这里写图片描述

    展开全文
  • SURF算法原理及代码实现(笔记)

    千次阅读 2019-04-14 20:52:55
    与Sift算法一样,Surf算法的基本路程可以分为三大部分:局部特征点的提取、特征点的描述、特征点的匹配。但Surf在执行效率上有两大制胜法宝——一个是积分图在Hessian(黑塞矩阵)上的使用,一个是降维的特征描述子...
  • surf算法原理-包你明白surf过程

    万次阅读 多人点赞 2017-10-10 18:23:00
    SURF 算法,全称是 Speeded-Up Robust Features。该算子在保持 SIFT 算子优良性能特点的基础上,同时解决了 SIFT 计算复杂度高、耗时长的缺点,对兴趣点提取及其特征向量描述方面进行了改进,且计算速度得到提高。 ...
  • 基于C#平台开发的surf算法,可直接运行查看结果。较为方便的可以转为C++版本
  • SURF算法原理

    2019-10-24 08:39:13
    1、先对图像进行高斯滤波 2、选用二阶标准高斯函数作为滤波器,通过特定核间的卷积计算二阶偏导数,计算出Hessian矩阵 3、计算判别式的值,根据判别式取值的正负,来判断该点...7、构造surf特征点的描述算子 ...
  • 本文介绍了python opencv之SURF算法示例,分享给大家,具体如下: 目标: SURF算法基础 opencv总SURF算法的使用 原理: 上节课使用了SIFT算法,当时这种算法效率不高,需要更快速的算法。在06年有人提出了SURF算法...
  • SURF算法

    万次阅读 多人点赞 2019-01-18 20:23:05
    SURF SURF(Speeded Up Robust Features)是对SIFT的一种改进,主要特点是快速。SURF与SIFT主要有以下几点不同处理: 1、 SIFT在构造DOG金字塔以及求DOG局部空间极值比较耗时,SURF的改进是使用Hessian矩阵变换...
  • 手把手教你理解SURF算法的全部过程

    千次阅读 2021-01-26 16:08:32
    首先介绍下SURF算法的流程,具体原理后面再介绍 一.对原图像进行积分图像处理 我们知道原始图像h(x,y)是二维空间的离散点,值的大小是每个像素点的灰度值。转化为积分图像的方法g(x)的方法是,g(x,y) 对应 以f(1,1)...
  • SURF的C#算法

    热门讨论 2012-09-26 20:58:34
    SURF算法是一种高效的图像匹配算法,本资源通过控制台应用程序的形式实现
  • Surf算法原理及openCV代码实现

    千次阅读 2015-05-01 16:46:21
     本文转自:http://blog.csdn.net/yangtrees/article/details/7482960 Surf(Speed Up Robust Feature) Surf算法原理 1.构建Hessia
  • C# vscode OpenCvSharp surf 算法 直接上代码 public static Mat MatchPicBySurf(Mat imgSrc, Mat imgSub, double threshold = 400) { var outMat = new Mat(); Mat matSrc = imgSrc; Mat matTo = imgSub; Mat...
  • SURF特征提取算法详解

    2019-11-18 14:18:55
    上一节我们已经介绍了SIFT算法,SIFT算法对旋转、尺度缩放、亮度变化等保持不变性,对视角变换、仿射变化、噪声也保持一定...SURF(Speeded Up Robust Features)算法改进了特征了提取和描述方式,用一种更为高效的方式...
  • Surf——算法原理

    万次阅读 2012-08-09 10:03:57
    Surf算法原理: 参考资料:Surf算法论文及实现源码 作为尺度不变特征变换算法(Sift算法)的加速版,Surf算法在适中的条件下完成两幅图像中物体的匹配基本实现了实时处理,其快速的基础实际上只有一
  • Surf算法学习心得(一)——算法原理

    万次阅读 2014-10-09 11:47:38
    Surf算法是对Sift算法的一种改进,主要是在算法的执行效率上,比Sift算法来讲运行更快!由于我也是初学者,刚刚才开始研究这个算法,然而网上对于Surf算法的资料又尤为极少,...1、算法原理;2、源码简析;3、OpenCV
  • SIFT与SURF算法

    千次阅读 2020-09-22 20:57:21
    SIFT与SURF算法 1. SIFT与SURF的特征 SIFT即尺度不变特征变换,是用于图像处理领域的一种描述。这种描述具有尺度不变性,可在图像中检测出关键点,是一种局部特征描述子。 Sift算法的优点是特征稳定,对旋转、尺度...
  • SURF原理总结

    千次阅读 2017-01-07 11:16:55
    如果说SIFT算法中使用DOG对LOG进行了简化,提高了搜索特征点的速度,那么SURF算法则是对DoH的简化与近似。虽然SIFT算法已经被认为是最有效的,也是最常用的特征点提取的算法,但如果不借助于硬件的加速和专用图像...
  • 基于SURF算法的图像拼接处理,运用RANSAC算法,单向性等原理进行图像拼接
  • SURF算法实现

    2015-04-25 16:03:49
    surf算法 里面有原理基本介绍和源代码实现
  • SURF算法之Opencv代码详解

    千次阅读 2022-01-20 16:03:16
    这篇博客详细介绍了SURF特征点检测算法的OpenCV实现代码

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,496
精华内容 1,398
关键字:

surf算法原理

友情链接: Copy of A4_DN.rar