trackerKCF.cpp 30 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
/*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) 2013, OpenCV Foundation, 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*/

#include "precomp.hpp"
#include <complex>

/*---------------------------
|  TrackerKCFModel
|---------------------------*/
namespace cv{
   /**
  * \brief Implementation of TrackerModel for MIL algorithm
  */
  class TrackerKCFModel : public TrackerModel{
  public:
    TrackerKCFModel(TrackerKCF::Params /*params*/){}
    ~TrackerKCFModel(){}
  protected:
57
    void modelEstimationImpl( const std::vector<Mat>& /*responses*/ ){}
58 59 60 61 62 63 64 65 66
    void modelUpdateImpl(){}
  };
} /* namespace cv */


/*---------------------------
|  TrackerKCF
|---------------------------*/
namespace cv{
Kurnianggoro's avatar
Kurnianggoro committed
67

68 69 70
  /*
 * Prototype
 */
71
  class TrackerKCFImpl : public TrackerKCF {
72 73
  public:
    TrackerKCFImpl( const TrackerKCF::Params &parameters = TrackerKCF::Params() );
74 75
    void read( const FileNode& /*fn*/ );
    void write( FileStorage& /*fs*/ ) const;
Kurnianggoro's avatar
Kurnianggoro committed
76
    void setFeatureExtractor(void (*f)(const Mat, const Rect, Mat&), bool pca_func = false);
Kurnianggoro's avatar
Kurnianggoro committed
77

78 79 80 81
  protected:
     /*
    * basic functions and vars
    */
82
    bool initImpl( const Mat& /*image*/, const Rect2d& boundingBox );
83
    bool updateImpl( const Mat& image, Rect2d& boundingBox );
Kurnianggoro's avatar
Kurnianggoro committed
84

85
    TrackerKCF::Params params;
Kurnianggoro's avatar
Kurnianggoro committed
86

87 88 89
    /*
    * KCF functions and vars
    */
Kurnianggoro's avatar
Kurnianggoro committed
90 91
    void createHanningWindow(OutputArray dest, const cv::Size winSize, const int type) const;
    void inline fft2(const Mat src, std::vector<Mat> & dest, std::vector<Mat> & layers_data) const;
92
    void inline fft2(const Mat src, Mat & dest) const;
Kurnianggoro's avatar
Kurnianggoro committed
93
    void inline ifft2(const Mat src, Mat & dest) const;
94 95
    void inline pixelWiseMult(const std::vector<Mat> src1, const std::vector<Mat>  src2, std::vector<Mat>  & dest, const int flags, const bool conjB=false) const;
    void inline sumChannels(std::vector<Mat> src, Mat & dest) const;
Kurnianggoro's avatar
Kurnianggoro committed
96 97 98 99 100 101 102 103 104 105
    void inline updateProjectionMatrix(const Mat src, Mat & old_cov,Mat &  proj_matrix,double pca_rate, int compressed_sz,
                                       std::vector<Mat> & layers_pca,std::vector<Scalar> & average, Mat pca_data, Mat new_cov, Mat w, Mat u, Mat v) const;
    void inline compress(const Mat proj_matrix, const Mat src, Mat & dest, Mat & data, Mat & compressed) const;
    bool getSubWindow(const Mat img, const Rect roi, Mat& feat, Mat& patch, TrackerKCF::MODE desc = GRAY) const;
    bool getSubWindow(const Mat img, const Rect roi, Mat& feat, void (*f)(const Mat, const Rect, Mat& )) const;
    void extractCN(Mat patch_data, Mat & cnFeatures) const;
    void denseGaussKernel(const double sigma, const Mat , const Mat y_data, Mat & k_data,
                          std::vector<Mat> & layers_data,std::vector<Mat> & xf_data,std::vector<Mat> & yf_data, std::vector<Mat> xyf_v, Mat xy, Mat xyf ) const;
    void calcResponse(const Mat alphaf_data, const Mat kf_data, Mat & response_data, Mat & spec_data) const;
    void calcResponse(const Mat alphaf_data, const Mat alphaf_den_data, const Mat kf_data, Mat & response_data, Mat & spec_data, Mat & spec2_data) const;
Kurnianggoro's avatar
Kurnianggoro committed
106 107

    void shiftRows(Mat& mat) const;
108 109
    void shiftRows(Mat& mat, int n) const;
    void shiftCols(Mat& mat, int n) const;
Kurnianggoro's avatar
Kurnianggoro committed
110

111 112 113 114
  private:
    double output_sigma;
    Rect2d roi;
    Mat hann; 	//hann window filter
Kurnianggoro's avatar
Kurnianggoro committed
115
    Mat hann_cn; //10 dimensional hann-window filter for CN features,
Kurnianggoro's avatar
Kurnianggoro committed
116

117
    Mat y,yf; 	// training response and its FFT
Kurnianggoro's avatar
Kurnianggoro committed
118
    Mat x; 	// observation and its FFT
119
    Mat k,kf;	// dense gaussian kernel and its FFT
120 121 122
    Mat kf_lambda; // kf+lambda
    Mat new_alphaf, alphaf;	// training coefficients
    Mat new_alphaf_den, alphaf_den; // for splitted training coefficients
Kurnianggoro's avatar
Kurnianggoro committed
123
    Mat z; // model
124
    Mat response; // detection result
125
    Mat old_cov_mtx, proj_mtx; // for feature compression
Kurnianggoro's avatar
Kurnianggoro committed
126

Kurnianggoro's avatar
Kurnianggoro committed
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
    // pre-defined Mat variables for optimization of private functions
    Mat spec, spec2;
    std::vector<Mat> layers;
    std::vector<Mat> vxf,vyf,vxyf;
    Mat xy_data,xyf_data;
    Mat data_temp, compress_data;
    std::vector<Mat> layers_pca_data;
    std::vector<Scalar> average_data;
    Mat img_Patch;

    // storage for the extracted features, KRLS model, KRLS compressed model
    Mat X[2],Z[2],Zc[2];

    // storage of the extracted features
    std::vector<Mat> features_pca;
    std::vector<Mat> features_npca;
    std::vector<MODE> descriptors_pca;
    std::vector<MODE> descriptors_npca;

    // optimization variables for updateProjectionMatrix
    Mat data_pca, new_covar,w_data,u_data,vt_data;

    // custom feature extractor
    bool use_custom_extractor_pca;
    bool use_custom_extractor_npca;
    std::vector<void(*)(const Mat img, const Rect roi, Mat& output)> extractor_pca;
    std::vector<void(*)(const Mat img, const Rect roi, Mat& output)> extractor_npca;

155
    bool resizeImage; // resize the image whenever needed and the patch size is large
Kurnianggoro's avatar
Kurnianggoro committed
156

157 158
    int frame;
  };
Kurnianggoro's avatar
Kurnianggoro committed
159

160 161 162 163 164 165 166 167 168 169
  /*
 * Constructor
 */
  Ptr<TrackerKCF> TrackerKCF::createTracker(const TrackerKCF::Params &parameters){
      return Ptr<TrackerKCFImpl>(new TrackerKCFImpl(parameters));
  }
  TrackerKCFImpl::TrackerKCFImpl( const TrackerKCF::Params &parameters ) :
      params( parameters )
  {
    isInit = false;
170
    resizeImage = false;
Kurnianggoro's avatar
Kurnianggoro committed
171 172
    use_custom_extractor_pca = false;
    use_custom_extractor_npca = false;
Kurnianggoro's avatar
Kurnianggoro committed
173

174
  }
Kurnianggoro's avatar
Kurnianggoro committed
175

176 177 178 179
  void TrackerKCFImpl::read( const cv::FileNode& fn ){
    params.read( fn );
  }

Kurnianggoro's avatar
Kurnianggoro committed
180
  void TrackerKCFImpl::write( cv::FileStorage& fs ) const {
181 182
    params.write( fs );
  }
Kurnianggoro's avatar
Kurnianggoro committed
183

184
  /*
Kurnianggoro's avatar
Kurnianggoro committed
185
   * Initialization:
186 187 188 189 190
   * - creating hann window filter
   * - ROI padding
   * - creating a gaussian response for the training ground-truth
   * - perform FFT to the gaussian response
   */
191
  bool TrackerKCFImpl::initImpl( const Mat& image, const Rect2d& boundingBox ){
192 193
    frame=0;
    roi = boundingBox;
Kurnianggoro's avatar
Kurnianggoro committed
194

195 196 197
    //calclulate output sigma
    output_sigma=sqrt(roi.width*roi.height)*params.output_sigma_factor;
    output_sigma=-0.5/(output_sigma*output_sigma);
Kurnianggoro's avatar
Kurnianggoro committed
198

199
    //resize the ROI whenever needed
200
    if(params.resize && roi.width*roi.height>params.max_patch_size){
201 202 203 204 205
      resizeImage=true;
      roi.x/=2.0;
      roi.y/=2.0;
      roi.width/=2.0;
      roi.height/=2.0;
Kurnianggoro's avatar
Kurnianggoro committed
206 207
    }

208 209
    // add padding to the roi
    roi.x-=roi.width/2;
210
    roi.y-=roi.height/2;
211 212
    roi.width*=2;
    roi.height*=2;
Kurnianggoro's avatar
Kurnianggoro committed
213

214 215
    // initialize the hann window filter
    createHanningWindow(hann, roi.size(), CV_64F);
Kurnianggoro's avatar
Kurnianggoro committed
216 217 218 219

    // hann window filter for CN feature
    Mat _layer[] = {hann, hann, hann, hann, hann, hann, hann, hann, hann, hann};
    merge(_layer, 10, hann_cn);
Kurnianggoro's avatar
Kurnianggoro committed
220

221
    // create gaussian response
222
    y=Mat::zeros((int)roi.height,(int)roi.width,CV_64F);
223 224
    for(unsigned i=0;i<roi.height;i++){
      for(unsigned j=0;j<roi.width;j++){
Kurnianggoro's avatar
Kurnianggoro committed
225
        y.at<double>(i,j)=(i-roi.height/2+1)*(i-roi.height/2+1)+(j-roi.width/2+1)*(j-roi.width/2+1);
226 227
      }
    }
Kurnianggoro's avatar
Kurnianggoro committed
228

229 230
    y*=(double)output_sigma;
    cv::exp(y,y);
Kurnianggoro's avatar
Kurnianggoro committed
231

232 233
    // perform fourier transfor to the gaussian response
    fft2(y,yf);
Kurnianggoro's avatar
Kurnianggoro committed
234

235
    model=Ptr<TrackerKCFModel>(new TrackerKCFModel(params));
Kurnianggoro's avatar
Kurnianggoro committed
236

Kurnianggoro's avatar
Kurnianggoro committed
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
    // record the non-compressed descriptors
    if((params.desc_npca & GRAY) == GRAY)descriptors_npca.push_back(GRAY);
    if((params.desc_npca & CN) == CN)descriptors_npca.push_back(CN);
    if(use_custom_extractor_npca)descriptors_npca.push_back(CUSTOM);
    features_npca.resize(descriptors_npca.size());

    // record the compressed descriptors
    if((params.desc_pca & GRAY) == GRAY)descriptors_pca.push_back(GRAY);
    if((params.desc_pca & CN) == CN)descriptors_pca.push_back(CN);
    if(use_custom_extractor_pca)descriptors_pca.push_back(CUSTOM);
    features_pca.resize(descriptors_pca.size());

    // accept only the available descriptor modes
    CV_Assert(
      (params.desc_pca & GRAY) == GRAY
      || (params.desc_npca & GRAY) == GRAY
      || (params.desc_pca & CN) == CN
      || (params.desc_npca & CN) == CN
      || use_custom_extractor_pca
      || use_custom_extractor_npca
    );

259 260 261 262 263
    //return true only if roi has intersection with the image
    if((roi & Rect2d(0,0, resizeImage ? image.cols / 2 : image.cols,
                     resizeImage ? image.rows / 2 : image.rows)) == Rect2d())
        return false;

264 265
    return true;
  }
Kurnianggoro's avatar
Kurnianggoro committed
266

267 268 269 270 271 272
  /*
   * Main part of the KCF algorithm
   */
  bool TrackerKCFImpl::updateImpl( const Mat& image, Rect2d& boundingBox ){
    double minVal, maxVal;	// min-max response
    Point minLoc,maxLoc;	// min-max location
Kurnianggoro's avatar
Kurnianggoro committed
273

274
    Mat img=image.clone();
275
    // check the channels of the input image, grayscale is preferred
Kurnianggoro's avatar
Kurnianggoro committed
276
    CV_Assert(img.channels() == 1 || img.channels() == 3);
Kurnianggoro's avatar
Kurnianggoro committed
277

278 279
    // resize the image whenever needed
    if(resizeImage)resize(img,img,Size(img.cols/2,img.rows/2));
Kurnianggoro's avatar
Kurnianggoro committed
280

281 282
    // detection part
    if(frame>0){
Kurnianggoro's avatar
Kurnianggoro committed
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325

      // extract and pre-process the patch
      // get non compressed descriptors
      for(unsigned i=0;i<descriptors_npca.size()-extractor_npca.size();i++){
        if(!getSubWindow(img,roi, features_npca[i], img_Patch, descriptors_npca[i]))return false;
      }
      //get non-compressed custom descriptors
      for(unsigned i=0,j=(unsigned)(descriptors_npca.size()-extractor_npca.size());i<extractor_npca.size();i++,j++){
        if(!getSubWindow(img,roi, features_npca[j], extractor_npca[i]))return false;
      }
      if(features_npca.size()>0)merge(features_npca,X[1]);

      // get compressed descriptors
      for(unsigned i=0;i<descriptors_pca.size()-extractor_pca.size();i++){
        if(!getSubWindow(img,roi, features_pca[i], img_Patch, descriptors_pca[i]))return false;
      }
      //get compressed custom descriptors
      for(unsigned i=0,j=(unsigned)(descriptors_pca.size()-extractor_pca.size());i<extractor_pca.size();i++,j++){
        if(!getSubWindow(img,roi, features_pca[j], extractor_pca[i]))return false;
      }
      if(features_pca.size()>0)merge(features_pca,X[0]);

      //compress the features and the KRSL model
      if(params.desc_pca !=0){
        compress(proj_mtx,X[0],X[0],data_temp,compress_data);
        compress(proj_mtx,Z[0],Zc[0],data_temp,compress_data);
      }

      // copy the compressed KRLS model
      Zc[1] = Z[1];

      // merge all features
      if(features_npca.size()==0){
        x = X[0];
        z = Zc[0];
      }else if(features_pca.size()==0){
        x = X[1];
        z = Z[1];
      }else{
        merge(X,2,x);
        merge(Zc,2,z);
      }

326
      //compute the gaussian kernel
Kurnianggoro's avatar
Kurnianggoro committed
327 328 329 330 331
      denseGaussKernel(params.sigma,x,z,k,layers,vxf,vyf,vxyf,xy_data,xyf_data);

      // compute the fourier transform of the kernel
      fft2(k,kf);
      if(frame==1)spec2=Mat_<Vec2d >(kf.rows, kf.cols);
Kurnianggoro's avatar
Kurnianggoro committed
332

333
      // calculate filter response
334
      if(params.split_coeff)
Kurnianggoro's avatar
Kurnianggoro committed
335
        calcResponse(alphaf,alphaf_den,kf,response, spec, spec2);
336
      else
Kurnianggoro's avatar
Kurnianggoro committed
337
        calcResponse(alphaf,kf,response, spec);
Kurnianggoro's avatar
Kurnianggoro committed
338

339 340 341 342
      // extract the maximum response
      minMaxLoc( response, &minVal, &maxVal, &minLoc, &maxLoc );
      roi.x+=(maxLoc.x-roi.width/2+1);
      roi.y+=(maxLoc.y-roi.height/2+1);
343
    }
Kurnianggoro's avatar
Kurnianggoro committed
344

345 346 347 348 349 350
    // update the bounding box
    boundingBox.x=(resizeImage?roi.x*2:roi.x)+(resizeImage?roi.width*2:roi.width)/4;
    boundingBox.y=(resizeImage?roi.y*2:roi.y)+(resizeImage?roi.height*2:roi.height)/4;
    boundingBox.width = (resizeImage?roi.width*2:roi.width)/2;
    boundingBox.height = (resizeImage?roi.height*2:roi.height)/2;

351
    // extract the patch for learning purpose
Kurnianggoro's avatar
Kurnianggoro committed
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
    // get non compressed descriptors
    for(unsigned i=0;i<descriptors_npca.size()-extractor_npca.size();i++){
      if(!getSubWindow(img,roi, features_npca[i], img_Patch, descriptors_npca[i]))return false;
    }
    //get non-compressed custom descriptors
    for(unsigned i=0,j=(unsigned)(descriptors_npca.size()-extractor_npca.size());i<extractor_npca.size();i++,j++){
      if(!getSubWindow(img,roi, features_npca[j], extractor_npca[i]))return false;
    }
    if(features_npca.size()>0)merge(features_npca,X[1]);

    // get compressed descriptors
    for(unsigned i=0;i<descriptors_pca.size()-extractor_pca.size();i++){
      if(!getSubWindow(img,roi, features_pca[i], img_Patch, descriptors_pca[i]))return false;
    }
    //get compressed custom descriptors
    for(unsigned i=0,j=(unsigned)(descriptors_pca.size()-extractor_pca.size());i<extractor_pca.size();i++,j++){
      if(!getSubWindow(img,roi, features_pca[j], extractor_pca[i]))return false;
    }
    if(features_pca.size()>0)merge(features_pca,X[0]);
Kurnianggoro's avatar
Kurnianggoro committed
371

372
    //update the training data
Kurnianggoro's avatar
Kurnianggoro committed
373 374 375 376 377 378 379 380 381 382 383 384 385 386
    if(frame==0){
      Z[0] = X[0].clone();
      Z[1] = X[1].clone();
    }else{
      Z[0]=(1.0-params.interp_factor)*Z[0]+params.interp_factor*X[0];
      Z[1]=(1.0-params.interp_factor)*Z[1]+params.interp_factor*X[1];
    }

    if(params.desc_pca !=0 || use_custom_extractor_pca){
      // initialize the vector of Mat variables
      if(frame==0){
        layers_pca_data.resize(Z[0].channels());
        average_data.resize(Z[0].channels());
      }
Kurnianggoro's avatar
Kurnianggoro committed
387

388
      // feature compression
Kurnianggoro's avatar
Kurnianggoro committed
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
      updateProjectionMatrix(Z[0],old_cov_mtx,proj_mtx,params.pca_learning_rate,params.compressed_size,layers_pca_data,average_data,data_pca, new_covar,w_data,u_data,vt_data);
      compress(proj_mtx,X[0],X[0],data_temp,compress_data);
    }

    // merge all features
    if(features_npca.size()==0)
      x = X[0];
    else if(features_pca.size()==0)
      x = X[1];
    else
      merge(X,2,x);

    // initialize some required Mat variables
    if(frame==0){
      layers.resize(x.channels());
      vxf.resize(x.channels());
      vyf.resize(x.channels());
      vxyf.resize(vyf.size());
      new_alphaf=Mat_<Vec2d >(yf.rows, yf.cols);
408
    }
Kurnianggoro's avatar
Kurnianggoro committed
409

410
    // Kernel Regularized Least-Squares, calculate alphas
Kurnianggoro's avatar
Kurnianggoro committed
411
    denseGaussKernel(params.sigma,x,x,k,layers,vxf,vyf,vxyf,xy_data,xyf_data);
Kurnianggoro's avatar
Kurnianggoro committed
412

Kurnianggoro's avatar
Kurnianggoro committed
413
    // compute the fourier transform of the kernel and add a small value
414
    fft2(k,kf);
415
    kf_lambda=kf+params.lambda;
Kurnianggoro's avatar
Kurnianggoro committed
416

Kurnianggoro's avatar
Kurnianggoro committed
417
    double den;
418
    if(params.split_coeff){
419 420 421 422
      mulSpectrums(yf,kf,new_alphaf,0);
      mulSpectrums(kf,kf_lambda,new_alphaf_den,0);
    }else{
      for(int i=0;i<yf.rows;i++){
Kurnianggoro's avatar
Kurnianggoro committed
423
        for(int j=0;j<yf.cols;j++){
Kurnianggoro's avatar
Kurnianggoro committed
424 425 426 427 428 429
          den = 1.0/(kf_lambda.at<Vec2d>(i,j)[0]*kf_lambda.at<Vec2d>(i,j)[0]+kf_lambda.at<Vec2d>(i,j)[1]*kf_lambda.at<Vec2d>(i,j)[1]);

          new_alphaf.at<Vec2d>(i,j)[0]=
          (yf.at<Vec2d>(i,j)[0]*kf_lambda.at<Vec2d>(i,j)[0]+yf.at<Vec2d>(i,j)[1]*kf_lambda.at<Vec2d>(i,j)[1])*den;
          new_alphaf.at<Vec2d>(i,j)[1]=
          (yf.at<Vec2d>(i,j)[1]*kf_lambda.at<Vec2d>(i,j)[0]-yf.at<Vec2d>(i,j)[0]*kf_lambda.at<Vec2d>(i,j)[1])*den;
Kurnianggoro's avatar
Kurnianggoro committed
430
        }
431 432
      }
    }
Kurnianggoro's avatar
Kurnianggoro committed
433

434
    // update the RLS model
435 436
    if(frame==0){
      alphaf=new_alphaf.clone();
437
      if(params.split_coeff)alphaf_den=new_alphaf_den.clone();
438 439
    }else{
      alphaf=(1.0-params.interp_factor)*alphaf+params.interp_factor*new_alphaf;
440
      if(params.split_coeff)alphaf_den=(1.0-params.interp_factor)*alphaf_den+params.interp_factor*new_alphaf_den;
441
    }
Kurnianggoro's avatar
Kurnianggoro committed
442

443 444 445
    frame++;
    return true;
  }
Kurnianggoro's avatar
Kurnianggoro committed
446 447


448 449 450
  /*-------------------------------------
  |  implementation of the KCF functions
  |-------------------------------------*/
Kurnianggoro's avatar
Kurnianggoro committed
451 452

  /*
453 454
   * hann window filter
   */
Kurnianggoro's avatar
Kurnianggoro committed
455
  void TrackerKCFImpl::createHanningWindow(OutputArray dest, const cv::Size winSize, const int type) const {
456 457
      CV_Assert( type == CV_32FC1 || type == CV_64FC1 );

Kurnianggoro's avatar
Kurnianggoro committed
458 459
      dest.create(winSize, type);
      Mat dst = dest.getMat();
460 461 462 463 464 465 466 467

      int rows = dst.rows, cols = dst.cols;

      AutoBuffer<double> _wc(cols);
      double * const wc = (double *)_wc;

      double coeff0 = 2.0 * CV_PI / (double)(cols - 1), coeff1 = 2.0f * CV_PI / (double)(rows - 1);
      for(int j = 0; j < cols; j++)
Kurnianggoro's avatar
Kurnianggoro committed
468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
        wc[j] = 0.5 * (1.0 - cos(coeff0 * j));

      if(dst.depth() == CV_32F){
        for(int i = 0; i < rows; i++){
          float* dstData = dst.ptr<float>(i);
          double wr = 0.5 * (1.0 - cos(coeff1 * i));
          for(int j = 0; j < cols; j++)
            dstData[j] = (float)(wr * wc[j]);
        }
      }else{
        for(int i = 0; i < rows; i++){
          double* dstData = dst.ptr<double>(i);
          double wr = 0.5 * (1.0 - cos(coeff1 * i));
          for(int j = 0; j < cols; j++)
            dstData[j] = wr * wc[j];
        }
484 485 486 487 488
      }

      // perform batch sqrt for SSE performance gains
      //cv::sqrt(dst, dst); //matlab do not use the square rooted version
  }
Kurnianggoro's avatar
Kurnianggoro committed
489

490
  /*
491
   * simplification of fourier transform function in opencv
492
   */
Kurnianggoro's avatar
Kurnianggoro committed
493
  void inline TrackerKCFImpl::fft2(const Mat src, Mat & dest) const {
Kurnianggoro's avatar
Kurnianggoro committed
494
    dft(src,dest,DFT_COMPLEX_OUTPUT);
495
  }
Kurnianggoro's avatar
Kurnianggoro committed
496

Kurnianggoro's avatar
Kurnianggoro committed
497 498
  void inline TrackerKCFImpl::fft2(const Mat src, std::vector<Mat> & dest, std::vector<Mat> & layers_data) const {
    split(src, layers_data);
Kurnianggoro's avatar
Kurnianggoro committed
499

500
    for(int i=0;i<src.channels();i++){
Kurnianggoro's avatar
Kurnianggoro committed
501
      dft(layers_data[i],dest[i],DFT_COMPLEX_OUTPUT);
502
    }
503 504 505
  }

  /*
506
   * simplification of inverse fourier transform function in opencv
507
   */
Kurnianggoro's avatar
Kurnianggoro committed
508
  void inline TrackerKCFImpl::ifft2(const Mat src, Mat & dest) const {
509 510
    idft(src,dest,DFT_SCALE+DFT_REAL_OUTPUT);
  }
Kurnianggoro's avatar
Kurnianggoro committed
511

512 513 514
  /*
   * Point-wise multiplication of two Multichannel Mat data
   */
Kurnianggoro's avatar
Kurnianggoro committed
515
  void inline TrackerKCFImpl::pixelWiseMult(const std::vector<Mat> src1, const std::vector<Mat>  src2, std::vector<Mat>  & dest, const int flags, const bool conjB) const {
516 517 518 519
    for(unsigned i=0;i<src1.size();i++){
      mulSpectrums(src1[i], src2[i], dest[i],flags,conjB);
    }
  }
Kurnianggoro's avatar
Kurnianggoro committed
520

521 522 523
  /*
   * Combines all channels in a multi-channels Mat data into a single channel
   */
Kurnianggoro's avatar
Kurnianggoro committed
524
  void inline TrackerKCFImpl::sumChannels(std::vector<Mat> src, Mat & dest) const {
525 526 527 528 529
    dest=src[0].clone();
    for(unsigned i=1;i<src.size();i++){
      dest+=src[i];
    }
  }
Kurnianggoro's avatar
Kurnianggoro committed
530

531 532 533
  /*
   * obtains the projection matrix using PCA
   */
Kurnianggoro's avatar
Kurnianggoro committed
534 535
  void inline TrackerKCFImpl::updateProjectionMatrix(const Mat src, Mat & old_cov,Mat &  proj_matrix, double pca_rate, int compressed_sz,
                                                     std::vector<Mat> & layers_pca,std::vector<Scalar> & average, Mat pca_data, Mat new_cov, Mat w, Mat u, Mat vt) const {
536
    CV_Assert(compressed_sz<=src.channels());
Kurnianggoro's avatar
Kurnianggoro committed
537

Kurnianggoro's avatar
Kurnianggoro committed
538
    split(src,layers_pca);
Kurnianggoro's avatar
Kurnianggoro committed
539

540
    for (int i=0;i<src.channels();i++){
Kurnianggoro's avatar
Kurnianggoro committed
541 542
      average[i]=mean(layers_pca[i]);
      layers_pca[i]-=average[i];
543 544 545
    }

    // calc covariance matrix
Kurnianggoro's avatar
Kurnianggoro committed
546 547
    merge(layers_pca,pca_data);
    pca_data=pca_data.reshape(1,src.rows*src.cols);
Kurnianggoro's avatar
Kurnianggoro committed
548

Kurnianggoro's avatar
Kurnianggoro committed
549
    new_cov=1.0/(double)(src.rows*src.cols-1)*(pca_data.t()*pca_data);
550
    if(old_cov.rows==0)old_cov=new_cov.clone();
Kurnianggoro's avatar
Kurnianggoro committed
551

552 553
    // calc PCA
    SVD::compute((1.0-pca_rate)*old_cov+pca_rate*new_cov, w, u, vt);
Kurnianggoro's avatar
Kurnianggoro committed
554

555
    // extract the projection matrix
Kurnianggoro's avatar
Kurnianggoro committed
556 557
    proj_matrix=u(Rect(0,0,compressed_sz,src.channels())).clone();
    Mat proj_vars=Mat::eye(compressed_sz,compressed_sz,proj_matrix.type());
558 559
    for(int i=0;i<compressed_sz;i++){
      proj_vars.at<double>(i,i)=w.at<double>(i);
Kurnianggoro's avatar
Kurnianggoro committed
560 561
    }

562
    // update the covariance matrix
Kurnianggoro's avatar
Kurnianggoro committed
563
    old_cov=(1.0-pca_rate)*old_cov+pca_rate*proj_matrix*proj_vars*proj_matrix.t();
564
  }
Kurnianggoro's avatar
Kurnianggoro committed
565

566 567 568
  /*
   * compress the features
   */
Kurnianggoro's avatar
Kurnianggoro committed
569 570 571 572
  void inline TrackerKCFImpl::compress(const Mat proj_matrix, const Mat src, Mat & dest, Mat & data, Mat & compressed) const {
    data=src.reshape(1,src.rows*src.cols);
    compressed=data*proj_matrix;
    dest=compressed.reshape(proj_matrix.cols,src.rows).clone();
573
  }
Kurnianggoro's avatar
Kurnianggoro committed
574

575 576 577
  /*
   * obtain the patch and apply hann window filter to it
   */
Kurnianggoro's avatar
Kurnianggoro committed
578
  bool TrackerKCFImpl::getSubWindow(const Mat img, const Rect _roi, Mat& feat, Mat& patch, TrackerKCF::MODE desc) const {
579

580
    Rect region=_roi;
Kurnianggoro's avatar
Kurnianggoro committed
581

582
    // return false if roi is outside the image
583 584
    if((roi & Rect2d(0,0, img.cols, img.rows)) == Rect2d() )
        return false;
Kurnianggoro's avatar
Kurnianggoro committed
585 586

    // extract patch inside the image
587 588 589 590
    if(_roi.x<0){region.x=0;region.width+=_roi.x;}
    if(_roi.y<0){region.y=0;region.height+=_roi.y;}
    if(_roi.x+_roi.width>img.cols)region.width=img.cols-_roi.x;
    if(_roi.y+_roi.height>img.rows)region.height=img.rows-_roi.y;
591 592 593 594
    if(region.width>img.cols)region.width=img.cols;
    if(region.height>img.rows)region.height=img.rows;

    patch=img(region).clone();
Kurnianggoro's avatar
Kurnianggoro committed
595

596 597
    // add some padding to compensate when the patch is outside image border
    int addTop,addBottom, addLeft, addRight;
598 599 600 601
    addTop=region.y-_roi.y;
    addBottom=(_roi.height+_roi.y>img.rows?_roi.height+_roi.y-img.rows:0);
    addLeft=region.x-_roi.x;
    addRight=(_roi.width+_roi.x>img.cols?_roi.width+_roi.x-img.cols:0);
602 603

    copyMakeBorder(patch,patch,addTop,addBottom,addLeft,addRight,BORDER_REPLICATE);
604
    if(patch.rows==0 || patch.cols==0)return false;
Kurnianggoro's avatar
Kurnianggoro committed
605

606
    // extract the desired descriptors
Kurnianggoro's avatar
Kurnianggoro committed
607
    switch(desc){
608
      case CN:
Kurnianggoro's avatar
Kurnianggoro committed
609
        CV_Assert(img.channels() == 3);
Kurnianggoro's avatar
Kurnianggoro committed
610 611
        extractCN(patch,feat);
        feat=feat.mul(hann_cn); // hann window filter
Kurnianggoro's avatar
Kurnianggoro committed
612
        break;
Kurnianggoro's avatar
Kurnianggoro committed
613 614 615 616 617 618 619 620
      default: // GRAY
        if(img.channels()>1)
          cvtColor(patch,feat, CV_BGR2GRAY);
        else
          feat=patch;
        feat.convertTo(feat,CV_64F);
        feat=feat/255.0-0.5; // normalize to range -0.5 .. 0.5
        feat=feat.mul(hann); // hann window filter
Kurnianggoro's avatar
Kurnianggoro committed
621
        break;
622
    }
Kurnianggoro's avatar
Kurnianggoro committed
623

624
    return true;
Kurnianggoro's avatar
Kurnianggoro committed
625

626
  }
Kurnianggoro's avatar
Kurnianggoro committed
627

Kurnianggoro's avatar
Kurnianggoro committed
628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
  /*
   * get feature using external function
   */
  bool TrackerKCFImpl::getSubWindow(const Mat img, const Rect _roi, Mat& feat, void (*f)(const Mat, const Rect, Mat& )) const{

    // return false if roi is outside the image
    if((_roi.x+_roi.width<0)
      ||(_roi.y+_roi.height<0)
      ||(_roi.x>=img.cols)
      ||(_roi.y>=img.rows)
    )return false;

    f(img, _roi, feat);

    if(_roi.width != feat.cols || _roi.height != feat.rows){
      printf("error in customized function of features extractor!\n");
      printf("Rules: roi.width==feat.cols && roi.height = feat.rows \n");
    }

    Mat hann_win;
    std::vector<Mat> _layers;

    for(int i=0;i<feat.channels();i++)
      _layers.push_back(hann);

    merge(_layers, hann_win);

    feat=feat.mul(hann_win); // hann window filter

    return true;
  }

660 661
  /* Convert BGR to ColorNames
   */
Kurnianggoro's avatar
Kurnianggoro committed
662 663
  void TrackerKCFImpl::extractCN(Mat patch_data, Mat & cnFeatures) const {
    Vec3b & pixel = patch_data.at<Vec3b>(0,0);
664
    unsigned index;
Kurnianggoro's avatar
Kurnianggoro committed
665

Kurnianggoro's avatar
Kurnianggoro committed
666 667
    if(cnFeatures.type() != CV_64FC(10))
      cnFeatures = Mat::zeros(patch_data.rows,patch_data.cols,CV_64FC(10));
Kurnianggoro's avatar
Kurnianggoro committed
668

Kurnianggoro's avatar
Kurnianggoro committed
669 670 671
    for(int i=0;i<patch_data.rows;i++){
      for(int j=0;j<patch_data.cols;j++){
        pixel=patch_data.at<Vec3b>(i,j);
672
        index=(unsigned)(floor((float)pixel[2]/8)+32*floor((float)pixel[1]/8)+32*32*floor((float)pixel[0]/8));
Kurnianggoro's avatar
Kurnianggoro committed
673 674 675

        //copy the values
        for(int _k=0;_k<10;_k++){
Kurnianggoro's avatar
Kurnianggoro committed
676
          cnFeatures.at<Vec<double,10> >(i,j)[_k]=ColorNames[index][_k];
Kurnianggoro's avatar
Kurnianggoro committed
677
        }
678 679
      }
    }
Kurnianggoro's avatar
Kurnianggoro committed
680

681
  }
Kurnianggoro's avatar
Kurnianggoro committed
682

683 684 685
  /*
   *  dense gauss kernel function
   */
Kurnianggoro's avatar
Kurnianggoro committed
686 687
  void TrackerKCFImpl::denseGaussKernel(const double sigma, const Mat x_data, const Mat y_data, Mat & k_data,
                                        std::vector<Mat> & layers_data,std::vector<Mat> & xf_data,std::vector<Mat> & yf_data, std::vector<Mat> xyf_v, Mat xy, Mat xyf ) const {
688
    double normX, normY;
Kurnianggoro's avatar
Kurnianggoro committed
689

Kurnianggoro's avatar
Kurnianggoro committed
690 691
    fft2(x_data,xf_data,layers_data);
    fft2(y_data,yf_data,layers_data);
Kurnianggoro's avatar
Kurnianggoro committed
692

Kurnianggoro's avatar
Kurnianggoro committed
693
    normX=norm(x_data);
694
    normX*=normX;
Kurnianggoro's avatar
Kurnianggoro committed
695
    normY=norm(y_data);
696
    normY*=normY;
Kurnianggoro's avatar
Kurnianggoro committed
697

Kurnianggoro's avatar
Kurnianggoro committed
698
    pixelWiseMult(xf_data,yf_data,xyf_v,0,true);
699
    sumChannels(xyf_v,xyf);
700
    ifft2(xyf,xyf);
Kurnianggoro's avatar
Kurnianggoro committed
701

702
    if(params.wrap_kernel){
Kurnianggoro's avatar
Kurnianggoro committed
703 704
      shiftRows(xyf, x_data.rows/2);
      shiftCols(xyf, x_data.cols/2);
705
    }
Kurnianggoro's avatar
Kurnianggoro committed
706

707
    //(xx + yy - 2 * xy) / numel(x)
Kurnianggoro's avatar
Kurnianggoro committed
708
    xy=(normX+normY-2*xyf)/(x_data.rows*x_data.cols*x_data.channels());
709 710 711

    // TODO: check wether we really need thresholding or not
    //threshold(xy,xy,0.0,0.0,THRESH_TOZERO);//max(0, (xx + yy - 2 * xy) / numel(x))
712 713
    for(int i=0;i<xy.rows;i++){
      for(int j=0;j<xy.cols;j++){
Kurnianggoro's avatar
Kurnianggoro committed
714
        if(xy.at<double>(i,j)<0.0)xy.at<double>(i,j)=0.0;
715 716
      }
    }
Kurnianggoro's avatar
Kurnianggoro committed
717

718 719
    double sig=-1.0/(sigma*sigma);
    xy=sig*xy;
Kurnianggoro's avatar
Kurnianggoro committed
720
    exp(xy,k_data);
721 722

  }
Kurnianggoro's avatar
Kurnianggoro committed
723

724
  /* CIRCULAR SHIFT Function
725 726 727
   * http://stackoverflow.com/questions/10420454/shift-like-matlab-function-rows-or-columns-of-a-matrix-in-opencv
   */
  // circular shift one row from up to down
728
  void TrackerKCFImpl::shiftRows(Mat& mat) const {
729 730 731

      Mat temp;
      Mat m;
732 733 734
      int _k = (mat.rows-1);
      mat.row(_k).copyTo(temp);
      for(; _k > 0 ; _k-- ) {
Kurnianggoro's avatar
Kurnianggoro committed
735 736
        m = mat.row(_k);
        mat.row(_k-1).copyTo(m);
737 738 739 740 741 742 743
      }
      m = mat.row(0);
      temp.copyTo(m);

  }

  // circular shift n rows from up to down if n > 0, -n rows from down to up if n < 0
744
  void TrackerKCFImpl::shiftRows(Mat& mat, int n) const {
745
      if( n < 0 ) {
Kurnianggoro's avatar
Kurnianggoro committed
746 747 748 749 750 751
        n = -n;
        flip(mat,mat,0);
        for(int _k=0; _k < n;_k++) {
          shiftRows(mat);
        }
        flip(mat,mat,0);
Kurnianggoro's avatar
Kurnianggoro committed
752
      }else{
Kurnianggoro's avatar
Kurnianggoro committed
753 754 755
        for(int _k=0; _k < n;_k++) {
          shiftRows(mat);
        }
756 757 758 759
      }
  }

  //circular shift n columns from left to right if n > 0, -n columns from right to left if n < 0
760
  void TrackerKCFImpl::shiftCols(Mat& mat, int n) const {
761
      if(n < 0){
Kurnianggoro's avatar
Kurnianggoro committed
762 763 764 765 766 767 768 769 770 771
        n = -n;
        flip(mat,mat,1);
        transpose(mat,mat);
        shiftRows(mat,n);
        transpose(mat,mat);
        flip(mat,mat,1);
      }else{
        transpose(mat,mat);
        shiftRows(mat,n);
        transpose(mat,mat);
772 773 774 775 776 777
      }
  }

  /*
   * calculate the detection response
   */
Kurnianggoro's avatar
Kurnianggoro committed
778
  void TrackerKCFImpl::calcResponse(const Mat alphaf_data, const Mat kf_data, Mat & response_data, Mat & spec_data) const {
Kurnianggoro's avatar
Kurnianggoro committed
779
    //alpha f--> 2channels ; k --> 1 channel;
Kurnianggoro's avatar
Kurnianggoro committed
780 781
    mulSpectrums(alphaf_data,kf_data,spec_data,0,false);
    ifft2(spec_data,response_data);
782
  }
Kurnianggoro's avatar
Kurnianggoro committed
783

784 785 786
  /*
   * calculate the detection response for splitted form
   */
Kurnianggoro's avatar
Kurnianggoro committed
787 788 789 790 791 792 793 794 795 796 797 798 799
  void TrackerKCFImpl::calcResponse(const Mat alphaf_data, const Mat _alphaf_den, const Mat kf_data, Mat & response_data, Mat & spec_data, Mat & spec2_data) const {

    mulSpectrums(alphaf_data,kf_data,spec_data,0,false);

    //z=(a+bi)/(c+di)=[(ac+bd)+i(bc-ad)]/(c^2+d^2)
    double den;
    for(int i=0;i<kf_data.rows;i++){
      for(int j=0;j<kf_data.cols;j++){
        den=1.0/(_alphaf_den.at<Vec2d>(i,j)[0]*_alphaf_den.at<Vec2d>(i,j)[0]+_alphaf_den.at<Vec2d>(i,j)[1]*_alphaf_den.at<Vec2d>(i,j)[1]);
        spec2_data.at<Vec2d>(i,j)[0]=
          (spec_data.at<Vec2d>(i,j)[0]*_alphaf_den.at<Vec2d>(i,j)[0]+spec_data.at<Vec2d>(i,j)[1]*_alphaf_den.at<Vec2d>(i,j)[1])*den;
        spec2_data.at<Vec2d>(i,j)[1]=
          (spec_data.at<Vec2d>(i,j)[1]*_alphaf_den.at<Vec2d>(i,j)[0]-spec_data.at<Vec2d>(i,j)[0]*_alphaf_den.at<Vec2d>(i,j)[1])*den;
800 801
      }
    }
Kurnianggoro's avatar
Kurnianggoro committed
802

Kurnianggoro's avatar
Kurnianggoro committed
803 804 805 806 807 808 809 810 811 812 813
    ifft2(spec2_data,response_data);
  }

  void TrackerKCFImpl::setFeatureExtractor(void (*f)(const Mat, const Rect, Mat&), bool pca_func){
    if(pca_func){
      extractor_pca.push_back(f);
      use_custom_extractor_pca = true;
    }else{
      extractor_npca.push_back(f);
      use_custom_extractor_npca = true;
    }
814
  }
815
  /*----------------------------------------------------------------------*/
Kurnianggoro's avatar
Kurnianggoro committed
816

817 818 819 820 821 822 823 824
  /*
 * Parameters
 */
  TrackerKCF::Params::Params(){
      sigma=0.2;
      lambda=0.01;
      interp_factor=0.075;
      output_sigma_factor=1.0/16.0;
825
      resize=true;
826
      max_patch_size=80*80;
827 828
      split_coeff=true;
      wrap_kernel=false;
Kurnianggoro's avatar
Kurnianggoro committed
829 830
      desc_npca = GRAY;
      desc_pca = CN;
Kurnianggoro's avatar
Kurnianggoro committed
831

832
      //feature compression
833
      compress_feature=true;
834 835
      compressed_size=2;
      pca_learning_rate=0.15;
836 837
  }

838 839
  void TrackerKCF::Params::read( const cv::FileNode& fn ){
      *this = TrackerKCF::Params();
840

841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896
      if (!fn["sigma"].empty())
          fn["sigma"] >> sigma;

      if (!fn["lambda"].empty())
          fn["lambda"] >> lambda;

      if (!fn["interp_factor"].empty())
          fn["interp_factor"] >> interp_factor;

      if (!fn["output_sigma_factor"].empty())
          fn["output_sigma_factor"] >> output_sigma_factor;

      if (!fn["resize"].empty())
          fn["resize"] >> resize;

      if (!fn["max_patch_size"].empty())
          fn["max_patch_size"] >> max_patch_size;

      if (!fn["split_coeff"].empty())
          fn["split_coeff"] >> split_coeff;

      if (!fn["wrap_kernel"].empty())
          fn["wrap_kernel"] >> wrap_kernel;


      if (!fn["desc_npca"].empty())
          fn["desc_npca"] >> desc_npca;

      if (!fn["desc_pca"].empty())
          fn["desc_pca"] >> desc_pca;

      if (!fn["compress_feature"].empty())
          fn["compress_feature"] >> compress_feature;

      if (!fn["compressed_size"].empty())
          fn["compressed_size"] >> compressed_size;

      if (!fn["pca_learning_rate"].empty())
          fn["pca_learning_rate"] >> pca_learning_rate;
  }

  void TrackerKCF::Params::write( cv::FileStorage& fs ) const{
    fs << "sigma" << sigma;
    fs << "lambda" << lambda;
    fs << "interp_factor" << interp_factor;
    fs << "output_sigma_factor" << output_sigma_factor;
    fs << "resize" << resize;
    fs << "max_patch_size" << max_patch_size;
    fs << "split_coeff" << split_coeff;
    fs << "wrap_kernel" << wrap_kernel;
    fs << "desc_npca" << desc_npca;
    fs << "desc_pca" << desc_pca;
    fs << "compress_feature" << compress_feature;
    fs << "compressed_size" << compressed_size;
    fs << "pca_learning_rate" << pca_learning_rate;
  }
Kurnianggoro's avatar
Kurnianggoro committed
897

Kurnianggoro's avatar
Kurnianggoro committed
898 899
  void TrackerKCF::setFeatureExtractor(void (*)(const Mat, const Rect, Mat&), bool ){};

900
} /* namespace cv */