sparse_matching_gpc.cpp 28.7 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) 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*/

43
#include "precomp.hpp"
44
#include "opencv2/core/core_c.h"
45 46
#include "opencv2/core/private.hpp"
#include "opencv2/flann/miniflann.hpp"
47
#include "opencv2/imgcodecs.hpp"
48
#include "opencl_kernels_optflow.hpp"
LaurentBerger's avatar
LaurentBerger committed
49
#include "opencv2/core/hal/intrin.hpp"
50 51 52
#ifdef CV_CXX11
#include <random>  // std::mt19937
#endif
53 54 55 56 57 58 59 60 61 62 63

/* Disable "from double to float" and "from size_t to int" warnings.
 * Fixing these would make the code look ugly by introducing explicit cast all around.
 * Here these warning are pointless anyway.
 */
#ifdef _MSC_VER
#pragma warning( disable : 4244 4267 4838 )
#endif
#ifdef __clang__
#pragma clang diagnostic ignored "-Wshorten-64-to-32"
#endif
64 65 66 67 68 69 70 71

namespace cv
{
namespace optflow
{
namespace
{

72 73 74 75 76
#define PATCH_RADIUS 10
#define PATCH_RADIUS_DOUBLED 20
#define SQRT2_INV 0.7071067811865475

const int patchRadius = PATCH_RADIUS;
77 78
const int globalIters = 3;
const int localIters = 500;
79 80 81 82 83 84 85 86 87 88
const double thresholdOutliers = 0.98;
const double thresholdMagnitudeFrac = 0.8;
const double epsTolerance = 1e-12;
const unsigned scoreGainPos = 5;
const unsigned scoreGainNeg = 1;
const unsigned negSearchKNN = 5;
const double simulatedAnnealingTemperatureCoef = 200.0;
const double sigmaGrowthRate = 0.2;

RNG rng;
89 90 91 92 93 94 95 96 97 98

struct Magnitude
{
  float val;
  int i;
  int j;

  Magnitude( float _val, int _i, int _j ) : val( _val ), i( _i ), j( _j ) {}
  Magnitude() {}

99
  bool operator<( const Magnitude &m ) const { return val > m.val; }
100 101 102 103
};

struct PartitionPredicate1
{
104
  Vec< double, GPCPatchDescriptor::nFeatures > coef;
105 106
  double rhs;

107
  PartitionPredicate1( const Vec< double, GPCPatchDescriptor::nFeatures > &_coef, double _rhs ) : coef( _coef ), rhs( _rhs ) {}
108 109 110

  bool operator()( const GPCPatchSample &sample ) const
  {
111 112 113
    bool refdir, posdir, negdir;
    sample.getDirections( refdir, posdir, negdir, coef, rhs );
    return refdir == false && ( posdir == false || negdir == true );
114 115 116 117 118
  }
};

struct PartitionPredicate2
{
119
  Vec< double, GPCPatchDescriptor::nFeatures > coef;
120 121
  double rhs;

122
  PartitionPredicate2( const Vec< double, GPCPatchDescriptor::nFeatures > &_coef, double _rhs ) : coef( _coef ), rhs( _rhs ) {}
123 124 125

  bool operator()( const GPCPatchSample &sample ) const
  {
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
    bool refdir, posdir, negdir;
    sample.getDirections( refdir, posdir, negdir, coef, rhs );
    return refdir != posdir && refdir == negdir;
  }
};

struct CompareWithTolerance
{
  double val;

  CompareWithTolerance( double _val ) : val( _val ) {};

  bool operator()( const double &elem ) const
  {
    const double diff = ( val + elem == 0 ) ? std::abs( val - elem ) : std::abs( ( val - elem ) / ( val + elem ) );
    return diff <= epsTolerance;
142 143 144 145 146
  }
};

float normL2Sqr( const Vec2f &v ) { return v[0] * v[0] + v[1] * v[1]; }

147 148
int normL2Sqr( const Point2i &v ) { return v.x * v.x + v.y * v.y; }

149 150 151 152 153
bool checkBounds( int i, int j, Size sz )
{
  return i >= patchRadius && j >= patchRadius && i + patchRadius < sz.height && j + patchRadius < sz.width;
}

154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
void getDCTPatchDescriptor( GPCPatchDescriptor &patchDescr, const Mat *imgCh, int i, int j )
{
  Rect roi( j - patchRadius, i - patchRadius, 2 * patchRadius, 2 * patchRadius );
  Mat freqDomain;
  dct( imgCh[0]( roi ), freqDomain );

  double *feature = patchDescr.feature.val;
  feature[0] = freqDomain.at< float >( 0, 0 );
  feature[1] = freqDomain.at< float >( 0, 1 );
  feature[2] = freqDomain.at< float >( 0, 2 );
  feature[3] = freqDomain.at< float >( 0, 3 );

  feature[4] = freqDomain.at< float >( 1, 0 );
  feature[5] = freqDomain.at< float >( 1, 1 );
  feature[6] = freqDomain.at< float >( 1, 2 );
  feature[7] = freqDomain.at< float >( 1, 3 );

  feature[8] = freqDomain.at< float >( 2, 0 );
  feature[9] = freqDomain.at< float >( 2, 1 );
  feature[10] = freqDomain.at< float >( 2, 2 );
  feature[11] = freqDomain.at< float >( 2, 3 );

  feature[12] = freqDomain.at< float >( 3, 0 );
  feature[13] = freqDomain.at< float >( 3, 1 );
  feature[14] = freqDomain.at< float >( 3, 2 );
  feature[15] = freqDomain.at< float >( 3, 3 );

  feature[16] = cv::sum( imgCh[1]( roi ) )[0] / ( 2 * patchRadius );
  feature[17] = cv::sum( imgCh[2]( roi ) )[0] / ( 2 * patchRadius );
}

double sumInt( const Mat &integ, int i, int j, int h, int w )
{
  return integ.at< double >( i + h, j + w ) - integ.at< double >( i + h, j ) - integ.at< double >( i, j + w ) + integ.at< double >( i, j );
}

void getWHTPatchDescriptor( GPCPatchDescriptor &patchDescr, const Mat *imgCh, int i, int j )
{
  i -= patchRadius;
  j -= patchRadius;
  const int k = 2 * patchRadius;
  const double s = sumInt( imgCh[0], i, j, k, k );
  double *feature = patchDescr.feature.val;

  feature[0] = s;
  feature[1] = s - 2 * sumInt( imgCh[0], i, j + k / 2, k, k / 2 );
  feature[2] = s - 2 * sumInt( imgCh[0], i, j + k / 4, k, k / 2 );
  feature[3] = s - 2 * sumInt( imgCh[0], i, j + k / 4, k, k / 4 ) - 2 * sumInt( imgCh[0], i, j + 3 * k / 4, k, k / 4 );

  feature[4] = s - 2 * sumInt( imgCh[0], i + k / 2, j, k / 2, k );
  feature[5] = s - 2 * sumInt( imgCh[0], i, j + k / 2, k / 2, k / 2 ) - 2 * sumInt( imgCh[0], i + k / 2, j, k / 2, k / 2 );
  feature[6] = s - 2 * sumInt( imgCh[0], i, j + k / 4, k / 2, k / 2 ) - 2 * sumInt( imgCh[0], i + k / 2, j, k / 2, k / 4 ) -
               2 * sumInt( imgCh[0], i + k / 2, j + 3 * k / 4, k / 2, k / 4 );
  feature[7] = s - 2 * sumInt( imgCh[0], i, j + k / 4, k / 2, k / 4 ) - 2 * sumInt( imgCh[0], i, j + 3 * k / 4, k / 2, k / 4 ) -
               2 * sumInt( imgCh[0], i + k / 2, j, k / 2, k / 4 ) - 2 * sumInt( imgCh[0], i + k / 2, j + k / 2, k / 2, k / 4 );

  feature[8] = s - 2 * sumInt( imgCh[0], i + k / 4, j, k / 2, k );
  feature[9] = s - 2 * sumInt( imgCh[0], i + k / 4, j, k / 2, k / 2 ) - 2 * sumInt( imgCh[0], i, j + k / 2, k / 4, k / 2 ) -
               2 * sumInt( imgCh[0], i + 3 * k / 4, j + k / 2, k / 4, k / 2 );
  feature[10] = s - 2 * sumInt( imgCh[0], i + k / 4, j, k / 2, k / 4 ) - 2 * sumInt( imgCh[0], i + k / 4, j + 3 * k / 4, k / 2, k / 4 ) -
                2 * sumInt( imgCh[0], i, j + k / 4, k / 4, k / 2 ) - 2 * sumInt( imgCh[0], i + 3 * k / 4, j + k / 4, k / 4, k / 2 );
  feature[11] = s - 2 * sumInt( imgCh[0], i, j + k / 4, k / 4, k / 4 ) - 2 * sumInt( imgCh[0], i, j + 3 * k / 4, k / 4, k / 4 ) -
                2 * sumInt( imgCh[0], i + k / 4, j, k / 2, k / 4 ) - 2 * sumInt( imgCh[0], i + k / 4, j + k / 2, k / 2, k / 4 ) -
                2 * sumInt( imgCh[0], i + 3 * k / 4, j + k / 4, k / 4, k / 4 ) -
                2 * sumInt( imgCh[0], i + 3 * k / 4, j + 3 * k / 4, k / 4, k / 4 );

  feature[12] = s - 2 * sumInt( imgCh[0], i + k / 4, j, k / 4, k ) - 2 * sumInt( imgCh[0], i + 3 * k / 4, j, k / 4, k );
  feature[13] = s - 2 * sumInt( imgCh[0], i + k / 4, j, k / 4, k / 2 ) - 2 * sumInt( imgCh[0], i + 3 * k / 4, j, k / 4, k / 2 ) -
                2 * sumInt( imgCh[0], i, j + k / 2, k / 4, k / 2 ) - 2 * sumInt( imgCh[0], i + k / 2, j + k / 2, k / 4, k / 2 );
  feature[14] = s - 2 * sumInt( imgCh[0], i + k / 4, j, k / 4, k / 4 ) - 2 * sumInt( imgCh[0], i + 3 * k / 4, j, k / 4, k / 4 ) -
                2 * sumInt( imgCh[0], i, j + k / 4, k / 4, k / 2 ) - 2 * sumInt( imgCh[0], i + k / 2, j + k / 4, k / 4, k / 2 ) -
                2 * sumInt( imgCh[0], i + k / 4, j + 3 * k / 4, k / 4, k / 4 ) -
                2 * sumInt( imgCh[0], i + 3 * k / 4, j + 3 * k / 4, k / 4, k / 4 );
  feature[15] = s - 2 * sumInt( imgCh[0], i, j + k / 4, k / 4, k / 4 ) - 2 * sumInt( imgCh[0], i, j + 3 * k / 4, k / 4, k / 4 ) -
                2 * sumInt( imgCh[0], i + k / 4, j, k / 4, k / 4 ) - 2 * sumInt( imgCh[0], i + k / 4, j + k / 2, k / 4, k / 4 ) -
                2 * sumInt( imgCh[0], i + k / 2, j + k / 4, k / 4, k / 4 ) -
                2 * sumInt( imgCh[0], i + k / 2, j + 3 * k / 4, k / 4, k / 4 ) - 2 * sumInt( imgCh[0], i + 3 * k / 4, j, k / 4, k / 4 ) -
                2 * sumInt( imgCh[0], i + 3 * k / 4, j + k / 2, k / 4, k / 4 );

  feature[16] = sumInt( imgCh[1], i, j, k, k );
  feature[17] = sumInt( imgCh[2], i, j, k, k );

  patchDescr.feature /= patchRadius;
}

class ParallelDCTFiller : public ParallelLoopBody
{
private:
  const Size sz;
  const Mat *imgCh;
  std::vector< GPCPatchDescriptor > *descr;

  ParallelDCTFiller &operator=( const ParallelDCTFiller & );

public:
  ParallelDCTFiller( const Size &_sz, const Mat *_imgCh, std::vector< GPCPatchDescriptor > *_descr )
      : sz( _sz ), imgCh( _imgCh ), descr( _descr ){};

252
  void operator()( const Range &range ) const CV_OVERRIDE
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
  {
    for ( int i = range.start; i < range.end; ++i )
    {
      int x, y;
      GPCDetails::getCoordinatesFromIndex( i, sz, x, y );
      getDCTPatchDescriptor( descr->at( i ), imgCh, y, x );
    }
  }
};

#ifdef HAVE_OPENCL

bool ocl_getAllDCTDescriptorsForImage( const Mat *imgCh, std::vector< GPCPatchDescriptor > &descr )
{
  const Size sz = imgCh[0].size();
  ocl::Kernel kernel( "getPatchDescriptor", ocl::optflow::sparse_matching_gpc_oclsrc,
                      format( "-DPATCH_RADIUS_DOUBLED=%d -DCV_PI=%f -DSQRT2_INV=%f", PATCH_RADIUS_DOUBLED, CV_PI, SQRT2_INV ) );
  size_t globSize[] = {sz.height - 2 * patchRadius, sz.width - 2 * patchRadius};
  UMat out( globSize[0] * globSize[1], GPCPatchDescriptor::nFeatures, CV_64F );
  if (
    kernel
    .args( cv::ocl::KernelArg::ReadOnlyNoSize( imgCh[0].getUMat( ACCESS_READ ) ),
           cv::ocl::KernelArg::ReadOnlyNoSize( imgCh[1].getUMat( ACCESS_READ ) ),
           cv::ocl::KernelArg::ReadOnlyNoSize( imgCh[2].getUMat( ACCESS_READ ) ),
           cv::ocl::KernelArg::WriteOnlyNoSize( out ),
           (int)globSize[0], (int)globSize[1], (int)patchRadius )
    .run( 2, globSize, 0, true ) == false )
    return false;
281
  Mat cpuOut = out.getMat( ACCESS_READ );
282 283 284 285 286 287 288 289 290 291 292 293 294
  for ( int i = 0; i + 2 * patchRadius < sz.height; ++i )
    for ( int j = 0; j + 2 * patchRadius < sz.width; ++j )
      descr.push_back( *cpuOut.ptr< GPCPatchDescriptor >( i * globSize[1] + j ) );
  return true;
}

#endif

void getAllDCTDescriptorsForImage( const Mat *imgCh, std::vector< GPCPatchDescriptor > &descr, const GPCMatchingParams &mp )
{
  const Size sz = imgCh[0].size();
  descr.reserve( ( sz.height - 2 * patchRadius ) * ( sz.width - 2 * patchRadius ) );

295
  CV_UNUSED(mp); // Fix unused parameter warning in case OpenCL is not available
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
  CV_OCL_RUN( mp.useOpenCL, ocl_getAllDCTDescriptorsForImage( imgCh, descr ) )

  descr.resize( ( sz.height - 2 * patchRadius ) * ( sz.width - 2 * patchRadius ) );
  parallel_for_( Range( 0, descr.size() ), ParallelDCTFiller( sz, imgCh, &descr ) );
}

class ParallelWHTFiller : public ParallelLoopBody
{
private:
  const Size sz;
  const Mat *imgChInt;
  std::vector< GPCPatchDescriptor > *descr;

  ParallelWHTFiller &operator=( const ParallelWHTFiller & );

public:
  ParallelWHTFiller( const Size &_sz, const Mat *_imgChInt, std::vector< GPCPatchDescriptor > *_descr )
      : sz( _sz ), imgChInt( _imgChInt ), descr( _descr ){};

315
  void operator()( const Range &range ) const CV_OVERRIDE
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 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
  {
    for ( int i = range.start; i < range.end; ++i )
    {
      int x, y;
      GPCDetails::getCoordinatesFromIndex( i, sz, x, y );
      getWHTPatchDescriptor( descr->at( i ), imgChInt, y, x );
    }
  }
};

void getAllWHTDescriptorsForImage( const Mat *imgCh, std::vector< GPCPatchDescriptor > &descr, const GPCMatchingParams & )
{
  const Size sz = imgCh[0].size();
  descr.resize( ( sz.height - 2 * patchRadius ) * ( sz.width - 2 * patchRadius ) );

  Mat imgChInt[3];
  integral( imgCh[0], imgChInt[0], CV_64F );
  integral( imgCh[1], imgChInt[1], CV_64F );
  integral( imgCh[2], imgChInt[2], CV_64F );

  parallel_for_( Range( 0, descr.size() ), ParallelWHTFiller( sz, imgChInt, &descr ) );
}

void buildIndex( OutputArray featuresOut, flann::Index &index, const Mat *imgCh,
                 void ( *getAllDescrFn )( const Mat *, std::vector< GPCPatchDescriptor > &, const GPCMatchingParams & ) )
{
  std::vector< GPCPatchDescriptor > descriptors;
  getAllDescrFn( imgCh, descriptors, GPCMatchingParams() );

  featuresOut.create( descriptors.size(), GPCPatchDescriptor::nFeatures, CV_32F );
  Mat features = featuresOut.getMat();

  for ( size_t i = 0; i < descriptors.size(); ++i )
    *features.ptr< Vec< float, GPCPatchDescriptor::nFeatures > >( i ) = descriptors[i].feature;

  cv::flann::KDTreeIndexParams indexParams;
  index.build( features, indexParams, cvflann::FLANN_DIST_L2 );
}

void getTriplet( const Magnitude &mag, const Mat &gt, const Mat *fromCh, const Mat *toCh, GPCSamplesVector &samples, flann::Index &index,
                 void ( *getDescFn )( GPCPatchDescriptor &, const Mat *, int, int ) )
{
  const Size sz = gt.size();
  const int i0 = mag.i;
  const int j0 = mag.j;
  const int i1 = i0 + cvRound( gt.at< Vec2f >( i0, j0 )[1] );
  const int j1 = j0 + cvRound( gt.at< Vec2f >( i0, j0 )[0] );
  if ( checkBounds( i1, j1, sz ) )
  {
    GPCPatchSample ps;
    getDescFn( ps.ref, fromCh, i0, j0 );
    getDescFn( ps.pos, toCh, i1, j1 );
    ps.neg.markAsSeparated();

    Matx< float, 1, GPCPatchDescriptor::nFeatures > ref32;
    Matx< int, 1, negSearchKNN > indices;
    int maxDist = 0;

    for ( unsigned i = 0; i < GPCPatchDescriptor::nFeatures; ++i )
      ref32( 0, i ) = ps.ref.feature[i];

    index.knnSearch( ref32, indices, noArray(), negSearchKNN );

    for ( unsigned i = 0; i < negSearchKNN; ++i )
    {
      int i2, j2;
      GPCDetails::getCoordinatesFromIndex( indices( 0, i ), sz, j2, i2 );
      const int dist = ( i2 - i1 ) * ( i2 - i1 ) + ( j2 - j1 ) * ( j2 - j1 );
      if ( maxDist < dist )
      {
        maxDist = dist;
        getDescFn( ps.neg, toCh, i2, j2 );
      }
    }

    samples.push_back( ps );
  }
}

void getTrainingSamples( const Mat &from, const Mat &to, const Mat &gt, GPCSamplesVector &samples, const int type )
396 397
{
  const Size sz = gt.size();
398
  std::vector< Magnitude > mag;
399 400 401

  for ( int i = patchRadius; i + patchRadius < sz.height; ++i )
    for ( int j = patchRadius; j + patchRadius < sz.width; ++j )
402
      mag.push_back( Magnitude( normL2Sqr( gt.at< Vec2f >( i, j ) ), i, j ) );
403

404 405
  size_t n = size_t( mag.size() * thresholdMagnitudeFrac ); // As suggested in the paper, we discard part of the training samples
                                                            // with a small displacement and train to better distinguish hard pairs.
406 407
  std::nth_element( mag.begin(), mag.begin() + n, mag.end() );
  mag.resize( n );
408 409 410 411
#ifdef CV_CXX11
  std::mt19937 std_rng(cv::theRNG()());
  std::shuffle(mag.begin(), mag.end(), std_rng);
#else
412
  std::random_shuffle( mag.begin(), mag.end() );
413
#endif
414 415 416
  n /= patchRadius;
  mag.resize( n );

417 418 419 420 421 422 423 424 425
  if ( type == GPC_DESCRIPTOR_DCT )
  {
    Mat fromCh[3], toCh[3];
    split( from, fromCh );
    split( to, toCh );

    Mat allDescriptors;
    flann::Index index;
    buildIndex( allDescriptors, index, toCh, getAllDCTDescriptorsForImage );
426

427 428 429 430
    for ( size_t k = 0; k < n; ++k )
      getTriplet( mag[k], gt, fromCh, toCh, samples, index, getDCTPatchDescriptor );
  }
  else if ( type == GPC_DESCRIPTOR_WHT )
431
  {
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
    Mat fromCh[3], toCh[3], fromChInt[3], toChInt[3];
    split( from, fromCh );
    split( to, toCh );
    integral( fromCh[0], fromChInt[0], CV_64F );
    integral( fromCh[1], fromChInt[1], CV_64F );
    integral( fromCh[2], fromChInt[2], CV_64F );
    integral( toCh[0], toChInt[0], CV_64F );
    integral( toCh[1], toChInt[1], CV_64F );
    integral( toCh[2], toChInt[2], CV_64F );

    Mat allDescriptors;
    flann::Index index;
    buildIndex( allDescriptors, index, toCh, getAllWHTDescriptorsForImage );

    for ( size_t k = 0; k < n; ++k )
      getTriplet( mag[k], gt, fromChInt, toChInt, samples, index, getWHTPatchDescriptor );
448
  }
449 450
  else
    CV_Error( CV_StsBadArg, "Unknown descriptor type" );
451 452 453 454 455 456 457 458 459 460 461 462
}

/* Sample random number from Cauchy distribution. */
double getRandomCauchyScalar()
{
  return tan( rng.uniform( -1.54, 1.54 ) ); // I intentionally used the value slightly less than PI/2 to enforce strictly
                                            // zero probability for large numbers. Resulting PDF for Cauchy has
                                            // truncated "tails".
}

/* Sample random vector from Cauchy distribution (pointwise, i.e. vector whose components are independent random
 * variables from Cauchy distribution) */
463
void getRandomCauchyVector( Vec< double, GPCPatchDescriptor::nFeatures > &v )
464 465 466 467
{
  for ( unsigned i = 0; i < GPCPatchDescriptor::nFeatures; ++i )
    v[i] = getRandomCauchyScalar();
}
468 469

double getRobustMedian( double m ) { return m < 0 ? m * ( 1.0 + epsTolerance ) : m * ( 1.0 - epsTolerance ); }
470 471
}

472
double GPCPatchDescriptor::dot( const Vec< double, nFeatures > &coef ) const
473
{
474 475 476 477
#if CV_SIMD128_64F
  v_float64x2 sum = v_setzero_f64();
  for ( unsigned i = 0; i < nFeatures; i += 2 )
  {
478 479
    v_float64x2 x = v_load( &feature.val[i] );
    v_float64x2 y = v_load( &coef.val[i] );
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
    sum = v_muladd( x, y, sum );
  }
#if CV_SSE2
  __m128d sumrev = _mm_shuffle_pd( sum.val, sum.val, _MM_SHUFFLE2( 0, 1 ) );
  return _mm_cvtsd_f64( _mm_add_pd( sum.val, sumrev ) );
#else
  double CV_DECL_ALIGNED( 16 ) buf[2];
  v_store_aligned( buf, sum );
  return OPENCV_HAL_ADD( buf[0], buf[1] );
#endif

#else
  return feature.dot( coef );
#endif
}
495

496 497 498 499 500 501
void GPCPatchSample::getDirections( bool &refdir, bool &posdir, bool &negdir, const Vec< double, GPCPatchDescriptor::nFeatures > &coef, double rhs ) const
{
  refdir = ( ref.dot( coef ) < rhs );
  posdir = pos.isSeparated() ? ( !refdir ) : ( pos.dot( coef ) < rhs );
  negdir = neg.isSeparated() ? ( !refdir ) : ( neg.dot( coef ) < rhs );
}
502

503 504 505 506 507 508 509 510 511 512
void GPCDetails::getAllDescriptorsForImage( const Mat *imgCh, std::vector< GPCPatchDescriptor > &descr, const GPCMatchingParams &mp,
                                            int type )
{
  if ( type == GPC_DESCRIPTOR_DCT )
    getAllDCTDescriptorsForImage( imgCh, descr, mp );
  else if ( type == GPC_DESCRIPTOR_WHT )
    getAllWHTDescriptorsForImage( imgCh, descr, mp );
  else
    CV_Error( CV_StsBadArg, "Unknown descriptor type" );
}
513

514 515 516 517 518 519
void GPCDetails::getCoordinatesFromIndex( size_t index, Size sz, int &x, int &y )
{
  const size_t stride = sz.width - patchRadius * 2;
  y = int( index / stride );
  x = int( index - y * stride + patchRadius );
  y += patchRadius;
520 521 522 523
}

bool GPCTree::trainNode( size_t nodeId, SIter begin, SIter end, unsigned depth )
{
524 525 526
  const int nSamples = (int)std::distance( begin, end );

  if ( nSamples < params.minNumberOfSamples || depth >= params.maxTreeDepth )
527 528 529 530 531 532 533 534 535
    return false;

  if ( nodeId >= nodes.size() )
    nodes.resize( nodeId + 1 );

  Node &node = nodes[nodeId];

  // Select the best hyperplane
  unsigned globalBestScore = 0;
536
  std::vector< double > values;
537
  values.reserve( nSamples * 2 );
538 539 540

  for ( int j = 0; j < globalIters; ++j )
  { // Global search step
541
    Vec< double, GPCPatchDescriptor::nFeatures > coef;
542 543 544 545 546
    unsigned localBestScore = 0;
    getRandomCauchyVector( coef );

    for ( int i = 0; i < localIters; ++i )
    { // Local search step
547
      double randomModification = getRandomCauchyScalar() * ( 1.0 + sigmaGrowthRate * int( i / GPCPatchDescriptor::nFeatures ) );
548 549 550 551 552
      const int pos = i % GPCPatchDescriptor::nFeatures;
      std::swap( coef[pos], randomModification );
      values.clear();

      for ( SIter iter = begin; iter != end; ++iter )
553 554 555 556 557 558 559 560 561 562
        values.push_back( iter->ref.dot( coef ) );

      std::nth_element( values.begin(), values.begin() + nSamples / 2, values.end() );
      double median = values[nSamples / 2];

      // Skip obviously malformed division. This may happen in case there are a large number of equal samples.
      // Most likely this won't happen with samples collected from a good dataset.
      // Happens in case dataset contains plain (or close to plain) images.
      if ( std::count_if( values.begin(), values.end(), CompareWithTolerance( median ) ) > std::max( 1, nSamples / 4 ) )
        continue;
563

564
      median = getRobustMedian( median );
565

566
      unsigned score = 0;
567 568
      for ( SIter iter = begin; iter != end; ++iter )
      {
569 570 571 572 573 574
        bool refdir, posdir, negdir;
        iter->getDirections( refdir, posdir, negdir, coef, median );
        if ( refdir == posdir )
          score += scoreGainPos;
        if ( refdir != negdir )
          score += scoreGainNeg;
575 576
      }

577 578
      if ( score > localBestScore )
        localBestScore = score;
579
      else
580
      {
581
        const double beta = simulatedAnnealingTemperatureCoef * std::sqrt( static_cast<float>(i) ) / ( nSamples * ( scoreGainPos + scoreGainNeg ) );
582 583 584
        if ( rng.uniform( 0.0, 1.0 ) > std::exp( -beta * ( localBestScore - score) ) )
          coef[pos] = randomModification;
      }
585

586
      if ( score > globalBestScore )
587
      {
588
        globalBestScore = score;
589 590 591 592 593
        node.coef = coef;
        node.rhs = median;
      }
    }
  }
594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621

  if ( globalBestScore == 0 )
    return false;

  if ( params.printProgress )
  {
    const int maxScore = nSamples * ( scoreGainPos + scoreGainNeg );
    const double correctRatio = double( globalBestScore ) / maxScore;
    printf( "[%u] Correct %.2f (%u/%d)\nWeights:", depth, correctRatio, globalBestScore, maxScore );
    for ( unsigned k = 0; k < GPCPatchDescriptor::nFeatures; ++k )
      printf( " %.3f", node.coef[k] );
    printf( "\n" );
  }

  for ( SIter iter = begin; iter != end; ++iter )
  {
    bool refdir, posdir, negdir;
    iter->getDirections( refdir, posdir, negdir, node.coef, node.rhs );
    // We shouldn't account for positive sample in the scoring in case it was separated before. So mark it as separated.
    // After all, we can't bring back samples which were separated from reference on early levels.
    if ( refdir != posdir )
      iter->pos.markAsSeparated();
    // The same for negative sample.
    if ( refdir != negdir )
      iter->neg.markAsSeparated();
    // If both positive and negative were separated before then such triplet doesn't make sense on deeper levels. We discard it.
  }

622 623 624 625 626 627 628
  // Partition vector with samples according to the hyperplane in QuickSort-like manner.
  // Unlike QuickSort, we need to partition it into 3 parts (left subtree samples; undefined samples; right subtree
  // samples), so we call it two times.
  SIter leftEnd = std::partition( begin, end, PartitionPredicate1( node.coef, node.rhs ) ); // Separate left subtree samples from others.
  SIter rightBegin =
    std::partition( leftEnd, end, PartitionPredicate2( node.coef, node.rhs ) ); // Separate undefined samples from right subtree samples.

629 630
  node.left = ( trainNode( nodeId * 2 + 1, begin, leftEnd, depth + 1 ) ) ? unsigned( nodeId * 2 + 1 ) : 0;
  node.right = ( trainNode( nodeId * 2 + 2, rightBegin, end, depth + 1 ) ) ? unsigned( nodeId * 2 + 2 ) : 0;
631 632 633 634

  return true;
}

635
void GPCTree::train( GPCTrainingSamples &samples, const GPCTrainingParams _params )
636
{
637 638 639
  if ( _params.descriptorType != samples.type() )
    CV_Error( CV_StsBadArg, "Descriptor type mismatch! Check that samples are collected with the same descriptor type." );
  nodes.clear();
640
  nodes.reserve( samples.size() * 2 - 1 ); // set upper bound for the possible number of nodes so all subsequent resize() will be no-op
641 642 643
  params = _params;
  GPCSamplesVector &sv = samples;
  trainNode( 0, sv.begin(), sv.end(), 0 );
644 645 646 647 648 649 650
}

void GPCTree::write( FileStorage &fs ) const
{
  if ( nodes.empty() )
    CV_Error( CV_StsBadArg, "Tree have not been trained" );
  fs << "nodes" << nodes;
651 652 653 654 655 656 657
  fs << "dtype" << (int)params.descriptorType;
}

void GPCTree::read( const FileNode &fn )
{
  fn["nodes"] >> nodes;
  fn["dtype"] >> (int &)params.descriptorType;
658 659
}

660 661 662 663 664 665 666 667 668 669 670 671 672
unsigned GPCTree::findLeafForPatch( const GPCPatchDescriptor &descr ) const
{
  unsigned id = 0, prevId;
  do
  {
    prevId = id;
    if ( descr.dot( nodes[id].coef ) < nodes[id].rhs )
      id = nodes[id].right;
    else
      id = nodes[id].left;
  } while ( id );
  return prevId;
}
673

674
Ptr< GPCTrainingSamples > GPCTrainingSamples::create( const std::vector< String > &imagesFrom, const std::vector< String > &imagesTo,
675
                                                      const std::vector< String > &gt, int _descriptorType )
676 677 678 679
{
  CV_Assert( imagesFrom.size() == imagesTo.size() );
  CV_Assert( imagesFrom.size() == gt.size() );

680
  Ptr< GPCTrainingSamples > ts = makePtr< GPCTrainingSamples >();
681 682 683

  ts->descriptorType = _descriptorType;

684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699
  for ( size_t i = 0; i < imagesFrom.size(); ++i )
  {
    Mat from = imread( imagesFrom[i] );
    Mat to = imread( imagesTo[i] );
    Mat gtFlow = readOpticalFlow( gt[i] );

    CV_Assert( from.size == to.size );
    CV_Assert( from.size == gtFlow.size );
    CV_Assert( from.channels() == 3 );
    CV_Assert( to.channels() == 3 );

    from.convertTo( from, CV_32FC3 );
    to.convertTo( to, CV_32FC3 );
    cvtColor( from, from, COLOR_BGR2YCrCb );
    cvtColor( to, to, COLOR_BGR2YCrCb );

700
    getTrainingSamples( from, to, gtFlow, ts->samples, ts->descriptorType );
701 702 703 704 705
  }

  return ts;
}

706 707 708 709 710 711 712 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
Ptr< GPCTrainingSamples > GPCTrainingSamples::create( InputArrayOfArrays imagesFrom, InputArrayOfArrays imagesTo,
                                                      InputArrayOfArrays gt, int _descriptorType )
{
  CV_Assert( imagesFrom.total() == imagesTo.total() );
  CV_Assert( imagesFrom.total() == gt.total() );

  Ptr< GPCTrainingSamples > ts = makePtr< GPCTrainingSamples >();

  ts->descriptorType = _descriptorType;

  for ( size_t i = 0; i < imagesFrom.total(); ++i )
  {
    Mat from = imagesFrom.getMat( static_cast<int>( i ) );
    Mat to = imagesTo.getMat( static_cast<int>( i ) );
    Mat gtFlow = gt.getMat( static_cast<int>( i ) );

    CV_Assert( from.size == to.size );
    CV_Assert( from.size == gtFlow.size );
    CV_Assert( from.channels() == 3 );
    CV_Assert( to.channels() == 3 );

    from.convertTo( from, CV_32FC3 );
    to.convertTo( to, CV_32FC3 );
    cvtColor( from, from, COLOR_BGR2YCrCb );
    cvtColor( to, to, COLOR_BGR2YCrCb );

    getTrainingSamples( from, to, gtFlow, ts->samples, ts->descriptorType );
  }

  return ts;
}

void GPCDetails::dropOutliers( std::vector< std::pair< Point2i, Point2i > > &corr )
{
740 741 742
  if ( corr.size() == 0 )
      return;

743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765
  std::vector< float > mag( corr.size() );

  for ( size_t i = 0; i < corr.size(); ++i )
    mag[i] = normL2Sqr( corr[i].first - corr[i].second );

  const size_t threshold = size_t( mag.size() * thresholdOutliers );
  std::nth_element( mag.begin(), mag.begin() + threshold, mag.end() );
  const float percentile = mag[threshold];
  size_t i = 0, j = 0;

  while ( i < corr.size() )
  {
    if ( normL2Sqr( corr[i].first - corr[i].second ) <= percentile )
    {
      corr[j] = corr[i];
      ++j;
    }
    ++i;
  }

  corr.resize( j );
}

766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786
} // namespace optflow

void write( FileStorage &fs, const String &name, const optflow::GPCTree::Node &node )
{
  cv::internal::WriteStructContext ws( fs, name, CV_NODE_SEQ + CV_NODE_FLOW );
  for ( unsigned i = 0; i < optflow::GPCPatchDescriptor::nFeatures; ++i )
    write( fs, node.coef[i] );
  write( fs, node.rhs );
  write( fs, (int)node.left );
  write( fs, (int)node.right );
}

void read( const FileNode &fn, optflow::GPCTree::Node &node, optflow::GPCTree::Node )
{
  FileNodeIterator it = fn.begin();
  for ( unsigned i = 0; i < optflow::GPCPatchDescriptor::nFeatures; ++i )
    it >> node.coef[i];
  it >> node.rhs >> (int &)node.left >> (int &)node.right;
}

} // namespace cv