]> rtime.felk.cvut.cz Git - hercules2020/kcf.git/commitdiff
Merge remote-tracking branch 'upstream/master' into rotation
authorShanigen <vkaraf@gmail.com>
Fri, 14 Sep 2018 20:39:49 +0000 (22:39 +0200)
committerShanigen <vkaraf@gmail.com>
Fri, 14 Sep 2018 20:39:49 +0000 (22:39 +0200)
Big batch is not working correctly.

1  2 
main_vot.cpp
src/CMakeLists.txt
src/fft_fftw.cpp
src/kcf.cpp
src/kcf.h
src/threadctx.hpp

diff --combined main_vot.cpp
index 528d263e678a3fc3110856a1ae5bd53c13cc1530,38aa35d28818aae3560f644ff335f80e0cd6e16e..711a00045065ae9060ca9e9a55135ef315777246
@@@ -2,6 -2,7 +2,7 @@@
  #include <getopt.h>
  #include <libgen.h>
  #include <unistd.h>
+ #include <iomanip>
  
  #include "kcf.h"
  #include "vot.hpp"
  double calcAccuracy(std::string line, cv::Rect bb_rect, cv::Rect &groundtruth_rect)
  {
      std::vector<float> numbers;
 -    std::istringstream s( line );
 +    std::istringstream s(line);
      float x;
      char ch;
  
 -    while (s >> x){
 +    while (s >> x) {
          numbers.push_back(x);
          s >> ch;
      }
      double y1 = std::min(numbers[1], std::min(numbers[3], std::min(numbers[5], numbers[7])));
      double y2 = std::max(numbers[1], std::max(numbers[3], std::max(numbers[5], numbers[7])));
  
 -    groundtruth_rect = cv::Rect(x1, y1, x2-x1, y2-y1);
 +    groundtruth_rect = cv::Rect(x1, y1, x2 - x1, y2 - y1);
  
      double rects_intersection = (groundtruth_rect & bb_rect).area();
      double rects_union = (groundtruth_rect | bb_rect).area();
 -    double accuracy = rects_intersection/rects_union;
 +    double accuracy = rects_intersection / rects_union;
  
      return accuracy;
  }
  
  int main(int argc, char *argv[])
  {
 -    //load region, images and prepare for output
 +    // load region, images and prepare for output
      std::string region, images, output;
      int visualize_delay = -1, fit_size_x = -1, fit_size_y = -1;
      KCF_Tracker tracker;
  
      while (1) {
          int option_index = 0;
 -        static struct option long_options[] = {
 -            {"debug",     no_argument,       0,  'd' },
 -            {"help",      no_argument,       0,  'h' },
 -            {"output",    required_argument, 0,  'o' },
 -            {"visualize", optional_argument, 0,  'v' },
 -            {"fit",       optional_argument, 0,  'f' },
 -            {0,           0,                 0,  0 }
 -        };
 -
 -        int c = getopt_long(argc, argv, "dhv::f::o:",
 -                        long_options, &option_index);
 -        if (c == -1)
 -            break;
 +        static struct option long_options[] = {{"debug", no_argument, 0, 'd'},
 +                                               {"visualDebug", no_argument, 0, 'p'},
 +                                               {"help", no_argument, 0, 'h'},
 +                                               {"output", required_argument, 0, 'o'},
 +                                               {"visualize", optional_argument, 0, 'v'},
 +                                               {"fit", optional_argument, 0, 'f'},
 +                                               {0, 0, 0, 0}};
 +
 +        int c = getopt_long(argc, argv, "dphv::f::o:", long_options, &option_index);
 +        if (c == -1) break;
  
          switch (c) {
          case 'd':
              tracker.m_debug = true;
              break;
 +        case 'p':
 +            tracker.m_visual_debug = true;
 +            visualize_delay = 500;
 +            break;
          case 'h':
 -            std::cerr << "Usage: \n"
 -                      << argv[0] << " [options]\n"
 -                      << argv[0] << " [options] <directory>\n"
 -                      << argv[0] << " [options] <path/to/region.txt or groundtruth.txt> <path/to/images.txt> [path/to/output.txt]\n"
 -                      << "Options:\n"
 -                      << " --visualize | -v[delay_ms]\n"
 -                      << " --output    | -o <output.txt>\n"
 -                      << " --debug     | -d\n"
 -                      << " --fit       | -f[WxH]\n";
 +            std::cerr
 +                << "Usage: \n"
 +                << argv[0] << " [options]\n"
 +                << argv[0] << " [options] <directory>\n"
 +                << argv[0]
 +                << " [options] <path/to/region.txt or groundtruth.txt> <path/to/images.txt> [path/to/output.txt]\n"
 +                << "Options:\n"
 +                << " --visualize | -v[delay_ms]\n"
 +                << " --output    | -o <output.txt>\n"
 +                << " --debug     | -d\n"
 +                << " --visualDebug | -p\n"
 +                << " --fit       | -f[WxH]\n";
              exit(0);
              break;
          case 'o':
@@@ -88,7 -85,7 +89,7 @@@
              sizes.erase(0, pos + delimiter.length());
  
              fit_size_x = stol(first_argument);
 -          fit_size_y = stol(sizes);
 +            fit_size_y = stol(sizes);
              break;
          }
      }
      case 0:
          region = access("groundtruth.txt", F_OK) == 0 ? "groundtruth.txt" : "region.txt";
          images = "images.txt";
 -        if (output.empty())
 -            output = "output.txt";
 +        if (output.empty()) output = "output.txt";
          break;
      case 2:
          // Fall through
  
      cv::Mat image;
  
 -    //img = firts frame, initPos = initial position in the first frame
 +    // img = firts frame, initPos = initial position in the first frame
      cv::Rect init_rect = vot_io.getInitRectangle();
      vot_io.outputBoundingBox(init_rect);
      vot_io.getNextImage(image);
      cv::Rect bb_rect;
      double avg_time = 0., sum_accuracy = 0.;
      int frames = 0;
-     while (vot_io.getNextImage(image) == 1) {
+     std::cout << std::fixed << std::setprecision(2);
+     while (vot_io.getNextImage(image) == 1){
          double time_profile_counter = cv::getCPUTickCount();
          tracker.track(image);
          time_profile_counter = cv::getCPUTickCount() - time_profile_counter;
-          std::cout << "  -> speed : " <<  time_profile_counter/((double)cvGetTickFrequency()*1000) << "ms. per frame, "
+          std::cout << "  -> speed : " <<  time_profile_counter/((double)cvGetTickFrequency()*1000) << "ms per frame, "
                        "response : " << tracker.getFilterResponse();
          avg_time += time_profile_counter/((double)cvGetTickFrequency()*1000);
          frames++;
  
          bb = tracker.getBBox();
 -        bb_rect = cv::Rect(bb.cx - bb.w/2., bb.cy - bb.h/2., bb.w, bb.h);
 +        bb_rect = cv::Rect(bb.cx - bb.w / 2., bb.cy - bb.h / 2., bb.w, bb.h);
          vot_io.outputBoundingBox(bb_rect);
  
          if (groundtruth_stream.is_open()) {
  
              cv::Rect groundtruthRect;
              double accuracy = calcAccuracy(line, bb_rect, groundtruthRect);
 -            if (visualize_delay >= 0)
 -                cv::rectangle(image, groundtruthRect, CV_RGB(255, 0,0), 1);
 +            if (visualize_delay >= 0) cv::rectangle(image, groundtruthRect, CV_RGB(255, 0, 0), 1);
              std::cout << ", accuracy: " << accuracy;
              sum_accuracy += accuracy;
          }
          std::cout << std::endl;
  
          if (visualize_delay >= 0) {
 -            cv::rectangle(image, bb_rect, CV_RGB(0,255,0), 2);
 +            cv::Point pt(bb.cx, bb.cy);
 +            cv::Size size(bb.w, bb.h);
 +            cv::RotatedRect rotatedRectangle(pt, size, bb.a);
 +
 +            cv::Point2f vertices[4];
 +            rotatedRectangle.points(vertices);
 +
 +            for (int i = 0; i < 4; i++)
 +                cv::line(image, vertices[i], vertices[(i + 1) % 4], cv::Scalar(0, 255, 0), 2);
 +            //             cv::rectangle(image, cv::Rect(bb.cx - bb.w/2., bb.cy - bb.h/2., bb.w, bb.h), CV_RGB(0,255,0),
 +            //             2);
 +            std::string angle = std::to_string(bb.a);
 +            angle.erase(angle.find_last_not_of('0') + 1, std::string::npos);
 +            angle.erase(angle.find_last_not_of('.') + 1, std::string::npos);
 +            cv::putText(image, "Frame: " + std::to_string(frames) + " " + angle + " angle",
 +                        cv::Point(0, image.rows - 1), cv::FONT_HERSHEY_SIMPLEX, 0.7, cv::Scalar(0, 255, 0), 2);
              cv::imshow("output", image);
              int ret = cv::waitKey(visualize_delay);
 -            if (visualize_delay > 0 && ret != -1 && ret != 255)
 -                break;
 +            if (visualize_delay > 0 && ret != -1 && ret != 255) break;
          }
  
 -//        std::stringstream s;
 -//        std::string ss;
 -//        int countTmp = frames;
 -//        s << "imgs" << "/img" << (countTmp/10000);
 -//        countTmp = countTmp%10000;
 -//        s << (countTmp/1000);
 -//        countTmp = countTmp%1000;
 -//        s << (countTmp/100);
 -//        countTmp = countTmp%100;
 -//        s << (countTmp/10);
 -//        countTmp = countTmp%10;
 -//        s << (countTmp);
 -//        s << ".jpg";
 -//        s >> ss;
 -//        //set image output parameters
 -//        std::vector<int> compression_params;
 -//        compression_params.push_back(CV_IMWRITE_JPEG_QUALITY);
 -//        compression_params.push_back(90);
 -//        cv::imwrite(ss.c_str(), image, compression_params);
 +        //        std::stringstream s;
 +        //        std::string ss;
 +        //        int countTmp = frames;
 +        //        s << "imgs" << "/img" << (countTmp/10000);
 +        //        countTmp = countTmp%10000;
 +        //        s << (countTmp/1000);
 +        //        countTmp = countTmp%1000;
 +        //        s << (countTmp/100);
 +        //        countTmp = countTmp%100;
 +        //        s << (countTmp/10);
 +        //        countTmp = countTmp%10;
 +        //        s << (countTmp);
 +        //        s << ".jpg";
 +        //        s >> ss;
 +        //        //set image output parameters
 +        //        std::vector<int> compression_params;
 +        //        compression_params.push_back(CV_IMWRITE_JPEG_QUALITY);
 +        //        compression_params.push_back(90);
 +        //        cv::imwrite(ss.c_str(), image, compression_params);
      }
  
-     std::cout << "Average processing speed: " << avg_time/frames <<  "ms (" << 1./(avg_time/frames)*1000 << " fps)";
+     std::cout << "Average processing speed: " << avg_time / frames << "ms (" << 1. / (avg_time / frames) * 1000 << " fps)";
      if (groundtruth_stream.is_open()) {
          std::cout << "; Average accuracy: " << sum_accuracy/frames << std::endl;
          groundtruth_stream.close();
diff --combined src/CMakeLists.txt
index 901f0dcae82048da05644889a0e8a338a58e1152,a6b511a2650e7635e39159a99df1a8628926e6f8..3e0aee471c678ef511a3ec344a0a03126d6198d2
@@@ -71,7 -71,14 +71,14 @@@ IF(use_cuda
    include_directories ( ${CUDA_INCLUDE_DIRS} )
    set(CUDA_SEPARABLE_COMPILATION ON)
    set(CUDA_PROPAGATE_HOST_FLAGS OFF)
-   list( APPEND CUDA_NVCC_FLAGS -O3 --gpu-architecture sm_62 -std=c++11)
+   if(NOT COMMAND CUDA_SELECT_NVCC_ARCH_FLAGS)
+     include(${CMAKE_SOURCE_DIR}/cmake/FindCUDA/select_compute_arch.cmake)
+   endif()
+   set(CUDA_ARCH_LIST "Auto" CACHE STRING "CUDA GPU architecture for building the code")
+   CUDA_SELECT_NVCC_ARCH_FLAGS(ARCH_FLAGS ${CUDA_ARCH_LIST})
+   list( APPEND CUDA_NVCC_FLAGS -O3 -std=c++11 ${ARCH_FLAGS}) # --gpu-architecture sm_62 )
    find_cuda_helper_libs(cufftw)
    IF(FFT STREQUAL "cuFFT")
    add_subdirectory(cuda)
@@@ -91,9 -98,9 +98,9 @@@ set_target_properties(kcf PROPERTIES VE
  IF(FFT STREQUAL "fftw")
    target_link_libraries(kcf ${FFTW_LDFLAGS})
    IF(OPENMP)
 -    target_link_libraries(kcf fftw3_omp)
 +    target_link_libraries(kcf fftw3f_omp)
    ELSEIF(NOT ASYNC)
 -    target_link_libraries(kcf fftw3_threads)
 +    target_link_libraries(kcf fftw3f_threads)
    ENDIF()
  ENDIF() #FFTW
  
diff --combined src/fft_fftw.cpp
index a3108844ad22a70394a46e72dbdc35563bda4621,ae3fa1c0b08db6303e7c69594b55c9d3f3d13499..2215e20318a898119f53324d4a3c10b12a285863
@@@ -6,46 -6,41 +6,45 @@@
  #include <omp.h>
  #endif
  
 -#if !defined(ASYNC) && !defined(OPENMP) && !defined(CUFFTW)
 -#define FFTW_PLAN_WITH_THREADS() fftw_plan_with_nthreads(4);
 +#if (defined(BIG_BATCH) && !defined(CUFFTW)) || (!defined(ASYNC) && !defined(OPENMP) && !defined(CUFFTW))
 +#define FFTW_PLAN_WITH_THREADS() fftwf_plan_with_nthreads(4);
 +#define FFTW_INIT_THREAD() fftwf_init_threads();
 +#define FFTW_CLEAN_THREADS() fftwf_cleanup_threads();
  #else
  #define FFTW_PLAN_WITH_THREADS()
 +#define FFTW_INIT_THREAD()
 +#define FFTW_CLEAN_THREADS()
  #endif
  
 -Fftw::Fftw(){}
 +Fftw::Fftw() {}
  
- void Fftw::init(unsigned width, unsigned height, unsigned num_of_feats, unsigned num_of_scales, bool big_batch_mode)
+ void Fftw::init(unsigned width, unsigned height, unsigned num_of_feats, unsigned num_of_scales)
  {
      m_width = width;
      m_height = height;
      m_num_of_feats = num_of_feats;
      m_num_of_scales = num_of_scales;
-     m_big_batch_mode = big_batch_mode;
  
 -#if (!defined(ASYNC) && !defined(CUFFTW)) && defined(OPENMP)
 -    fftw_init_threads();
 -#endif // OPENMP
 -
  #ifndef CUFFTW
      std::cout << "FFT: FFTW" << std::endl;
  #else
      std::cout << "FFT: cuFFTW" << std::endl;
  #endif
 -    fftwf_cleanup();
 +
 +     FFTW_INIT_THREAD();
 +
      // FFT forward one scale
      {
          cv::Mat in_f = cv::Mat::zeros(int(m_height), int(m_width), CV_32FC1);
          ComplexMat out_f(int(m_height), m_width / 2 + 1, 1);
 +
 +        FFTW_PLAN_WITH_THREADS();
          plan_f = fftwf_plan_dft_r2c_2d(int(m_height), int(m_width), reinterpret_cast<float *>(in_f.data),
                                         reinterpret_cast<fftwf_complex *>(out_f.get_p_data()), FFTW_PATIENT);
      }
  #ifdef BIG_BATCH
      // FFT forward all scales
-     if (m_num_of_scales > 1 && m_big_batch_mode) {
+     if (m_num_of_scales > 1 && BIG_BATCH_MODE) {
          cv::Mat in_f_all = cv::Mat::zeros(m_height * m_num_of_scales, m_width, CV_32F);
          ComplexMat out_f_all(m_height, m_width / 2 + 1, m_num_of_scales);
          float *in = reinterpret_cast<float *>(in_f_all.data);
@@@ -81,7 -76,7 +80,7 @@@
      }
  #ifdef BIG_BATCH
      // FFT forward window all scales all feats
-     if (m_num_of_scales > 1 && m_big_batch_mode) {
+     if (m_num_of_scales > 1 && BIG_BATCH_MODE) {
          cv::Mat in_all = cv::Mat::zeros(m_height * (m_num_of_scales * m_num_of_feats), m_width, CV_32F);
          ComplexMat out_all(m_height, m_width / 2 + 1, m_num_of_scales * m_num_of_feats);
          float *in = reinterpret_cast<float *>(in_all.data);
      }
      // FFT inverse all scales
  #ifdef BIG_BATCH
-     if (m_num_of_scales > 1 && m_big_batch_mode) {
+     if (m_num_of_scales > 1 && BIG_BATCH_MODE) {
          ComplexMat in_i_all(m_height, m_width, m_num_of_feats * m_num_of_scales);
          cv::Mat out_i_all = cv::Mat::zeros(m_height, m_width, CV_32FC(m_num_of_feats * m_num_of_scales));
          fftwf_complex *in = reinterpret_cast<fftwf_complex *>(in_i_all.get_p_data());
      }
  #ifdef BIG_BATCH
      // FFT inver one channel all scales
-     if (m_num_of_scales > 1 && m_big_batch_mode) {
+     if (m_num_of_scales > 1 && BIG_BATCH_MODE) {
          ComplexMat in_i1_all(m_height, m_width, m_num_of_scales);
          cv::Mat out_i1_all = cv::Mat::zeros(m_height, m_width, CV_32FC(m_num_of_scales));
          fftwf_complex *in = reinterpret_cast<fftwf_complex *>(in_i1_all.get_p_data());
@@@ -182,7 -177,7 +181,7 @@@ void Fftw::forward(const cv::Mat &real_
      (void)real_input_arr;
      (void)stream;
  
-     if (m_big_batch_mode && real_input.rows == int(m_height * m_num_of_scales)) {
+     if (BIG_BATCH_MODE && real_input.rows == int(m_height * m_num_of_scales)) {
          fftwf_execute_dft_r2c(plan_f_all_scales, reinterpret_cast<float *>(real_input.data),
                                reinterpret_cast<fftwf_complex *>(complex_result.get_p_data()));
      } else {
@@@ -225,9 -220,9 +224,9 @@@ void Fftw::inverse(ComplexMat &complex_
  
      if (n_channels == 1)
          fftwf_execute_dft_c2r(plan_i_1ch, in, out);
-     else if (m_big_batch_mode && n_channels == int(m_num_of_scales))
+     else if (BIG_BATCH_MODE && n_channels == int(m_num_of_scales))
          fftwf_execute_dft_c2r(plan_i_1ch_all_scales, in, out);
-     else if (m_big_batch_mode && n_channels == int(m_num_of_feats) * int(m_num_of_scales))
+     else if (BIG_BATCH_MODE && n_channels == int(m_num_of_feats) * int(m_num_of_scales))
          fftwf_execute_dft_c2r(plan_i_features_all_scales, in, out);
      else
          fftwf_execute_dft_c2r(plan_i_features, in, out);
@@@ -243,11 -238,10 +242,11 @@@ Fftw::~Fftw(
      fftwf_destroy_plan(plan_i_features);
      fftwf_destroy_plan(plan_i_1ch);
  
-     if (m_big_batch_mode) {
+     if (BIG_BATCH_MODE) {
          fftwf_destroy_plan(plan_f_all_scales);
          fftwf_destroy_plan(plan_i_features_all_scales);
          fftwf_destroy_plan(plan_fw_all_scales);
          fftwf_destroy_plan(plan_i_1ch_all_scales);
      }
 +    FFTW_CLEAN_THREADS();
  }
diff --combined src/kcf.cpp
index b32532766901e76e536e9d7d7ca5f08e10518c03,663f6f118627fb8f465d09e6c678596b8abac495..3195bab37fdeb0c7e44adc44c68e9f71845735e7
@@@ -19,7 -19,7 +19,7 @@@
  #endif // OPENMP
  
  #define DEBUG_PRINT(obj)                                                                                               \
 -    if (m_debug) {                                                                                                     \
 +    if (m_debug || m_visual_debug) {                                                                                   \
          std::cout << #obj << " @" << __LINE__ << std::endl << (obj) << std::endl;                                      \
      }
  #define DEBUG_PRINTM(obj)                                                                                              \
                    << (obj) << std::endl;                                                                               \
      }
  
+ template <typename T>
+ T clamp(const T& n, const T& lower, const T& upper)
+ {
+     return std::max(lower, std::min(n, upper));
+ }
+ template <typename T>
+ void clamp2(T& n, const T& lower, const T& upper)
+ {
+     n = std::max(lower, std::min(n, upper));
+ }
  KCF_Tracker::KCF_Tracker(double padding, double kernel_sigma, double lambda, double interp_factor,
                           double output_sigma_factor, int cell_size)
      : fft(*new FFT()), p_padding(padding), p_output_sigma_factor(output_sigma_factor), p_kernel_sigma(kernel_sigma),
@@@ -122,31 -134,24 +134,31 @@@ void KCF_Tracker::init(cv::Mat &img, co
      p_roi.width = p_windows_size.width / p_cell_size;
      p_roi.height = p_windows_size.height / p_cell_size;
  
 -    p_num_of_feats = 31;
 -    if (m_use_color) p_num_of_feats += 3;
 -    if (m_use_cnfeat) p_num_of_feats += 10;
 -
      p_scales.clear();
 -    if (m_use_scale)
 +    if (m_use_scale) {
          for (int i = -int(p_num_scales) / 2; i <= int(p_num_scales) / 2; ++i)
              p_scales.push_back(std::pow(p_scale_step, i));
 -    else
 +    } else {
          p_scales.push_back(1.);
 +        p_num_scales = 1;
 +    }
 +
 +    if (m_use_angle) {
 +        for (int i = p_angle_min; i <= p_angle_max; i += p_angle_step)
 +            p_angles.push_back(i);
 +    } else {
 +        p_angles.push_back(0);
 +        p_num_angles = 1;
 +    }
  
  #ifdef CUFFT
      if (p_roi.height * (p_roi.width / 2 + 1) > 1024) {
          std::cerr << "Window after forward FFT is too big for CUDA kernels. Plese use -f to set "
                       "the window dimensions so its size is less or equal to "
                    << 1024 * p_cell_size * p_cell_size * 2 + 1
 -                  << " pixels . Currently the size of the window is: " << p_windows_size.width << "x" << p_windows_size.height
 -                  << " which is  " << p_windows_size.width * p_windows_size.height << " pixels. " << std::endl;
 +                  << " pixels . Currently the size of the window is: " << p_windows_size.width << "x"
 +                  << p_windows_size.height << " which is  " << p_windows_size.width * p_windows_size.height
 +                  << " pixels. " << std::endl;
          std::exit(EXIT_FAILURE);
      }
  
          std::cerr << "cuFFT supports only Gaussian kernel." << std::endl;
          std::exit(EXIT_FAILURE);
      }
 +
      CudaSafeCall(cudaSetDeviceFlags(cudaDeviceMapHost));
 +
      p_rot_labels_data = DynMem(p_roi.width * p_roi.height * sizeof(float));
      p_rot_labels = cv::Mat(p_roi, CV_32FC1, p_rot_labels_data.hostMem());
 -#else
 -    p_xf.create(p_roi.height, p_roi.height / 2 + 1, p_num_of_feats);
  #endif
  
  #if defined(CUFFT) || defined(FFTW)
      p_yf.create(p_roi.height, width, 1);
      p_xf.create(p_roi.height, width, p_num_of_feats);
  
-     int max1 = m_use_big_batch ? 2 : p_num_scales;
-     int max2 = m_use_big_batch ? 1 : p_num_angles;
 -    int max = BIG_BATCH_MODE ? 2 : p_num_scales;
 -    for (int i = 0; i < max; ++i) {
 -        if (BIG_BATCH_MODE && i == 1)
 -            p_threadctxs.emplace_back(p_roi, p_num_of_feats * p_num_scales, 1, p_num_scales);
 -        else
 -            p_threadctxs.emplace_back(p_roi, p_num_of_feats, p_scales[i], 1);
++    int max1 = BIG_BATCH_MODE ? 2 : p_num_scales;
++    int max2 = BIG_BATCH_MODE ? 1 : p_num_angles;
 +    for (int i = 0; i < max1; ++i) {
 +        for (int j = 0; j < max2; ++j) {
-             if (m_use_big_batch && i == 1)
++            if (BIG_BATCH_MODE && i == 1)
 +                p_threadctxs.emplace_back(p_roi, p_num_of_feats * p_num_scales * p_num_angles, 1, 0, p_num_scales,
 +                                          p_num_angles);
 +            else
 +                p_threadctxs.emplace_back(p_roi, p_num_of_feats, p_scales[i], p_angles[j]);
 +        }
      }
  
      p_current_scale = 1.;
      p_min_max_scale[0] = std::pow(p_scale_step, std::ceil(std::log(min_size_ratio) / log(p_scale_step)));
      p_min_max_scale[1] = std::pow(p_scale_step, std::floor(std::log(max_size_ratio) / log(p_scale_step)));
  
-     std::cout << "init: img size " << img.cols << " " << img.rows << std::endl;
-     std::cout << "init: win size. " << p_windows_size.width << " " << p_windows_size.height << std::endl;
+     std::cout << "init: img size " << img.cols << "x" << img.rows << std::endl;
+     std::cout << "init: win size " << p_windows_size.width << "x" << p_windows_size.height << std::endl;
+     std::cout << "init: FFT size " << p_roi.width << "x" << p_roi.height << std::endl;
      std::cout << "init: min max scales factors: " << p_min_max_scale[0] << " " << p_min_max_scale[1] << std::endl;
  
-     p_output_sigma = std::sqrt(p_pose.w * p_pose.h) * p_output_sigma_factor / static_cast<double>(p_cell_size);
+     p_output_sigma = std::sqrt(p_pose.w * p_pose.h) * p_output_sigma_factor / p_cell_size;
  
-     fft.init(p_roi.width, p_roi.height, p_num_of_feats, p_num_scales * p_num_angles, m_use_big_batch);
+     fft.init(p_roi.width, p_roi.height, p_num_of_feats, p_num_scales);
      fft.set_window(cosine_window_function(p_roi.width, p_roi.height));
  
      // window weights, i.e. labels
 -    fft.forward(
 -        gaussian_shaped_labels(p_output_sigma, p_roi.width, p_roi.height), p_yf,
 -        m_use_cuda ? p_rot_labels_data.deviceMem() : nullptr, p_threadctxs.front().stream);
 +    fft.forward(gaussian_shaped_labels(p_output_sigma, p_roi.width, p_roi.height), p_yf,
 +                m_use_cuda ? p_rot_labels_data.deviceMem() : nullptr, p_threadctxs.front().stream);
      DEBUG_PRINTM(p_yf);
  
      // obtain a sub-window for training initial model
-     p_threadctxs.front().patch_feats.clear();
 -    std::vector<cv::Mat> patch_feats = get_features(input_rgb, input_gray, p_pose.cx, p_pose.cy,
 -                                                    p_windows_size.width, p_windows_size.height);
 +    int size_x_scaled = floor(p_windows_size.width);
 +    int size_y_scaled = floor(p_windows_size.height);
 +
 +    cv::Mat patch_gray = get_subwindow(input_gray, this->p_pose.cx, this->p_pose.cy, size_x_scaled, size_y_scaled);
 +    geometric_transformations(patch_gray, p_windows_size.width, p_windows_size.height, 0, false);
 +
-     cv::Mat patch_rgb = cv::Mat::zeros(size_y_scaled, size_x_scaled, CV_32F);
++    cv::Mat patch_rgb;
 +    if ((m_use_color || m_use_cnfeat) && input_rgb.channels() == 3) {
 +        patch_rgb = get_subwindow(input_rgb, this->p_pose.cx, this->p_pose.cy, size_x_scaled, size_y_scaled);
 +        geometric_transformations(patch_rgb, p_windows_size.width, p_windows_size.height, 0, false);
 +    }
 +
-     get_features(patch_rgb, patch_gray, p_threadctxs.front());
-     fft.forward_window(p_threadctxs.front().patch_feats, p_model_xf, p_threadctxs.front().fw_all,
++    std::vector<cv::Mat> patch_feats = get_features(patch_rgb, patch_gray);
+     fft.forward_window(patch_feats, p_model_xf, p_threadctxs.front().fw_all,
                         m_use_cuda ? p_threadctxs.front().data_features.deviceMem() : nullptr,
                         p_threadctxs.front().stream);
      DEBUG_PRINTM(p_model_xf);
 +
  #if !defined(BIG_BATCH) && defined(CUFFT) && (defined(ASYNC) || defined(OPENMP))
      p_threadctxs.front().model_xf = p_model_xf;
      p_threadctxs.front().model_xf.set_stream(p_threadctxs.front().stream);
@@@ -295,7 -284,6 +306,7 @@@ BBox_c KCF_Tracker::getBBox(
      BBox_c tmp = p_pose;
      tmp.w *= p_current_scale;
      tmp.h *= p_current_scale;
 +    tmp.a = p_current_angle;
  
      if (p_resize_image) tmp.scale(1 / p_downscale_factor);
      if (p_fit_to_pw2) {
@@@ -313,7 -301,7 +324,7 @@@ double KCF_Tracker::getFilterResponse(
  
  void KCF_Tracker::track(cv::Mat &img)
  {
 -    if (m_debug) std::cout << "NEW FRAME" << '\n';
 +    if (m_debug || m_visual_debug) std::cout << "\nNEW FRAME" << std::endl;
      cv::Mat input_gray, input_rgb = img.clone();
      if (img.channels() == 3) {
          cv::cvtColor(img, input_gray, CV_BGR2GRAY);
              cv::resize(input_rgb, input_rgb, cv::Size(0, 0), p_scale_factor_x, p_scale_factor_y, cv::INTER_LINEAR);
          }
      }
 -
      max_response = -1.;
      ThreadCtx *max = nullptr;
      cv::Point2i *max_response_pt = nullptr;
          });
      for (auto const &it : p_threadctxs)
          it.async_res.wait();
 -
  #else  // !ASYNC
 -    // FIXME: Iterate correctly in big batch mode - perhaps have only one element in the list
      NORMAL_OMP_PARALLEL_FOR
-     for (uint i = m_use_big_batch ? 1 : 0; i < p_threadctxs.size(); ++i)
 -    for (uint i = 0; i < p_threadctxs.size(); ++i)
++    for (uint i =  BIG_BATCH_MODE ? 1 : 0; i < p_threadctxs.size(); ++i)
          scale_track(p_threadctxs[i], input_rgb, input_gray);
  #endif
  
          }
      }
  #else
 -    // FIXME: Iterate correctly in big batch mode - perhaps have only one element in the list
 -    for (uint j = 0; j < p_scales.size(); ++j) {
 -        if (p_threadctxs[0].max_responses[j] > max_response) {
 -            max_response = p_threadctxs[0].max_responses[j];
 -            max_response_pt = &p_threadctxs[0].max_locs[j];
 -            max_response_map = &p_threadctxs[0].response_maps[j];
 -            max = &p_threadctxs[0];
 +    for (uint j = 0; j < p_num_scales; ++j) {
 +        for (uint k = 0; k < p_num_angles; ++k) {
 +            if (p_threadctxs.back().max_responses[j + k] > max_response) {
 +                max_response = p_threadctxs.back().max_responses[j + k];
 +                max_response_pt = &p_threadctxs.back().max_locs[j + k];
 +                max_response_map = &p_threadctxs.back().response_maps[j + k];
 +            }
          }
      }
 +    max = &p_threadctxs.back();
  #endif
 +    if (m_visual_debug) {
 +        cv::Mat all_responses(cv::Size(p_num_angles* p_debug_image_size, p_num_scales * p_debug_image_size),
 +                              p_debug_scale_responses[0].type(), cv::Scalar::all(0));
 +        cv::Mat all_subwindows(cv::Size(p_num_angles* p_debug_image_size, p_num_scales* p_debug_image_size),
 +                               p_debug_subwindows[0].type(), cv::Scalar::all(0));
 +        for (size_t i = 0; i < p_num_scales; ++i) {
 +            for (size_t j = 0; j < p_num_angles; ++j) {
 +                cv::Mat in_roi(all_responses, cv::Rect(j * p_debug_image_size, i * p_debug_image_size,
 +                                                       p_debug_image_size, p_debug_image_size));
 +                p_debug_scale_responses[5 * i + j].copyTo(in_roi);
 +                in_roi = all_subwindows(
 +                    cv::Rect(j * p_debug_image_size, i * p_debug_image_size, p_debug_image_size, p_debug_image_size));
 +                p_debug_subwindows[5 * i + j].copyTo(in_roi);
 +            }
 +        }
 +        cv::namedWindow("All subwindows", CV_WINDOW_AUTOSIZE);
 +        cv::imshow("All subwindows", all_subwindows);
 +        cv::namedWindow("All responses", CV_WINDOW_AUTOSIZE);
 +        cv::imshow("All responses", all_responses);
 +        cv::waitKey();
 +        p_debug_scale_responses.clear();
 +        p_debug_subwindows.clear();
 +    }
  
      DEBUG_PRINTM(*max_response_map);
      DEBUG_PRINT(*max_response_pt);
          new_location = sub_pixel_peak(*max_response_pt, *max_response_map);
      DEBUG_PRINT(new_location);
  
 +    if (m_visual_debug) std::cout << "Old p_pose, cx: " << p_pose.cx << " cy: " << p_pose.cy << std::endl;
 +
      p_pose.cx += p_current_scale * p_cell_size * double(new_location.x);
      p_pose.cy += p_current_scale * p_cell_size * double(new_location.y);
 +
 +    if (m_visual_debug) std::cout << "New p_pose, cx: " << p_pose.cx << " cy: " << p_pose.cy << std::endl;
 +
      if (p_fit_to_pw2) {
-         if (p_pose.cx < 0) p_pose.cx = 0;
-         if (p_pose.cx > (img.cols * p_scale_factor_x) - 1) p_pose.cx = (img.cols * p_scale_factor_x) - 1;
-         if (p_pose.cy < 0) p_pose.cy = 0;
-         if (p_pose.cy > (img.rows * p_scale_factor_y) - 1) p_pose.cy = (img.rows * p_scale_factor_y) - 1;
+         clamp2(p_pose.cx, 0.0, (img.cols * p_scale_factor_x) - 1);
+         clamp2(p_pose.cy, 0.0, (img.rows * p_scale_factor_y) - 1);
      } else {
-         if (p_pose.cx < 0) p_pose.cx = 0;
-         if (p_pose.cx > img.cols - 1) p_pose.cx = img.cols - 1;
-         if (p_pose.cy < 0) p_pose.cy = 0;
-         if (p_pose.cy > img.rows - 1) p_pose.cy = img.rows - 1;
+         clamp2(p_pose.cx, 0.0, img.cols - 1.0);
+         clamp2(p_pose.cy, 0.0, img.rows - 1.0);
      }
  
      // sub grid scale interpolation
          p_current_scale *= max->scale;
      }
  
+     clamp2(p_current_scale, p_min_max_scale[0], p_min_max_scale[1]);
  
 -    ThreadCtx &ctx = p_threadctxs.front();
 +    if (p_current_scale < p_min_max_scale[0]) p_current_scale = p_min_max_scale[0];
 +    if (p_current_scale > p_min_max_scale[1]) p_current_scale = p_min_max_scale[1];
 +
 +    p_current_angle = (p_current_angle + max->angle) < 0
 +                          ? -std::abs(p_current_angle + max->angle) % 360
 +                          : (p_current_angle + max->angle) % 360;
 +
      // obtain a subwindow for training at newly estimated target position
 -    std::vector<cv::Mat> patch_feats = get_features(input_rgb, input_gray, p_pose.cx, p_pose.cy,
 -                                                    p_windows_size.width, p_windows_size.height,
 -                                                    p_current_scale);
 -    fft.forward_window(patch_feats, p_xf, ctx.fw_all,
 -                       m_use_cuda ? ctx.data_features.deviceMem() : nullptr, ctx.stream);
 +    int size_x_scaled = floor(p_windows_size.width * p_current_scale);
 +    int size_y_scaled = floor(p_windows_size.height * p_current_scale);
 +
 +    cv::Mat patch_gray = get_subwindow(input_gray, this->p_pose.cx, this->p_pose.cy, size_x_scaled, size_y_scaled);
 +    geometric_transformations(patch_gray, p_windows_size.width, p_windows_size.height, p_current_angle, false);
 +
 +    cv::Mat patch_rgb = cv::Mat::zeros(size_y_scaled, size_x_scaled, CV_32F);
 +    if ((m_use_color || m_use_cnfeat) && input_rgb.channels() == 3) {
 +        patch_rgb = get_subwindow(input_rgb, this->p_pose.cx, this->p_pose.cy, size_x_scaled, size_y_scaled);
 +        geometric_transformations(patch_rgb, p_windows_size.width, p_windows_size.height, p_current_angle, false);
 +    }
 +
-     p_threadctxs.front().patch_feats.clear();
-     get_features(patch_rgb, patch_gray, p_threadctxs.front());
-     fft.forward_window(p_threadctxs.front().patch_feats, p_xf, p_threadctxs.front().fw_all,
-                        m_use_cuda ? p_threadctxs.front().data_features.deviceMem() : nullptr, p_threadctxs.front().stream);
++    ThreadCtx &ctx = p_threadctxs.front();
++    std::vector<cv::Mat> patch_feats = get_features(patch_rgb, patch_gray);
++    fft.forward_window(patch_feats, p_xf, ctx.fw_all, m_use_cuda ? ctx.data_features.deviceMem() : nullptr, ctx.stream);
  
      // subsequent frames, interpolate model
      p_model_xf = p_model_xf * float((1. - p_interp_factor)) + p_xf * float(p_interp_factor);
          alphaf_den = (p_xf * xfconj);
      } else {
          // Kernel Ridge Regression, calculate alphas (in Fourier domain)
-         gaussian_correlation(p_threadctxs.front(), p_xf, p_xf, p_kernel_sigma,
+         gaussian_correlation(ctx, p_xf, p_xf, p_kernel_sigma,
                               true);
          //        ComplexMat alphaf = p_yf / (kf + p_lambda); //equation for fast training
          //        p_model_alphaf = p_model_alphaf * (1. - p_interp_factor) + alphaf * p_interp_factor;
-         alphaf_num = p_yf * p_threadctxs.front().kf;
-         alphaf_den = p_threadctxs.front().kf * (p_threadctxs.front().kf + float(p_lambda));
+         alphaf_num = p_yf * ctx.kf;
+         alphaf_den = ctx.kf * (ctx.kf + float(p_lambda));
      }
  
      p_model_alphaf_num = p_model_alphaf_num * float((1. - p_interp_factor)) + alphaf_num * float(p_interp_factor);
  
  void KCF_Tracker::scale_track(ThreadCtx &vars, cv::Mat &input_rgb, cv::Mat &input_gray)
  {
-     if (m_use_big_batch) {
-         vars.patch_feats.clear();
+     std::vector<cv::Mat> patch_feats;
+     if (BIG_BATCH_MODE) {
          BIG_BATCH_OMP_PARALLEL_FOR
 -        for (uint i = 0; i < p_num_scales; ++i) {
 -            patch_feats = get_features(input_rgb, input_gray, this->p_pose.cx, this->p_pose.cy,
 -                                       this->p_windows_size.width, this->p_windows_size.height,
 -                                       this->p_current_scale * this->p_scales[i]);
 +        for (uint i = 0; i < this->p_scales.size(); ++i) {
 +            for (uint j = 0; j < this->p_angles.size(); ++j) {
 +                int size_x_scaled = floor(this->p_windows_size.width * this->p_current_scale * this->p_scales[i]);
 +                int size_y_scaled = floor(this->p_windows_size.height * this->p_current_scale * this->p_scales[i]);
 +
 +                cv::Mat patch_gray =
 +                    get_subwindow(input_gray, this->p_pose.cx, this->p_pose.cy, size_x_scaled, size_y_scaled);
 +                geometric_transformations(patch_gray, p_windows_size.width, p_windows_size.height,
 +                                          p_current_scale * this->p_scales[i], p_current_angle + this->p_angles[j]);
 +
-                 cv::Mat patch_rgb = cv::Mat::zeros(size_y_scaled, size_x_scaled, CV_32F);
++                cv::Mat patch_rgb;
 +                if ((m_use_color || m_use_cnfeat) && input_rgb.channels() == 3) {
 +                    patch_rgb =
 +                        get_subwindow(input_rgb, this->p_pose.cx, this->p_pose.cy, size_x_scaled, size_y_scaled);
 +                    geometric_transformations(patch_rgb, p_windows_size.width, p_windows_size.height,
 +                                              p_current_scale * this->p_scales[i], p_current_angle + this->p_angles[j]);
 +                }
-                 get_features(patch_rgb, patch_gray, vars);
++                std::vector<cv::Mat> tmp = get_features(patch_rgb, patch_gray);
++                BIG_BATCH_OMP_ORDERED
++                patch_feats.insert(patch_feats.end(), tmp.begin(), tmp.end());
 +            }
          }
      } else {
 -        patch_feats = get_features(input_rgb, input_gray, this->p_pose.cx, this->p_pose.cy,
 -                                   this->p_windows_size.width, this->p_windows_size.height,
 -                                   this->p_current_scale * vars.scale);
 +        int size_x_scaled = floor(this->p_windows_size.width * this->p_current_scale * vars.scale);
 +        int size_y_scaled = floor(this->p_windows_size.height * this->p_current_scale * vars.scale);
 +
 +        cv::Mat patch_gray = get_subwindow(input_gray, this->p_pose.cx, this->p_pose.cy, size_x_scaled, size_y_scaled);
 +        geometric_transformations(patch_gray, p_windows_size.width, p_windows_size.height, p_current_scale * vars.scale);
 +
-         cv::Mat patch_rgb = cv::Mat::zeros(size_y_scaled, size_x_scaled, CV_32F);
++        cv::Mat patch_rgb;
 +        if ((m_use_color || m_use_cnfeat) && input_rgb.channels() == 3) {
 +            patch_rgb = get_subwindow(input_rgb, this->p_pose.cx, this->p_pose.cy, size_x_scaled, size_y_scaled);
 +            geometric_transformations(patch_rgb, p_windows_size.width, p_windows_size.height, p_current_scale * vars.scale,
 +                                      p_current_angle + vars.angle);
 +        }
-         vars.patch_feats.clear();
-         get_features(patch_rgb, patch_gray, vars);
++        patch_feats = get_features(patch_rgb, patch_gray);
      }
  
-     fft.forward_window(vars.patch_feats, vars.zf, vars.fw_all, m_use_cuda ? vars.data_features.deviceMem() : nullptr,
+     fft.forward_window(patch_feats, vars.zf, vars.fw_all, m_use_cuda ? vars.data_features.deviceMem() : nullptr,
                         vars.stream);
      DEBUG_PRINTM(vars.zf);
  
      if (m_use_linearkernel) {
-         vars.kzf = m_use_big_batch ? (vars.zf.mul2(this->p_model_alphaf)).sum_over_channels()
+         vars.kzf = BIG_BATCH_MODE ? (vars.zf.mul2(this->p_model_alphaf)).sum_over_channels()
                                     : (p_model_alphaf * vars.zf).sum_over_channels();
          fft.inverse(vars.kzf, vars.response, m_use_cuda ? vars.data_i_1ch.deviceMem() : nullptr, vars.stream);
      } else {
          gaussian_correlation(vars, vars.zf, this->p_model_xf, this->p_kernel_sigma);
          DEBUG_PRINTM(this->p_model_alphaf);
          DEBUG_PRINTM(vars.kzf);
-         vars.kzf = m_use_big_batch ? vars.kzf.mul(this->p_model_alphaf) : this->p_model_alphaf * vars.kzf;
+         vars.kzf = BIG_BATCH_MODE ? vars.kzf.mul(this->p_model_alphaf) : this->p_model_alphaf * vars.kzf;
  #endif
          fft.inverse(vars.kzf, vars.response, m_use_cuda ? vars.data_i_1ch.deviceMem() : nullptr, vars.stream);
      }
      account the fact that, if the target doesn't move, the peak
      will appear at the top-left corner, not at the center (this is
      discussed in the paper). the responses wrap around cyclically. */
-     if (m_use_big_batch) {
-         cv::split(vars.response, vars.response_maps);
-         for (size_t i = 0; i < p_scales.size(); ++i) {
-             double min_val, max_val;
-             cv::Point2i min_loc, max_loc;
-             cv::minMaxLoc(vars.response_maps[i], &min_val, &max_val, &min_loc, &max_loc);
-             DEBUG_PRINT(max_loc);
-             double weight = p_scales[i] < 1. ? p_scales[i] : 1. / p_scales[i];
-             vars.max_responses[i] = max_val * weight;
-             vars.max_locs[i] = max_loc;
-         }
-     } else {
-         double min_val;
-         cv::Point2i min_loc;
-         cv::minMaxLoc(vars.response, &min_val, &vars.max_val, &min_loc, &vars.max_loc);
+ #ifdef BIG_BATCH
+     cv::split(vars.response, vars.response_maps);
+     for (size_t i = 0; i < p_scales.size(); ++i) {
+         double min_val, max_val;
+         cv::Point2i min_loc, max_loc;
+         cv::minMaxLoc(vars.response_maps[i], &min_val, &max_val, &min_loc, &max_loc);
+         DEBUG_PRINT(max_loc);
+         double weight = p_scales[i] < 1. ? p_scales[i] : 1. / p_scales[i];
+         vars.max_responses[i] = max_val * weight;
+         vars.max_locs[i] = max_loc;
+     }
+ #else
+     double min_val;
+     cv::Point2i min_loc;
+     cv::minMaxLoc(vars.response, &min_val, &vars.max_val, &min_loc, &vars.max_loc);
  
-         DEBUG_PRINT(vars.max_loc);
+     DEBUG_PRINT(vars.max_loc);
  
-         double weight = vars.scale < 1. ? vars.scale : 1. / vars.scale;
-         vars.max_response = vars.max_val * weight;
-     }
+     double weight = vars.scale < 1. ? vars.scale : 1. / vars.scale;
+     vars.max_response = vars.max_val * weight;
+ #endif
      return;
  }
  
  // ****************************************************************************
  
- void KCF_Tracker::get_features(cv::Mat &patch_rgb, cv::Mat &patch_gray, ThreadCtx &vars)
 -std::vector<cv::Mat> KCF_Tracker::get_features(cv::Mat & input_rgb, cv::Mat & input_gray, int cx, int cy, int size_x, int size_y, double scale)
++std::vector<cv::Mat> KCF_Tracker::get_features(cv::Mat &patch_rgb, cv::Mat &patch_gray)
  {
 -    int size_x_scaled = floor(size_x * scale);
 -    int size_y_scaled = floor(size_y * scale);
 -
 -    cv::Mat patch_gray = get_subwindow(input_gray, cx, cy, size_x_scaled, size_y_scaled);
 -    cv::Mat patch_rgb = get_subwindow(input_rgb, cx, cy, size_x_scaled, size_y_scaled);
 -
 -    // resize to default size
 -    if (scale > 1.) {
 -        // if we downsample use  INTER_AREA interpolation
 -        cv::resize(patch_gray, patch_gray, cv::Size(size_x, size_y), 0., 0., cv::INTER_AREA);
 -    } else {
 -        cv::resize(patch_gray, patch_gray, cv::Size(size_x, size_y), 0., 0., cv::INTER_LINEAR);
 -    }
 -
      // get hog(Histogram of Oriented Gradients) features
-     vars.patch_feats = FHoG::extract(patch_gray, 2, p_cell_size, 9);
+     std::vector<cv::Mat> hog_feat = FHoG::extract(patch_gray, 2, p_cell_size, 9);
  
      // get color rgb features (simple r,g,b channels)
      std::vector<cv::Mat> color_feat;
 -    if ((m_use_color || m_use_cnfeat) && input_rgb.channels() == 3) {
 -        // resize to default size
 -        if (scale > 1.) {
 -            // if we downsample use  INTER_AREA interpolation
 -            cv::resize(patch_rgb, patch_rgb, cv::Size(size_x / p_cell_size, size_y / p_cell_size), 0., 0., cv::INTER_AREA);
 -        } else {
 -            cv::resize(patch_rgb, patch_rgb, cv::Size(size_x / p_cell_size, size_y / p_cell_size), 0., 0., cv::INTER_LINEAR);
 -        }
 -    }
  
 -    if (m_use_color && input_rgb.channels() == 3) {
 +    if (m_use_color && patch_rgb.channels() == 3) {
          // use rgb color space
          cv::Mat patch_rgb_norm;
          patch_rgb.convertTo(patch_rgb_norm, CV_32F, 1. / 255., -0.5);
          color_feat.insert(color_feat.end(), rgb.begin(), rgb.end());
      }
  
 -    if (m_use_cnfeat && input_rgb.channels() == 3) {
 +    if (m_use_cnfeat && patch_rgb.channels() == 3) {
          std::vector<cv::Mat> cn_feat = CNFeat::extract(patch_rgb);
          color_feat.insert(color_feat.end(), cn_feat.begin(), cn_feat.end());
      }
-     BIG_BATCH_OMP_ORDERED
-     vars.patch_feats.insert(vars.patch_feats.end(), color_feat.begin(), color_feat.end());
-     return;
+     hog_feat.insert(hog_feat.end(), color_feat.begin(), color_feat.end());
+     return hog_feat;
  }
  
  cv::Mat KCF_Tracker::gaussian_shaped_labels(double sigma, int dim1, int dim2)
          float *row_ptr = labels.ptr<float>(j);
          double y_s = y * y;
          for (int x = range_x[0], i = 0; x < range_x[1]; ++x, ++i) {
-             row_ptr[i] = float(std::exp(-0.5 * (y_s + x * x) / sigma_s)); //-1/2*e^((y^2+x^2)/sigma^2)
+             row_ptr[i] = std::exp(-0.5 * (y_s + x * x) / sigma_s); //-1/2*e^((y^2+x^2)/sigma^2)
          }
      }
  
@@@ -779,9 -718,12 +787,9 @@@ cv::Mat KCF_Tracker::get_subwindow(cons
  
      if (x2 - x1 == 0 || y2 - y1 == 0)
          patch = cv::Mat::zeros(height, width, CV_32FC1);
 -    else {
 +    else
          cv::copyMakeBorder(input(cv::Range(y1, y2), cv::Range(x1, x2)), patch, top, bottom, left, right,
                             cv::BORDER_REPLICATE);
 -        //      imshow( "copyMakeBorder", patch);
 -        //      cv::waitKey();
 -    }
  
      // sanity check
      assert(patch.cols == width && patch.rows == height);
      return patch;
  }
  
 -void KCF_Tracker::gaussian_correlation(struct ThreadCtx &vars, const ComplexMat &xf, const ComplexMat &yf,
 -                                       double sigma, bool auto_correlation)
 +void KCF_Tracker::geometric_transformations(cv::Mat &patch, int size_x, int size_y, int angle, bool allow_debug)
 +{
 +    if (m_use_angle) {
 +        cv::Point2f center((patch.cols - 1) / 2., (patch.rows - 1) / 2.);
 +        cv::Mat r = cv::getRotationMatrix2D(center, angle, 1.0);
 +
 +        cv::warpAffine(patch, patch, r, cv::Size(patch.cols, patch.rows), cv::INTER_LINEAR, cv::BORDER_REPLICATE);
 +    }
 +
 +    // resize to default size
 +    if (patch.channels() != 3) {
 +        if (patch.cols / size_x > 1.) {
 +            // if we downsample use  INTER_AREA interpolation
 +            cv::resize(patch, patch, cv::Size(size_x, size_y), 0., 0., cv::INTER_AREA);
 +        } else {
 +            cv::resize(patch, patch, cv::Size(size_x, size_y), 0., 0., cv::INTER_LINEAR);
 +        }
 +    } else {
 +        if (patch.cols / size_x > 1.) {
 +            // if we downsample use  INTER_AREA interpolation
 +            cv::resize(patch, patch, cv::Size(size_x / p_cell_size, size_y / p_cell_size), 0., 0., cv::INTER_AREA);
 +        } else {
 +            cv::resize(patch, patch, cv::Size(size_x / p_cell_size, size_y / p_cell_size), 0., 0., cv::INTER_LINEAR);
 +        }
 +        if (m_visual_debug && allow_debug) {
 +            cv::Mat input_clone = patch.clone();
 +            cv::resize(input_clone, input_clone, cv::Size(p_debug_image_size, p_debug_image_size), 0., 0.,
 +                       cv::INTER_LINEAR);
 +
 +            std::string angle_string = std::to_string(p_current_angle + angle);
 +
 +            cv::putText(input_clone, angle_string, cv::Point(1, input_clone.rows - 5), cv::FONT_HERSHEY_COMPLEX_SMALL,
 +                        0.5, cv::Scalar(0, 255, 0), 1);
 +
 +            p_debug_subwindows.push_back(input_clone);
 +        }
 +    }
 +}
 +
 +void KCF_Tracker::gaussian_correlation(struct ThreadCtx &vars, const ComplexMat &xf, const ComplexMat &yf, double sigma,
 +                                       bool auto_correlation)
  {
- #ifdef CUFFT
-     xf.sqr_norm(vars.xf_sqr_norm.deviceMem());
-     if (!auto_correlation) yf.sqr_norm(vars.yf_sqr_norm.deviceMem());
- #else
-     xf.sqr_norm(vars.xf_sqr_norm.hostMem());
+     xf.sqr_norm(vars.xf_sqr_norm);
      if (auto_correlation) {
          vars.yf_sqr_norm.hostMem()[0] = vars.xf_sqr_norm.hostMem()[0];
      } else {
-         yf.sqr_norm(vars.yf_sqr_norm.hostMem());
+         yf.sqr_norm(vars.yf_sqr_norm);
      }
- #endif
      vars.xyf = auto_correlation ? xf.sqr_mag() : xf.mul2(yf.conj());
      DEBUG_PRINTM(vars.xyf);
      fft.inverse(vars.xyf, vars.ifft2_res, m_use_cuda ? vars.data_i_features.deviceMem() : nullptr, vars.stream);
  #ifdef CUFFT
-     if (auto_correlation)
-         cuda_gaussian_correlation(vars.data_i_features.deviceMem(), vars.gauss_corr_res.deviceMem(), vars.xf_sqr_norm.deviceMem(), vars.xf_sqr_norm.deviceMem(),
-                                   sigma, xf.n_channels, xf.n_scales, p_roi.height, p_roi.width, vars.stream);
-     else
-         cuda_gaussian_correlation(vars.data_i_features.deviceMem(), vars.gauss_corr_res.deviceMem(), vars.xf_sqr_norm.deviceMem(), vars.yf_sqr_norm.deviceMem(),
-                                   sigma, xf.n_channels, xf.n_scales, p_roi.height, p_roi.width, vars.stream);
+     cuda_gaussian_correlation(vars.data_i_features.deviceMem(), vars.gauss_corr_res.deviceMem(),
+                               vars.xf_sqr_norm.deviceMem(), vars.xf_sqr_norm.deviceMem(), sigma, xf.n_channels,
+                               xf.n_scales, p_roi.height, p_roi.width, vars.stream);
  #else
      // ifft2 and sum over 3rd dimension, we dont care about individual channels
      DEBUG_PRINTM(vars.ifft2_res);
@@@ -966,7 -861,11 +966,11 @@@ double KCF_Tracker::sub_grid_scale(uin
              A.at<float>(i, 0) = float(p_scales[i] * p_scales[i]);
              A.at<float>(i, 1) = float(p_scales[i]);
              A.at<float>(i, 2) = 1;
-             fval.at<float>(i) = m_use_big_batch ? p_threadctxs.back().max_responses[i] : p_threadctxs[i].max_response;
+ #ifdef BIG_BATCH
+             fval.at<float>(i) = p_threadctxs.back().max_responses[i];
+ #else
+             fval.at<float>(i) = p_threadctxs[i].max_response;
+ #endif
          }
      } else {
          // only from neighbours
               p_scales[index - 1] * p_scales[index - 1], p_scales[index - 1], 1,
               p_scales[index + 0] * p_scales[index + 0], p_scales[index + 0], 1,
               p_scales[index + 1] * p_scales[index + 1], p_scales[index + 1], 1);
+ #ifdef BIG_BATCH
          fval = (cv::Mat_<float>(3, 1) <<
-                 (m_use_big_batch ? p_threadctxs.back().max_responses[index - 1] : p_threadctxs[index - 1].max_response),
-                 (m_use_big_batch ? p_threadctxs.back().max_responses[index + 0] : p_threadctxs[index + 0].max_response),
-                 (m_use_big_batch ? p_threadctxs.back().max_responses[index + 1] : p_threadctxs[index + 1].max_response));
+                 p_threadctxs.back().max_responses[index - 1],
+                 p_threadctxs.back().max_responses[index + 0],
+                 p_threadctxs.back().max_responses[index + 1]);
+ #else
+         fval = (cv::Mat_<float>(3, 1) <<
+                 p_threadctxs[index - 1].max_response,
+                 p_threadctxs[index + 0].max_response,
+                 p_threadctxs[index + 1].max_response);
+ #endif
      }
  
      cv::Mat x;
diff --combined src/kcf.h
index 825cfd6d983a080ae6876b64dcf823073dad6591,e2e10ef0db2c78eb133e36ca40c9a7000bbf489f..7600e40b423fa4e23836317e3e8a839c3c06fde7
+++ b/src/kcf.h
  #include "threadctx.hpp"
  #include "pragmas.h"
  
 -struct BBox_c
 -{
 -    double cx, cy, w, h;
 +struct BBox_c {
 +    double cx, cy, w, h, a;
  
      inline void scale(double factor)
      {
          cx *= factor;
          cy *= factor;
 -        w  *= factor;
 -        h  *= factor;
 +        w *= factor;
 +        h *= factor;
      }
  
      inline void scale_x(double factor)
      {
          cx *= factor;
 -        w  *= factor;
 +        w *= factor;
      }
  
      inline void scale_y(double factor)
      {
          cy *= factor;
 -        h  *= factor;
 -    }
 -
 -    inline cv::Rect get_rect()
 -    {
 -        return cv::Rect(int(cx-w/2.), int(cy-h/2.), int(w), int(h));
 +        h *= factor;
      }
  
 +    inline cv::Rect get_rect() { return cv::Rect(int(cx - w / 2.), int(cy - h / 2.), int(w), int(h)); }
  };
  
 -class KCF_Tracker
 -{
 -public:
 -    bool m_debug     {false};
 -    bool m_use_scale {true};
 -    bool m_use_color {true};
 +class KCF_Tracker {
 +  public:
 +    bool m_debug{false};
 +    bool m_visual_debug{false};
 +    bool m_use_scale{true};
 +    bool m_use_angle{false}; // Doesn't work with FFTW-BIG version
 +    bool m_use_color{true};
  #ifdef ASYNC
 -    bool m_use_multithreading {true};
 +    bool m_use_multithreading{true};
  #else
-     bool m_use_multithreading{false};
- #endif // ASYNC
-     bool m_use_subpixel_localization{true};
-     bool m_use_subgrid_scale{true};
-     bool m_use_cnfeat{true};
-     bool m_use_linearkernel{false};
- #ifdef BIG_BATCH
-     bool m_use_big_batch{true};
- #else
-     bool m_use_big_batch{false};
- #endif
+     bool m_use_multithreading {false};
+ #endif //ASYNC
+     bool m_use_subpixel_localization {true};
+     bool m_use_subgrid_scale {true};
+     bool m_use_cnfeat {true};
+     bool m_use_linearkernel {false};
  #ifdef CUFFT
 -    bool m_use_cuda {true};
 +    bool m_use_cuda{true};
  #else
 -    bool m_use_cuda {false};
 +    bool m_use_cuda{false};
  #endif
  
      /*
      output_sigma_factor ... spatial bandwidth (proportional to target)  (0.1)
      cell_size           ... hog cell size                               (4)
      */
 -    KCF_Tracker(double padding, double kernel_sigma, double lambda, double interp_factor, double output_sigma_factor, int cell_size);
 +    KCF_Tracker(double padding, double kernel_sigma, double lambda, double interp_factor, double output_sigma_factor,
 +                int cell_size);
      KCF_Tracker();
      ~KCF_Tracker();
  
      // Init/re-init methods
 -    void init(cv::Mat & img, const cv::Rect & bbox, int fit_size_x, int fit_size_y);
 -    void setTrackerPose(BBox_c & bbox, cv::Mat & img, int fit_size_x, int fit_size_y);
 -    void updateTrackerPosition(BBox_c & bbox);
 +    void init(cv::Mat &img, const cv::Rect &bbox, int fit_size_x, int fit_size_y);
 +    void setTrackerPose(BBox_c &bbox, cv::Mat &img, int fit_size_x, int fit_size_y);
 +    void updateTrackerPosition(BBox_c &bbox);
  
      // frame-to-frame object tracking
 -    void track(cv::Mat & img);
 +    void track(cv::Mat &img);
      BBox_c getBBox();
      double getFilterResponse() const; // Measure of tracking accuracy
  
 -private:
 +  private:
      Fft &fft;
  
      BBox_c p_pose;
      double p_padding = 1.5;
      double p_output_sigma_factor = 0.1;
      double p_output_sigma;
 -    double p_kernel_sigma = 0.5;    //def = 0.5
 -    double p_lambda = 1e-4;         //regularization in learning step
 -    double p_interp_factor = 0.02;  //def = 0.02, linear interpolation factor for adaptation
 -    int p_cell_size = 4;            //4 for hog (= bin_size)
 +    double p_kernel_sigma = 0.5;   // def = 0.5
 +    double p_lambda = 1e-4;        // regularization in learning step
 +    double p_interp_factor = 0.02; // def = 0.02, linear interpolation factor for adaptation
 +    int p_cell_size = 4;           // 4 for hog (= bin_size)
      cv::Size p_windows_size;
      uint p_num_scales {7};
      double p_scale_step = 1.02;
      double p_current_scale = 1.;
      double p_min_max_scale[2];
      std::vector<double> p_scales;
-     int p_count = 0;
 +    int p_current_angle = 0;
 +    uint p_num_angles {5};
 +    int p_angle_min = -20, p_angle_max = 20;
 +    int p_angle_step = 10;
 +    std::vector<int> p_angles;
 +
 +    // for visual debug
 +    int p_debug_image_size = 100;
 +    std::vector<cv::Mat> p_debug_scale_responses;
 +    std::vector<cv::Mat> p_debug_subwindows;
  
      //for big batch
 -    int p_num_of_feats;
 +    int p_num_of_feats = 31 + (m_use_color ? 3 : 0) + (m_use_cnfeat ? 10 : 0);
      cv::Size p_roi;
  
      std::vector<ThreadCtx> p_threadctxs;
      cv::Mat p_rot_labels;
      DynMem p_rot_labels_data;
  
 -    //model
 +    // model
      ComplexMat p_yf;
      ComplexMat p_model_alphaf;
      ComplexMat p_model_alphaf_num;
      void scale_track(ThreadCtx & vars, cv::Mat & input_rgb, cv::Mat & input_gray);
      cv::Mat get_subwindow(const cv::Mat & input, int cx, int cy, int size_x, int size_y);
      cv::Mat gaussian_shaped_labels(double sigma, int dim1, int dim2);
 -    void gaussian_correlation(struct ThreadCtx &vars, const ComplexMat & xf, const ComplexMat & yf, double sigma, bool auto_correlation = false);
 -    cv::Mat circshift(const cv::Mat & patch, int x_rot, int y_rot);
 +    void gaussian_correlation(struct ThreadCtx &vars, const ComplexMat &xf, const ComplexMat &yf, double sigma,
 +                              bool auto_correlation = false);
 +    cv::Mat circshift(const cv::Mat &patch, int x_rot, int y_rot);
      cv::Mat cosine_window_function(int dim1, int dim2);
-     void get_features(cv::Mat &patch_rgb, cv::Mat &patch_gray, ThreadCtx &vars);
 -    std::vector<cv::Mat> get_features(cv::Mat & input_rgb, cv::Mat & input_gray, int cx, int cy, int size_x, int size_y, double scale = 1.);
 -    cv::Point2f sub_pixel_peak(cv::Point & max_loc, cv::Mat & response);
++    std::vector<cv::Mat> get_features(cv::Mat &patch_rgb, cv::Mat &patch_gray);
 +    void geometric_transformations(cv::Mat &patch, int size_x, int size_y, int angle = 0, bool allow_debug = true);
 +    cv::Point2f sub_pixel_peak(cv::Point &max_loc, cv::Mat &response);
      double sub_grid_scale(uint index);
 -
  };
  
 -#endif //KCF_HEADER_6565467831231
 +#endif // KCF_HEADER_6565467831231
diff --combined src/threadctx.hpp
index d6a2cbef807af3f92da76dae55e99512542abc51,de9638e32ad4d32d9cebda4242a3842c8e6d54b5..92dd54e2a15fb87de0a87b1523352f3e73d3939d
@@@ -16,12 -16,11 +16,11 @@@ typedef int *cudaStream_t
  
  struct ThreadCtx {
    public:
 -    ThreadCtx(cv::Size roi, uint num_of_feats, double scale, uint num_of_scales)
 -        : scale(scale)
 +    ThreadCtx(cv::Size roi, uint num_of_feats, double scale, int angle, uint num_of_scales = 1, uint num_of_angles = 1)
 +        : scale(scale), angle(angle)
      {
 -        this->xf_sqr_norm = DynMem(num_of_scales * sizeof(float));
 +        this->xf_sqr_norm = DynMem(num_of_scales * num_of_angles * sizeof(float));
          this->yf_sqr_norm = DynMem(sizeof(float));
-         this->patch_feats.reserve(uint(num_of_feats));
  
          uint cells_size = roi.width * roi.height * sizeof(float);
  
  #endif
  
  #if defined(CUFFT) || defined(FFTW)
 -        this->gauss_corr_res = DynMem(cells_size * num_of_scales);
 +        this->gauss_corr_res = DynMem(cells_size * num_of_scales * num_of_angles);
          this->data_features = DynMem(cells_size * num_of_feats);
  
          uint width_freq = roi.width / 2 + 1;
  
 -        this->in_all = cv::Mat(roi.height * num_of_scales, roi.width, CV_32F, this->gauss_corr_res.hostMem());
 +        this->in_all = cv::Mat(roi.height * num_of_scales * num_of_angles, roi.width, CV_32F, this->gauss_corr_res.hostMem());
          this->fw_all = cv::Mat(roi.height * num_of_feats, roi.width, CV_32F, this->data_features.hostMem());
  #else
          uint width_freq = roi.width;
  #endif
  
          this->data_i_features = DynMem(cells_size * num_of_feats);
 -        this->data_i_1ch = DynMem(cells_size * num_of_scales);
 +        this->data_i_1ch = DynMem(cells_size * num_of_scales * num_of_angles);
  
          this->ifft2_res = cv::Mat(roi, CV_32FC(num_of_feats), this->data_i_features.hostMem());
 -        this->response = cv::Mat(roi, CV_32FC(num_of_scales), this->data_i_1ch.hostMem());
 +        this->response = cv::Mat(roi, CV_32FC(num_of_scales * num_of_angles), this->data_i_1ch.hostMem());
  
-         this->patch_feats.reserve(num_of_feats);
  #ifdef CUFFT
 -        this->zf.create(roi.height, width_freq, num_of_feats, num_of_scales, this->stream);
 -        this->kzf.create(roi.height, width_freq, num_of_scales, this->stream);
 -        this->kf.create(roi.height, width_freq, num_of_scales, this->stream);
 +        this->zf.create(roi.height, width_freq, num_of_feats, num_of_scales * num_of_angles, this->stream);
 +        this->kzf.create(roi.height, width_freq, num_of_scales * num_of_angles, this->stream);
 +        this->kf.create(roi.height, width_freq, num_of_scales * num_of_angles, this->stream);
  #else
 -        this->zf.create(roi.height, width_freq, num_of_feats, num_of_scales);
 -        this->kzf.create(roi.height, width_freq, num_of_scales);
 -        this->kf.create(roi.height, width_freq, num_of_scales);
 +        this->zf.create(roi.height, width_freq, num_of_feats, num_of_scales * num_of_angles);
 +        this->kzf.create(roi.height, width_freq, num_of_scales * num_of_angles);
 +        this->kf.create(roi.height, width_freq, num_of_scales * num_of_angles);
  #endif
  
+ #ifdef BIG_BATCH
          if (num_of_scales > 1) {
 -            this->max_responses.reserve(num_of_scales);
 -            this->max_locs.reserve(num_of_scales);
 -            this->response_maps.reserve(num_of_scales);
 +            this->max_responses.reserve(num_of_scales * num_of_angles);
 +            this->max_locs.reserve(num_of_scales * num_of_angles);
 +            this->response_maps.reserve(num_of_scales * num_of_angles);
          }
+ #endif
      }
      ThreadCtx(ThreadCtx &&) = default;
      ~ThreadCtx()
      }
  
      const double scale;
 +    const int angle;
  #ifdef ASYNC
      std::future<void> async_res;
  #endif
  
      DynMem xf_sqr_norm, yf_sqr_norm;
-     std::vector<cv::Mat> patch_feats;
  
      cv::Mat in_all, fw_all, ifft2_res, response;
      ComplexMat zf, kzf, kf, xyf;
      cv::Point2i max_loc;
      double max_val, max_response;
  
-     // Big batch variables
+ #ifdef BIG_BATCH
      // Stores value of responses, location of maximal response and response maps for each scale
      std::vector<double> max_responses;
      std::vector<cv::Point2i> max_locs;
      std::vector<cv::Mat> response_maps;
+ #endif
  };
  
  #endif // SCALE_VARS_HPP