trackerKCF.cpp 33.2 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
/*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"
43
#include "opencl_kernels_tracking.hpp"
44
#include <complex>
45
#include <cmath>
46 47 48 49 50 51

/*---------------------------
|  TrackerKCFModel
|---------------------------*/
namespace cv{
   /**
f3rm4rf3r's avatar
f3rm4rf3r committed
52
  * \brief Implementation of TrackerModel for KCF algorithm
53 54 55 56 57 58
  */
  class TrackerKCFModel : public TrackerModel{
  public:
    TrackerKCFModel(TrackerKCF::Params /*params*/){}
    ~TrackerKCFModel(){}
  protected:
59 60
    void modelEstimationImpl( const std::vector<Mat>& /*responses*/ ) CV_OVERRIDE {}
    void modelUpdateImpl() CV_OVERRIDE {}
61 62 63 64 65 66 67 68
  };
} /* namespace cv */


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

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

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

87
    TrackerKCF::Params params;
Kurnianggoro's avatar
Kurnianggoro committed
88

89 90 91
    /*
    * KCF functions and vars
    */
Kurnianggoro's avatar
Kurnianggoro committed
92 93
    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;
94
    void inline fft2(const Mat src, Mat & dest) const;
Kurnianggoro's avatar
Kurnianggoro committed
95
    void inline ifft2(const Mat src, Mat & dest) const;
96 97
    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;
98 99
    void inline updateProjectionMatrix(const Mat src, Mat & old_cov,Mat &  proj_matrix,float 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);
Kurnianggoro's avatar
Kurnianggoro committed
100 101 102 103
    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;
104
    void denseGaussKernel(const float sigma, const Mat , const Mat y_data, Mat & k_data,
Kurnianggoro's avatar
Kurnianggoro committed
105 106 107
                          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
108 109

    void shiftRows(Mat& mat) const;
110 111
    void shiftRows(Mat& mat, int n) const;
    void shiftCols(Mat& mat, int n) const;
112 113 114
#ifdef HAVE_OPENCL
    bool inline oclTransposeMM(const Mat src, float alpha, UMat &dst);
#endif
Kurnianggoro's avatar
Kurnianggoro committed
115

116
  private:
117
    float output_sigma;
118 119
    Rect2d roi;
    Mat hann; 	//hann window filter
Kurnianggoro's avatar
Kurnianggoro committed
120
    Mat hann_cn; //10 dimensional hann-window filter for CN features,
Kurnianggoro's avatar
Kurnianggoro committed
121

122
    Mat y,yf; 	// training response and its FFT
Kurnianggoro's avatar
Kurnianggoro committed
123
    Mat x; 	// observation and its FFT
124
    Mat k,kf;	// dense gaussian kernel and its FFT
125 126 127
    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
128
    Mat z; // model
129
    Mat response; // detection result
130
    Mat old_cov_mtx, proj_mtx; // for feature compression
Kurnianggoro's avatar
Kurnianggoro committed
131

Kurnianggoro's avatar
Kurnianggoro committed
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
    // 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;

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

162 163 164 165
#ifdef HAVE_OPENCL
    ocl::Kernel transpose_mm_ker; // OCL kernel to compute transpose matrix multiply matrix.
#endif

166 167
    int frame;
  };
Kurnianggoro's avatar
Kurnianggoro committed
168

169 170 171
  /*
 * Constructor
 */
172
  Ptr<TrackerKCF> TrackerKCF::create(const TrackerKCF::Params &parameters){
173 174
      return Ptr<TrackerKCFImpl>(new TrackerKCFImpl(parameters));
  }
175 176 177
  Ptr<TrackerKCF> TrackerKCF::create(){
      return Ptr<TrackerKCFImpl>(new TrackerKCFImpl());
  }
178 179 180 181
  TrackerKCFImpl::TrackerKCFImpl( const TrackerKCF::Params &parameters ) :
      params( parameters )
  {
    isInit = false;
182
    resizeImage = false;
Kurnianggoro's avatar
Kurnianggoro committed
183 184
    use_custom_extractor_pca = false;
    use_custom_extractor_npca = false;
Kurnianggoro's avatar
Kurnianggoro committed
185

186 187 188 189 190 191 192 193 194 195
#ifdef HAVE_OPENCL
    // For update proj matrix's multiplication
    if(ocl::useOpenCL())
    {
        cv::String err;
        ocl::ProgramSource tmmSrc = ocl::tracking::tmm_oclsrc;
        ocl::Program tmmProg(tmmSrc, String(), err);
        transpose_mm_ker.create("tmm", tmmProg);
    }
#endif
196
  }
Kurnianggoro's avatar
Kurnianggoro committed
197

198 199 200 201
  void TrackerKCFImpl::read( const cv::FileNode& fn ){
    params.read( fn );
  }

Kurnianggoro's avatar
Kurnianggoro committed
202
  void TrackerKCFImpl::write( cv::FileStorage& fs ) const {
203 204
    params.write( fs );
  }
Kurnianggoro's avatar
Kurnianggoro committed
205

206
  /*
Kurnianggoro's avatar
Kurnianggoro committed
207
   * Initialization:
208 209 210 211 212
   * - creating hann window filter
   * - ROI padding
   * - creating a gaussian response for the training ground-truth
   * - perform FFT to the gaussian response
   */
213
  bool TrackerKCFImpl::initImpl( const Mat& image, const Rect2d& boundingBox ){
214
    frame=0;
215 216 217 218
    roi.x = cvRound(boundingBox.x);
    roi.y = cvRound(boundingBox.y);
    roi.width = cvRound(boundingBox.width);
    roi.height = cvRound(boundingBox.height);
Kurnianggoro's avatar
Kurnianggoro committed
219

220
    //calclulate output sigma
221 222
    output_sigma=std::sqrt(static_cast<float>(roi.width*roi.height))*params.output_sigma_factor;
    output_sigma=-0.5f/(output_sigma*output_sigma);
Kurnianggoro's avatar
Kurnianggoro committed
223

224
    //resize the ROI whenever needed
225
    if(params.resize && roi.width*roi.height>params.max_patch_size){
226 227 228 229 230
      resizeImage=true;
      roi.x/=2.0;
      roi.y/=2.0;
      roi.width/=2.0;
      roi.height/=2.0;
Kurnianggoro's avatar
Kurnianggoro committed
231 232
    }

233 234
    // add padding to the roi
    roi.x-=roi.width/2;
235
    roi.y-=roi.height/2;
236 237
    roi.width*=2;
    roi.height*=2;
Kurnianggoro's avatar
Kurnianggoro committed
238

239
    // initialize the hann window filter
240
    createHanningWindow(hann, roi.size(), CV_32F);
Kurnianggoro's avatar
Kurnianggoro committed
241 242 243 244

    // 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
245

246
    // create gaussian response
247
    y=Mat::zeros((int)roi.height,(int)roi.width,CV_32F);
248 249
    for(int i=0;i<int(roi.height);i++){
      for(int j=0;j<int(roi.width);j++){
250 251
        y.at<float>(i,j) =
                static_cast<float>((i-roi.height/2+1)*(i-roi.height/2+1)+(j-roi.width/2+1)*(j-roi.width/2+1));
252 253
      }
    }
Kurnianggoro's avatar
Kurnianggoro committed
254

255
    y*=(float)output_sigma;
256
    cv::exp(y,y);
Kurnianggoro's avatar
Kurnianggoro committed
257

258 259
    // perform fourier transfor to the gaussian response
    fft2(y,yf);
Kurnianggoro's avatar
Kurnianggoro committed
260

261 262 263 264
    if (image.channels() == 1) { // disable CN for grayscale images
      params.desc_pca &= ~(CN);
      params.desc_npca &= ~(CN);
    }
265
    model=Ptr<TrackerKCFModel>(new TrackerKCFModel(params));
Kurnianggoro's avatar
Kurnianggoro committed
266

Kurnianggoro's avatar
Kurnianggoro committed
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
    // 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
    );

289 290 291 292
  //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;
293

294 295
    return true;
  }
Kurnianggoro's avatar
Kurnianggoro committed
296

297 298 299 300 301 302
  /*
   * 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
303

304
    Mat img=image.clone();
305
    // check the channels of the input image, grayscale is preferred
Kurnianggoro's avatar
Kurnianggoro committed
306
    CV_Assert(img.channels() == 1 || img.channels() == 3);
Kurnianggoro's avatar
Kurnianggoro committed
307

308
    // resize the image whenever needed
309
    if(resizeImage)resize(img,img,Size(img.cols/2,img.rows/2),0,0,INTER_LINEAR_EXACT);
Kurnianggoro's avatar
Kurnianggoro committed
310

311 312
    // detection part
    if(frame>0){
Kurnianggoro's avatar
Kurnianggoro committed
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355

      // 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);
      }

356
      //compute the gaussian kernel
Kurnianggoro's avatar
Kurnianggoro committed
357 358 359 360
      denseGaussKernel(params.sigma,x,z,k,layers,vxf,vyf,vxyf,xy_data,xyf_data);

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

363
      // calculate filter response
364
      if(params.split_coeff)
Kurnianggoro's avatar
Kurnianggoro committed
365
        calcResponse(alphaf,alphaf_den,kf,response, spec, spec2);
366
      else
Kurnianggoro's avatar
Kurnianggoro committed
367
        calcResponse(alphaf,kf,response, spec);
Kurnianggoro's avatar
Kurnianggoro committed
368

369 370
      // extract the maximum response
      minMaxLoc( response, &minVal, &maxVal, &minLoc, &maxLoc );
371 372 373 374
      if (maxVal < params.detect_thresh)
      {
          return false;
      }
375 376
      roi.x+=(maxLoc.x-roi.width/2+1);
      roi.y+=(maxLoc.y-roi.height/2+1);
377
    }
Kurnianggoro's avatar
Kurnianggoro committed
378

379 380 381 382 383 384
    // 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;

385
    // extract the patch for learning purpose
Kurnianggoro's avatar
Kurnianggoro committed
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
    // 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
405

406
    //update the training data
Kurnianggoro's avatar
Kurnianggoro committed
407 408 409 410 411 412 413 414 415 416 417 418 419 420
    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
421

422
      // feature compression
Kurnianggoro's avatar
Kurnianggoro committed
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
      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());
441
      new_alphaf=Mat_<Vec2f >(yf.rows, yf.cols);
442
    }
Kurnianggoro's avatar
Kurnianggoro committed
443

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

Kurnianggoro's avatar
Kurnianggoro committed
447
    // compute the fourier transform of the kernel and add a small value
448
    fft2(k,kf);
449
    kf_lambda=kf+params.lambda;
Kurnianggoro's avatar
Kurnianggoro committed
450

451
    float den;
452
    if(params.split_coeff){
453 454 455 456
      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
457
        for(int j=0;j<yf.cols;j++){
458
          den = 1.0f/(kf_lambda.at<Vec2f>(i,j)[0]*kf_lambda.at<Vec2f>(i,j)[0]+kf_lambda.at<Vec2f>(i,j)[1]*kf_lambda.at<Vec2f>(i,j)[1]);
Kurnianggoro's avatar
Kurnianggoro committed
459

460 461 462 463
          new_alphaf.at<Vec2f>(i,j)[0]=
          (yf.at<Vec2f>(i,j)[0]*kf_lambda.at<Vec2f>(i,j)[0]+yf.at<Vec2f>(i,j)[1]*kf_lambda.at<Vec2f>(i,j)[1])*den;
          new_alphaf.at<Vec2f>(i,j)[1]=
          (yf.at<Vec2f>(i,j)[1]*kf_lambda.at<Vec2f>(i,j)[0]-yf.at<Vec2f>(i,j)[0]*kf_lambda.at<Vec2f>(i,j)[1])*den;
Kurnianggoro's avatar
Kurnianggoro committed
464
        }
465 466
      }
    }
Kurnianggoro's avatar
Kurnianggoro committed
467

468
    // update the RLS model
469 470
    if(frame==0){
      alphaf=new_alphaf.clone();
471
      if(params.split_coeff)alphaf_den=new_alphaf_den.clone();
472 473
    }else{
      alphaf=(1.0-params.interp_factor)*alphaf+params.interp_factor*new_alphaf;
474
      if(params.split_coeff)alphaf_den=(1.0-params.interp_factor)*alphaf_den+params.interp_factor*new_alphaf_den;
475
    }
Kurnianggoro's avatar
Kurnianggoro committed
476

477 478 479
    frame++;
    return true;
  }
Kurnianggoro's avatar
Kurnianggoro committed
480 481


482 483 484
  /*-------------------------------------
  |  implementation of the KCF functions
  |-------------------------------------*/
Kurnianggoro's avatar
Kurnianggoro committed
485 486

  /*
487 488
   * hann window filter
   */
Kurnianggoro's avatar
Kurnianggoro committed
489
  void TrackerKCFImpl::createHanningWindow(OutputArray dest, const cv::Size winSize, const int type) const {
490 491
      CV_Assert( type == CV_32FC1 || type == CV_64FC1 );

Kurnianggoro's avatar
Kurnianggoro committed
492 493
      dest.create(winSize, type);
      Mat dst = dest.getMat();
494 495 496

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

497
      AutoBuffer<float> _wc(cols);
498
      float * const wc = _wc.data();
499

500 501
      const float coeff0 = 2.0f * (float)CV_PI / (cols - 1);
      const float coeff1 = 2.0f * (float)CV_PI / (rows - 1);
502
      for(int j = 0; j < cols; j++)
503
        wc[j] = 0.5f * (1.0f - cos(coeff0 * j));
Kurnianggoro's avatar
Kurnianggoro committed
504 505 506 507

      if(dst.depth() == CV_32F){
        for(int i = 0; i < rows; i++){
          float* dstData = dst.ptr<float>(i);
508
          float wr = 0.5f * (1.0f - cos(coeff1 * i));
Kurnianggoro's avatar
Kurnianggoro committed
509 510 511 512 513 514
          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);
515
          double wr = 0.5f * (1.0f - cos(coeff1 * i));
Kurnianggoro's avatar
Kurnianggoro committed
516 517 518
          for(int j = 0; j < cols; j++)
            dstData[j] = wr * wc[j];
        }
519 520 521 522 523
      }

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

525
  /*
526
   * simplification of fourier transform function in opencv
527
   */
Kurnianggoro's avatar
Kurnianggoro committed
528
  void inline TrackerKCFImpl::fft2(const Mat src, Mat & dest) const {
Kurnianggoro's avatar
Kurnianggoro committed
529
    dft(src,dest,DFT_COMPLEX_OUTPUT);
530
  }
Kurnianggoro's avatar
Kurnianggoro committed
531

Kurnianggoro's avatar
Kurnianggoro committed
532 533
  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
534

535
    for(int i=0;i<src.channels();i++){
Kurnianggoro's avatar
Kurnianggoro committed
536
      dft(layers_data[i],dest[i],DFT_COMPLEX_OUTPUT);
537
    }
538 539 540
  }

  /*
541
   * simplification of inverse fourier transform function in opencv
542
   */
Kurnianggoro's avatar
Kurnianggoro committed
543
  void inline TrackerKCFImpl::ifft2(const Mat src, Mat & dest) const {
544 545
    idft(src,dest,DFT_SCALE+DFT_REAL_OUTPUT);
  }
Kurnianggoro's avatar
Kurnianggoro committed
546

547 548 549
  /*
   * Point-wise multiplication of two Multichannel Mat data
   */
Kurnianggoro's avatar
Kurnianggoro committed
550
  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 {
551 552 553 554
    for(unsigned i=0;i<src1.size();i++){
      mulSpectrums(src1[i], src2[i], dest[i],flags,conjB);
    }
  }
Kurnianggoro's avatar
Kurnianggoro committed
555

556 557 558
  /*
   * Combines all channels in a multi-channels Mat data into a single channel
   */
Kurnianggoro's avatar
Kurnianggoro committed
559
  void inline TrackerKCFImpl::sumChannels(std::vector<Mat> src, Mat & dest) const {
560 561 562 563 564
    dest=src[0].clone();
    for(unsigned i=1;i<src.size();i++){
      dest+=src[i];
    }
  }
Kurnianggoro's avatar
Kurnianggoro committed
565

566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591
#ifdef HAVE_OPENCL
  bool inline TrackerKCFImpl::oclTransposeMM(const Mat src, float alpha, UMat &dst){
    // Current kernel only support matrix's rows is multiple of 4.
    // And if one line is less than 512KB, CPU will likely be faster.
    if (transpose_mm_ker.empty() ||
        src.rows % 4 != 0 ||
        (src.rows * 10) < (1024 * 1024 / 4))
      return false;

    Size s(src.rows, src.cols);
    const Mat tmp = src.t();
    const UMat uSrc = tmp.getUMat(ACCESS_READ);
    transpose_mm_ker.args(
        ocl::KernelArg::PtrReadOnly(uSrc),
        (int)uSrc.rows,
        (int)uSrc.cols,
        alpha,
        ocl::KernelArg::PtrWriteOnly(dst));
    size_t globSize[2] = {static_cast<size_t>(src.cols * 64), static_cast<size_t>(src.cols)};
    size_t localSize[2] = {64, 1};
    if (!transpose_mm_ker.run(2, globSize, localSize, true))
      return false;
    return true;
  }
#endif

592 593 594
  /*
   * obtains the projection matrix using PCA
   */
595 596
  void inline TrackerKCFImpl::updateProjectionMatrix(const Mat src, Mat & old_cov,Mat &  proj_matrix, float 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) {
597
    CV_Assert(compressed_sz<=src.channels());
Kurnianggoro's avatar
Kurnianggoro committed
598

Kurnianggoro's avatar
Kurnianggoro committed
599
    split(src,layers_pca);
Kurnianggoro's avatar
Kurnianggoro committed
600

601
    for (int i=0;i<src.channels();i++){
Kurnianggoro's avatar
Kurnianggoro committed
602 603
      average[i]=mean(layers_pca[i]);
      layers_pca[i]-=average[i];
604 605 606
    }

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

610 611 612 613
#ifdef HAVE_OPENCL
    bool oclSucceed = false;
    Size s(pca_data.cols, pca_data.cols);
    UMat result(s, pca_data.type());
614
    if (oclTransposeMM(pca_data, 1.0f/(float)(src.rows*src.cols-1), result)) {
615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633
      if(old_cov.rows==0) old_cov=result.getMat(ACCESS_READ).clone();
      SVD::compute((1.0-pca_rate)*old_cov + pca_rate * result.getMat(ACCESS_READ), w, u, vt);
      oclSucceed = true;
    }
#define TMM_VERIFICATION 0

    if (oclSucceed == false || TMM_VERIFICATION) {
      new_cov=1.0f/(float)(src.rows*src.cols-1)*(pca_data.t()*pca_data);
#if TMM_VERIFICATION
      for(int i = 0; i < new_cov.rows; i++)
        for(int j = 0; j < new_cov.cols; j++)
          if (abs(new_cov.at<float>(i, j) - result.getMat(ACCESS_RW).at<float>(i , j)) > abs(new_cov.at<float>(i, j)) * 1e-3)
            printf("error @ i %d j %d got %G expected %G \n", i, j, result.getMat(ACCESS_RW).at<float>(i , j), new_cov.at<float>(i, j));
#endif
      if(old_cov.rows==0)old_cov=new_cov.clone();
      SVD::compute((1.0f - pca_rate) * old_cov + pca_rate * new_cov, w, u, vt);
    }
#else
    new_cov=1.0/(float)(src.rows*src.cols-1)*(pca_data.t()*pca_data);
634
    if(old_cov.rows==0)old_cov=new_cov.clone();
Kurnianggoro's avatar
Kurnianggoro committed
635

636 637
    // calc PCA
    SVD::compute((1.0-pca_rate)*old_cov+pca_rate*new_cov, w, u, vt);
638
#endif
639
    // extract the projection matrix
Kurnianggoro's avatar
Kurnianggoro committed
640 641
    proj_matrix=u(Rect(0,0,compressed_sz,src.channels())).clone();
    Mat proj_vars=Mat::eye(compressed_sz,compressed_sz,proj_matrix.type());
642
    for(int i=0;i<compressed_sz;i++){
643
      proj_vars.at<float>(i,i)=w.at<float>(i);
Kurnianggoro's avatar
Kurnianggoro committed
644 645
    }

646
    // update the covariance matrix
Kurnianggoro's avatar
Kurnianggoro committed
647
    old_cov=(1.0-pca_rate)*old_cov+pca_rate*proj_matrix*proj_vars*proj_matrix.t();
648
  }
Kurnianggoro's avatar
Kurnianggoro committed
649

650 651 652
  /*
   * compress the features
   */
Kurnianggoro's avatar
Kurnianggoro committed
653 654 655 656
  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();
657
  }
Kurnianggoro's avatar
Kurnianggoro committed
658

659 660 661
  /*
   * obtain the patch and apply hann window filter to it
   */
Kurnianggoro's avatar
Kurnianggoro committed
662
  bool TrackerKCFImpl::getSubWindow(const Mat img, const Rect _roi, Mat& feat, Mat& patch, TrackerKCF::MODE desc) const {
663

664
    Rect region=_roi;
Kurnianggoro's avatar
Kurnianggoro committed
665

666
    // return false if roi is outside the image
667 668
    if((roi & Rect2d(0,0, img.cols, img.rows)) == Rect2d() )
        return false;
Kurnianggoro's avatar
Kurnianggoro committed
669 670

    // extract patch inside the image
671 672 673 674
    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;
675 676 677 678
    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
679

680 681
    // add some padding to compensate when the patch is outside image border
    int addTop,addBottom, addLeft, addRight;
682 683 684 685
    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);
686 687

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

690
    // extract the desired descriptors
Kurnianggoro's avatar
Kurnianggoro committed
691
    switch(desc){
692
      case CN:
Kurnianggoro's avatar
Kurnianggoro committed
693
        CV_Assert(img.channels() == 3);
Kurnianggoro's avatar
Kurnianggoro committed
694 695
        extractCN(patch,feat);
        feat=feat.mul(hann_cn); // hann window filter
Kurnianggoro's avatar
Kurnianggoro committed
696
        break;
Kurnianggoro's avatar
Kurnianggoro committed
697 698 699 700 701
      default: // GRAY
        if(img.channels()>1)
          cvtColor(patch,feat, CV_BGR2GRAY);
        else
          feat=patch;
702 703 704
        //feat.convertTo(feat,CV_32F);
        feat.convertTo(feat,CV_32F, 1.0/255.0, -0.5);
        //feat=feat/255.0-0.5; // normalize to range -0.5 .. 0.5
Kurnianggoro's avatar
Kurnianggoro committed
705
        feat=feat.mul(hann); // hann window filter
Kurnianggoro's avatar
Kurnianggoro committed
706
        break;
707
    }
Kurnianggoro's avatar
Kurnianggoro committed
708

709
    return true;
Kurnianggoro's avatar
Kurnianggoro committed
710

711
  }
Kurnianggoro's avatar
Kurnianggoro committed
712

Kurnianggoro's avatar
Kurnianggoro committed
713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744
  /*
   * 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;
  }

745 746
  /* Convert BGR to ColorNames
   */
Kurnianggoro's avatar
Kurnianggoro committed
747 748
  void TrackerKCFImpl::extractCN(Mat patch_data, Mat & cnFeatures) const {
    Vec3b & pixel = patch_data.at<Vec3b>(0,0);
749
    unsigned index;
Kurnianggoro's avatar
Kurnianggoro committed
750

751 752
    if(cnFeatures.type() != CV_32FC(10))
      cnFeatures = Mat::zeros(patch_data.rows,patch_data.cols,CV_32FC(10));
Kurnianggoro's avatar
Kurnianggoro committed
753

Kurnianggoro's avatar
Kurnianggoro committed
754 755 756
    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);
757
        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
758 759 760

        //copy the values
        for(int _k=0;_k<10;_k++){
761
          cnFeatures.at<Vec<float,10> >(i,j)[_k]=ColorNames[index][_k];
Kurnianggoro's avatar
Kurnianggoro committed
762
        }
763 764
      }
    }
Kurnianggoro's avatar
Kurnianggoro committed
765

766
  }
Kurnianggoro's avatar
Kurnianggoro committed
767

768 769 770
  /*
   *  dense gauss kernel function
   */
771
  void TrackerKCFImpl::denseGaussKernel(const float sigma, const Mat x_data, const Mat y_data, Mat & k_data,
Kurnianggoro's avatar
Kurnianggoro committed
772
                                        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 {
773
    double normX, normY;
Kurnianggoro's avatar
Kurnianggoro committed
774

Kurnianggoro's avatar
Kurnianggoro committed
775 776
    fft2(x_data,xf_data,layers_data);
    fft2(y_data,yf_data,layers_data);
Kurnianggoro's avatar
Kurnianggoro committed
777

Kurnianggoro's avatar
Kurnianggoro committed
778
    normX=norm(x_data);
779
    normX*=normX;
Kurnianggoro's avatar
Kurnianggoro committed
780
    normY=norm(y_data);
781
    normY*=normY;
Kurnianggoro's avatar
Kurnianggoro committed
782

Kurnianggoro's avatar
Kurnianggoro committed
783
    pixelWiseMult(xf_data,yf_data,xyf_v,0,true);
784
    sumChannels(xyf_v,xyf);
785
    ifft2(xyf,xyf);
Kurnianggoro's avatar
Kurnianggoro committed
786

787
    if(params.wrap_kernel){
Kurnianggoro's avatar
Kurnianggoro committed
788 789
      shiftRows(xyf, x_data.rows/2);
      shiftCols(xyf, x_data.cols/2);
790
    }
Kurnianggoro's avatar
Kurnianggoro committed
791

792
    //(xx + yy - 2 * xy) / numel(x)
Kurnianggoro's avatar
Kurnianggoro committed
793
    xy=(normX+normY-2*xyf)/(x_data.rows*x_data.cols*x_data.channels());
794 795 796

    // 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))
797 798
    for(int i=0;i<xy.rows;i++){
      for(int j=0;j<xy.cols;j++){
799
        if(xy.at<float>(i,j)<0.0)xy.at<float>(i,j)=0.0;
800 801
      }
    }
Kurnianggoro's avatar
Kurnianggoro committed
802

803
    float sig=-1.0f/(sigma*sigma);
804
    xy=sig*xy;
Kurnianggoro's avatar
Kurnianggoro committed
805
    exp(xy,k_data);
806 807

  }
Kurnianggoro's avatar
Kurnianggoro committed
808

809
  /* CIRCULAR SHIFT Function
810 811 812
   * 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
813
  void TrackerKCFImpl::shiftRows(Mat& mat) const {
814 815 816

      Mat temp;
      Mat m;
817 818 819
      int _k = (mat.rows-1);
      mat.row(_k).copyTo(temp);
      for(; _k > 0 ; _k-- ) {
Kurnianggoro's avatar
Kurnianggoro committed
820 821
        m = mat.row(_k);
        mat.row(_k-1).copyTo(m);
822 823 824 825 826 827 828
      }
      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
829
  void TrackerKCFImpl::shiftRows(Mat& mat, int n) const {
830
      if( n < 0 ) {
Kurnianggoro's avatar
Kurnianggoro committed
831 832 833 834 835 836
        n = -n;
        flip(mat,mat,0);
        for(int _k=0; _k < n;_k++) {
          shiftRows(mat);
        }
        flip(mat,mat,0);
Kurnianggoro's avatar
Kurnianggoro committed
837
      }else{
Kurnianggoro's avatar
Kurnianggoro committed
838 839 840
        for(int _k=0; _k < n;_k++) {
          shiftRows(mat);
        }
841 842 843 844
      }
  }

  //circular shift n columns from left to right if n > 0, -n columns from right to left if n < 0
845
  void TrackerKCFImpl::shiftCols(Mat& mat, int n) const {
846
      if(n < 0){
Kurnianggoro's avatar
Kurnianggoro committed
847 848 849 850 851 852 853 854 855 856
        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);
857 858 859 860 861 862
      }
  }

  /*
   * calculate the detection response
   */
Kurnianggoro's avatar
Kurnianggoro committed
863
  void TrackerKCFImpl::calcResponse(const Mat alphaf_data, const Mat kf_data, Mat & response_data, Mat & spec_data) const {
Kurnianggoro's avatar
Kurnianggoro committed
864
    //alpha f--> 2channels ; k --> 1 channel;
Kurnianggoro's avatar
Kurnianggoro committed
865 866
    mulSpectrums(alphaf_data,kf_data,spec_data,0,false);
    ifft2(spec_data,response_data);
867
  }
Kurnianggoro's avatar
Kurnianggoro committed
868

869 870 871
  /*
   * calculate the detection response for splitted form
   */
Kurnianggoro's avatar
Kurnianggoro committed
872 873 874 875 876
  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)
877
    float den;
Kurnianggoro's avatar
Kurnianggoro committed
878 879
    for(int i=0;i<kf_data.rows;i++){
      for(int j=0;j<kf_data.cols;j++){
880 881 882 883 884
        den=1.0f/(_alphaf_den.at<Vec2f>(i,j)[0]*_alphaf_den.at<Vec2f>(i,j)[0]+_alphaf_den.at<Vec2f>(i,j)[1]*_alphaf_den.at<Vec2f>(i,j)[1]);
        spec2_data.at<Vec2f>(i,j)[0]=
          (spec_data.at<Vec2f>(i,j)[0]*_alphaf_den.at<Vec2f>(i,j)[0]+spec_data.at<Vec2f>(i,j)[1]*_alphaf_den.at<Vec2f>(i,j)[1])*den;
        spec2_data.at<Vec2f>(i,j)[1]=
          (spec_data.at<Vec2f>(i,j)[1]*_alphaf_den.at<Vec2f>(i,j)[0]-spec_data.at<Vec2f>(i,j)[0]*_alphaf_den.at<Vec2f>(i,j)[1])*den;
885 886
      }
    }
Kurnianggoro's avatar
Kurnianggoro committed
887

Kurnianggoro's avatar
Kurnianggoro committed
888 889 890 891 892 893 894 895 896 897 898
    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;
    }
899
  }
900
  /*----------------------------------------------------------------------*/
Kurnianggoro's avatar
Kurnianggoro committed
901

902 903 904 905
  /*
 * Parameters
 */
  TrackerKCF::Params::Params(){
906 907 908 909 910
      detect_thresh = 0.5f;
      sigma=0.2f;
      lambda=0.0001f;
      interp_factor=0.075f;
      output_sigma_factor=1.0f / 16.0f;
911
      resize=true;
912
      max_patch_size=80*80;
913 914
      split_coeff=true;
      wrap_kernel=false;
Kurnianggoro's avatar
Kurnianggoro committed
915 916
      desc_npca = GRAY;
      desc_pca = CN;
Kurnianggoro's avatar
Kurnianggoro committed
917

918
      //feature compression
919
      compress_feature=true;
920
      compressed_size=2;
921
      pca_learning_rate=0.15f;
922 923
  }

924 925
  void TrackerKCF::Params::read( const cv::FileNode& fn ){
      *this = TrackerKCF::Params();
926

927 928 929
      if (!fn["detect_thresh"].empty())
          fn["detect_thresh"] >> detect_thresh;

930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971
      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{
972
    fs << "detect_thresh" << detect_thresh;
973 974 975 976 977 978 979 980 981 982 983 984 985 986
    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;
  }
987
} /* namespace cv */