zoukankan      html  css  js  c++  java
  • OpenCV Sift源码分析

    sift原理图:

    sift的几个函数:

    buildGaussianPyramid

    buildDoGPyramid

    calcOrientationHist

    adjustLocalExtrema

    findScaleSpaceExtrema

    calcSIFTDescriptor

    calcDescriptors

    继承自feature2d

     

     

     

    代码:

    /*M///////////////////////////////////////////////////////////////////////////////////////
    //
    // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
    //
    // By downloading, copying, installing or using the software you agree to this license.
    // If you do not agree to this license, do not download, install,
    // copy or use the software.
    //
    //
    // License Agreement
    // For Open Source Computer Vision Library
    //
    // Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
    // Copyright (C) 2009, Willow Garage Inc., all rights reserved.
    // Third party copyrights are property of their respective owners.
    //
    // Redistribution and use in source and binary forms, with or without modification,
    // are permitted provided that the following conditions are met:
    //
    // * Redistribution's of source code must retain the above copyright notice,
    // this list of conditions and the following disclaimer.
    //
    // * Redistribution's in binary form must reproduce the above copyright notice,
    // this list of conditions and the following disclaimer in the documentation
    // and/or other materials provided with the distribution.
    //
    // * The name of the copyright holders may not be used to endorse or promote products
    // derived from this software without specific prior written permission.
    //
    // This software is provided by the copyright holders and contributors "as is" and
    // any express or implied warranties, including, but not limited to, the implied
    // warranties of merchantability and fitness for a particular purpose are disclaimed.
    // In no event shall the Intel Corporation or contributors be liable for any direct,
    // indirect, incidental, special, exemplary, or consequential damages
    // (including, but not limited to, procurement of substitute goods or services;
    // loss of use, data, or profits; or business interruption) however caused
    // and on any theory of liability, whether in contract, strict liability,
    // or tort (including negligence or otherwise) arising in any way out of
    // the use of this software, even if advised of the possibility of such damage.
    //
    //M*/

    /**********************************************************************************************
    Implementation of SIFT is based on the code from http://blogs.oregonstate.edu/hess/code/sift/
    Below is the original copyright.

    // Copyright (c) 2006-2010, Rob Hess <hess@eecs.oregonstate.edu>
    // All rights reserved.

    // The following patent has been issued for methods embodied in this
    // software: "Method and apparatus for identifying scale invariant features
    // in an image and use of same for locating an object in an image," David
    // G. Lowe, US Patent 6,711,293 (March 23, 2004). Provisional application
    // filed March 8, 1999. Asignee: The University of British Columbia. For
    // further details, contact David Lowe (lowe@cs.ubc.ca) or the
    // University-Industry Liaison Office of the University of British
    // Columbia.

    // Note that restrictions imposed by this patent (and possibly others)
    // exist independently of and may be in conflict with the freedoms granted
    // in this license, which refers to copyright of the program, not patents
    // for any methods that it implements. Both copyright and patent law must
    // be obeyed to legally use and redistribute this program and it is not the
    // purpose of this license to induce you to infringe any patents or other
    // property right claims or to contest validity of any such claims. If you
    // redistribute or use the program, then this license merely protects you
    // from committing copyright infringement. It does not protect you from
    // committing patent infringement. So, before you do anything with this
    // program, make sure that you have permission to do so not merely in terms
    // of copyright, but also in terms of patent law.

    // Please note that this license is not to be understood as a guarantee
    // either. If you use the program according to this license, but in
    // conflict with patent law, it does not mean that the licensor will refund
    // you for any losses that you incur if you are sued for your patent
    // infringement.

    // Redistribution and use in source and binary forms, with or without
    // modification, are permitted provided that the following conditions are
    // met:
    // * Redistributions of source code must retain the above copyright and
    // patent notices, this list of conditions and the following
    // disclaimer.
    // * Redistributions in binary form must reproduce the above copyright
    // notice, this list of conditions and the following disclaimer in
    // the documentation and/or other materials provided with the
    // distribution.
    // * Neither the name of Oregon State University nor the names of its
    // contributors may be used to endorse or promote products derived
    // from this software without specific prior written permission.

    // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
    // IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
    // TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
    // PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    // HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
    // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
    // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
    // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
    // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    **********************************************************************************************/

    #include "precomp.hpp"
    #include <iostream>
    #include <stdarg.h>

    namespace cv
    {

    /******************************* Defs and macros *****************************/

    // default number of sampled intervals per octave
    static const int SIFT_INTVLS = 3;

    // default sigma for initial gaussian smoothing
    static const float SIFT_SIGMA = 1.6f;

    // default threshold on keypoint contrast |D(x)|
    static const float SIFT_CONTR_THR = 0.04f;

    // default threshold on keypoint ratio of principle curvatures
    static const float SIFT_CURV_THR = 10.f;

    // double image size before pyramid construction?
    static const bool SIFT_IMG_DBL = true;

    // default width of descriptor histogram array
    static const int SIFT_DESCR_WIDTH = 4;

    // default number of bins per histogram in descriptor array
    static const int SIFT_DESCR_HIST_BINS = 8;

    // assumed gaussian blur for input image
    static const float SIFT_INIT_SIGMA = 0.5f;

    // width of border in which to ignore keypoints
    static const int SIFT_IMG_BORDER = 5;

    // maximum steps of keypoint interpolation before failure
    static const int SIFT_MAX_INTERP_STEPS = 5;

    // default number of bins in histogram for orientation assignment
    static const int SIFT_ORI_HIST_BINS = 36;

    // determines gaussian sigma for orientation assignment
    static const float SIFT_ORI_SIG_FCTR = 1.5f;

    // determines the radius of the region used in orientation assignment
    static const float SIFT_ORI_RADIUS = 3 * SIFT_ORI_SIG_FCTR;

    // orientation magnitude relative to max that results in new feature
    static const float SIFT_ORI_PEAK_RATIO = 0.8f;

    // determines the size of a single descriptor orientation histogram
    static const float SIFT_DESCR_SCL_FCTR = 3.f;

    // threshold on magnitude of elements of descriptor vector
    static const float SIFT_DESCR_MAG_THR = 0.2f;

    // factor used to convert floating-point descriptor to unsigned char
    static const float SIFT_INT_DESCR_FCTR = 512.f;

    #if 0
    // intermediate type used for DoG pyramids
    typedef short sift_wt;
    static const int SIFT_FIXPT_SCALE = 48;
    #else
    // intermediate type used for DoG pyramids
    typedef float sift_wt;
    static const int SIFT_FIXPT_SCALE = 1;
    #endif

    static inline void
    unpackOctave(const KeyPoint& kpt, int& octave, int& layer, float& scale)
    {
    octave = kpt.octave & 255;
    layer = (kpt.octave >> 8) & 255;
    octave = octave < 128 ? octave : (-128 | octave);
    scale = octave >= 0 ? 1.f/(1 << octave) : (float)(1 << -octave);
    }

    static Mat createInitialImage( const Mat& img, bool doubleImageSize, float sigma )
    {
    Mat gray, gray_fpt;
    if( img.channels() == 3 || img.channels() == 4 )
    cvtColor(img, gray, COLOR_BGR2GRAY);
    else
    img.copyTo(gray);
    gray.convertTo(gray_fpt, DataType<sift_wt>::type, SIFT_FIXPT_SCALE, 0);

    float sig_diff;

    if( doubleImageSize )
    {
    sig_diff = sqrtf( std::max(sigma * sigma - SIFT_INIT_SIGMA * SIFT_INIT_SIGMA * 4, 0.01f) );
    Mat dbl;
    resize(gray_fpt, dbl, Size(gray.cols*2, gray.rows*2), 0, 0, INTER_LINEAR);
    GaussianBlur(dbl, dbl, Size(), sig_diff, sig_diff);
    return dbl;
    }
    else
    {
    sig_diff = sqrtf( std::max(sigma * sigma - SIFT_INIT_SIGMA * SIFT_INIT_SIGMA, 0.01f) );
    GaussianBlur(gray_fpt, gray_fpt, Size(), sig_diff, sig_diff);
    return gray_fpt;
    }
    }


    void SIFT::buildGaussianPyramid( const Mat& base, vector<Mat>& pyr, int nOctaves ) const
    {
    vector<double> sig(nOctaveLayers + 3);
    pyr.resize(nOctaves*(nOctaveLayers + 3));

    // precompute Gaussian sigmas using the following formula:
    // sigma_{total}^2 = sigma_{i}^2 + sigma_{i-1}^2
    sig[0] = sigma;
    double k = pow( 2., 1. / nOctaveLayers );
    for( int i = 1; i < nOctaveLayers + 3; i++ )
    {
    double sig_prev = pow(k, (double)(i-1))*sigma;
    double sig_total = sig_prev*k;
    sig[i] = std::sqrt(sig_total*sig_total - sig_prev*sig_prev);
    }

    for( int o = 0; o < nOctaves; o++ )
    {
    for( int i = 0; i < nOctaveLayers + 3; i++ )
    {
    Mat& dst = pyr[o*(nOctaveLayers + 3) + i];
    if( o == 0 && i == 0 )
    dst = base;
    // base of new octave is halved image from end of previous octave
    else if( i == 0 )
    {
    const Mat& src = pyr[(o-1)*(nOctaveLayers + 3) + nOctaveLayers];
    resize(src, dst, Size(src.cols/2, src.rows/2),
    0, 0, INTER_NEAREST);
    }
    else
    {
    const Mat& src = pyr[o*(nOctaveLayers + 3) + i-1];
    GaussianBlur(src, dst, Size(), sig[i], sig[i]);
    }
    }
    }
    }


    void SIFT::buildDoGPyramid( const vector<Mat>& gpyr, vector<Mat>& dogpyr ) const
    {
    int nOctaves = (int)gpyr.size()/(nOctaveLayers + 3);
    dogpyr.resize( nOctaves*(nOctaveLayers + 2) );

    for( int o = 0; o < nOctaves; o++ )
    {
    for( int i = 0; i < nOctaveLayers + 2; i++ )
    {
    const Mat& src1 = gpyr[o*(nOctaveLayers + 3) + i];
    const Mat& src2 = gpyr[o*(nOctaveLayers + 3) + i + 1];
    Mat& dst = dogpyr[o*(nOctaveLayers + 2) + i];
    subtract(src2, src1, dst, noArray(), DataType<sift_wt>::type);
    }
    }
    }


    // Computes a gradient orientation histogram at a specified pixel
    static float calcOrientationHist( const Mat& img, Point pt, int radius,
    float sigma, float* hist, int n )
    {
    int i, j, k, len = (radius*2+1)*(radius*2+1);

    float expf_scale = -1.f/(2.f * sigma * sigma);
    AutoBuffer<float> buf(len*4 + n+4);
    float *X = buf, *Y = X + len, *Mag = X, *Ori = Y + len, *W = Ori + len;
    float* temphist = W + len + 2;

    for( i = 0; i < n; i++ )
    temphist[i] = 0.f;

    for( i = -radius, k = 0; i <= radius; i++ )
    {
    int y = pt.y + i;
    if( y <= 0 || y >= img.rows - 1 )
    continue;
    for( j = -radius; j <= radius; j++ )
    {
    int x = pt.x + j;
    if( x <= 0 || x >= img.cols - 1 )
    continue;

    float dx = (float)(img.at<sift_wt>(y, x+1) - img.at<sift_wt>(y, x-1));
    float dy = (float)(img.at<sift_wt>(y-1, x) - img.at<sift_wt>(y+1, x));

    X[k] = dx; Y[k] = dy; W[k] = (i*i + j*j)*expf_scale;
    k++;
    }
    }

    len = k;

    // compute gradient values, orientations and the weights over the pixel neighborhood
    exp(W, W, len);
    fastAtan2(Y, X, Ori, len, true);
    magnitude(X, Y, Mag, len);

    for( k = 0; k < len; k++ )
    {
    int bin = cvRound((n/360.f)*Ori[k]);
    if( bin >= n )
    bin -= n;
    if( bin < 0 )
    bin += n;
    temphist[bin] += W[k]*Mag[k];
    }

    // smooth the histogram
    temphist[-1] = temphist[n-1];
    temphist[-2] = temphist[n-2];
    temphist[n] = temphist[0];
    temphist[n+1] = temphist[1];
    for( i = 0; i < n; i++ )
    {
    hist[i] = (temphist[i-2] + temphist[i+2])*(1.f/16.f) +
    (temphist[i-1] + temphist[i+1])*(4.f/16.f) +
    temphist[i]*(6.f/16.f);
    }

    float maxval = hist[0];
    for( i = 1; i < n; i++ )
    maxval = std::max(maxval, hist[i]);

    return maxval;
    }


    //
    // Interpolates a scale-space extremum's location and scale to subpixel
    // accuracy to form an image feature. Rejects features with low contrast.
    // Based on Section 4 of Lowe's paper.
    static bool adjustLocalExtrema( const vector<Mat>& dog_pyr, KeyPoint& kpt, int octv,
    int& layer, int& r, int& c, int nOctaveLayers,
    float contrastThreshold, float edgeThreshold, float sigma )
    {
    const float img_scale = 1.f/(255*SIFT_FIXPT_SCALE);
    const float deriv_scale = img_scale*0.5f;
    const float second_deriv_scale = img_scale;
    const float cross_deriv_scale = img_scale*0.25f;

    float xi=0, xr=0, xc=0, contr=0;
    int i = 0;

    for( ; i < SIFT_MAX_INTERP_STEPS; i++ )
    {
    int idx = octv*(nOctaveLayers+2) + layer;
    const Mat& img = dog_pyr[idx];
    const Mat& prev = dog_pyr[idx-1];
    const Mat& next = dog_pyr[idx+1];

    Vec3f dD((img.at<sift_wt>(r, c+1) - img.at<sift_wt>(r, c-1))*deriv_scale,
    (img.at<sift_wt>(r+1, c) - img.at<sift_wt>(r-1, c))*deriv_scale,
    (next.at<sift_wt>(r, c) - prev.at<sift_wt>(r, c))*deriv_scale);

    float v2 = (float)img.at<sift_wt>(r, c)*2;
    float dxx = (img.at<sift_wt>(r, c+1) + img.at<sift_wt>(r, c-1) - v2)*second_deriv_scale;
    float dyy = (img.at<sift_wt>(r+1, c) + img.at<sift_wt>(r-1, c) - v2)*second_deriv_scale;
    float dss = (next.at<sift_wt>(r, c) + prev.at<sift_wt>(r, c) - v2)*second_deriv_scale;
    float dxy = (img.at<sift_wt>(r+1, c+1) - img.at<sift_wt>(r+1, c-1) -
    img.at<sift_wt>(r-1, c+1) + img.at<sift_wt>(r-1, c-1))*cross_deriv_scale;
    float dxs = (next.at<sift_wt>(r, c+1) - next.at<sift_wt>(r, c-1) -
    prev.at<sift_wt>(r, c+1) + prev.at<sift_wt>(r, c-1))*cross_deriv_scale;
    float dys = (next.at<sift_wt>(r+1, c) - next.at<sift_wt>(r-1, c) -
    prev.at<sift_wt>(r+1, c) + prev.at<sift_wt>(r-1, c))*cross_deriv_scale;

    Matx33f H(dxx, dxy, dxs,
    dxy, dyy, dys,
    dxs, dys, dss);

    Vec3f X = H.solve(dD, DECOMP_LU);

    xi = -X[2];
    xr = -X[1];
    xc = -X[0];

    if( std::abs(xi) < 0.5f && std::abs(xr) < 0.5f && std::abs(xc) < 0.5f )
    break;

    if( std::abs(xi) > (float)(INT_MAX/3) ||
    std::abs(xr) > (float)(INT_MAX/3) ||
    std::abs(xc) > (float)(INT_MAX/3) )
    return false;

    c += cvRound(xc);
    r += cvRound(xr);
    layer += cvRound(xi);

    if( layer < 1 || layer > nOctaveLayers ||
    c < SIFT_IMG_BORDER || c >= img.cols - SIFT_IMG_BORDER ||
    r < SIFT_IMG_BORDER || r >= img.rows - SIFT_IMG_BORDER )
    return false;
    }

    // ensure convergence of interpolation
    if( i >= SIFT_MAX_INTERP_STEPS )
    return false;

    {
    int idx = octv*(nOctaveLayers+2) + layer;
    const Mat& img = dog_pyr[idx];
    const Mat& prev = dog_pyr[idx-1];
    const Mat& next = dog_pyr[idx+1];
    Matx31f dD((img.at<sift_wt>(r, c+1) - img.at<sift_wt>(r, c-1))*deriv_scale,
    (img.at<sift_wt>(r+1, c) - img.at<sift_wt>(r-1, c))*deriv_scale,
    (next.at<sift_wt>(r, c) - prev.at<sift_wt>(r, c))*deriv_scale);
    float t = dD.dot(Matx31f(xc, xr, xi));

    contr = img.at<sift_wt>(r, c)*img_scale + t * 0.5f;
    if( std::abs( contr ) * nOctaveLayers < contrastThreshold )
    return false;

    // principal curvatures are computed using the trace and det of Hessian
    float v2 = img.at<sift_wt>(r, c)*2.f;
    float dxx = (img.at<sift_wt>(r, c+1) + img.at<sift_wt>(r, c-1) - v2)*second_deriv_scale;
    float dyy = (img.at<sift_wt>(r+1, c) + img.at<sift_wt>(r-1, c) - v2)*second_deriv_scale;
    float dxy = (img.at<sift_wt>(r+1, c+1) - img.at<sift_wt>(r+1, c-1) -
    img.at<sift_wt>(r-1, c+1) + img.at<sift_wt>(r-1, c-1)) * cross_deriv_scale;
    float tr = dxx + dyy;
    float det = dxx * dyy - dxy * dxy;

    if( det <= 0 || tr*tr*edgeThreshold >= (edgeThreshold + 1)*(edgeThreshold + 1)*det )
    return false;
    }

    kpt.pt.x = (c + xc) * (1 << octv);
    kpt.pt.y = (r + xr) * (1 << octv);
    kpt.octave = octv + (layer << 8) + (cvRound((xi + 0.5)*255) << 16);
    kpt.size = sigma*powf(2.f, (layer + xi) / nOctaveLayers)*(1 << octv)*2;
    kpt.response = std::abs(contr);

    return true;
    }


    //
    // Detects features at extrema in DoG scale space. Bad features are discarded
    // based on contrast and ratio of principal curvatures.
    void SIFT::findScaleSpaceExtrema( const vector<Mat>& gauss_pyr, const vector<Mat>& dog_pyr,
    vector<KeyPoint>& keypoints ) const
    {
    int nOctaves = (int)gauss_pyr.size()/(nOctaveLayers + 3);
    int threshold = cvFloor(0.5 * contrastThreshold / nOctaveLayers * 255 * SIFT_FIXPT_SCALE);
    const int n = SIFT_ORI_HIST_BINS;
    float hist[n];
    KeyPoint kpt;

    keypoints.clear();

    for( int o = 0; o < nOctaves; o++ )
    for( int i = 1; i <= nOctaveLayers; i++ )
    {
    int idx = o*(nOctaveLayers+2)+i;
    const Mat& img = dog_pyr[idx];
    const Mat& prev = dog_pyr[idx-1];
    const Mat& next = dog_pyr[idx+1];
    int step = (int)img.step1();
    int rows = img.rows, cols = img.cols;

    for( int r = SIFT_IMG_BORDER; r < rows-SIFT_IMG_BORDER; r++)
    {
    const sift_wt* currptr = img.ptr<sift_wt>(r);
    const sift_wt* prevptr = prev.ptr<sift_wt>(r);
    const sift_wt* nextptr = next.ptr<sift_wt>(r);

    for( int c = SIFT_IMG_BORDER; c < cols-SIFT_IMG_BORDER; c++)
    {
    sift_wt val = currptr[c];

    // find local extrema with pixel accuracy
    if( std::abs(val) > threshold &&
    ((val > 0 && val >= currptr[c-1] && val >= currptr[c+1] &&
    val >= currptr[c-step-1] && val >= currptr[c-step] && val >= currptr[c-step+1] &&
    val >= currptr[c+step-1] && val >= currptr[c+step] && val >= currptr[c+step+1] &&
    val >= nextptr[c] && val >= nextptr[c-1] && val >= nextptr[c+1] &&
    val >= nextptr[c-step-1] && val >= nextptr[c-step] && val >= nextptr[c-step+1] &&
    val >= nextptr[c+step-1] && val >= nextptr[c+step] && val >= nextptr[c+step+1] &&
    val >= prevptr[c] && val >= prevptr[c-1] && val >= prevptr[c+1] &&
    val >= prevptr[c-step-1] && val >= prevptr[c-step] && val >= prevptr[c-step+1] &&
    val >= prevptr[c+step-1] && val >= prevptr[c+step] && val >= prevptr[c+step+1]) ||
    (val < 0 && val <= currptr[c-1] && val <= currptr[c+1] &&
    val <= currptr[c-step-1] && val <= currptr[c-step] && val <= currptr[c-step+1] &&
    val <= currptr[c+step-1] && val <= currptr[c+step] && val <= currptr[c+step+1] &&
    val <= nextptr[c] && val <= nextptr[c-1] && val <= nextptr[c+1] &&
    val <= nextptr[c-step-1] && val <= nextptr[c-step] && val <= nextptr[c-step+1] &&
    val <= nextptr[c+step-1] && val <= nextptr[c+step] && val <= nextptr[c+step+1] &&
    val <= prevptr[c] && val <= prevptr[c-1] && val <= prevptr[c+1] &&
    val <= prevptr[c-step-1] && val <= prevptr[c-step] && val <= prevptr[c-step+1] &&
    val <= prevptr[c+step-1] && val <= prevptr[c+step] && val <= prevptr[c+step+1])))
    {
    int r1 = r, c1 = c, layer = i;
    if( !adjustLocalExtrema(dog_pyr, kpt, o, layer, r1, c1,
    nOctaveLayers, (float)contrastThreshold,
    (float)edgeThreshold, (float)sigma) )
    continue;
    float scl_octv = kpt.size*0.5f/(1 << o);
    float omax = calcOrientationHist(gauss_pyr[o*(nOctaveLayers+3) + layer],
    Point(c1, r1),
    cvRound(SIFT_ORI_RADIUS * scl_octv),
    SIFT_ORI_SIG_FCTR * scl_octv,
    hist, n);
    float mag_thr = (float)(omax * SIFT_ORI_PEAK_RATIO);
    for( int j = 0; j < n; j++ )
    {
    int l = j > 0 ? j - 1 : n - 1;
    int r2 = j < n-1 ? j + 1 : 0;

    if( hist[j] > hist[l] && hist[j] > hist[r2] && hist[j] >= mag_thr )
    {
    float bin = j + 0.5f * (hist[l]-hist[r2]) / (hist[l] - 2*hist[j] + hist[r2]);
    bin = bin < 0 ? n + bin : bin >= n ? bin - n : bin;
    kpt.angle = 360.f - (float)((360.f/n) * bin);
    if(std::abs(kpt.angle - 360.f) < FLT_EPSILON)
    kpt.angle = 0.f;
    keypoints.push_back(kpt);
    }
    }
    }
    }
    }
    }
    }


    static void calcSIFTDescriptor( const Mat& img, Point2f ptf, float ori, float scl,
    int d, int n, float* dst )
    {
    Point pt(cvRound(ptf.x), cvRound(ptf.y));
    float cos_t = cosf(ori*(float)(CV_PI/180));
    float sin_t = sinf(ori*(float)(CV_PI/180));
    float bins_per_rad = n / 360.f;
    float exp_scale = -1.f/(d * d * 0.5f);
    float hist_width = SIFT_DESCR_SCL_FCTR * scl;
    int radius = cvRound(hist_width * 1.4142135623730951f * (d + 1) * 0.5f);
    // Clip the radius to the diagonal of the image to avoid autobuffer too large exception
    radius = std::min(radius, (int) sqrt((double) img.cols*img.cols + img.rows*img.rows));
    cos_t /= hist_width;
    sin_t /= hist_width;

    int i, j, k, len = (radius*2+1)*(radius*2+1), histlen = (d+2)*(d+2)*(n+2);
    int rows = img.rows, cols = img.cols;

    AutoBuffer<float> buf(len*6 + histlen);
    float *X = buf, *Y = X + len, *Mag = Y, *Ori = Mag + len, *W = Ori + len;
    float *RBin = W + len, *CBin = RBin + len, *hist = CBin + len;

    for( i = 0; i < d+2; i++ )
    {
    for( j = 0; j < d+2; j++ )
    for( k = 0; k < n+2; k++ )
    hist[(i*(d+2) + j)*(n+2) + k] = 0.;
    }

    for( i = -radius, k = 0; 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.
    float c_rot = j * cos_t - i * sin_t;
    float r_rot = j * sin_t + i * cos_t;
    float rbin = r_rot + d/2 - 0.5f;
    float cbin = c_rot + d/2 - 0.5f;
    int r = pt.y + i, c = pt.x + j;

    if( rbin > -1 && rbin < d && cbin > -1 && cbin < d &&
    r > 0 && r < rows - 1 && c > 0 && c < cols - 1 )
    {
    float dx = (float)(img.at<sift_wt>(r, c+1) - img.at<sift_wt>(r, c-1));
    float dy = (float)(img.at<sift_wt>(r-1, c) - img.at<sift_wt>(r+1, c));
    X[k] = dx; Y[k] = dy; RBin[k] = rbin; CBin[k] = cbin;
    W[k] = (c_rot * c_rot + r_rot * r_rot)*exp_scale;
    k++;
    }
    }

    len = k;
    fastAtan2(Y, X, Ori, len, true);
    magnitude(X, Y, Mag, len);
    exp(W, W, len);

    for( k = 0; k < len; k++ )
    {
    float rbin = RBin[k], cbin = CBin[k];
    float obin = (Ori[k] - ori)*bins_per_rad;
    float mag = Mag[k]*W[k];

    int r0 = cvFloor( rbin );
    int c0 = cvFloor( cbin );
    int o0 = cvFloor( obin );
    rbin -= r0;
    cbin -= c0;
    obin -= o0;

    if( o0 < 0 )
    o0 += n;
    if( o0 >= n )
    o0 -= n;

    // histogram update using tri-linear interpolation
    float v_r1 = mag*rbin, v_r0 = mag - v_r1;
    float v_rc11 = v_r1*cbin, v_rc10 = v_r1 - v_rc11;
    float v_rc01 = v_r0*cbin, v_rc00 = v_r0 - v_rc01;
    float v_rco111 = v_rc11*obin, v_rco110 = v_rc11 - v_rco111;
    float v_rco101 = v_rc10*obin, v_rco100 = v_rc10 - v_rco101;
    float v_rco011 = v_rc01*obin, v_rco010 = v_rc01 - v_rco011;
    float v_rco001 = v_rc00*obin, v_rco000 = v_rc00 - v_rco001;

    int idx = ((r0+1)*(d+2) + c0+1)*(n+2) + o0;
    hist[idx] += v_rco000;
    hist[idx+1] += v_rco001;
    hist[idx+(n+2)] += v_rco010;
    hist[idx+(n+3)] += v_rco011;
    hist[idx+(d+2)*(n+2)] += v_rco100;
    hist[idx+(d+2)*(n+2)+1] += v_rco101;
    hist[idx+(d+3)*(n+2)] += v_rco110;
    hist[idx+(d+3)*(n+2)+1] += v_rco111;
    }

    // finalize histogram, since the orientation histograms are circular
    for( i = 0; i < d; i++ )
    for( j = 0; j < d; j++ )
    {
    int idx = ((i+1)*(d+2) + (j+1))*(n+2);
    hist[idx] += hist[idx+n];
    hist[idx+1] += hist[idx+n+1];
    for( k = 0; k < n; k++ )
    dst[(i*d + j)*n + k] = hist[idx+k];
    }
    // copy histogram to the descriptor,
    // apply hysteresis thresholding
    // and scale the result, so that it can be easily converted
    // to byte array
    float nrm2 = 0;
    len = d*d*n;
    for( k = 0; k < len; k++ )
    nrm2 += dst[k]*dst[k];
    float thr = std::sqrt(nrm2)*SIFT_DESCR_MAG_THR;
    for( i = 0, nrm2 = 0; i < k; i++ )
    {
    float val = std::min(dst[i], thr);
    dst[i] = val;
    nrm2 += val*val;
    }
    nrm2 = SIFT_INT_DESCR_FCTR/std::max(std::sqrt(nrm2), FLT_EPSILON);

    #if 1
    for( k = 0; k < len; k++ )
    {
    dst[k] = saturate_cast<uchar>(dst[k]*nrm2);
    }
    #else
    float nrm1 = 0;
    for( k = 0; k < len; k++ )
    {
    dst[k] *= nrm2;
    nrm1 += dst[k];
    }
    nrm1 = 1.f/std::max(nrm1, FLT_EPSILON);
    for( k = 0; k < len; k++ )
    {
    dst[k] = std::sqrt(dst[k] * nrm1);//saturate_cast<uchar>(std::sqrt(dst[k] * nrm1)*SIFT_INT_DESCR_FCTR);
    }
    #endif
    }

    static void calcDescriptors(const vector<Mat>& gpyr, const vector<KeyPoint>& keypoints,
    Mat& descriptors, int nOctaveLayers, int firstOctave )
    {
    int d = SIFT_DESCR_WIDTH, n = SIFT_DESCR_HIST_BINS;

    for( size_t i = 0; i < keypoints.size(); i++ )
    {
    KeyPoint kpt = keypoints[i];
    int octave, layer;
    float scale;
    unpackOctave(kpt, octave, layer, scale);
    CV_Assert(octave >= firstOctave && layer <= nOctaveLayers+2);
    float size=kpt.size*scale;
    Point2f ptf(kpt.pt.x*scale, kpt.pt.y*scale);
    const Mat& img = gpyr[(octave - firstOctave)*(nOctaveLayers + 3) + layer];

    float angle = 360.f - kpt.angle;
    if(std::abs(angle - 360.f) < FLT_EPSILON)
    angle = 0.f;
    calcSIFTDescriptor(img, ptf, angle, size*0.5f, d, n, descriptors.ptr<float>((int)i));
    }
    }

    //////////////////////////////////////////////////////////////////////////////////////////

    SIFT::SIFT( int _nfeatures, int _nOctaveLayers,
    double _contrastThreshold, double _edgeThreshold, double _sigma )
    : nfeatures(_nfeatures), nOctaveLayers(_nOctaveLayers),
    contrastThreshold(_contrastThreshold), edgeThreshold(_edgeThreshold), sigma(_sigma)
    {
    }

    int SIFT::descriptorSize() const
    {
    return SIFT_DESCR_WIDTH*SIFT_DESCR_WIDTH*SIFT_DESCR_HIST_BINS;
    }

    int SIFT::descriptorType() const
    {
    return CV_32F;
    }


    void SIFT::operator()(InputArray _image, InputArray _mask,
    vector<KeyPoint>& keypoints) const
    {
    (*this)(_image, _mask, keypoints, noArray());
    }


    void SIFT::operator()(InputArray _image, InputArray _mask,
    vector<KeyPoint>& keypoints,
    OutputArray _descriptors,
    bool useProvidedKeypoints) const
    {
    int firstOctave = -1, actualNOctaves = 0, actualNLayers = 0;
    Mat image = _image.getMat(), mask = _mask.getMat();

    if( image.empty() || image.depth() != CV_8U )
    CV_Error( CV_StsBadArg, "image is empty or has incorrect depth (!=CV_8U)" );

    if( !mask.empty() && mask.type() != CV_8UC1 )
    CV_Error( CV_StsBadArg, "mask has incorrect type (!=CV_8UC1)" );

    if( useProvidedKeypoints )
    {
    firstOctave = 0;
    int maxOctave = INT_MIN;
    for( size_t i = 0; i < keypoints.size(); i++ )
    {
    int octave, layer;
    float scale;
    unpackOctave(keypoints[i], octave, layer, scale);
    firstOctave = std::min(firstOctave, octave);
    maxOctave = std::max(maxOctave, octave);
    actualNLayers = std::max(actualNLayers, layer-2);
    }

    firstOctave = std::min(firstOctave, 0);
    CV_Assert( firstOctave >= -1 && actualNLayers <= nOctaveLayers );
    actualNOctaves = maxOctave - firstOctave + 1;
    }

    Mat base = createInitialImage(image, firstOctave < 0, (float)sigma);
    vector<Mat> gpyr, dogpyr;
    int nOctaves = actualNOctaves > 0 ? actualNOctaves : cvRound(log( (double)std::min( base.cols, base.rows ) ) / log(2.) - 2) - firstOctave;

    //double t, tf = getTickFrequency();
    //t = (double)getTickCount();
    buildGaussianPyramid(base, gpyr, nOctaves);
    buildDoGPyramid(gpyr, dogpyr);

    //t = (double)getTickCount() - t;
    //printf("pyramid construction time: %g ", t*1000./tf);

    if( !useProvidedKeypoints )
    {
    //t = (double)getTickCount();
    findScaleSpaceExtrema(gpyr, dogpyr, keypoints);
    KeyPointsFilter::removeDuplicated( keypoints );

    if( nfeatures > 0 )
    KeyPointsFilter::retainBest(keypoints, nfeatures);
    //t = (double)getTickCount() - t;
    //printf("keypoint detection time: %g ", t*1000./tf);

    if( firstOctave < 0 )
    for( size_t i = 0; i < keypoints.size(); i++ )
    {
    KeyPoint& kpt = keypoints[i];
    float scale = 1.f/(float)(1 << -firstOctave);
    kpt.octave = (kpt.octave & ~255) | ((kpt.octave + firstOctave) & 255);
    kpt.pt *= scale;
    kpt.size *= scale;
    }

    if( !mask.empty() )
    KeyPointsFilter::runByPixelsMask( keypoints, mask );
    }
    else
    {
    // filter keypoints by mask
    //KeyPointsFilter::runByPixelsMask( keypoints, mask );
    }

    if( _descriptors.needed() )
    {
    //t = (double)getTickCount();
    int dsize = descriptorSize();
    _descriptors.create((int)keypoints.size(), dsize, CV_32F);
    Mat descriptors = _descriptors.getMat();

    calcDescriptors(gpyr, keypoints, descriptors, nOctaveLayers, firstOctave);
    //t = (double)getTickCount() - t;
    //printf("descriptor extraction time: %g ", t*1000./tf);
    }
    }

    void SIFT::detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask) const
    {
    (*this)(image, mask, keypoints, noArray());
    }

    void SIFT::computeImpl( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors) const
    {
    (*this)(image, Mat(), keypoints, descriptors, true);
    }

    }

  • 相关阅读:
    XNA游戏编程等
    DirectX游戏编程(一):创建一个Direct3D程序
    POJ 1163 The Triangle(简单动态规划)
    web前段canvasjs图表制作一
    ubuntu 使用mysql
    Nginx+ uWSGI +django进行部署
    matplotlib如何绘制直方图、条形图和饼图
    matplotlib animation
    matplotlib 画图中图和次坐标轴
    matplotlib subplot 多图合一
  • 原文地址:https://www.cnblogs.com/2008nmj/p/11687072.html
Copyright © 2011-2022 走看看