vendor: OpenCV 5.0.0 snapshot at 755e50675d97db9b7d449d8bd6b09888646f6c6e
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
set(the_description "Tracking API")
|
||||
|
||||
set(debug_modules "")
|
||||
if(DEBUG_opencv_tracking)
|
||||
list(APPEND debug_modules opencv_highgui)
|
||||
endif()
|
||||
|
||||
ocv_define_module(tracking
|
||||
opencv_imgproc
|
||||
opencv_core
|
||||
opencv_video
|
||||
opencv_plot # samples only
|
||||
${debug_modules}
|
||||
OPTIONAL
|
||||
opencv_dnn
|
||||
opencv_datasets
|
||||
opencv_highgui
|
||||
WRAP
|
||||
java
|
||||
python
|
||||
objc
|
||||
)
|
||||
|
||||
ocv_warnings_disable(CMAKE_CXX_FLAGS -Wno-shadow /wd4458)
|
||||
|
||||
if(TARGET opencv_test_${name})
|
||||
ocv_target_include_directories(opencv_test_${name} "${OpenCV_SOURCE_DIR}/modules") # use common files from video tests
|
||||
endif()
|
||||
@@ -0,0 +1,4 @@
|
||||
Object tracking API
|
||||
===================
|
||||
|
||||
Use and/or evaluate one of 5 different visual object tracking techniques.
|
||||
@@ -0,0 +1,135 @@
|
||||
|
||||
/*---------------STEP 1---------------------*/
|
||||
/* modify this file
|
||||
* opencv2/tracking/tracker.hpp
|
||||
* and put several lines of snippet similar to
|
||||
* the following:
|
||||
*/
|
||||
/*------------------------------------------*/
|
||||
|
||||
class CV_EXPORTS_W TrackerKCF : public Tracker
|
||||
{
|
||||
public:
|
||||
struct CV_EXPORTS Params
|
||||
{
|
||||
Params();
|
||||
void read( const FileNode& /*fn*/ );
|
||||
void write( FileStorage& /*fs*/ ) const;
|
||||
};
|
||||
|
||||
/** @brief Constructor
|
||||
@param parameters KCF parameters TrackerKCF::Params
|
||||
*/
|
||||
BOILERPLATE_CODE("KCF",TrackerKCF);
|
||||
};
|
||||
|
||||
|
||||
/*---------------STEP 2---------------------*/
|
||||
/* modify this file
|
||||
* src/tracker.cpp
|
||||
* add one line in function
|
||||
* Ptr<Tracker> Tracker::create( const String& trackerType )
|
||||
*/
|
||||
/*------------------------------------------*/
|
||||
|
||||
Ptr<Tracker> Tracker::create( const String& trackerType )
|
||||
{
|
||||
BOILERPLATE_CODE("MIL",TrackerMIL);
|
||||
BOILERPLATE_CODE("BOOSTING",TrackerBoosting);
|
||||
BOILERPLATE_CODE("MEDIANFLOW",TrackerMedianFlow);
|
||||
BOILERPLATE_CODE("TLD",TrackerTLD);
|
||||
BOILERPLATE_CODE("KCF",TrackerKCF); // add this line!
|
||||
return Ptr<Tracker>();
|
||||
}
|
||||
|
||||
|
||||
/*---------------STEP 3---------------------*/
|
||||
/* make a new file and paste the snippet below
|
||||
* and modify it according to your needs.
|
||||
* also make sure to put the LICENSE part.
|
||||
* src/trackerKCF.cpp
|
||||
*/
|
||||
/*------------------------------------------*/
|
||||
|
||||
/*---------------------------
|
||||
| TrackerKCFModel
|
||||
|---------------------------*/
|
||||
namespace cv{
|
||||
/**
|
||||
* \brief Implementation of TrackerModel for MIL algorithm
|
||||
*/
|
||||
class TrackerKCFModel : public TrackerModel{
|
||||
public:
|
||||
TrackerKCFModel(TrackerKCF::Params /*params*/){}
|
||||
~TrackerKCFModel(){}
|
||||
protected:
|
||||
void modelEstimationImpl( const std::vector<Mat>& responses ){}
|
||||
void modelUpdateImpl(){}
|
||||
};
|
||||
} /* namespace cv */
|
||||
|
||||
|
||||
/*---------------------------
|
||||
| TrackerKCF
|
||||
|---------------------------*/
|
||||
namespace cv{
|
||||
|
||||
/*
|
||||
* Prototype
|
||||
*/
|
||||
class TrackerKCFImpl : public TrackerKCF{
|
||||
public:
|
||||
TrackerKCFImpl( const TrackerKCF::Params ¶meters = TrackerKCF::Params() );
|
||||
void read( const FileNode& fn );
|
||||
void write( FileStorage& fs ) const;
|
||||
|
||||
protected:
|
||||
bool initImpl( const Mat& image, const Rect2d& boundingBox );
|
||||
bool updateImpl( const Mat& image, Rect2d& boundingBox );
|
||||
|
||||
TrackerKCF::Params params;
|
||||
};
|
||||
|
||||
/*
|
||||
* Constructor
|
||||
*/
|
||||
Ptr<TrackerKCF> TrackerKCF::createTracker(const TrackerKCF::Params ¶meters){
|
||||
return Ptr<TrackerKCFImpl>(new TrackerKCFImpl(parameters));
|
||||
}
|
||||
TrackerKCFImpl::TrackerKCFImpl( const TrackerKCF::Params ¶meters ) :
|
||||
params( parameters )
|
||||
{
|
||||
isInit = false;
|
||||
}
|
||||
|
||||
void TrackerKCFImpl::read( const cv::FileNode& fn ){
|
||||
params.read( fn );
|
||||
}
|
||||
|
||||
void TrackerKCFImpl::write( cv::FileStorage& fs ) const{
|
||||
params.write( fs );
|
||||
}
|
||||
|
||||
|
||||
bool TrackerKCFImpl::initImpl( const Mat& image, const Rect2d& boundingBox ){
|
||||
model=Ptr<TrackerKCFModel>(new TrackerKCFModel(params));
|
||||
return true;
|
||||
}
|
||||
bool TrackerKCFImpl::updateImpl( const Mat& image, Rect2d& boundingBox ){return true;}
|
||||
|
||||
/*
|
||||
* Parameters
|
||||
*/
|
||||
TrackerKCF::Params::Params(){
|
||||
|
||||
}
|
||||
|
||||
void TrackerKCF::Params::read( const cv::FileNode& fn ){
|
||||
|
||||
}
|
||||
|
||||
void TrackerKCF::Params::write( cv::FileStorage& fs ) const{
|
||||
|
||||
}
|
||||
|
||||
} /* namespace cv */
|
||||
@@ -0,0 +1,118 @@
|
||||
@inproceedings{OLB,
|
||||
title={Real-Time Tracking via On-line Boosting.},
|
||||
author={Grabner, Helmut and Grabner, Michael and Bischof, Horst},
|
||||
booktitle={BMVC},
|
||||
volume={1},
|
||||
number={5},
|
||||
pages={6},
|
||||
year={2006}
|
||||
}
|
||||
|
||||
@inproceedings{MedianFlow,
|
||||
title={Forward-backward error: Automatic detection of tracking failures},
|
||||
author={Kalal, Zdenek and Mikolajczyk, Krystian and Matas, Jiri},
|
||||
booktitle={Pattern Recognition (ICPR), 2010 20th International Conference on},
|
||||
pages={2756--2759},
|
||||
year={2010},
|
||||
organization={IEEE}
|
||||
}
|
||||
|
||||
@article{TLD,
|
||||
title={Tracking-learning-detection},
|
||||
author={Kalal, Zdenek and Mikolajczyk, Krystian and Matas, Jiri},
|
||||
journal={Pattern Analysis and Machine Intelligence, IEEE Transactions on},
|
||||
volume={34},
|
||||
number={7},
|
||||
pages={1409--1422},
|
||||
year={2012},
|
||||
publisher={IEEE}
|
||||
}
|
||||
|
||||
@inproceedings{OOT,
|
||||
title={Online object tracking: A benchmark},
|
||||
author={Wu, Yi and Lim, Jongwoo and Yang, Ming-Hsuan},
|
||||
booktitle={Computer Vision and Pattern Recognition (CVPR), 2013 IEEE Conference on},
|
||||
pages={2411--2418},
|
||||
year={2013},
|
||||
organization={IEEE}
|
||||
}
|
||||
|
||||
@article{KCF,
|
||||
title = {High-Speed Tracking with Kernelized Correlation Filters},
|
||||
journal = {Pattern Analysis and Machine Intelligence, IEEE Transactions on},
|
||||
author = {Henriques, J. F. and Caseiro, R. and Martins, P. and Batista, J.},
|
||||
year = {2015},
|
||||
doi = {10.1109/TPAMI.2014.2345390},
|
||||
}
|
||||
|
||||
@inproceedings{KCF_ECCV,
|
||||
title = {Exploiting the Circulant Structure of Tracking-by-detection with Kernels},
|
||||
author = {Henriques, J. F. and Caseiro, R. and Martins, P. and Batista, J.},
|
||||
booktitle = {proceedings of the European Conference on Computer Vision},
|
||||
year = {2012},
|
||||
}
|
||||
|
||||
@INPROCEEDINGS{KCF_CN,
|
||||
author={Danelljan, M. and Khan, F.S. and Felsberg, M. and van de Weijer, J.},
|
||||
booktitle={Computer Vision and Pattern Recognition (CVPR), 2014 IEEE Conference on},
|
||||
title={Adaptive Color Attributes for Real-Time Visual Tracking},
|
||||
year={2014},
|
||||
month={June},
|
||||
pages={1090-1097},
|
||||
keywords={computer vision;feature extraction;image colour analysis;image representation;image sequences;adaptive color attributes;benchmark color sequences;color features;color representations;computer vision;image description;real-time visual tracking;tracking-by-detection framework;Color;Computational modeling;Covariance matrices;Image color analysis;Kernel;Target tracking;Visualization;Adaptive Dimensionality Reduction;Appearance Model;Color Features;Visual Tracking},
|
||||
doi={10.1109/CVPR.2014.143},
|
||||
}
|
||||
|
||||
@inproceedings{MOSSE,
|
||||
title={Visual Object Tracking using Adaptive Correlation Filters},
|
||||
author={Bolme, David S. and Beveridge, J. Ross and Draper, Bruce A. and Lui Yui, Man},
|
||||
booktitle = {Conference on Computer Vision and Pattern Recognition (CVPR)},
|
||||
year = {2010}
|
||||
}
|
||||
|
||||
@Article{Lukezic_IJCV2018,
|
||||
author={Luke{\v{z}}i{\v{c}}, Alan and Voj{'i}{\v{r}}, Tom{'a}{\v{s}} and {\v{C}}ehovin Zajc, Luka and Matas, Ji{\v{r}}{'i} and Kristan, Matej},
|
||||
title={Discriminative Correlation Filter Tracker with Channel and Spatial Reliability},
|
||||
journal={International Journal of Computer Vision},
|
||||
year={2018},
|
||||
}
|
||||
|
||||
@article{chaumette:inria-00350283,
|
||||
title={{Visual servo control, Part I: Basic approaches}},
|
||||
author={Chaumette, Fran{\c c}ois and Hutchinson, S.},
|
||||
url={https://inria.hal.science/inria-00350283},
|
||||
journal={{IEEE Robotics and Automation Magazine}},
|
||||
publisher={{Institute of Electrical and Electronics Engineers}},
|
||||
volume={13},
|
||||
number={4},
|
||||
pages={82-90},
|
||||
year={2006},
|
||||
pdf={https://inria.hal.science/inria-00350283/file/2006_ieee_ram_chaumette.pdf},
|
||||
hal_id={inria-00350283},
|
||||
hal_version={v1},
|
||||
}
|
||||
|
||||
@article{chaumette:inria-00350638,
|
||||
title={{Visual servo control, Part II: Advanced approaches}},
|
||||
author={Chaumette, Fran{\c c}ois and Hutchinson, S.},
|
||||
url={https://inria.hal.science/inria-00350638},
|
||||
journal={{IEEE Robotics and Automation Magazine}},
|
||||
publisher={{Institute of Electrical and Electronics Engineers}},
|
||||
volume={14},
|
||||
number={1},
|
||||
pages={109-118},
|
||||
year={2007},
|
||||
pdf={https://inria.hal.science/inria-00350638/file/2007_ieee_ram_chaumette.pdf},
|
||||
hal_id={inria-00350638},
|
||||
hal_version={v1},
|
||||
}
|
||||
|
||||
@article{Hutchinson1996ATO,
|
||||
title={A tutorial on visual servo control},
|
||||
author={Seth A. Hutchinson and Gregory Hager and Peter Corke},
|
||||
journal={IEEE Trans. Robotics Autom.},
|
||||
year={1996},
|
||||
volume={12},
|
||||
pages={651-670},
|
||||
url={https://api.semanticscholar.org/CorpusID:1814423}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef OPENCV_CONTRIB_TRACKING_HPP
|
||||
#define OPENCV_CONTRIB_TRACKING_HPP
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/video/tracking.hpp"
|
||||
|
||||
namespace cv {
|
||||
#ifndef CV_DOXYGEN
|
||||
inline namespace tracking {
|
||||
#endif
|
||||
|
||||
/** @defgroup tracking Tracking API
|
||||
@{
|
||||
@defgroup tracking_detail Tracking API implementation details
|
||||
@defgroup tracking_legacy Legacy Tracking API
|
||||
@}
|
||||
*/
|
||||
|
||||
/** @addtogroup tracking
|
||||
@{
|
||||
Tracking is an important issue for many computer vision applications in real world scenario.
|
||||
The development in this area is very fragmented and this API is an interface useful for plug several algorithms and compare them.
|
||||
*/
|
||||
|
||||
|
||||
/** @brief the CSRT tracker
|
||||
|
||||
The implementation is based on @cite Lukezic_IJCV2018 Discriminative Correlation Filter with Channel and Spatial Reliability
|
||||
*/
|
||||
class CV_EXPORTS_W TrackerCSRT : public Tracker
|
||||
{
|
||||
protected:
|
||||
TrackerCSRT(); // use ::create()
|
||||
public:
|
||||
virtual ~TrackerCSRT() CV_OVERRIDE;
|
||||
|
||||
struct CV_EXPORTS_W_SIMPLE Params
|
||||
{
|
||||
CV_WRAP Params();
|
||||
|
||||
CV_PROP_RW bool use_hog;
|
||||
CV_PROP_RW bool use_color_names;
|
||||
CV_PROP_RW bool use_gray;
|
||||
CV_PROP_RW bool use_rgb;
|
||||
CV_PROP_RW bool use_channel_weights;
|
||||
CV_PROP_RW bool use_segmentation;
|
||||
|
||||
CV_PROP_RW std::string window_function; //!< Window function: "hann", "cheb", "kaiser"
|
||||
CV_PROP_RW float kaiser_alpha;
|
||||
CV_PROP_RW float cheb_attenuation;
|
||||
|
||||
CV_PROP_RW float template_size;
|
||||
CV_PROP_RW float gsl_sigma;
|
||||
CV_PROP_RW float hog_orientations;
|
||||
CV_PROP_RW float hog_clip;
|
||||
CV_PROP_RW float padding;
|
||||
CV_PROP_RW float filter_lr;
|
||||
CV_PROP_RW float weights_lr;
|
||||
CV_PROP_RW int num_hog_channels_used;
|
||||
CV_PROP_RW int admm_iterations;
|
||||
CV_PROP_RW int histogram_bins;
|
||||
CV_PROP_RW float histogram_lr;
|
||||
CV_PROP_RW int background_ratio;
|
||||
CV_PROP_RW int number_of_scales;
|
||||
CV_PROP_RW float scale_sigma_factor;
|
||||
CV_PROP_RW float scale_model_max_area;
|
||||
CV_PROP_RW float scale_lr;
|
||||
CV_PROP_RW float scale_step;
|
||||
|
||||
CV_PROP_RW float psr_threshold; //!< we lost the target, if the psr is lower than this.
|
||||
};
|
||||
|
||||
/** @brief Create CSRT tracker instance
|
||||
@param parameters CSRT parameters TrackerCSRT::Params
|
||||
*/
|
||||
static CV_WRAP
|
||||
Ptr<TrackerCSRT> create(const TrackerCSRT::Params ¶meters = TrackerCSRT::Params());
|
||||
|
||||
//void init(InputArray image, const Rect& boundingBox) CV_OVERRIDE;
|
||||
//bool update(InputArray image, CV_OUT Rect& boundingBox) CV_OVERRIDE;
|
||||
|
||||
CV_WRAP virtual void setInitialMask(InputArray mask) = 0;
|
||||
};
|
||||
|
||||
|
||||
/** @brief the KCF (Kernelized Correlation Filter) tracker
|
||||
|
||||
* KCF is a novel tracking framework that utilizes properties of circulant matrix to enhance the processing speed.
|
||||
* This tracking method is an implementation of @cite KCF_ECCV which is extended to KCF with color-names features (@cite KCF_CN).
|
||||
* The original paper of KCF is available at <http://www.robots.ox.ac.uk/~joao/publications/henriques_tpami2015.pdf>
|
||||
* as well as the matlab implementation. For more information about KCF with color-names features, please refer to
|
||||
* <http://www.cvl.isy.liu.se/research/objrec/visualtracking/colvistrack/index.html>.
|
||||
*/
|
||||
class CV_EXPORTS_W TrackerKCF : public Tracker
|
||||
{
|
||||
protected:
|
||||
TrackerKCF(); // use ::create()
|
||||
public:
|
||||
virtual ~TrackerKCF() CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* \brief Feature type to be used in the tracking grayscale, colornames, compressed color-names
|
||||
* The modes available now:
|
||||
- "GRAY" -- Use grayscale values as the feature
|
||||
- "CN" -- Color-names feature
|
||||
*/
|
||||
enum MODE {
|
||||
GRAY = (1 << 0),
|
||||
CN = (1 << 1),
|
||||
CUSTOM = (1 << 2)
|
||||
};
|
||||
|
||||
struct CV_EXPORTS_W_SIMPLE Params
|
||||
{
|
||||
CV_WRAP Params();
|
||||
|
||||
CV_PROP_RW float detect_thresh; //!< detection confidence threshold
|
||||
CV_PROP_RW float sigma; //!< gaussian kernel bandwidth
|
||||
CV_PROP_RW float lambda; //!< regularization
|
||||
CV_PROP_RW float interp_factor; //!< linear interpolation factor for adaptation
|
||||
CV_PROP_RW float output_sigma_factor; //!< spatial bandwidth (proportional to target)
|
||||
CV_PROP_RW float pca_learning_rate; //!< compression learning rate
|
||||
CV_PROP_RW bool resize; //!< activate the resize feature to improve the processing speed
|
||||
CV_PROP_RW bool split_coeff; //!< split the training coefficients into two matrices
|
||||
CV_PROP_RW bool wrap_kernel; //!< wrap around the kernel values
|
||||
CV_PROP_RW bool compress_feature; //!< activate the pca method to compress the features
|
||||
CV_PROP_RW int max_patch_size; //!< threshold for the ROI size
|
||||
CV_PROP_RW int compressed_size; //!< feature size after compression
|
||||
CV_PROP_RW int desc_pca; //!< compressed descriptors of TrackerKCF::MODE
|
||||
CV_PROP_RW int desc_npca; //!< non-compressed descriptors of TrackerKCF::MODE
|
||||
};
|
||||
|
||||
/** @brief Create KCF tracker instance
|
||||
@param parameters KCF parameters TrackerKCF::Params
|
||||
*/
|
||||
static CV_WRAP
|
||||
Ptr<TrackerKCF> create(const TrackerKCF::Params ¶meters = TrackerKCF::Params());
|
||||
|
||||
//void init(InputArray image, const Rect& boundingBox) CV_OVERRIDE;
|
||||
//bool update(InputArray image, CV_OUT Rect& boundingBox) CV_OVERRIDE;
|
||||
|
||||
// FIXIT use interface
|
||||
typedef void (*FeatureExtractorCallbackFN)(const Mat, const Rect, Mat&);
|
||||
virtual void setFeatureExtractor(FeatureExtractorCallbackFN callback, bool pca_func = false) = 0;
|
||||
};
|
||||
|
||||
|
||||
//! @}
|
||||
|
||||
#ifndef CV_DOXYGEN
|
||||
}
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
#endif // OPENCV_CONTRIB_TRACKING_HPP
|
||||
@@ -0,0 +1,421 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef __OPENCV_FEATURE_HPP__
|
||||
#define __OPENCV_FEATURE_HPP__
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <time.h>
|
||||
|
||||
/*
|
||||
* TODO This implementation is based on apps/traincascade/
|
||||
* TODO Changed CvHaarEvaluator based on ADABOOSTING implementation (Grabner et al.)
|
||||
*/
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
//! @addtogroup tracking_detail
|
||||
//! @{
|
||||
|
||||
inline namespace contrib_feature {
|
||||
|
||||
#define FEATURES "features"
|
||||
|
||||
#define CC_FEATURES FEATURES
|
||||
#define CC_FEATURE_PARAMS "featureParams"
|
||||
#define CC_MAX_CAT_COUNT "maxCatCount"
|
||||
#define CC_FEATURE_SIZE "featSize"
|
||||
#define CC_NUM_FEATURES "numFeat"
|
||||
#define CC_ISINTEGRAL "isIntegral"
|
||||
#define CC_RECTS "rects"
|
||||
#define CC_TILTED "tilted"
|
||||
#define CC_RECT "rect"
|
||||
|
||||
#define LBPF_NAME "lbpFeatureParams"
|
||||
#define HOGF_NAME "HOGFeatureParams"
|
||||
#define HFP_NAME "haarFeatureParams"
|
||||
|
||||
#define CV_HAAR_FEATURE_MAX 3
|
||||
#define N_BINS 9
|
||||
#define N_CELLS 4
|
||||
|
||||
#define CV_SUM_OFFSETS( p0, p1, p2, p3, rect, step ) \
|
||||
/* (x, y) */ \
|
||||
(p0) = (rect).x + (step) * (rect).y; \
|
||||
/* (x + w, y) */ \
|
||||
(p1) = (rect).x + (rect).width + (step) * (rect).y; \
|
||||
/* (x + w, y) */ \
|
||||
(p2) = (rect).x + (step) * ((rect).y + (rect).height); \
|
||||
/* (x + w, y + h) */ \
|
||||
(p3) = (rect).x + (rect).width + (step) * ((rect).y + (rect).height);
|
||||
|
||||
#define CV_TILTED_OFFSETS( p0, p1, p2, p3, rect, step ) \
|
||||
/* (x, y) */ \
|
||||
(p0) = (rect).x + (step) * (rect).y; \
|
||||
/* (x - h, y + h) */ \
|
||||
(p1) = (rect).x - (rect).height + (step) * ((rect).y + (rect).height);\
|
||||
/* (x + w, y + w) */ \
|
||||
(p2) = (rect).x + (rect).width + (step) * ((rect).y + (rect).width); \
|
||||
/* (x + w - h, y + w + h) */ \
|
||||
(p3) = (rect).x + (rect).width - (rect).height \
|
||||
+ (step) * ((rect).y + (rect).width + (rect).height);
|
||||
|
||||
float calcNormFactor( const Mat& sum, const Mat& sqSum );
|
||||
|
||||
template<class Feature>
|
||||
void _writeFeatures( const std::vector<Feature> features, FileStorage &fs, const Mat& featureMap )
|
||||
{
|
||||
fs << FEATURES << "[";
|
||||
const Mat_<int>& featureMap_ = (const Mat_<int>&) featureMap;
|
||||
for ( int fi = 0; fi < featureMap.cols; fi++ )
|
||||
if( featureMap_( 0, fi ) >= 0 )
|
||||
{
|
||||
fs << "{";
|
||||
features[fi].write( fs );
|
||||
fs << "}";
|
||||
}
|
||||
fs << "]";
|
||||
}
|
||||
|
||||
class CvParams
|
||||
{
|
||||
public:
|
||||
CvParams();
|
||||
virtual ~CvParams()
|
||||
{
|
||||
}
|
||||
// from|to file
|
||||
virtual void write( FileStorage &fs ) const = 0;
|
||||
virtual bool read( const FileNode &node ) = 0;
|
||||
// from|to screen
|
||||
virtual void printDefaults() const;
|
||||
virtual void printAttrs() const;
|
||||
virtual bool scanAttr( const std::string prmName, const std::string val );
|
||||
std::string name;
|
||||
};
|
||||
|
||||
class CvFeatureParams : public CvParams
|
||||
{
|
||||
public:
|
||||
enum FeatureType
|
||||
{
|
||||
HAAR = 0,
|
||||
LBP = 1,
|
||||
HOG = 2
|
||||
};
|
||||
|
||||
CvFeatureParams();
|
||||
virtual void init( const CvFeatureParams& fp );
|
||||
virtual void write( FileStorage &fs ) const CV_OVERRIDE;
|
||||
virtual bool read( const FileNode &node ) CV_OVERRIDE;
|
||||
static Ptr<CvFeatureParams> create(CvFeatureParams::FeatureType featureType);
|
||||
int maxCatCount; // 0 in case of numerical features
|
||||
int featSize; // 1 in case of simple features (HAAR, LBP) and N_BINS(9)*N_CELLS(4) in case of Dalal's HOG features
|
||||
int numFeatures;
|
||||
};
|
||||
|
||||
class CvFeatureEvaluator
|
||||
{
|
||||
public:
|
||||
virtual ~CvFeatureEvaluator()
|
||||
{
|
||||
}
|
||||
virtual void init( const CvFeatureParams *_featureParams, int _maxSampleCount, Size _winSize );
|
||||
virtual void setImage( const Mat& img, uchar clsLabel, int idx );
|
||||
virtual void writeFeatures( FileStorage &fs, const Mat& featureMap ) const = 0;
|
||||
virtual float operator()( int featureIdx, int sampleIdx ) = 0;
|
||||
static Ptr<CvFeatureEvaluator> create(CvFeatureParams::FeatureType type);
|
||||
|
||||
int getNumFeatures() const
|
||||
{
|
||||
return numFeatures;
|
||||
}
|
||||
int getMaxCatCount() const
|
||||
{
|
||||
return featureParams->maxCatCount;
|
||||
}
|
||||
int getFeatureSize() const
|
||||
{
|
||||
return featureParams->featSize;
|
||||
}
|
||||
const Mat& getCls() const
|
||||
{
|
||||
return cls;
|
||||
}
|
||||
float getCls( int si ) const
|
||||
{
|
||||
return cls.at<float>( si, 0 );
|
||||
}
|
||||
protected:
|
||||
virtual void generateFeatures() = 0;
|
||||
|
||||
int npos, nneg;
|
||||
int numFeatures;
|
||||
Size winSize;
|
||||
CvFeatureParams *featureParams;
|
||||
Mat cls;
|
||||
};
|
||||
|
||||
class CvHaarFeatureParams : public CvFeatureParams
|
||||
{
|
||||
public:
|
||||
|
||||
CvHaarFeatureParams();
|
||||
|
||||
virtual void init( const CvFeatureParams& fp ) CV_OVERRIDE;
|
||||
virtual void write( FileStorage &fs ) const CV_OVERRIDE;
|
||||
virtual bool read( const FileNode &node ) CV_OVERRIDE;
|
||||
|
||||
virtual void printDefaults() const CV_OVERRIDE;
|
||||
virtual void printAttrs() const CV_OVERRIDE;
|
||||
virtual bool scanAttr( const std::string prm, const std::string val ) CV_OVERRIDE;
|
||||
|
||||
bool isIntegral;
|
||||
};
|
||||
|
||||
class CvHaarEvaluator : public CvFeatureEvaluator
|
||||
{
|
||||
public:
|
||||
|
||||
class FeatureHaar
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
FeatureHaar( Size patchSize );
|
||||
bool eval( const Mat& image, Rect ROI, float* result ) const;
|
||||
int getNumAreas();
|
||||
const std::vector<float>& getWeights() const;
|
||||
const std::vector<Rect>& getAreas() const;
|
||||
void write( FileStorage ) const
|
||||
{
|
||||
}
|
||||
;
|
||||
float getInitMean() const;
|
||||
float getInitSigma() const;
|
||||
|
||||
private:
|
||||
int m_type;
|
||||
int m_numAreas;
|
||||
std::vector<float> m_weights;
|
||||
float m_initMean;
|
||||
float m_initSigma;
|
||||
void generateRandomFeature( Size imageSize );
|
||||
float getSum( const Mat& image, Rect imgROI ) const;
|
||||
std::vector<Rect> m_areas; // areas within the patch over which to compute the feature
|
||||
cv::Size m_initSize; // size of the patch used during training
|
||||
cv::Size m_curSize; // size of the patches currently under investigation
|
||||
float m_scaleFactorHeight; // scaling factor in vertical direction
|
||||
float m_scaleFactorWidth; // scaling factor in horizontal direction
|
||||
std::vector<Rect> m_scaleAreas; // areas after scaling
|
||||
std::vector<float> m_scaleWeights; // weights after scaling
|
||||
|
||||
};
|
||||
|
||||
virtual void init( const CvFeatureParams *_featureParams, int _maxSampleCount, Size _winSize ) CV_OVERRIDE;
|
||||
virtual void setImage( const Mat& img, uchar clsLabel = 0, int idx = 1 ) CV_OVERRIDE;
|
||||
virtual float operator()( int featureIdx, int sampleIdx ) CV_OVERRIDE;
|
||||
virtual void writeFeatures( FileStorage &fs, const Mat& featureMap ) const CV_OVERRIDE;
|
||||
void writeFeature( FileStorage &fs ) const; // for old file format
|
||||
const std::vector<CvHaarEvaluator::FeatureHaar>& getFeatures() const;
|
||||
inline CvHaarEvaluator::FeatureHaar& getFeatures( int idx )
|
||||
{
|
||||
return features[idx];
|
||||
}
|
||||
void setWinSize( Size patchSize );
|
||||
Size setWinSize() const;
|
||||
virtual void generateFeatures() CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* TODO new method
|
||||
* \brief Overload the original generateFeatures in order to limit the number of the features
|
||||
* @param numFeatures Number of the features
|
||||
*/
|
||||
|
||||
virtual void generateFeatures( int numFeatures );
|
||||
|
||||
protected:
|
||||
bool isIntegral;
|
||||
|
||||
/* TODO Added from MIL implementation */
|
||||
Mat _ii_img;
|
||||
void compute_integral( const cv::Mat & img, std::vector<cv::Mat_<float> > & ii_imgs )
|
||||
{
|
||||
Mat ii_img;
|
||||
integral( img, ii_img, CV_32F );
|
||||
split( ii_img, ii_imgs );
|
||||
}
|
||||
|
||||
std::vector<FeatureHaar> features;
|
||||
Mat sum; /* sum images (each row represents image) */
|
||||
};
|
||||
|
||||
struct CvHOGFeatureParams : public CvFeatureParams
|
||||
{
|
||||
CvHOGFeatureParams();
|
||||
};
|
||||
|
||||
class CvHOGEvaluator : public CvFeatureEvaluator
|
||||
{
|
||||
public:
|
||||
virtual ~CvHOGEvaluator()
|
||||
{
|
||||
}
|
||||
virtual void init( const CvFeatureParams *_featureParams, int _maxSampleCount, Size _winSize ) CV_OVERRIDE;
|
||||
virtual void setImage( const Mat& img, uchar clsLabel, int idx ) CV_OVERRIDE;
|
||||
virtual float operator()( int varIdx, int sampleIdx ) CV_OVERRIDE;
|
||||
virtual void writeFeatures( FileStorage &fs, const Mat& featureMap ) const CV_OVERRIDE;
|
||||
protected:
|
||||
virtual void generateFeatures() CV_OVERRIDE;
|
||||
virtual void integralHistogram( const Mat &img, std::vector<Mat> &histogram, Mat &norm, int nbins ) const;
|
||||
class Feature
|
||||
{
|
||||
public:
|
||||
Feature();
|
||||
Feature( int offset, int x, int y, int cellW, int cellH );
|
||||
float calc( const std::vector<Mat> &_hists, const Mat &_normSum, size_t y, int featComponent ) const;
|
||||
void write( FileStorage &fs ) const;
|
||||
void write( FileStorage &fs, int varIdx ) const;
|
||||
|
||||
Rect rect[N_CELLS]; //cells
|
||||
|
||||
struct
|
||||
{
|
||||
int p0, p1, p2, p3;
|
||||
} fastRect[N_CELLS];
|
||||
};
|
||||
std::vector<Feature> features;
|
||||
|
||||
Mat normSum; //for nomalization calculation (L1 or L2)
|
||||
std::vector<Mat> hist;
|
||||
};
|
||||
|
||||
inline float CvHOGEvaluator::operator()( int varIdx, int sampleIdx )
|
||||
{
|
||||
int featureIdx = varIdx / ( N_BINS * N_CELLS );
|
||||
int componentIdx = varIdx % ( N_BINS * N_CELLS );
|
||||
//return features[featureIdx].calc( hist, sampleIdx, componentIdx);
|
||||
return features[featureIdx].calc( hist, normSum, sampleIdx, componentIdx );
|
||||
}
|
||||
|
||||
inline float CvHOGEvaluator::Feature::calc( const std::vector<Mat>& _hists, const Mat& _normSum, size_t y, int featComponent ) const
|
||||
{
|
||||
float normFactor;
|
||||
float res;
|
||||
|
||||
int binIdx = featComponent % N_BINS;
|
||||
int cellIdx = featComponent / N_BINS;
|
||||
|
||||
const float *phist = _hists[binIdx].ptr<float>( (int) y );
|
||||
res = phist[fastRect[cellIdx].p0] - phist[fastRect[cellIdx].p1] - phist[fastRect[cellIdx].p2] + phist[fastRect[cellIdx].p3];
|
||||
|
||||
const float *pnormSum = _normSum.ptr<float>( (int) y );
|
||||
normFactor = (float) ( pnormSum[fastRect[0].p0] - pnormSum[fastRect[1].p1] - pnormSum[fastRect[2].p2] + pnormSum[fastRect[3].p3] );
|
||||
res = ( res > 0.001f ) ? ( res / ( normFactor + 0.001f ) ) : 0.f; //for cutting negative values, which apper due to floating precision
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
struct CvLBPFeatureParams : CvFeatureParams
|
||||
{
|
||||
CvLBPFeatureParams();
|
||||
|
||||
};
|
||||
|
||||
class CvLBPEvaluator : public CvFeatureEvaluator
|
||||
{
|
||||
public:
|
||||
virtual ~CvLBPEvaluator() CV_OVERRIDE
|
||||
{
|
||||
}
|
||||
virtual void init( const CvFeatureParams *_featureParams, int _maxSampleCount, Size _winSize ) CV_OVERRIDE;
|
||||
virtual void setImage( const Mat& img, uchar clsLabel, int idx ) CV_OVERRIDE;
|
||||
virtual float operator()( int featureIdx, int sampleIdx ) CV_OVERRIDE
|
||||
{
|
||||
return (float) features[featureIdx].calc( sum, sampleIdx );
|
||||
}
|
||||
virtual void writeFeatures( FileStorage &fs, const Mat& featureMap ) const CV_OVERRIDE;
|
||||
protected:
|
||||
virtual void generateFeatures() CV_OVERRIDE;
|
||||
|
||||
class Feature
|
||||
{
|
||||
public:
|
||||
Feature();
|
||||
Feature( int offset, int x, int y, int _block_w, int _block_h );
|
||||
uchar calc( const Mat& _sum, size_t y ) const;
|
||||
void write( FileStorage &fs ) const;
|
||||
|
||||
Rect rect;
|
||||
int p[16];
|
||||
};
|
||||
std::vector<Feature> features;
|
||||
|
||||
Mat sum;
|
||||
};
|
||||
|
||||
inline uchar CvLBPEvaluator::Feature::calc( const Mat &_sum, size_t y ) const
|
||||
{
|
||||
const int* psum = _sum.ptr<int>( (int) y );
|
||||
int cval = psum[p[5]] - psum[p[6]] - psum[p[9]] + psum[p[10]];
|
||||
|
||||
return (uchar) ( ( psum[p[0]] - psum[p[1]] - psum[p[4]] + psum[p[5]] >= cval ? 128 : 0 ) | // 0
|
||||
( psum[p[1]] - psum[p[2]] - psum[p[5]] + psum[p[6]] >= cval ? 64 : 0 ) | // 1
|
||||
( psum[p[2]] - psum[p[3]] - psum[p[6]] + psum[p[7]] >= cval ? 32 : 0 ) | // 2
|
||||
( psum[p[6]] - psum[p[7]] - psum[p[10]] + psum[p[11]] >= cval ? 16 : 0 ) | // 5
|
||||
( psum[p[10]] - psum[p[11]] - psum[p[14]] + psum[p[15]] >= cval ? 8 : 0 ) | // 8
|
||||
( psum[p[9]] - psum[p[10]] - psum[p[13]] + psum[p[14]] >= cval ? 4 : 0 ) | // 7
|
||||
( psum[p[8]] - psum[p[9]] - psum[p[12]] + psum[p[13]] >= cval ? 2 : 0 ) | // 6
|
||||
( psum[p[4]] - psum[p[5]] - psum[p[8]] + psum[p[9]] >= cval ? 1 : 0 ) ); // 3
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
//! @}
|
||||
|
||||
}}} // namespace cv
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,235 @@
|
||||
/*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) 2015, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef __OPENCV_TRACKING_KALMAN_HPP_
|
||||
#define __OPENCV_TRACKING_KALMAN_HPP_
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include <limits>
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
//! @addtogroup tracking_detail
|
||||
//! @{
|
||||
|
||||
inline namespace kalman_filters {
|
||||
|
||||
/** @brief The interface for Unscented Kalman filter and Augmented Unscented Kalman filter.
|
||||
*/
|
||||
class CV_EXPORTS UnscentedKalmanFilter
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~UnscentedKalmanFilter(){}
|
||||
|
||||
/** The function performs prediction step of the algorithm
|
||||
* @param control - the current control vector,
|
||||
* @return the predicted estimate of the state.
|
||||
*/
|
||||
virtual Mat predict( InputArray control = noArray() ) = 0;
|
||||
|
||||
/** The function performs correction step of the algorithm
|
||||
* @param measurement - the current measurement vector,
|
||||
* @return the corrected estimate of the state.
|
||||
*/
|
||||
virtual Mat correct( InputArray measurement ) = 0;
|
||||
|
||||
/**
|
||||
* @return the process noise cross-covariance matrix.
|
||||
*/
|
||||
virtual Mat getProcessNoiseCov() const = 0;
|
||||
|
||||
/**
|
||||
* @return the measurement noise cross-covariance matrix.
|
||||
*/
|
||||
virtual Mat getMeasurementNoiseCov() const = 0;
|
||||
|
||||
/**
|
||||
* @return the error cross-covariance matrix.
|
||||
*/
|
||||
virtual Mat getErrorCov() const = 0;
|
||||
|
||||
/**
|
||||
* @return the current estimate of the state.
|
||||
*/
|
||||
virtual Mat getState() const = 0;
|
||||
};
|
||||
|
||||
/** @brief Model of dynamical system for Unscented Kalman filter.
|
||||
* The interface for dynamical system model. It contains functions for computing the next state and the measurement.
|
||||
* It must be inherited for using UKF.
|
||||
*/
|
||||
class CV_EXPORTS UkfSystemModel
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~UkfSystemModel(){}
|
||||
|
||||
/** The function for computing the next state from the previous state
|
||||
* @param x_k - previous state vector,
|
||||
* @param u_k - control vector,
|
||||
* @param v_k - noise vector,
|
||||
* @param x_kplus1 - next state vector.
|
||||
*/
|
||||
virtual void stateConversionFunction( const Mat& x_k, const Mat& u_k, const Mat& v_k, Mat& x_kplus1 ) = 0;
|
||||
/** The function for computing the measurement from the state
|
||||
* @param x_k - state vector,
|
||||
* @param n_k - noise vector,
|
||||
* @param z_k - measurement vector.
|
||||
*/
|
||||
virtual void measurementFunction( const Mat& x_k, const Mat& n_k, Mat& z_k ) = 0;
|
||||
};
|
||||
|
||||
|
||||
/** @brief Unscented Kalman filter parameters.
|
||||
* The class for initialization parameters of Unscented Kalman filter
|
||||
*/
|
||||
class CV_EXPORTS UnscentedKalmanFilterParams
|
||||
{
|
||||
public:
|
||||
|
||||
int DP; //!< Dimensionality of the state vector.
|
||||
int MP; //!< Dimensionality of the measurement vector.
|
||||
int CP; //!< Dimensionality of the control vector.
|
||||
int dataType; //!< Type of elements of vectors and matrices, default is CV_64F.
|
||||
|
||||
Mat stateInit; //!< Initial state, DP x 1, default is zero.
|
||||
Mat errorCovInit; //!< State estimate cross-covariance matrix, DP x DP, default is identity.
|
||||
|
||||
Mat processNoiseCov; //!< Process noise cross-covariance matrix, DP x DP.
|
||||
Mat measurementNoiseCov; //!< Measurement noise cross-covariance matrix, MP x MP.
|
||||
|
||||
// Parameters of algorithm
|
||||
double alpha; //!< Default is 1e-3.
|
||||
double k; //!< Default is 0.
|
||||
double beta; //!< Default is 2.0.
|
||||
|
||||
//Dynamical system model
|
||||
Ptr<UkfSystemModel> model; //!< Object of the class containing functions for computing the next state and the measurement.
|
||||
|
||||
/** The constructors.
|
||||
*/
|
||||
UnscentedKalmanFilterParams(){}
|
||||
|
||||
/**
|
||||
* @param dp - dimensionality of the state vector,
|
||||
* @param mp - dimensionality of the measurement vector,
|
||||
* @param cp - dimensionality of the control vector,
|
||||
* @param processNoiseCovDiag - value of elements on main diagonal process noise cross-covariance matrix,
|
||||
* @param measurementNoiseCovDiag - value of elements on main diagonal measurement noise cross-covariance matrix,
|
||||
* @param dynamicalSystem - ptr to object of the class containing functions for computing the next state and the measurement,
|
||||
* @param type - type of the created matrices that should be CV_32F or CV_64F.
|
||||
*/
|
||||
UnscentedKalmanFilterParams( int dp, int mp, int cp, double processNoiseCovDiag, double measurementNoiseCovDiag,
|
||||
Ptr<UkfSystemModel> dynamicalSystem, int type = CV_64F );
|
||||
|
||||
/** The function for initialization of Unscented Kalman filter
|
||||
* @param dp - dimensionality of the state vector,
|
||||
* @param mp - dimensionality of the measurement vector,
|
||||
* @param cp - dimensionality of the control vector,
|
||||
* @param processNoiseCovDiag - value of elements on main diagonal process noise cross-covariance matrix,
|
||||
* @param measurementNoiseCovDiag - value of elements on main diagonal measurement noise cross-covariance matrix,
|
||||
* @param dynamicalSystem - ptr to object of the class containing functions for computing the next state and the measurement,
|
||||
* @param type - type of the created matrices that should be CV_32F or CV_64F.
|
||||
*/
|
||||
void init( int dp, int mp, int cp, double processNoiseCovDiag, double measurementNoiseCovDiag,
|
||||
Ptr<UkfSystemModel> dynamicalSystem, int type = CV_64F );
|
||||
};
|
||||
|
||||
/** @brief Augmented Unscented Kalman filter parameters.
|
||||
* The class for initialization parameters of Augmented Unscented Kalman filter
|
||||
*/
|
||||
class CV_EXPORTS AugmentedUnscentedKalmanFilterParams: public UnscentedKalmanFilterParams
|
||||
{
|
||||
public:
|
||||
|
||||
AugmentedUnscentedKalmanFilterParams(){}
|
||||
|
||||
/**
|
||||
* @param dp - dimensionality of the state vector,
|
||||
* @param mp - dimensionality of the measurement vector,
|
||||
* @param cp - dimensionality of the control vector,
|
||||
* @param processNoiseCovDiag - value of elements on main diagonal process noise cross-covariance matrix,
|
||||
* @param measurementNoiseCovDiag - value of elements on main diagonal measurement noise cross-covariance matrix,
|
||||
* @param dynamicalSystem - ptr to object of the class containing functions for computing the next state and the measurement,
|
||||
* @param type - type of the created matrices that should be CV_32F or CV_64F.
|
||||
*/
|
||||
AugmentedUnscentedKalmanFilterParams( int dp, int mp, int cp, double processNoiseCovDiag, double measurementNoiseCovDiag,
|
||||
Ptr<UkfSystemModel> dynamicalSystem, int type = CV_64F );
|
||||
|
||||
/** The function for initialization of Augmented Unscented Kalman filter
|
||||
* @param dp - dimensionality of the state vector,
|
||||
* @param mp - dimensionality of the measurement vector,
|
||||
* @param cp - dimensionality of the control vector,
|
||||
* @param processNoiseCovDiag - value of elements on main diagonal process noise cross-covariance matrix,
|
||||
* @param measurementNoiseCovDiag - value of elements on main diagonal measurement noise cross-covariance matrix,
|
||||
* @param dynamicalSystem - object of the class containing functions for computing the next state and the measurement,
|
||||
* @param type - type of the created matrices that should be CV_32F or CV_64F.
|
||||
*/
|
||||
void init( int dp, int mp, int cp, double processNoiseCovDiag, double measurementNoiseCovDiag,
|
||||
Ptr<UkfSystemModel> dynamicalSystem, int type = CV_64F );
|
||||
};
|
||||
|
||||
/** @brief Unscented Kalman Filter factory method
|
||||
|
||||
* The class implements an Unscented Kalman filter <https://en.wikipedia.org/wiki/Kalman_filter#Unscented_Kalman_filter>.
|
||||
* @param params - an object of the UnscentedKalmanFilterParams class containing UKF parameters.
|
||||
* @return pointer to the object of the UnscentedKalmanFilterImpl class implementing UnscentedKalmanFilter.
|
||||
*/
|
||||
CV_EXPORTS Ptr<UnscentedKalmanFilter> createUnscentedKalmanFilter( const UnscentedKalmanFilterParams ¶ms );
|
||||
/** @brief Augmented Unscented Kalman Filter factory method
|
||||
|
||||
* The class implements an Augmented Unscented Kalman filter http://becs.aalto.fi/en/research/bayes/ekfukf/documentation.pdf, page 31-33.
|
||||
* AUKF is more accurate than UKF but its computational complexity is larger.
|
||||
* @param params - an object of the AugmentedUnscentedKalmanFilterParams class containing AUKF parameters.
|
||||
* @return pointer to the object of the AugmentedUnscentedKalmanFilterImpl class implementing UnscentedKalmanFilter.
|
||||
*/
|
||||
CV_EXPORTS Ptr<UnscentedKalmanFilter> createAugmentedUnscentedKalmanFilter( const AugmentedUnscentedKalmanFilterParams ¶ms );
|
||||
|
||||
} // namespace
|
||||
|
||||
//! @}
|
||||
|
||||
}}} // namespace
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,293 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef __OPENCV_ONLINEBOOSTING_HPP__
|
||||
#define __OPENCV_ONLINEBOOSTING_HPP__
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
//! @addtogroup tracking_detail
|
||||
//! @{
|
||||
|
||||
inline namespace online_boosting {
|
||||
|
||||
//TODO based on the original implementation
|
||||
//http://vision.ucsd.edu/~bbabenko/project_miltrack.shtml
|
||||
|
||||
class BaseClassifier;
|
||||
class WeakClassifierHaarFeature;
|
||||
class EstimatedGaussDistribution;
|
||||
class ClassifierThreshold;
|
||||
class Detector;
|
||||
|
||||
class StrongClassifierDirectSelection
|
||||
{
|
||||
public:
|
||||
|
||||
StrongClassifierDirectSelection( int numBaseClf, int numWeakClf, Size patchSz, const Rect& sampleROI, bool useFeatureEx = false, int iterationInit =
|
||||
0 );
|
||||
virtual ~StrongClassifierDirectSelection();
|
||||
|
||||
void initBaseClassifier();
|
||||
|
||||
bool update( const Mat& image, int target, float importance = 1.0 );
|
||||
float eval( const Mat& response );
|
||||
std::vector<int> getSelectedWeakClassifier();
|
||||
float classifySmooth( const std::vector<Mat>& images, const Rect& sampleROI, int& idx );
|
||||
int getNumBaseClassifier();
|
||||
Size getPatchSize() const;
|
||||
Rect getROI() const;
|
||||
bool getUseFeatureExchange() const;
|
||||
int getReplacedClassifier() const;
|
||||
|
||||
void replaceWeakClassifier( int idx );
|
||||
int getSwappedClassifier() const;
|
||||
private:
|
||||
|
||||
//StrongClassifier
|
||||
int numBaseClassifier;
|
||||
int numAllWeakClassifier;
|
||||
int numWeakClassifier;
|
||||
int iterInit;
|
||||
BaseClassifier** baseClassifier;
|
||||
std::vector<float> alpha;
|
||||
cv::Size patchSize;
|
||||
|
||||
bool useFeatureExchange;
|
||||
|
||||
//StrongClassifierDirectSelection
|
||||
std::vector<bool> m_errorMask;
|
||||
std::vector<float> m_errors;
|
||||
std::vector<float> m_sumErrors;
|
||||
|
||||
Detector* detector;
|
||||
Rect ROI;
|
||||
|
||||
int replacedClassifier;
|
||||
int swappedClassifier;
|
||||
};
|
||||
|
||||
class BaseClassifier
|
||||
{
|
||||
public:
|
||||
|
||||
BaseClassifier( int numWeakClassifier, int iterationInit );
|
||||
BaseClassifier( int numWeakClassifier, int iterationInit, WeakClassifierHaarFeature** weakCls );
|
||||
|
||||
WeakClassifierHaarFeature** getReferenceWeakClassifier()
|
||||
{
|
||||
return weakClassifier;
|
||||
}
|
||||
;
|
||||
void trainClassifier( const Mat& image, int target, float importance, std::vector<bool>& errorMask );
|
||||
int selectBestClassifier( std::vector<bool>& errorMask, float importance, std::vector<float> & errors );
|
||||
int computeReplaceWeakestClassifier( const std::vector<float> & errors );
|
||||
void replaceClassifierStatistic( int sourceIndex, int targetIndex );
|
||||
int getIdxOfNewWeakClassifier()
|
||||
{
|
||||
return m_idxOfNewWeakClassifier;
|
||||
}
|
||||
;
|
||||
int eval( const Mat& image );
|
||||
virtual ~BaseClassifier();
|
||||
float getError( int curWeakClassifier );
|
||||
void getErrors( float* errors );
|
||||
int getSelectedClassifier() const;
|
||||
void replaceWeakClassifier( int index );
|
||||
|
||||
protected:
|
||||
|
||||
void generateRandomClassifier();
|
||||
WeakClassifierHaarFeature** weakClassifier;
|
||||
bool m_referenceWeakClassifier;
|
||||
int m_numWeakClassifier;
|
||||
int m_selectedClassifier;
|
||||
int m_idxOfNewWeakClassifier;
|
||||
std::vector<float> m_wCorrect;
|
||||
std::vector<float> m_wWrong;
|
||||
int m_iterationInit;
|
||||
|
||||
};
|
||||
|
||||
class EstimatedGaussDistribution
|
||||
{
|
||||
public:
|
||||
|
||||
EstimatedGaussDistribution();
|
||||
EstimatedGaussDistribution( float P_mean, float R_mean, float P_sigma, float R_sigma );
|
||||
virtual ~EstimatedGaussDistribution();
|
||||
void update( float value ); //, float timeConstant = -1.0);
|
||||
float getMean();
|
||||
float getSigma();
|
||||
void setValues( float mean, float sigma );
|
||||
|
||||
private:
|
||||
|
||||
float m_mean;
|
||||
float m_sigma;
|
||||
float m_P_mean;
|
||||
float m_P_sigma;
|
||||
float m_R_mean;
|
||||
float m_R_sigma;
|
||||
};
|
||||
|
||||
class WeakClassifierHaarFeature
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
WeakClassifierHaarFeature();
|
||||
virtual ~WeakClassifierHaarFeature();
|
||||
|
||||
bool update( float value, int target );
|
||||
int eval( float value );
|
||||
|
||||
private:
|
||||
|
||||
float sigma;
|
||||
float mean;
|
||||
ClassifierThreshold* m_classifier;
|
||||
|
||||
void getInitialDistribution( EstimatedGaussDistribution *distribution );
|
||||
void generateRandomClassifier( EstimatedGaussDistribution* m_posSamples, EstimatedGaussDistribution* m_negSamples );
|
||||
|
||||
};
|
||||
|
||||
class Detector
|
||||
{
|
||||
public:
|
||||
|
||||
Detector( StrongClassifierDirectSelection* classifier );
|
||||
virtual
|
||||
~Detector( void );
|
||||
|
||||
void
|
||||
classifySmooth( const std::vector<Mat>& image, float minMargin = 0 );
|
||||
|
||||
int
|
||||
getNumDetections();
|
||||
float
|
||||
getConfidence( int patchIdx );
|
||||
float
|
||||
getConfidenceOfDetection( int detectionIdx );
|
||||
|
||||
float getConfidenceOfBestDetection()
|
||||
{
|
||||
return m_maxConfidence;
|
||||
}
|
||||
;
|
||||
int
|
||||
getPatchIdxOfBestDetection();
|
||||
|
||||
int
|
||||
getPatchIdxOfDetection( int detectionIdx );
|
||||
|
||||
const std::vector<int> &
|
||||
getIdxDetections() const
|
||||
{
|
||||
return m_idxDetections;
|
||||
}
|
||||
;
|
||||
const std::vector<float> &
|
||||
getConfidences() const
|
||||
{
|
||||
return m_confidences;
|
||||
}
|
||||
;
|
||||
|
||||
const cv::Mat &
|
||||
getConfImageDisplay() const
|
||||
{
|
||||
return m_confImageDisplay;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void
|
||||
prepareConfidencesMemory( int numPatches );
|
||||
void
|
||||
prepareDetectionsMemory( int numDetections );
|
||||
|
||||
StrongClassifierDirectSelection* m_classifier;
|
||||
std::vector<float> m_confidences;
|
||||
int m_sizeConfidences;
|
||||
int m_numDetections;
|
||||
std::vector<int> m_idxDetections;
|
||||
int m_sizeDetections;
|
||||
int m_idxBestDetection;
|
||||
float m_maxConfidence;
|
||||
cv::Mat_<float> m_confMatrix;
|
||||
cv::Mat_<float> m_confMatrixSmooth;
|
||||
cv::Mat_<unsigned char> m_confImageDisplay;
|
||||
};
|
||||
|
||||
class ClassifierThreshold
|
||||
{
|
||||
public:
|
||||
|
||||
ClassifierThreshold( EstimatedGaussDistribution* posSamples, EstimatedGaussDistribution* negSamples );
|
||||
virtual ~ClassifierThreshold();
|
||||
|
||||
void update( float value, int target );
|
||||
int eval( float value );
|
||||
|
||||
void* getDistribution( int target );
|
||||
|
||||
private:
|
||||
|
||||
EstimatedGaussDistribution* m_posSamples;
|
||||
EstimatedGaussDistribution* m_negSamples;
|
||||
|
||||
float m_threshold;
|
||||
int m_parity;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
//! @}
|
||||
|
||||
}}} // namespace
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,64 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef OPENCV_TLD_DATASET
|
||||
#define OPENCV_TLD_DATASET
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
//! @addtogroup tracking_detail
|
||||
//! @{
|
||||
|
||||
namespace tld
|
||||
{
|
||||
CV_EXPORTS cv::Rect2d tld_InitDataset(int videoInd, const char* rootPath = "TLD_dataset", int datasetInd = 0);
|
||||
CV_EXPORTS cv::String tld_getNextDatasetFrame();
|
||||
}
|
||||
|
||||
//! @}
|
||||
|
||||
}}}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,46 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifdef __OPENCV_BUILD
|
||||
#error this is a compatibility header which should not be used inside the OpenCV library
|
||||
#endif
|
||||
|
||||
#include "opencv2/tracking.hpp"
|
||||
@@ -0,0 +1,566 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef __OPENCV_TRACKING_TRACKING_BY_MATCHING_HPP__
|
||||
#define __OPENCV_TRACKING_TRACKING_BY_MATCHING_HPP__
|
||||
|
||||
#include <deque>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <tuple>
|
||||
#include <set>
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
//! @addtogroup tracking_detail
|
||||
//! @{
|
||||
|
||||
namespace tbm { //Tracking-by-Matching
|
||||
///
|
||||
/// \brief The TrackedObject struct defines properties of detected object.
|
||||
///
|
||||
struct CV_EXPORTS TrackedObject {
|
||||
cv::Rect rect; ///< Detected object ROI (zero area if N/A).
|
||||
double confidence; ///< Detection confidence level (-1 if N/A).
|
||||
int frame_idx; ///< Frame index where object was detected (-1 if N/A).
|
||||
int object_id; ///< Unique object identifier (-1 if N/A).
|
||||
uint64_t timestamp; ///< Timestamp in milliseconds.
|
||||
|
||||
///
|
||||
/// \brief Default constructor.
|
||||
///
|
||||
TrackedObject()
|
||||
: confidence(-1),
|
||||
frame_idx(-1),
|
||||
object_id(-1),
|
||||
timestamp(0) {}
|
||||
|
||||
///
|
||||
/// \brief Constructor with parameters.
|
||||
/// \param rect Bounding box of detected object.
|
||||
/// \param confidence Confidence of detection.
|
||||
/// \param frame_idx Index of frame.
|
||||
/// \param object_id Object ID.
|
||||
///
|
||||
TrackedObject(const cv::Rect &rect, float confidence, int frame_idx,
|
||||
int object_id)
|
||||
: rect(rect),
|
||||
confidence(confidence),
|
||||
frame_idx(frame_idx),
|
||||
object_id(object_id),
|
||||
timestamp(0) {}
|
||||
};
|
||||
|
||||
using TrackedObjects = std::deque<TrackedObject>;
|
||||
|
||||
bool operator==(const TrackedObject& first, const TrackedObject& second);
|
||||
bool operator!=(const TrackedObject& first, const TrackedObject& second);
|
||||
/// (object id, detected objects) pairs collection.
|
||||
using ObjectTracks = std::unordered_map<int, TrackedObjects>;
|
||||
|
||||
///
|
||||
/// \brief The IImageDescriptor class declares base class for image
|
||||
/// descriptor.
|
||||
///
|
||||
class CV_EXPORTS IImageDescriptor {
|
||||
public:
|
||||
///
|
||||
/// \brief Descriptor size getter.
|
||||
/// \return Descriptor size.
|
||||
///
|
||||
virtual cv::Size size() const = 0;
|
||||
|
||||
///
|
||||
/// \brief Computes image descriptor.
|
||||
/// \param[in] mat Color image.
|
||||
/// \param[out] descr Computed descriptor.
|
||||
///
|
||||
virtual void compute(const cv::Mat &mat, CV_OUT cv::Mat& descr) = 0;
|
||||
|
||||
///
|
||||
/// \brief Computes image descriptors in batches.
|
||||
/// \param[in] mats Images of interest.
|
||||
/// \param[out] descrs Matrices to store the computed descriptors.
|
||||
///
|
||||
virtual void compute(const std::vector<cv::Mat> &mats,
|
||||
CV_OUT std::vector<cv::Mat>& descrs) = 0;
|
||||
|
||||
virtual ~IImageDescriptor() {}
|
||||
};
|
||||
|
||||
|
||||
///
|
||||
/// \brief Uses resized image as descriptor.
|
||||
///
|
||||
class CV_EXPORTS ResizedImageDescriptor : public IImageDescriptor {
|
||||
public:
|
||||
///
|
||||
/// \brief Constructor.
|
||||
/// \param[in] descr_size Size of the descriptor (resized image).
|
||||
/// \param[in] interpolation Interpolation algorithm.
|
||||
///
|
||||
explicit ResizedImageDescriptor(const cv::Size &descr_size,
|
||||
const cv::InterpolationFlags interpolation)
|
||||
: descr_size_(descr_size), interpolation_(interpolation) {
|
||||
CV_Assert(descr_size.width > 0);
|
||||
CV_Assert(descr_size.height > 0);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Returns descriptor size.
|
||||
/// \return Number of elements in the descriptor.
|
||||
///
|
||||
cv::Size size() const override { return descr_size_; }
|
||||
|
||||
///
|
||||
/// \brief Computes image descriptor.
|
||||
/// \param[in] mat Frame containing the image of interest.
|
||||
/// \param[out] descr Matrix to store the computed descriptor.
|
||||
///
|
||||
void compute(const cv::Mat &mat, CV_OUT cv::Mat& descr) override {
|
||||
CV_Assert(!mat.empty());
|
||||
cv::resize(mat, descr, descr_size_, 0, 0, interpolation_);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Computes images descriptors.
|
||||
/// \param[in] mats Frames containing images of interest.
|
||||
/// \param[out] descrs Matrices to store the computed descriptors.
|
||||
//
|
||||
void compute(const std::vector<cv::Mat> &mats,
|
||||
CV_OUT std::vector<cv::Mat>& descrs) override {
|
||||
descrs.resize(mats.size());
|
||||
for (size_t i = 0; i < mats.size(); i++) {
|
||||
compute(mats[i], descrs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
cv::Size descr_size_;
|
||||
|
||||
cv::InterpolationFlags interpolation_;
|
||||
};
|
||||
|
||||
|
||||
///
|
||||
/// \brief The IDescriptorDistance class declares an interface for distance
|
||||
/// computation between reidentification descriptors.
|
||||
///
|
||||
class CV_EXPORTS IDescriptorDistance {
|
||||
public:
|
||||
///
|
||||
/// \brief Computes distance between two descriptors.
|
||||
/// \param[in] descr1 First descriptor.
|
||||
/// \param[in] descr2 Second descriptor.
|
||||
/// \return Distance between two descriptors.
|
||||
///
|
||||
virtual float compute(const cv::Mat &descr1, const cv::Mat &descr2) = 0;
|
||||
|
||||
///
|
||||
/// \brief Computes distances between two descriptors in batches.
|
||||
/// \param[in] descrs1 Batch of first descriptors.
|
||||
/// \param[in] descrs2 Batch of second descriptors.
|
||||
/// \return Distances between descriptors.
|
||||
///
|
||||
virtual std::vector<float> compute(const std::vector<cv::Mat> &descrs1,
|
||||
const std::vector<cv::Mat> &descrs2) = 0;
|
||||
|
||||
virtual ~IDescriptorDistance() {}
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief The CosDistance class allows computing cosine distance between two
|
||||
/// reidentification descriptors.
|
||||
///
|
||||
class CV_EXPORTS CosDistance : public IDescriptorDistance {
|
||||
public:
|
||||
///
|
||||
/// \brief CosDistance constructor.
|
||||
/// \param[in] descriptor_size Descriptor size.
|
||||
///
|
||||
explicit CosDistance(const cv::Size &descriptor_size);
|
||||
|
||||
///
|
||||
/// \brief Computes distance between two descriptors.
|
||||
/// \param descr1 First descriptor.
|
||||
/// \param descr2 Second descriptor.
|
||||
/// \return Distance between two descriptors.
|
||||
///
|
||||
float compute(const cv::Mat &descr1, const cv::Mat &descr2) override;
|
||||
|
||||
///
|
||||
/// \brief Computes distances between two descriptors in batches.
|
||||
/// \param[in] descrs1 Batch of first descriptors.
|
||||
/// \param[in] descrs2 Batch of second descriptors.
|
||||
/// \return Distances between descriptors.
|
||||
///
|
||||
std::vector<float> compute(
|
||||
const std::vector<cv::Mat> &descrs1,
|
||||
const std::vector<cv::Mat> &descrs2) override;
|
||||
|
||||
private:
|
||||
cv::Size descriptor_size_;
|
||||
};
|
||||
|
||||
|
||||
///
|
||||
/// \brief Computes distance between images
|
||||
/// using MatchTemplate function from OpenCV library
|
||||
/// and its cross-correlation computation method in particular.
|
||||
///
|
||||
class CV_EXPORTS MatchTemplateDistance : public IDescriptorDistance {
|
||||
public:
|
||||
///
|
||||
/// \brief Constructs the distance object.
|
||||
///
|
||||
/// \param[in] type Method of MatchTemplate function computation.
|
||||
/// \param[in] scale Scale parameter for the distance.
|
||||
/// Final distance is computed as:
|
||||
/// scale * distance + offset.
|
||||
/// \param[in] offset Offset parameter for the distance.
|
||||
/// Final distance is computed as:
|
||||
/// scale * distance + offset.
|
||||
///
|
||||
MatchTemplateDistance(int type = cv::TemplateMatchModes::TM_CCORR_NORMED,
|
||||
float scale = -1, float offset = 1)
|
||||
: type_(type), scale_(scale), offset_(offset) {}
|
||||
///
|
||||
/// \brief Computes distance between image descriptors.
|
||||
/// \param[in] descr1 First image descriptor.
|
||||
/// \param[in] descr2 Second image descriptor.
|
||||
/// \return Distance between image descriptors.
|
||||
///
|
||||
float compute(const cv::Mat &descr1, const cv::Mat &descr2) override;
|
||||
///
|
||||
/// \brief Computes distances between two descriptors in batches.
|
||||
/// \param[in] descrs1 Batch of first descriptors.
|
||||
/// \param[in] descrs2 Batch of second descriptors.
|
||||
/// \return Distances between descriptors.
|
||||
///
|
||||
std::vector<float> compute(const std::vector<cv::Mat> &descrs1,
|
||||
const std::vector<cv::Mat> &descrs2) override;
|
||||
virtual ~MatchTemplateDistance() {}
|
||||
|
||||
private:
|
||||
int type_; ///< Method of MatchTemplate function computation.
|
||||
float scale_; ///< Scale parameter for the distance. Final distance is
|
||||
/// computed as: scale * distance + offset.
|
||||
float offset_; ///< Offset parameter for the distance. Final distance is
|
||||
/// computed as: scale * distance + offset.
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief The TrackerParams struct stores parameters of TrackerByMatching
|
||||
///
|
||||
struct CV_EXPORTS TrackerParams {
|
||||
size_t min_track_duration; ///< Min track duration in milliseconds.
|
||||
|
||||
size_t forget_delay; ///< Forget about track if the last bounding box in
|
||||
/// track was detected more than specified number of
|
||||
/// frames ago.
|
||||
|
||||
float aff_thr_fast; ///< Affinity threshold which is used to determine if
|
||||
/// tracklet and detection should be combined (fast
|
||||
/// descriptor is used).
|
||||
|
||||
float aff_thr_strong; ///< Affinity threshold which is used to determine if
|
||||
/// tracklet and detection should be combined(strong
|
||||
/// descriptor is used).
|
||||
|
||||
float shape_affinity_w; ///< Shape affinity weight.
|
||||
|
||||
float motion_affinity_w; ///< Motion affinity weight.
|
||||
|
||||
float time_affinity_w; ///< Time affinity weight.
|
||||
|
||||
float min_det_conf; ///< Min confidence of detection.
|
||||
|
||||
cv::Vec2f bbox_aspect_ratios_range; ///< Bounding box aspect ratios range.
|
||||
|
||||
cv::Vec2f bbox_heights_range; ///< Bounding box heights range.
|
||||
|
||||
int predict; ///< How many frames are used to predict bounding box in case
|
||||
/// of lost track.
|
||||
|
||||
float strong_affinity_thr; ///< If 'fast' confidence is greater than this
|
||||
/// threshold then 'strong' Re-ID approach is
|
||||
/// used.
|
||||
|
||||
float reid_thr; ///< Affinity threshold for re-identification.
|
||||
|
||||
bool drop_forgotten_tracks; ///< Drop forgotten tracks. If it's enabled it
|
||||
/// disables an ability to get detection log.
|
||||
|
||||
int max_num_objects_in_track; ///< The number of objects in track is
|
||||
/// restricted by this parameter. If it is negative or zero, the max number of
|
||||
/// objects in track is not restricted.
|
||||
|
||||
///
|
||||
/// Default constructor.
|
||||
///
|
||||
TrackerParams();
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief The Track class describes tracks.
|
||||
///
|
||||
class CV_EXPORTS Track {
|
||||
public:
|
||||
///
|
||||
/// \brief Track constructor.
|
||||
/// \param objs Detected objects sequence.
|
||||
/// \param last_image Image of last image in the detected object sequence.
|
||||
/// \param descriptor_fast Fast descriptor.
|
||||
/// \param descriptor_strong Strong descriptor (reid embedding).
|
||||
///
|
||||
Track(const TrackedObjects &objs, const cv::Mat &last_image,
|
||||
const cv::Mat &descriptor_fast, const cv::Mat &descriptor_strong)
|
||||
: objects(objs),
|
||||
predicted_rect(!objs.empty() ? objs.back().rect : cv::Rect()),
|
||||
last_image(last_image),
|
||||
descriptor_fast(descriptor_fast),
|
||||
descriptor_strong(descriptor_strong),
|
||||
lost(0),
|
||||
length(1) {
|
||||
CV_Assert(!objs.empty());
|
||||
first_object = objs[0];
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief empty returns if track does not contain objects.
|
||||
/// \return true if track does not contain objects.
|
||||
///
|
||||
bool empty() const { return objects.empty(); }
|
||||
|
||||
///
|
||||
/// \brief size returns number of detected objects in a track.
|
||||
/// \return number of detected objects in a track.
|
||||
///
|
||||
size_t size() const { return objects.size(); }
|
||||
|
||||
///
|
||||
/// \brief operator [] return const reference to detected object with
|
||||
/// specified index.
|
||||
/// \param i Index of object.
|
||||
/// \return const reference to detected object with specified index.
|
||||
///
|
||||
const TrackedObject &operator[](size_t i) const { return objects[i]; }
|
||||
|
||||
///
|
||||
/// \brief operator [] return non-const reference to detected object with
|
||||
/// specified index.
|
||||
/// \param i Index of object.
|
||||
/// \return non-const reference to detected object with specified index.
|
||||
///
|
||||
TrackedObject &operator[](size_t i) { return objects[i]; }
|
||||
|
||||
///
|
||||
/// \brief back returns const reference to last object in track.
|
||||
/// \return const reference to last object in track.
|
||||
///
|
||||
const TrackedObject &back() const {
|
||||
CV_Assert(!empty());
|
||||
return objects.back();
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief back returns non-const reference to last object in track.
|
||||
/// \return non-const reference to last object in track.
|
||||
///
|
||||
TrackedObject &back() {
|
||||
CV_Assert(!empty());
|
||||
return objects.back();
|
||||
}
|
||||
|
||||
TrackedObjects objects; ///< Detected objects;
|
||||
cv::Rect predicted_rect; ///< Rectangle that represents predicted position
|
||||
/// and size of bounding box if track has been lost.
|
||||
cv::Mat last_image; ///< Image of last detected object in track.
|
||||
cv::Mat descriptor_fast; ///< Fast descriptor.
|
||||
cv::Mat descriptor_strong; ///< Strong descriptor (reid embedding).
|
||||
size_t lost; ///< How many frames ago track has been lost.
|
||||
|
||||
TrackedObject first_object; ///< First object in track.
|
||||
size_t length; ///< Length of a track including number of objects that were
|
||||
/// removed from track in order to avoid memory usage growth.
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Tracker-by-Matching algorithm interface.
|
||||
///
|
||||
/// This class is implementation of tracking-by-matching system. It uses two
|
||||
/// different appearance measures to compute affinity between bounding boxes:
|
||||
/// some fast descriptor and some strong descriptor. Each time the assignment
|
||||
/// problem is solved. The assignment problem in our case is how to establish
|
||||
/// correspondence between existing tracklets and recently detected objects.
|
||||
/// First step is to compute an affinity matrix between tracklets and
|
||||
/// detections. The affinity equals to
|
||||
/// appearance_affinity * motion_affinity * shape_affinity.
|
||||
/// Where appearance is 1 - distance(tracklet_fast_dscr, detection_fast_dscr).
|
||||
/// Second step is to solve the assignment problem using Kuhn-Munkres
|
||||
/// algorithm. If correspondence between some tracklet and detection is
|
||||
/// established with low confidence (affinity) then the strong descriptor is
|
||||
/// used to determine if there is correspondence between tracklet and detection.
|
||||
///
|
||||
class CV_EXPORTS ITrackerByMatching {
|
||||
public:
|
||||
using Descriptor = std::shared_ptr<IImageDescriptor>;
|
||||
using Distance = std::shared_ptr<IDescriptorDistance>;
|
||||
|
||||
///
|
||||
/// \brief Destructor for the tracker
|
||||
///
|
||||
virtual ~ITrackerByMatching() {}
|
||||
|
||||
///
|
||||
/// \brief Process given frame.
|
||||
/// \param[in] frame Colored image (CV_8UC3).
|
||||
/// \param[in] detections Detected objects on the frame.
|
||||
/// \param[in] timestamp Timestamp must be positive and measured in
|
||||
/// milliseconds
|
||||
///
|
||||
virtual void process(const cv::Mat &frame, const TrackedObjects &detections,
|
||||
uint64_t timestamp) = 0;
|
||||
|
||||
///
|
||||
/// \brief Pipeline parameters getter.
|
||||
/// \return Parameters of pipeline.
|
||||
///
|
||||
virtual const TrackerParams ¶ms() const = 0;
|
||||
|
||||
///
|
||||
/// \brief Pipeline parameters setter.
|
||||
/// \param[in] params Parameters of pipeline.
|
||||
///
|
||||
virtual void setParams(const TrackerParams ¶ms) = 0;
|
||||
|
||||
///
|
||||
/// \brief Fast descriptor getter.
|
||||
/// \return Fast descriptor used in pipeline.
|
||||
///
|
||||
virtual const Descriptor &descriptorFast() const = 0;
|
||||
|
||||
///
|
||||
/// \brief Fast descriptor setter.
|
||||
/// \param[in] val Fast descriptor used in pipeline.
|
||||
///
|
||||
virtual void setDescriptorFast(const Descriptor &val) = 0;
|
||||
|
||||
///
|
||||
/// \brief Strong descriptor getter.
|
||||
/// \return Strong descriptor used in pipeline.
|
||||
///
|
||||
virtual const Descriptor &descriptorStrong() const = 0;
|
||||
|
||||
///
|
||||
/// \brief Strong descriptor setter.
|
||||
/// \param[in] val Strong descriptor used in pipeline.
|
||||
///
|
||||
virtual void setDescriptorStrong(const Descriptor &val) = 0;
|
||||
|
||||
///
|
||||
/// \brief Fast distance getter.
|
||||
/// \return Fast distance used in pipeline.
|
||||
///
|
||||
virtual const Distance &distanceFast() const = 0;
|
||||
|
||||
///
|
||||
/// \brief Fast distance setter.
|
||||
/// \param[in] val Fast distance used in pipeline.
|
||||
///
|
||||
virtual void setDistanceFast(const Distance &val) = 0;
|
||||
|
||||
///
|
||||
/// \brief Strong distance getter.
|
||||
/// \return Strong distance used in pipeline.
|
||||
///
|
||||
virtual const Distance &distanceStrong() const = 0;
|
||||
|
||||
///
|
||||
/// \brief Strong distance setter.
|
||||
/// \param[in] val Strong distance used in pipeline.
|
||||
///
|
||||
virtual void setDistanceStrong(const Distance &val) = 0;
|
||||
|
||||
///
|
||||
/// \brief Returns number of counted people.
|
||||
/// \return a number of counted people.
|
||||
///
|
||||
virtual size_t count() const = 0;
|
||||
|
||||
///
|
||||
/// \brief Get active tracks to draw
|
||||
/// \return Active tracks.
|
||||
///
|
||||
virtual std::unordered_map<size_t, std::vector<cv::Point> > getActiveTracks() const = 0;
|
||||
|
||||
///
|
||||
/// \brief Get tracked detections.
|
||||
/// \return Tracked detections.
|
||||
///
|
||||
virtual TrackedObjects trackedDetections() const = 0;
|
||||
|
||||
///
|
||||
/// \brief Draws active tracks on a given frame.
|
||||
/// \param[in] frame Colored image (CV_8UC3).
|
||||
/// \return Colored image with drawn active tracks.
|
||||
///
|
||||
virtual cv::Mat drawActiveTracks(const cv::Mat &frame) = 0;
|
||||
|
||||
///
|
||||
/// \brief isTrackForgotten returns true if track is forgotten.
|
||||
/// \param id Track ID.
|
||||
/// \return true if track is forgotten.
|
||||
///
|
||||
virtual bool isTrackForgotten(size_t id) const = 0;
|
||||
|
||||
///
|
||||
/// \brief tracks Returns all tracks including forgotten (lost too many frames
|
||||
/// ago).
|
||||
/// \return Set of tracks {id, track}.
|
||||
///
|
||||
virtual const std::unordered_map<size_t, Track> &tracks() const = 0;
|
||||
|
||||
///
|
||||
/// \brief isTrackValid Checks whether track is valid (duration > threshold).
|
||||
/// \param track_id Index of checked track.
|
||||
/// \return True if track duration exceeds some predefined value.
|
||||
///
|
||||
virtual bool isTrackValid(size_t track_id) const = 0;
|
||||
|
||||
///
|
||||
/// \brief dropForgottenTracks Removes tracks from memory that were lost too
|
||||
/// many frames ago.
|
||||
///
|
||||
virtual void dropForgottenTracks() = 0;
|
||||
|
||||
///
|
||||
/// \brief dropForgottenTrack Check that the track was lost too many frames
|
||||
/// ago
|
||||
/// and removes it frm memory.
|
||||
///
|
||||
virtual void dropForgottenTrack(size_t track_id) = 0;
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief The factory to create Tracker-by-Matching algorithm implementation.
|
||||
///
|
||||
CV_EXPORTS cv::Ptr<ITrackerByMatching> createTrackerByMatching(const TrackerParams ¶ms = TrackerParams());
|
||||
|
||||
} // namespace tbm
|
||||
|
||||
//! @}
|
||||
|
||||
}}} // namespace
|
||||
#endif // #ifndef __OPENCV_TRACKING_TRACKING_BY_MATCHING_HPP__
|
||||
@@ -0,0 +1,932 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef OPENCV_TRACKING_DETAIL_HPP
|
||||
#define OPENCV_TRACKING_DETAIL_HPP
|
||||
|
||||
/*
|
||||
* Partially based on:
|
||||
* ====================================================================================================================
|
||||
* - [AAM] S. Salti, A. Cavallaro, L. Di Stefano, Adaptive Appearance Modeling for Video Tracking: Survey and Evaluation
|
||||
* - [AMVOT] X. Li, W. Hu, C. Shen, Z. Zhang, A. Dick, A. van den Hengel, A Survey of Appearance Models in Visual Object Tracking
|
||||
*
|
||||
* This Tracking API has been designed with PlantUML. If you modify this API please change UML files under modules/tracking/doc/uml
|
||||
*
|
||||
*/
|
||||
|
||||
#include "opencv2/video/detail/tracking.detail.hpp"
|
||||
|
||||
#include "feature.hpp" // CvHaarEvaluator
|
||||
#include "onlineBoosting.hpp" // StrongClassifierDirectSelection
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
/** @addtogroup tracking_detail
|
||||
@{
|
||||
|
||||
Long-term optical tracking API
|
||||
------------------------------
|
||||
|
||||
Long-term optical tracking is an important issue for many computer vision applications in
|
||||
real world scenario. The development in this area is very fragmented and this API is an unique
|
||||
interface useful for plug several algorithms and compare them. This work is partially based on
|
||||
@cite AAM and @cite AMVOT .
|
||||
|
||||
These algorithms start from a bounding box of the target and with their internal representation they
|
||||
avoid the drift during the tracking. These long-term trackers are able to evaluate online the
|
||||
quality of the location of the target in the new frame, without ground truth.
|
||||
|
||||
There are three main components: the TrackerContribSampler, the TrackerContribFeatureSet and the TrackerModel. The
|
||||
first component is the object that computes the patches over the frame based on the last target
|
||||
location. The TrackerContribFeatureSet is the class that manages the Features, is possible plug many kind
|
||||
of these (HAAR, HOG, LBP, Feature2D, etc). The last component is the internal representation of the
|
||||
target, it is the appearance model. It stores all state candidates and compute the trajectory (the
|
||||
most likely target states). The class TrackerTargetState represents a possible state of the target.
|
||||
The TrackerContribSampler and the TrackerContribFeatureSet are the visual representation of the target, instead
|
||||
the TrackerModel is the statistical model.
|
||||
|
||||
A recent benchmark between these algorithms can be found in @cite OOT
|
||||
|
||||
Creating Your Own %Tracker
|
||||
--------------------
|
||||
|
||||
If you want to create a new tracker, here's what you have to do. First, decide on the name of the class
|
||||
for the tracker (to meet the existing style, we suggest something with prefix "tracker", e.g.
|
||||
trackerMIL, trackerBoosting) -- we shall refer to this choice as to "classname" in subsequent.
|
||||
|
||||
- Declare your tracker in modules/tracking/include/opencv2/tracking/tracker.hpp. Your tracker should inherit from
|
||||
Tracker (please, see the example below). You should declare the specialized Param structure,
|
||||
where you probably will want to put the data, needed to initialize your tracker. You should
|
||||
get something similar to :
|
||||
@code
|
||||
class CV_EXPORTS_W TrackerMIL : public Tracker
|
||||
{
|
||||
public:
|
||||
struct CV_EXPORTS Params
|
||||
{
|
||||
Params();
|
||||
//parameters for sampler
|
||||
float samplerInitInRadius; // radius for gathering positive instances during init
|
||||
int samplerInitMaxNegNum; // # negative samples to use during init
|
||||
float samplerSearchWinSize; // size of search window
|
||||
float samplerTrackInRadius; // radius for gathering positive instances during tracking
|
||||
int samplerTrackMaxPosNum; // # positive samples to use during tracking
|
||||
int samplerTrackMaxNegNum; // # negative samples to use during tracking
|
||||
int featureSetNumFeatures; // #features
|
||||
|
||||
void read( const FileNode& fn );
|
||||
void write( FileStorage& fs ) const;
|
||||
};
|
||||
@endcode
|
||||
of course, you can also add any additional methods of your choice. It should be pointed out,
|
||||
however, that it is not expected to have a constructor declared, as creation should be done via
|
||||
the corresponding create() method.
|
||||
- Finally, you should implement the function with signature :
|
||||
@code
|
||||
Ptr<classname> classname::create(const classname::Params ¶meters){
|
||||
...
|
||||
}
|
||||
@endcode
|
||||
That function can (and probably will) return a pointer to some derived class of "classname",
|
||||
which will probably have a real constructor.
|
||||
|
||||
Every tracker has three component TrackerContribSampler, TrackerContribFeatureSet and TrackerModel. The first two
|
||||
are instantiated from Tracker base class, instead the last component is abstract, so you must
|
||||
implement your TrackerModel.
|
||||
|
||||
### TrackerContribSampler
|
||||
|
||||
TrackerContribSampler is already instantiated, but you should define the sampling algorithm and add the
|
||||
classes (or single class) to TrackerContribSampler. You can choose one of the ready implementation as
|
||||
TrackerContribSamplerCSC or you can implement your sampling method, in this case the class must inherit
|
||||
TrackerContribSamplerAlgorithm. Fill the samplingImpl method that writes the result in "sample" output
|
||||
argument.
|
||||
|
||||
Example of creating specialized TrackerContribSamplerAlgorithm TrackerContribSamplerCSC : :
|
||||
@code
|
||||
class CV_EXPORTS_W TrackerContribSamplerCSC : public TrackerContribSamplerAlgorithm
|
||||
{
|
||||
public:
|
||||
TrackerContribSamplerCSC( const TrackerContribSamplerCSC::Params ¶meters = TrackerContribSamplerCSC::Params() );
|
||||
~TrackerContribSamplerCSC();
|
||||
...
|
||||
|
||||
protected:
|
||||
bool samplingImpl( const Mat& image, Rect boundingBox, std::vector<Mat>& sample );
|
||||
...
|
||||
|
||||
};
|
||||
@endcode
|
||||
|
||||
Example of adding TrackerContribSamplerAlgorithm to TrackerContribSampler : :
|
||||
@code
|
||||
//sampler is the TrackerContribSampler
|
||||
Ptr<TrackerContribSamplerAlgorithm> CSCSampler = new TrackerContribSamplerCSC( CSCparameters );
|
||||
if( !sampler->addTrackerSamplerAlgorithm( CSCSampler ) )
|
||||
return false;
|
||||
|
||||
//or add CSC sampler with default parameters
|
||||
//sampler->addTrackerSamplerAlgorithm( "CSC" );
|
||||
@endcode
|
||||
@sa
|
||||
TrackerContribSamplerCSC, TrackerContribSamplerAlgorithm
|
||||
|
||||
### TrackerContribFeatureSet
|
||||
|
||||
TrackerContribFeatureSet is already instantiated (as first) , but you should define what kinds of features
|
||||
you'll use in your tracker. You can use multiple feature types, so you can add a ready
|
||||
implementation as TrackerContribFeatureHAAR in your TrackerContribFeatureSet or develop your own implementation.
|
||||
In this case, in the computeImpl method put the code that extract the features and in the selection
|
||||
method optionally put the code for the refinement and selection of the features.
|
||||
|
||||
Example of creating specialized TrackerFeature TrackerContribFeatureHAAR : :
|
||||
@code
|
||||
class CV_EXPORTS_W TrackerContribFeatureHAAR : public TrackerFeature
|
||||
{
|
||||
public:
|
||||
TrackerContribFeatureHAAR( const TrackerContribFeatureHAAR::Params ¶meters = TrackerContribFeatureHAAR::Params() );
|
||||
~TrackerContribFeatureHAAR();
|
||||
void selection( Mat& response, int npoints );
|
||||
...
|
||||
|
||||
protected:
|
||||
bool computeImpl( const std::vector<Mat>& images, Mat& response );
|
||||
...
|
||||
|
||||
};
|
||||
@endcode
|
||||
Example of adding TrackerFeature to TrackerContribFeatureSet : :
|
||||
@code
|
||||
//featureSet is the TrackerContribFeatureSet
|
||||
Ptr<TrackerFeature> trackerFeature = new TrackerContribFeatureHAAR( HAARparameters );
|
||||
featureSet->addTrackerFeature( trackerFeature );
|
||||
@endcode
|
||||
@sa
|
||||
TrackerContribFeatureHAAR, TrackerContribFeatureSet
|
||||
|
||||
### TrackerModel
|
||||
|
||||
TrackerModel is abstract, so in your implementation you must develop your TrackerModel that inherit
|
||||
from TrackerModel. Fill the method for the estimation of the state "modelEstimationImpl", that
|
||||
estimates the most likely target location, see @cite AAM table I (ME) for further information. Fill
|
||||
"modelUpdateImpl" in order to update the model, see @cite AAM table I (MU). In this class you can use
|
||||
the :cConfidenceMap and :cTrajectory to storing the model. The first represents the model on the all
|
||||
possible candidate states and the second represents the list of all estimated states.
|
||||
|
||||
Example of creating specialized TrackerModel TrackerMILModel : :
|
||||
@code
|
||||
class TrackerMILModel : public TrackerModel
|
||||
{
|
||||
public:
|
||||
TrackerMILModel( const Rect& boundingBox );
|
||||
~TrackerMILModel();
|
||||
...
|
||||
|
||||
protected:
|
||||
void modelEstimationImpl( const std::vector<Mat>& responses );
|
||||
void modelUpdateImpl();
|
||||
...
|
||||
|
||||
};
|
||||
@endcode
|
||||
And add it in your Tracker : :
|
||||
@code
|
||||
bool TrackerMIL::initImpl( const Mat& image, const Rect2d& boundingBox )
|
||||
{
|
||||
...
|
||||
//model is the general TrackerModel field of the general Tracker
|
||||
model = new TrackerMILModel( boundingBox );
|
||||
...
|
||||
}
|
||||
@endcode
|
||||
In the last step you should define the TrackerStateEstimator based on your implementation or you can
|
||||
use one of ready class as TrackerStateEstimatorMILBoosting. It represent the statistical part of the
|
||||
model that estimates the most likely target state.
|
||||
|
||||
Example of creating specialized TrackerStateEstimator TrackerStateEstimatorMILBoosting : :
|
||||
@code
|
||||
class CV_EXPORTS_W TrackerStateEstimatorMILBoosting : public TrackerStateEstimator
|
||||
{
|
||||
class TrackerMILTargetState : public TrackerTargetState
|
||||
{
|
||||
...
|
||||
};
|
||||
|
||||
public:
|
||||
TrackerStateEstimatorMILBoosting( int nFeatures = 250 );
|
||||
~TrackerStateEstimatorMILBoosting();
|
||||
...
|
||||
|
||||
protected:
|
||||
Ptr<TrackerTargetState> estimateImpl( const std::vector<ConfidenceMap>& confidenceMaps );
|
||||
void updateImpl( std::vector<ConfidenceMap>& confidenceMaps );
|
||||
...
|
||||
|
||||
};
|
||||
@endcode
|
||||
And add it in your TrackerModel : :
|
||||
@code
|
||||
//model is the TrackerModel of your Tracker
|
||||
Ptr<TrackerStateEstimatorMILBoosting> stateEstimator = new TrackerStateEstimatorMILBoosting( params.featureSetNumFeatures );
|
||||
model->setTrackerStateEstimator( stateEstimator );
|
||||
@endcode
|
||||
@sa
|
||||
TrackerModel, TrackerStateEstimatorMILBoosting, TrackerTargetState
|
||||
|
||||
During this step, you should define your TrackerTargetState based on your implementation.
|
||||
TrackerTargetState base class has only the bounding box (upper-left position, width and height), you
|
||||
can enrich it adding scale factor, target rotation, etc.
|
||||
|
||||
Example of creating specialized TrackerTargetState TrackerMILTargetState : :
|
||||
@code
|
||||
class TrackerMILTargetState : public TrackerTargetState
|
||||
{
|
||||
public:
|
||||
TrackerMILTargetState( const Point2f& position, int targetWidth, int targetHeight, bool foreground, const Mat& features );
|
||||
~TrackerMILTargetState();
|
||||
...
|
||||
|
||||
private:
|
||||
bool isTarget;
|
||||
Mat targetFeatures;
|
||||
...
|
||||
|
||||
};
|
||||
@endcode
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/************************************ TrackerContribFeature Base Classes ************************************/
|
||||
|
||||
/** @brief Abstract base class for TrackerContribFeature that represents the feature.
|
||||
*/
|
||||
class CV_EXPORTS TrackerContribFeature : public TrackerFeature
|
||||
{
|
||||
public:
|
||||
virtual ~TrackerContribFeature();
|
||||
|
||||
/** @brief Create TrackerContribFeature by tracker feature type
|
||||
@param trackerFeatureType The TrackerContribFeature name
|
||||
|
||||
The modes available now:
|
||||
|
||||
- "HAAR" -- Haar Feature-based
|
||||
|
||||
The modes that will be available soon:
|
||||
|
||||
- "HOG" -- Histogram of Oriented Gradients features
|
||||
- "LBP" -- Local Binary Pattern features
|
||||
- "FEATURE2D" -- All types of Feature2D
|
||||
*/
|
||||
static Ptr<TrackerContribFeature> create( const String& trackerFeatureType );
|
||||
|
||||
/** @brief Identify most effective features
|
||||
@param response Collection of response for the specific TrackerContribFeature
|
||||
@param npoints Max number of features
|
||||
|
||||
@note This method modifies the response parameter
|
||||
*/
|
||||
virtual void selection( Mat& response, int npoints ) = 0;
|
||||
|
||||
/** @brief Get the name of the specific TrackerContribFeature
|
||||
*/
|
||||
String getClassName() const;
|
||||
|
||||
protected:
|
||||
String className;
|
||||
};
|
||||
|
||||
/** @brief Class that manages the extraction and selection of features
|
||||
|
||||
@cite AAM Feature Extraction and Feature Set Refinement (Feature Processing and Feature Selection).
|
||||
See table I and section III C @cite AMVOT Appearance modelling -\> Visual representation (Table II,
|
||||
section 3.1 - 3.2)
|
||||
|
||||
TrackerContribFeatureSet is an aggregation of TrackerContribFeature
|
||||
|
||||
@sa
|
||||
TrackerContribFeature
|
||||
|
||||
*/
|
||||
class CV_EXPORTS TrackerContribFeatureSet
|
||||
{
|
||||
public:
|
||||
|
||||
TrackerContribFeatureSet();
|
||||
|
||||
~TrackerContribFeatureSet();
|
||||
|
||||
/** @brief Extract features from the images collection
|
||||
@param images The input images
|
||||
*/
|
||||
void extraction( const std::vector<Mat>& images );
|
||||
|
||||
/** @brief Identify most effective features for all feature types (optional)
|
||||
*/
|
||||
void selection();
|
||||
|
||||
/** @brief Remove outliers for all feature types (optional)
|
||||
*/
|
||||
void removeOutliers();
|
||||
|
||||
/** @brief Add TrackerContribFeature in the collection. Return true if TrackerContribFeature is added, false otherwise
|
||||
@param trackerFeatureType The TrackerContribFeature name
|
||||
|
||||
The modes available now:
|
||||
|
||||
- "HAAR" -- Haar Feature-based
|
||||
|
||||
The modes that will be available soon:
|
||||
|
||||
- "HOG" -- Histogram of Oriented Gradients features
|
||||
- "LBP" -- Local Binary Pattern features
|
||||
- "FEATURE2D" -- All types of Feature2D
|
||||
|
||||
Example TrackerContribFeatureSet::addTrackerFeature : :
|
||||
@code
|
||||
//sample usage:
|
||||
|
||||
Ptr<TrackerContribFeature> trackerFeature = ...;
|
||||
featureSet->addTrackerFeature( trackerFeature );
|
||||
|
||||
//or add CSC sampler with default parameters
|
||||
//featureSet->addTrackerFeature( "HAAR" );
|
||||
@endcode
|
||||
@note If you use the second method, you must initialize the TrackerContribFeature
|
||||
*/
|
||||
bool addTrackerFeature( String trackerFeatureType );
|
||||
|
||||
/** @overload
|
||||
@param feature The TrackerContribFeature class
|
||||
*/
|
||||
bool addTrackerFeature( Ptr<TrackerContribFeature>& feature );
|
||||
|
||||
/** @brief Get the TrackerContribFeature collection (TrackerContribFeature name, TrackerContribFeature pointer)
|
||||
*/
|
||||
const std::vector<std::pair<String, Ptr<TrackerContribFeature> > >& getTrackerFeature() const;
|
||||
|
||||
/** @brief Get the responses
|
||||
|
||||
@note Be sure to call extraction before getResponses Example TrackerContribFeatureSet::getResponses : :
|
||||
*/
|
||||
const std::vector<Mat>& getResponses() const;
|
||||
|
||||
private:
|
||||
|
||||
void clearResponses();
|
||||
bool blockAddTrackerFeature;
|
||||
|
||||
std::vector<std::pair<String, Ptr<TrackerContribFeature> > > features; //list of features
|
||||
std::vector<Mat> responses; //list of response after compute
|
||||
|
||||
};
|
||||
|
||||
|
||||
/************************************ TrackerContribSampler Base Classes ************************************/
|
||||
|
||||
/** @brief Abstract base class for TrackerContribSamplerAlgorithm that represents the algorithm for the specific
|
||||
sampler.
|
||||
*/
|
||||
class CV_EXPORTS TrackerContribSamplerAlgorithm : public TrackerSamplerAlgorithm
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief Destructor
|
||||
*/
|
||||
virtual ~TrackerContribSamplerAlgorithm();
|
||||
|
||||
/** @brief Create TrackerContribSamplerAlgorithm by tracker sampler type.
|
||||
@param trackerSamplerType The trackerSamplerType name
|
||||
|
||||
The modes available now:
|
||||
|
||||
- "CSC" -- Current State Center
|
||||
- "CS" -- Current State
|
||||
*/
|
||||
static Ptr<TrackerContribSamplerAlgorithm> create( const String& trackerSamplerType );
|
||||
|
||||
/** @brief Computes the regions starting from a position in an image.
|
||||
|
||||
Return true if samples are computed, false otherwise
|
||||
|
||||
@param image The current frame
|
||||
@param boundingBox The bounding box from which regions can be calculated
|
||||
|
||||
@param sample The computed samples @cite AAM Fig. 1 variable Sk
|
||||
*/
|
||||
virtual bool sampling(const Mat& image, const Rect& boundingBox, std::vector<Mat>& sample) CV_OVERRIDE;
|
||||
|
||||
/** @brief Get the name of the specific TrackerContribSamplerAlgorithm
|
||||
*/
|
||||
String getClassName() const;
|
||||
|
||||
protected:
|
||||
String className;
|
||||
|
||||
virtual bool samplingImpl( const Mat& image, Rect boundingBox, std::vector<Mat>& sample ) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Class that manages the sampler in order to select regions for the update the model of the tracker
|
||||
* [AAM] Sampling e Labeling. See table I and section III B
|
||||
*/
|
||||
|
||||
/** @brief Class that manages the sampler in order to select regions for the update the model of the tracker
|
||||
|
||||
@cite AAM Sampling e Labeling. See table I and section III B
|
||||
|
||||
TrackerContribSampler is an aggregation of TrackerContribSamplerAlgorithm
|
||||
@sa
|
||||
TrackerContribSamplerAlgorithm
|
||||
*/
|
||||
class CV_EXPORTS TrackerContribSampler
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* \brief Constructor
|
||||
*/
|
||||
TrackerContribSampler();
|
||||
|
||||
/**
|
||||
* \brief Destructor
|
||||
*/
|
||||
~TrackerContribSampler();
|
||||
|
||||
/** @brief Computes the regions starting from a position in an image
|
||||
@param image The current frame
|
||||
@param boundingBox The bounding box from which regions can be calculated
|
||||
*/
|
||||
void sampling( const Mat& image, Rect boundingBox );
|
||||
|
||||
/** @brief Return the collection of the TrackerContribSamplerAlgorithm
|
||||
*/
|
||||
const std::vector<std::pair<String, Ptr<TrackerContribSamplerAlgorithm> > >& getSamplers() const;
|
||||
|
||||
/** @brief Return the samples from all TrackerContribSamplerAlgorithm, @cite AAM Fig. 1 variable Sk
|
||||
*/
|
||||
const std::vector<Mat>& getSamples() const;
|
||||
|
||||
/** @brief Add TrackerContribSamplerAlgorithm in the collection. Return true if sampler is added, false otherwise
|
||||
@param trackerSamplerAlgorithmType The TrackerContribSamplerAlgorithm name
|
||||
|
||||
The modes available now:
|
||||
- "CSC" -- Current State Center
|
||||
- "CS" -- Current State
|
||||
- "PF" -- Particle Filtering
|
||||
|
||||
Example TrackerContribSamplerAlgorithm::addTrackerContribSamplerAlgorithm : :
|
||||
@code
|
||||
TrackerContribSamplerCSC::Params CSCparameters;
|
||||
Ptr<TrackerContribSamplerAlgorithm> CSCSampler = new TrackerContribSamplerCSC( CSCparameters );
|
||||
|
||||
if( !sampler->addTrackerSamplerAlgorithm( CSCSampler ) )
|
||||
return false;
|
||||
|
||||
//or add CSC sampler with default parameters
|
||||
//sampler->addTrackerSamplerAlgorithm( "CSC" );
|
||||
@endcode
|
||||
@note If you use the second method, you must initialize the TrackerContribSamplerAlgorithm
|
||||
*/
|
||||
bool addTrackerSamplerAlgorithm( String trackerSamplerAlgorithmType );
|
||||
|
||||
/** @overload
|
||||
@param sampler The TrackerContribSamplerAlgorithm
|
||||
*/
|
||||
bool addTrackerSamplerAlgorithm( Ptr<TrackerContribSamplerAlgorithm>& sampler );
|
||||
|
||||
private:
|
||||
std::vector<std::pair<String, Ptr<TrackerContribSamplerAlgorithm> > > samplers;
|
||||
std::vector<Mat> samples;
|
||||
bool blockAddTrackerSampler;
|
||||
|
||||
void clearSamples();
|
||||
};
|
||||
|
||||
|
||||
/** @brief TrackerStateEstimatorAdaBoosting based on ADA-Boosting
|
||||
*/
|
||||
class CV_EXPORTS TrackerStateEstimatorAdaBoosting : public TrackerStateEstimator
|
||||
{
|
||||
public:
|
||||
/** @brief Implementation of the target state for TrackerAdaBoostingTargetState
|
||||
*/
|
||||
class CV_EXPORTS TrackerAdaBoostingTargetState : public TrackerTargetState
|
||||
{
|
||||
|
||||
public:
|
||||
/**
|
||||
* \brief Constructor
|
||||
* \param position Top left corner of the bounding box
|
||||
* \param width Width of the bounding box
|
||||
* \param height Height of the bounding box
|
||||
* \param foreground label for target or background
|
||||
* \param responses list of features
|
||||
*/
|
||||
TrackerAdaBoostingTargetState( const Point2f& position, int width, int height, bool foreground, const Mat& responses );
|
||||
|
||||
/**
|
||||
* \brief Destructor
|
||||
*/
|
||||
~TrackerAdaBoostingTargetState()
|
||||
{
|
||||
}
|
||||
;
|
||||
|
||||
/** @brief Set the features extracted from TrackerContribFeatureSet
|
||||
@param responses The features extracted
|
||||
*/
|
||||
void setTargetResponses( const Mat& responses );
|
||||
/** @brief Set label: true for target foreground, false for background
|
||||
@param foreground Label for background/foreground
|
||||
*/
|
||||
void setTargetFg( bool foreground );
|
||||
/** @brief Get the features extracted
|
||||
*/
|
||||
Mat getTargetResponses() const;
|
||||
/** @brief Get the label. Return true for target foreground, false for background
|
||||
*/
|
||||
bool isTargetFg() const;
|
||||
|
||||
private:
|
||||
bool isTarget;
|
||||
Mat targetResponses;
|
||||
|
||||
};
|
||||
|
||||
/** @brief Constructor
|
||||
@param numClassifer Number of base classifiers
|
||||
@param initIterations Number of iterations in the initialization
|
||||
@param nFeatures Number of features/weak classifiers
|
||||
@param patchSize tracking rect
|
||||
@param ROI initial ROI
|
||||
*/
|
||||
TrackerStateEstimatorAdaBoosting( int numClassifer, int initIterations, int nFeatures, Size patchSize, const Rect& ROI );
|
||||
|
||||
/**
|
||||
* \brief Destructor
|
||||
*/
|
||||
~TrackerStateEstimatorAdaBoosting();
|
||||
|
||||
/** @brief Get the sampling ROI
|
||||
*/
|
||||
Rect getSampleROI() const;
|
||||
|
||||
/** @brief Set the sampling ROI
|
||||
@param ROI the sampling ROI
|
||||
*/
|
||||
void setSampleROI( const Rect& ROI );
|
||||
|
||||
/** @brief Set the current confidenceMap
|
||||
@param confidenceMap The current :cConfidenceMap
|
||||
*/
|
||||
void setCurrentConfidenceMap( ConfidenceMap& confidenceMap );
|
||||
|
||||
/** @brief Get the list of the selected weak classifiers for the classification step
|
||||
*/
|
||||
std::vector<int> computeSelectedWeakClassifier();
|
||||
|
||||
/** @brief Get the list of the weak classifiers that should be replaced
|
||||
*/
|
||||
std::vector<int> computeReplacedClassifier();
|
||||
|
||||
/** @brief Get the list of the weak classifiers that replace those to be replaced
|
||||
*/
|
||||
std::vector<int> computeSwappedClassifier();
|
||||
|
||||
protected:
|
||||
Ptr<TrackerTargetState> estimateImpl( const std::vector<ConfidenceMap>& confidenceMaps ) CV_OVERRIDE;
|
||||
void updateImpl( std::vector<ConfidenceMap>& confidenceMaps ) CV_OVERRIDE;
|
||||
|
||||
Ptr<StrongClassifierDirectSelection> boostClassifier;
|
||||
|
||||
private:
|
||||
int numBaseClassifier;
|
||||
int iterationInit;
|
||||
int numFeatures;
|
||||
bool trained;
|
||||
Size initPatchSize;
|
||||
Rect sampleROI;
|
||||
std::vector<int> replacedClassifier;
|
||||
std::vector<int> swappedClassifier;
|
||||
|
||||
ConfidenceMap currentConfidenceMap;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief TrackerStateEstimator based on SVM
|
||||
*/
|
||||
class CV_EXPORTS TrackerStateEstimatorSVM : public TrackerStateEstimator
|
||||
{
|
||||
public:
|
||||
TrackerStateEstimatorSVM();
|
||||
~TrackerStateEstimatorSVM();
|
||||
|
||||
protected:
|
||||
Ptr<TrackerTargetState> estimateImpl( const std::vector<ConfidenceMap>& confidenceMaps ) CV_OVERRIDE;
|
||||
void updateImpl( std::vector<ConfidenceMap>& confidenceMaps ) CV_OVERRIDE;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/************************************ Specific TrackerSamplerAlgorithm Classes ************************************/
|
||||
|
||||
/** @brief TrackerSampler based on CSC (current state centered), used by MIL algorithm TrackerMIL
|
||||
*/
|
||||
class CV_EXPORTS TrackerContribSamplerCSC : public TrackerContribSamplerAlgorithm
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
MODE_INIT_POS = 1, //!< mode for init positive samples
|
||||
MODE_INIT_NEG = 2, //!< mode for init negative samples
|
||||
MODE_TRACK_POS = 3, //!< mode for update positive samples
|
||||
MODE_TRACK_NEG = 4, //!< mode for update negative samples
|
||||
MODE_DETECT = 5 //!< mode for detect samples
|
||||
};
|
||||
|
||||
struct CV_EXPORTS Params
|
||||
{
|
||||
Params();
|
||||
float initInRad; //!< radius for gathering positive instances during init
|
||||
float trackInPosRad; //!< radius for gathering positive instances during tracking
|
||||
float searchWinSize; //!< size of search window
|
||||
int initMaxNegNum; //!< # negative samples to use during init
|
||||
int trackMaxPosNum; //!< # positive samples to use during training
|
||||
int trackMaxNegNum; //!< # negative samples to use during training
|
||||
};
|
||||
|
||||
/** @brief Constructor
|
||||
@param parameters TrackerContribSamplerCSC parameters TrackerContribSamplerCSC::Params
|
||||
*/
|
||||
TrackerContribSamplerCSC( const TrackerContribSamplerCSC::Params ¶meters = TrackerContribSamplerCSC::Params() );
|
||||
|
||||
/** @brief Set the sampling mode of TrackerContribSamplerCSC
|
||||
@param samplingMode The sampling mode
|
||||
|
||||
The modes are:
|
||||
|
||||
- "MODE_INIT_POS = 1" -- for the positive sampling in initialization step
|
||||
- "MODE_INIT_NEG = 2" -- for the negative sampling in initialization step
|
||||
- "MODE_TRACK_POS = 3" -- for the positive sampling in update step
|
||||
- "MODE_TRACK_NEG = 4" -- for the negative sampling in update step
|
||||
- "MODE_DETECT = 5" -- for the sampling in detection step
|
||||
*/
|
||||
void setMode( int samplingMode );
|
||||
|
||||
~TrackerContribSamplerCSC();
|
||||
|
||||
protected:
|
||||
|
||||
bool samplingImpl(const Mat& image, Rect boundingBox, std::vector<Mat>& sample) CV_OVERRIDE;
|
||||
|
||||
private:
|
||||
|
||||
Params params;
|
||||
int mode;
|
||||
RNG rng;
|
||||
|
||||
std::vector<Mat> sampleImage( const Mat& img, int x, int y, int w, int h, float inrad, float outrad = 0, int maxnum = 1000000 );
|
||||
};
|
||||
|
||||
|
||||
/** @brief TrackerContribSampler based on CS (current state), used by algorithm TrackerBoosting
|
||||
*/
|
||||
class CV_EXPORTS TrackerSamplerCS : public TrackerContribSamplerAlgorithm
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
MODE_POSITIVE = 1, //!< mode for positive samples
|
||||
MODE_NEGATIVE = 2, //!< mode for negative samples
|
||||
MODE_CLASSIFY = 3 //!< mode for classify samples
|
||||
};
|
||||
|
||||
struct CV_EXPORTS Params
|
||||
{
|
||||
Params();
|
||||
float overlap; //!<overlapping for the search windows
|
||||
float searchFactor; //!<search region parameter
|
||||
};
|
||||
/** @brief Constructor
|
||||
@param parameters TrackerSamplerCS parameters TrackerSamplerCS::Params
|
||||
*/
|
||||
TrackerSamplerCS( const TrackerSamplerCS::Params ¶meters = TrackerSamplerCS::Params() );
|
||||
|
||||
/** @brief Set the sampling mode of TrackerSamplerCS
|
||||
@param samplingMode The sampling mode
|
||||
|
||||
The modes are:
|
||||
|
||||
- "MODE_POSITIVE = 1" -- for the positive sampling
|
||||
- "MODE_NEGATIVE = 2" -- for the negative sampling
|
||||
- "MODE_CLASSIFY = 3" -- for the sampling in classification step
|
||||
*/
|
||||
void setMode( int samplingMode );
|
||||
|
||||
~TrackerSamplerCS();
|
||||
|
||||
bool samplingImpl( const Mat& image, Rect boundingBox, std::vector<Mat>& sample ) CV_OVERRIDE;
|
||||
Rect getROI() const;
|
||||
private:
|
||||
Rect getTrackingROI( float searchFactor );
|
||||
Rect RectMultiply( const Rect & rect, float f );
|
||||
std::vector<Mat> patchesRegularScan( const Mat& image, Rect trackingROI, Size patchSize );
|
||||
void setCheckedROI( Rect imageROI );
|
||||
|
||||
Params params;
|
||||
int mode;
|
||||
Rect trackedPatch;
|
||||
Rect validROI;
|
||||
Rect ROI;
|
||||
|
||||
};
|
||||
|
||||
/** @brief This sampler is based on particle filtering.
|
||||
|
||||
In principle, it can be thought of as performing some sort of optimization (and indeed, this
|
||||
tracker uses opencv's optim module), where tracker seeks to find the rectangle in given frame,
|
||||
which is the most *"similar"* to the initial rectangle (the one, given through the constructor).
|
||||
|
||||
The optimization performed is stochastic and somehow resembles genetic algorithms, where on each new
|
||||
image received (submitted via TrackerSamplerPF::sampling()) we start with the region bounded by
|
||||
boundingBox, then generate several "perturbed" boxes, take the ones most similar to the original.
|
||||
This selection round is repeated several times. At the end, we hope that only the most promising box
|
||||
remaining, and these are combined to produce the subrectangle of image, which is put as a sole
|
||||
element in array sample.
|
||||
|
||||
It should be noted, that the definition of "similarity" between two rectangles is based on comparing
|
||||
their histograms. As experiments show, tracker is *not* very succesfull if target is assumed to
|
||||
strongly change its dimensions.
|
||||
*/
|
||||
class CV_EXPORTS TrackerSamplerPF : public TrackerContribSamplerAlgorithm
|
||||
{
|
||||
public:
|
||||
/** @brief This structure contains all the parameters that can be varied during the course of sampling
|
||||
algorithm. Below is the structure exposed, together with its members briefly explained with
|
||||
reference to the above discussion on algorithm's working.
|
||||
*/
|
||||
struct CV_EXPORTS Params
|
||||
{
|
||||
Params();
|
||||
int iterationNum; //!< number of selection rounds
|
||||
int particlesNum; //!< number of "perturbed" boxes on each round
|
||||
double alpha; //!< with each new round we exponentially decrease the amount of "perturbing" we allow (like in simulated annealing)
|
||||
//!< and this very alpha controls how fast annealing happens, ie. how fast perturbing decreases
|
||||
Mat_<double> std; //!< initial values for perturbing (1-by-4 array, as each rectangle is given by 4 values -- coordinates of opposite vertices,
|
||||
//!< hence we have 4 values to perturb)
|
||||
};
|
||||
/** @brief Constructor
|
||||
@param chosenRect Initial rectangle, that is supposed to contain target we'd like to track.
|
||||
@param parameters
|
||||
*/
|
||||
TrackerSamplerPF(const Mat& chosenRect,const TrackerSamplerPF::Params ¶meters = TrackerSamplerPF::Params());
|
||||
protected:
|
||||
bool samplingImpl( const Mat& image, Rect boundingBox, std::vector<Mat>& sample ) CV_OVERRIDE;
|
||||
private:
|
||||
Params params;
|
||||
Ptr<MinProblemSolver> _solver;
|
||||
Ptr<MinProblemSolver::Function> _function;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/************************************ Specific TrackerContribFeature Classes ************************************/
|
||||
|
||||
/**
|
||||
* \brief TrackerContribFeature based on Feature2D
|
||||
*/
|
||||
class CV_EXPORTS TrackerFeatureFeature2d : public TrackerContribFeature
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* \brief Constructor
|
||||
* \param detectorType string of FeatureDetector
|
||||
* \param descriptorType string of DescriptorExtractor
|
||||
*/
|
||||
TrackerFeatureFeature2d( String detectorType, String descriptorType );
|
||||
|
||||
~TrackerFeatureFeature2d() CV_OVERRIDE;
|
||||
|
||||
void selection( Mat& response, int npoints ) CV_OVERRIDE;
|
||||
|
||||
protected:
|
||||
|
||||
bool computeImpl( const std::vector<Mat>& images, Mat& response ) CV_OVERRIDE;
|
||||
|
||||
private:
|
||||
|
||||
std::vector<KeyPoint> keypoints;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief TrackerContribFeature based on HOG
|
||||
*/
|
||||
class CV_EXPORTS TrackerFeatureHOG : public TrackerContribFeature
|
||||
{
|
||||
public:
|
||||
|
||||
TrackerFeatureHOG();
|
||||
|
||||
~TrackerFeatureHOG() CV_OVERRIDE;
|
||||
|
||||
void selection( Mat& response, int npoints ) CV_OVERRIDE;
|
||||
|
||||
protected:
|
||||
|
||||
bool computeImpl( const std::vector<Mat>& images, Mat& response ) CV_OVERRIDE;
|
||||
|
||||
};
|
||||
|
||||
/** @brief TrackerContribFeature based on HAAR features, used by TrackerMIL and many others algorithms
|
||||
@note HAAR features implementation is copied from apps/traincascade and modified according to MIL
|
||||
*/
|
||||
class CV_EXPORTS TrackerContribFeatureHAAR : public TrackerContribFeature
|
||||
{
|
||||
public:
|
||||
struct CV_EXPORTS Params
|
||||
{
|
||||
Params();
|
||||
int numFeatures; //!< # of rects
|
||||
Size rectSize; //!< rect size
|
||||
bool isIntegral; //!< true if input images are integral, false otherwise
|
||||
};
|
||||
|
||||
/** @brief Constructor
|
||||
@param parameters TrackerContribFeatureHAAR parameters TrackerContribFeatureHAAR::Params
|
||||
*/
|
||||
TrackerContribFeatureHAAR( const TrackerContribFeatureHAAR::Params ¶meters = TrackerContribFeatureHAAR::Params() );
|
||||
|
||||
~TrackerContribFeatureHAAR() CV_OVERRIDE;
|
||||
|
||||
/** @brief Compute the features only for the selected indices in the images collection
|
||||
@param selFeatures indices of selected features
|
||||
@param images The images
|
||||
@param response Collection of response for the specific TrackerContribFeature
|
||||
*/
|
||||
bool extractSelected( const std::vector<int> selFeatures, const std::vector<Mat>& images, Mat& response );
|
||||
|
||||
/** @brief Identify most effective features
|
||||
@param response Collection of response for the specific TrackerContribFeature
|
||||
@param npoints Max number of features
|
||||
|
||||
@note This method modifies the response parameter
|
||||
*/
|
||||
void selection( Mat& response, int npoints ) CV_OVERRIDE;
|
||||
|
||||
/** @brief Swap the feature in position source with the feature in position target
|
||||
@param source The source position
|
||||
@param target The target position
|
||||
*/
|
||||
bool swapFeature( int source, int target );
|
||||
|
||||
/** @brief Swap the feature in position id with the feature input
|
||||
@param id The position
|
||||
@param feature The feature
|
||||
*/
|
||||
bool swapFeature( int id, CvHaarEvaluator::FeatureHaar& feature );
|
||||
|
||||
/** @brief Get the feature in position id
|
||||
@param id The position
|
||||
*/
|
||||
CvHaarEvaluator::FeatureHaar& getFeatureAt( int id );
|
||||
|
||||
protected:
|
||||
bool computeImpl( const std::vector<Mat>& images, Mat& response ) CV_OVERRIDE;
|
||||
|
||||
private:
|
||||
|
||||
Params params;
|
||||
Ptr<CvHaarEvaluator> featureEvaluator;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief TrackerContribFeature based on LBP
|
||||
*/
|
||||
class CV_EXPORTS TrackerFeatureLBP : public TrackerContribFeature
|
||||
{
|
||||
public:
|
||||
|
||||
TrackerFeatureLBP();
|
||||
|
||||
~TrackerFeatureLBP();
|
||||
|
||||
void selection( Mat& response, int npoints ) CV_OVERRIDE;
|
||||
|
||||
protected:
|
||||
|
||||
bool computeImpl( const std::vector<Mat>& images, Mat& response ) CV_OVERRIDE;
|
||||
|
||||
};
|
||||
|
||||
//! @}
|
||||
|
||||
}}} // namespace
|
||||
|
||||
#endif // OPENCV_TRACKING_DETAIL_HPP
|
||||
@@ -0,0 +1,499 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef OPENCV_TRACKING_LEGACY_HPP
|
||||
#define OPENCV_TRACKING_LEGACY_HPP
|
||||
|
||||
/*
|
||||
* Partially based on:
|
||||
* ====================================================================================================================
|
||||
* - [AAM] S. Salti, A. Cavallaro, L. Di Stefano, Adaptive Appearance Modeling for Video Tracking: Survey and Evaluation
|
||||
* - [AMVOT] X. Li, W. Hu, C. Shen, Z. Zhang, A. Dick, A. van den Hengel, A Survey of Appearance Models in Visual Object Tracking
|
||||
*
|
||||
* This Tracking API has been designed with PlantUML. If you modify this API please change UML files under modules/tracking/doc/uml
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tracking_internals.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace legacy {
|
||||
#ifndef CV_DOXYGEN
|
||||
inline namespace tracking {
|
||||
#endif
|
||||
using namespace cv::detail::tracking;
|
||||
|
||||
/** @addtogroup tracking_legacy
|
||||
@{
|
||||
*/
|
||||
|
||||
/************************************ Tracker Base Class ************************************/
|
||||
|
||||
/** @brief Base abstract class for the long-term tracker:
|
||||
*/
|
||||
class CV_EXPORTS_W Tracker : public virtual Algorithm
|
||||
{
|
||||
public:
|
||||
Tracker();
|
||||
virtual ~Tracker() CV_OVERRIDE;
|
||||
|
||||
/** @brief Initialize the tracker with a known bounding box that surrounded the target
|
||||
@param image The initial frame
|
||||
@param boundingBox The initial bounding box
|
||||
|
||||
@return True if initialization went succesfully, false otherwise
|
||||
*/
|
||||
CV_WRAP bool init( InputArray image, const Rect2d& boundingBox );
|
||||
|
||||
/** @brief Update the tracker, find the new most likely bounding box for the target
|
||||
@param image The current frame
|
||||
@param boundingBox The bounding box that represent the new target location, if true was returned, not
|
||||
modified otherwise
|
||||
|
||||
@return True means that target was located and false means that tracker cannot locate target in
|
||||
current frame. Note, that latter *does not* imply that tracker has failed, maybe target is indeed
|
||||
missing from the frame (say, out of sight)
|
||||
*/
|
||||
CV_WRAP bool update( InputArray image, CV_OUT Rect2d& boundingBox );
|
||||
|
||||
virtual void read( const FileNode& fn ) CV_OVERRIDE = 0;
|
||||
virtual void write( FileStorage& fs ) const CV_OVERRIDE = 0;
|
||||
|
||||
protected:
|
||||
|
||||
virtual bool initImpl( const Mat& image, const Rect2d& boundingBox ) = 0;
|
||||
virtual bool updateImpl( const Mat& image, Rect2d& boundingBox ) = 0;
|
||||
|
||||
bool isInit;
|
||||
|
||||
Ptr<TrackerContribFeatureSet> featureSet;
|
||||
Ptr<TrackerContribSampler> sampler;
|
||||
Ptr<TrackerModel> model;
|
||||
};
|
||||
|
||||
|
||||
/************************************ Specific Tracker Classes ************************************/
|
||||
|
||||
/** @brief The MIL algorithm trains a classifier in an online manner to separate the object from the
|
||||
background.
|
||||
|
||||
Multiple Instance Learning avoids the drift problem for a robust tracking. The implementation is
|
||||
based on @cite MIL .
|
||||
|
||||
Original code can be found here <http://vision.ucsd.edu/~bbabenko/project_miltrack.shtml>
|
||||
*/
|
||||
class CV_EXPORTS_W TrackerMIL : public cv::legacy::Tracker
|
||||
{
|
||||
public:
|
||||
struct CV_EXPORTS Params : cv::TrackerMIL::Params
|
||||
{
|
||||
void read( const FileNode& fn );
|
||||
void write( FileStorage& fs ) const;
|
||||
};
|
||||
|
||||
/** @brief Constructor
|
||||
@param parameters MIL parameters TrackerMIL::Params
|
||||
*/
|
||||
static Ptr<legacy::TrackerMIL> create(const TrackerMIL::Params ¶meters);
|
||||
|
||||
CV_WRAP static Ptr<legacy::TrackerMIL> create();
|
||||
|
||||
virtual ~TrackerMIL() CV_OVERRIDE {}
|
||||
};
|
||||
|
||||
/** @brief the Boosting tracker
|
||||
|
||||
This is a real-time object tracking based on a novel on-line version of the AdaBoost algorithm.
|
||||
The classifier uses the surrounding background as negative examples in update step to avoid the
|
||||
drifting problem. The implementation is based on @cite OLB .
|
||||
*/
|
||||
class CV_EXPORTS_W TrackerBoosting : public cv::legacy::Tracker
|
||||
{
|
||||
public:
|
||||
struct CV_EXPORTS Params
|
||||
{
|
||||
Params();
|
||||
int numClassifiers; //!<the number of classifiers to use in a OnlineBoosting algorithm
|
||||
float samplerOverlap; //!<search region parameters to use in a OnlineBoosting algorithm
|
||||
float samplerSearchFactor; //!< search region parameters to use in a OnlineBoosting algorithm
|
||||
int iterationInit; //!<the initial iterations
|
||||
int featureSetNumFeatures; //!< # features
|
||||
/**
|
||||
* \brief Read parameters from a file
|
||||
*/
|
||||
void read( const FileNode& fn );
|
||||
|
||||
/**
|
||||
* \brief Write parameters to a file
|
||||
*/
|
||||
void write( FileStorage& fs ) const;
|
||||
};
|
||||
|
||||
/** @brief Constructor
|
||||
@param parameters BOOSTING parameters TrackerBoosting::Params
|
||||
*/
|
||||
static Ptr<legacy::TrackerBoosting> create(const TrackerBoosting::Params ¶meters);
|
||||
|
||||
CV_WRAP static Ptr<legacy::TrackerBoosting> create();
|
||||
|
||||
virtual ~TrackerBoosting() CV_OVERRIDE {}
|
||||
};
|
||||
|
||||
/** @brief the Median Flow tracker
|
||||
|
||||
Implementation of a paper @cite MedianFlow .
|
||||
|
||||
The tracker is suitable for very smooth and predictable movements when object is visible throughout
|
||||
the whole sequence. It's quite and accurate for this type of problems (in particular, it was shown
|
||||
by authors to outperform MIL). During the implementation period the code at
|
||||
<http://www.aonsquared.co.uk/node/5>, the courtesy of the author Arthur Amarra, was used for the
|
||||
reference purpose.
|
||||
*/
|
||||
class CV_EXPORTS_W TrackerMedianFlow : public cv::legacy::Tracker
|
||||
{
|
||||
public:
|
||||
struct CV_EXPORTS Params
|
||||
{
|
||||
Params(); //!<default constructor
|
||||
//!<note that the default values of parameters are recommended for most of use cases
|
||||
int pointsInGrid; //!<square root of number of keypoints used; increase it to trade
|
||||
//!<accurateness for speed
|
||||
cv::Size winSize; //!<window size parameter for Lucas-Kanade optical flow
|
||||
int maxLevel; //!<maximal pyramid level number for Lucas-Kanade optical flow
|
||||
TermCriteria termCriteria; //!<termination criteria for Lucas-Kanade optical flow
|
||||
cv::Size winSizeNCC; //!<window size around a point for normalized cross-correlation check
|
||||
double maxMedianLengthOfDisplacementDifference; //!<criterion for loosing the tracked object
|
||||
|
||||
void read( const FileNode& /*fn*/ );
|
||||
void write( FileStorage& /*fs*/ ) const;
|
||||
};
|
||||
|
||||
/** @brief Constructor
|
||||
@param parameters Median Flow parameters TrackerMedianFlow::Params
|
||||
*/
|
||||
static Ptr<legacy::TrackerMedianFlow> create(const TrackerMedianFlow::Params ¶meters);
|
||||
|
||||
CV_WRAP static Ptr<legacy::TrackerMedianFlow> create();
|
||||
|
||||
virtual ~TrackerMedianFlow() CV_OVERRIDE {}
|
||||
};
|
||||
|
||||
/** @brief the TLD (Tracking, learning and detection) tracker
|
||||
|
||||
TLD is a novel tracking framework that explicitly decomposes the long-term tracking task into
|
||||
tracking, learning and detection.
|
||||
|
||||
The tracker follows the object from frame to frame. The detector localizes all appearances that
|
||||
have been observed so far and corrects the tracker if necessary. The learning estimates detector's
|
||||
errors and updates it to avoid these errors in the future. The implementation is based on @cite TLD .
|
||||
|
||||
The Median Flow algorithm (see cv::TrackerMedianFlow) was chosen as a tracking component in this
|
||||
implementation, following authors. The tracker is supposed to be able to handle rapid motions, partial
|
||||
occlusions, object absence etc.
|
||||
*/
|
||||
class CV_EXPORTS_W TrackerTLD : public cv::legacy::Tracker
|
||||
{
|
||||
public:
|
||||
struct CV_EXPORTS Params
|
||||
{
|
||||
Params();
|
||||
void read( const FileNode& /*fn*/ );
|
||||
void write( FileStorage& /*fs*/ ) const;
|
||||
};
|
||||
|
||||
/** @brief Constructor
|
||||
@param parameters TLD parameters TrackerTLD::Params
|
||||
*/
|
||||
static Ptr<legacy::TrackerTLD> create(const TrackerTLD::Params ¶meters);
|
||||
|
||||
CV_WRAP static Ptr<legacy::TrackerTLD> create();
|
||||
|
||||
virtual ~TrackerTLD() CV_OVERRIDE {}
|
||||
};
|
||||
|
||||
/** @brief the KCF (Kernelized Correlation Filter) tracker
|
||||
|
||||
* KCF is a novel tracking framework that utilizes properties of circulant matrix to enhance the processing speed.
|
||||
* This tracking method is an implementation of @cite KCF_ECCV which is extended to KCF with color-names features (@cite KCF_CN).
|
||||
* The original paper of KCF is available at <http://www.robots.ox.ac.uk/~joao/publications/henriques_tpami2015.pdf>
|
||||
* as well as the matlab implementation. For more information about KCF with color-names features, please refer to
|
||||
* <http://www.cvl.isy.liu.se/research/objrec/visualtracking/colvistrack/index.html>.
|
||||
*/
|
||||
class CV_EXPORTS_W TrackerKCF : public cv::legacy::Tracker
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief Feature type to be used in the tracking grayscale, colornames, compressed color-names
|
||||
* The modes available now:
|
||||
- "GRAY" -- Use grayscale values as the feature
|
||||
- "CN" -- Color-names feature
|
||||
*/
|
||||
typedef enum cv::tracking::TrackerKCF::MODE MODE;
|
||||
|
||||
struct CV_EXPORTS Params : cv::tracking::TrackerKCF::Params
|
||||
{
|
||||
void read(const FileNode& /*fn*/);
|
||||
void write(FileStorage& /*fs*/) const;
|
||||
};
|
||||
|
||||
virtual void setFeatureExtractor(void(*)(const Mat, const Rect, Mat&), bool pca_func = false) = 0;
|
||||
|
||||
/** @brief Constructor
|
||||
@param parameters KCF parameters TrackerKCF::Params
|
||||
*/
|
||||
static Ptr<legacy::TrackerKCF> create(const TrackerKCF::Params ¶meters);
|
||||
|
||||
CV_WRAP static Ptr<legacy::TrackerKCF> create();
|
||||
|
||||
virtual ~TrackerKCF() CV_OVERRIDE {}
|
||||
};
|
||||
|
||||
/** @brief the MOSSE (Minimum Output Sum of Squared %Error) tracker
|
||||
|
||||
The implementation is based on @cite MOSSE Visual Object Tracking using Adaptive Correlation Filters
|
||||
@note this tracker works with grayscale images, if passed bgr ones, they will get converted internally.
|
||||
*/
|
||||
|
||||
class CV_EXPORTS_W TrackerMOSSE : public cv::legacy::Tracker
|
||||
{
|
||||
public:
|
||||
/** @brief Constructor
|
||||
*/
|
||||
CV_WRAP static Ptr<legacy::TrackerMOSSE> create();
|
||||
|
||||
virtual ~TrackerMOSSE() CV_OVERRIDE {}
|
||||
};
|
||||
|
||||
|
||||
/************************************ MultiTracker Class ---By Laksono Kurnianggoro---) ************************************/
|
||||
/** @brief This class is used to track multiple objects using the specified tracker algorithm.
|
||||
|
||||
* The %MultiTracker is naive implementation of multiple object tracking.
|
||||
* It process the tracked objects independently without any optimization accross the tracked objects.
|
||||
*/
|
||||
class CV_EXPORTS_W MultiTracker : public Algorithm
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* \brief Constructor.
|
||||
*/
|
||||
CV_WRAP MultiTracker();
|
||||
|
||||
/**
|
||||
* \brief Destructor
|
||||
*/
|
||||
~MultiTracker() CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* \brief Add a new object to be tracked.
|
||||
*
|
||||
* @param newTracker tracking algorithm to be used
|
||||
* @param image input image
|
||||
* @param boundingBox a rectangle represents ROI of the tracked object
|
||||
*/
|
||||
CV_WRAP bool add(Ptr<cv::legacy::Tracker> newTracker, InputArray image, const Rect2d& boundingBox);
|
||||
|
||||
/**
|
||||
* \brief Add a set of objects to be tracked.
|
||||
* @param newTrackers list of tracking algorithms to be used
|
||||
* @param image input image
|
||||
* @param boundingBox list of the tracked objects
|
||||
*/
|
||||
bool add(std::vector<Ptr<legacy::Tracker> > newTrackers, InputArray image, std::vector<Rect2d> boundingBox);
|
||||
|
||||
/**
|
||||
* \brief Update the current tracking status.
|
||||
* The result will be saved in the internal storage.
|
||||
* @param image input image
|
||||
*/
|
||||
bool update(InputArray image);
|
||||
|
||||
/**
|
||||
* \brief Update the current tracking status.
|
||||
* @param image input image
|
||||
* @param boundingBox the tracking result, represent a list of ROIs of the tracked objects.
|
||||
*/
|
||||
CV_WRAP bool update(InputArray image, CV_OUT std::vector<Rect2d> & boundingBox);
|
||||
|
||||
/**
|
||||
* \brief Returns a reference to a storage for the tracked objects, each object corresponds to one tracker algorithm
|
||||
*/
|
||||
CV_WRAP const std::vector<Rect2d>& getObjects() const;
|
||||
|
||||
/**
|
||||
* \brief Returns a pointer to a new instance of MultiTracker
|
||||
*/
|
||||
CV_WRAP static Ptr<legacy::MultiTracker> create();
|
||||
|
||||
protected:
|
||||
//!< storage for the tracker algorithms.
|
||||
std::vector< Ptr<Tracker> > trackerList;
|
||||
|
||||
//!< storage for the tracked objects, each object corresponds to one tracker algorithm.
|
||||
std::vector<Rect2d> objects;
|
||||
};
|
||||
|
||||
/************************************ Multi-Tracker Classes ---By Tyan Vladimir---************************************/
|
||||
|
||||
/** @brief Base abstract class for the long-term Multi Object Trackers:
|
||||
|
||||
@sa Tracker, MultiTrackerTLD
|
||||
*/
|
||||
class CV_EXPORTS MultiTracker_Alt
|
||||
{
|
||||
public:
|
||||
/** @brief Constructor for Multitracker
|
||||
*/
|
||||
MultiTracker_Alt()
|
||||
{
|
||||
targetNum = 0;
|
||||
}
|
||||
|
||||
/** @brief Add a new target to a tracking-list and initialize the tracker with a known bounding box that surrounded the target
|
||||
@param image The initial frame
|
||||
@param boundingBox The initial bounding box of target
|
||||
@param tracker_algorithm Multi-tracker algorithm
|
||||
|
||||
@return True if new target initialization went succesfully, false otherwise
|
||||
*/
|
||||
bool addTarget(InputArray image, const Rect2d& boundingBox, Ptr<legacy::Tracker> tracker_algorithm);
|
||||
|
||||
/** @brief Update all trackers from the tracking-list, find a new most likely bounding boxes for the targets
|
||||
@param image The current frame
|
||||
|
||||
@return True means that all targets were located and false means that tracker couldn't locate one of the targets in
|
||||
current frame. Note, that latter *does not* imply that tracker has failed, maybe target is indeed
|
||||
missing from the frame (say, out of sight)
|
||||
*/
|
||||
bool update(InputArray image);
|
||||
|
||||
/** @brief Current number of targets in tracking-list
|
||||
*/
|
||||
int targetNum;
|
||||
|
||||
/** @brief Trackers list for Multi-Object-Tracker
|
||||
*/
|
||||
std::vector <Ptr<Tracker> > trackers;
|
||||
|
||||
/** @brief Bounding Boxes list for Multi-Object-Tracker
|
||||
*/
|
||||
std::vector <Rect2d> boundingBoxes;
|
||||
/** @brief List of randomly generated colors for bounding boxes display
|
||||
*/
|
||||
std::vector<Scalar> colors;
|
||||
};
|
||||
|
||||
/** @brief Multi Object %Tracker for TLD.
|
||||
|
||||
TLD is a novel tracking framework that explicitly decomposes
|
||||
the long-term tracking task into tracking, learning and detection.
|
||||
|
||||
The tracker follows the object from frame to frame. The detector localizes all appearances that
|
||||
have been observed so far and corrects the tracker if necessary. The learning estimates detector's
|
||||
errors and updates it to avoid these errors in the future. The implementation is based on @cite TLD .
|
||||
|
||||
The Median Flow algorithm (see cv::TrackerMedianFlow) was chosen as a tracking component in this
|
||||
implementation, following authors. The tracker is supposed to be able to handle rapid motions, partial
|
||||
occlusions, object absence etc.
|
||||
|
||||
@sa Tracker, MultiTracker, TrackerTLD
|
||||
*/
|
||||
class CV_EXPORTS MultiTrackerTLD : public MultiTracker_Alt
|
||||
{
|
||||
public:
|
||||
/** @brief Update all trackers from the tracking-list, find a new most likely bounding boxes for the targets by
|
||||
optimized update method using some techniques to speedup calculations specifically for MO TLD. The only limitation
|
||||
is that all target bounding boxes should have approximately same aspect ratios. Speed boost is around 20%
|
||||
|
||||
@param image The current frame.
|
||||
|
||||
@return True means that all targets were located and false means that tracker couldn't locate one of the targets in
|
||||
current frame. Note, that latter *does not* imply that tracker has failed, maybe target is indeed
|
||||
missing from the frame (say, out of sight)
|
||||
*/
|
||||
bool update_opt(InputArray image);
|
||||
};
|
||||
|
||||
/*********************************** CSRT ************************************/
|
||||
/** @brief the CSRT tracker
|
||||
|
||||
The implementation is based on @cite Lukezic_IJCV2018 Discriminative Correlation Filter with Channel and Spatial Reliability
|
||||
*/
|
||||
class CV_EXPORTS_W TrackerCSRT : public cv::legacy::Tracker
|
||||
{
|
||||
public:
|
||||
struct CV_EXPORTS Params : cv::tracking::TrackerCSRT::Params
|
||||
{
|
||||
/**
|
||||
* \brief Read parameters from a file
|
||||
*/
|
||||
void read(const FileNode& /*fn*/);
|
||||
|
||||
/**
|
||||
* \brief Write parameters to a file
|
||||
*/
|
||||
void write(cv::FileStorage& fs) const;
|
||||
};
|
||||
|
||||
/** @brief Constructor
|
||||
@param parameters CSRT parameters TrackerCSRT::Params
|
||||
*/
|
||||
static Ptr<legacy::TrackerCSRT> create(const TrackerCSRT::Params ¶meters);
|
||||
|
||||
CV_WRAP static Ptr<legacy::TrackerCSRT> create();
|
||||
|
||||
CV_WRAP virtual void setInitialMask(InputArray mask) = 0;
|
||||
|
||||
virtual ~TrackerCSRT() CV_OVERRIDE {}
|
||||
};
|
||||
|
||||
|
||||
CV_EXPORTS_W Ptr<cv::Tracker> upgradeTrackingAPI(const Ptr<legacy::Tracker>& legacy_tracker);
|
||||
|
||||
//! @}
|
||||
|
||||
#ifndef CV_DOXYGEN
|
||||
} // namespace
|
||||
#endif
|
||||
}} // namespace
|
||||
|
||||
#endif // OPENCV_TRACKING_LEGACY_HPP
|
||||
@@ -0,0 +1,54 @@
|
||||
#ifndef OPENCV_TWIST_HPP
|
||||
#define OPENCV_TWIST_HPP
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
inline namespace tracking
|
||||
{
|
||||
//! @addtogroup tracking_detail
|
||||
//! @{
|
||||
|
||||
/**
|
||||
* @brief Compute the camera twist from a set of 2D pixel locations, their
|
||||
* velocities, depth values and intrinsic parameters of the camera. The pixel
|
||||
* velocities are usually obtained from optical flow algorithms, both dense and
|
||||
* sparse flow can be used to compute the flow between images and \p duv computed by
|
||||
* dividing the flow by the time interval between the images.
|
||||
*
|
||||
* @param uv 2xN matrix of 2D pixel locations
|
||||
* @param duv 2Nx1 matrix of 2D pixel velocities
|
||||
* @param depths 1xN matrix of depth values
|
||||
* @param K 3x3 camera intrinsic matrix
|
||||
*
|
||||
* @return cv::Vec6d 6x1 camera twist
|
||||
*/
|
||||
CV_EXPORTS cv::Vec6d computeTwist(const cv::Mat& uv, const cv::Mat& duv, const cv::Mat& depths,
|
||||
const cv::Mat& K);
|
||||
|
||||
/**
|
||||
* @brief Compute the interaction matrix ( @cite Hutchinson1996ATO @cite chaumette:inria-00350283
|
||||
* @cite chaumette:inria-00350638 ) for a set of 2D pixels. This is usually
|
||||
* used in visual servoing applications to command a robot to move at desired pixel
|
||||
* locations/velocities. By inverting this matrix, one can estimate camera spatial
|
||||
* velocity i.e., the twist.
|
||||
*
|
||||
* @param uv 2xN matrix of 2D pixel locations
|
||||
* @param depths 1xN matrix of depth values
|
||||
* @param K 3x3 camera intrinsic matrix
|
||||
* @param J 2Nx6 interaction matrix
|
||||
*
|
||||
*/
|
||||
CV_EXPORTS void computeInteractionMatrix(const cv::Mat& uv, const cv::Mat& depths, const cv::Mat& K,
|
||||
cv::Mat& J);
|
||||
|
||||
//! @}
|
||||
|
||||
} // namespace tracking
|
||||
} // namespace detail
|
||||
} // namespace cv
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"namespaces_dict": {
|
||||
"cv.legacy": "legacy"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.opencv.test.tracking;
|
||||
|
||||
import org.opencv.core.Core;
|
||||
import org.opencv.core.CvException;
|
||||
import org.opencv.core.CvType;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.Rect2d;
|
||||
import org.opencv.test.OpenCVTestCase;
|
||||
|
||||
import org.opencv.tracking.Tracking;
|
||||
import org.opencv.tracking.legacy_Tracker;
|
||||
import org.opencv.tracking.legacy_TrackerTLD;
|
||||
import org.opencv.tracking.legacy_MultiTracker;
|
||||
|
||||
public class TrackerCreateLegacyTest extends OpenCVTestCase {
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
|
||||
public void testCreateLegacyTrackerTLD() {
|
||||
legacy_Tracker tracker = legacy_TrackerTLD.create();
|
||||
}
|
||||
|
||||
public void testCreateLegacyMultiTracker() {
|
||||
legacy_MultiTracker multiTracker = legacy_MultiTracker.create();
|
||||
assert(multiTracker != null);
|
||||
}
|
||||
|
||||
public void testAddLegacyMultiTracker() {
|
||||
legacy_MultiTracker multiTracker = legacy_MultiTracker.create();
|
||||
legacy_Tracker tracker = legacy_TrackerTLD.create();
|
||||
Mat image = new Mat(100, 100, CvType.CV_8UC3);
|
||||
Rect2d boundingBox = new Rect2d(10, 10, 50, 50);
|
||||
|
||||
boolean result = multiTracker.add(tracker, image, boundingBox);
|
||||
assert(result);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.opencv.test.tracking;
|
||||
|
||||
import org.opencv.core.Core;
|
||||
import org.opencv.core.CvException;
|
||||
import org.opencv.test.OpenCVTestCase;
|
||||
|
||||
import org.opencv.video.Tracker;
|
||||
import org.opencv.tracking.TrackerKCF;
|
||||
import org.opencv.video.TrackerMIL;
|
||||
|
||||
public class TrackerCreateTest extends OpenCVTestCase {
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
public void testCreateTrackerKCF() {
|
||||
Tracker tracker = TrackerKCF.create();
|
||||
}
|
||||
|
||||
public void testCreateTrackerMIL() {
|
||||
Tracker tracker = TrackerMIL.create();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"AdditionalImports" : {
|
||||
"*" : [ "\"tracking.hpp\"" ]
|
||||
},
|
||||
"namespace_ignore_list" : [
|
||||
"cv.legacy"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
#ifdef HAVE_OPENCV_TRACKING
|
||||
typedef TrackerCSRT::Params TrackerCSRT_Params;
|
||||
typedef TrackerKCF::Params TrackerKCF_Params;
|
||||
#endif
|
||||
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env python
|
||||
import os
|
||||
import numpy as np
|
||||
import cv2 as cv
|
||||
|
||||
from tests_common import NewOpenCVTests, unittest
|
||||
|
||||
class tracking_contrib_test(NewOpenCVTests):
|
||||
|
||||
def test_createTracker(self):
|
||||
|
||||
t = cv.TrackerMIL_create()
|
||||
t = cv.TrackerKCF_create()
|
||||
|
||||
def test_createLegacyTracker(self):
|
||||
|
||||
t = cv.legacy.TrackerBoosting_create()
|
||||
t = cv.legacy.TrackerMIL_create()
|
||||
t = cv.legacy.TrackerKCF_create()
|
||||
t = cv.legacy.TrackerMedianFlow_create()
|
||||
t = cv.legacy.TrackerMOSSE_create()
|
||||
t = cv.legacy.TrackerCSRT_create()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
NewOpenCVTests.bootstrap()
|
||||
@@ -0,0 +1,21 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
static
|
||||
void initTrackingTests()
|
||||
{
|
||||
const char* extraTestDataPath =
|
||||
#ifdef WINRT
|
||||
NULL;
|
||||
#else
|
||||
getenv("OPENCV_DNN_TEST_DATA_PATH");
|
||||
#endif
|
||||
if (extraTestDataPath)
|
||||
cvtest::addDataSearchPath(extraTestDataPath);
|
||||
|
||||
cvtest::addDataSearchSubDirectory(""); // override "cv" prefix below to access without "../dnn" hacks
|
||||
}
|
||||
|
||||
CV_PERF_TEST_MAIN(tracking, initTrackingTests())
|
||||
@@ -0,0 +1,14 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
#ifndef __OPENCV_TRACKING_PRECOMP_HPP__
|
||||
#define __OPENCV_TRACKING_PRECOMP_HPP__
|
||||
|
||||
#include "opencv2/ts.hpp"
|
||||
#include <opencv2/tracking.hpp>
|
||||
|
||||
namespace opencv_test {
|
||||
using namespace perf;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,102 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
#include <opencv2/tracking/tracking_legacy.hpp>
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
using namespace perf;
|
||||
|
||||
//using namespace cv::tracking;
|
||||
|
||||
typedef tuple<string, int, Rect> TrackingParams_t;
|
||||
|
||||
std::vector<TrackingParams_t> getTrackingParams()
|
||||
{
|
||||
std::vector<TrackingParams_t> params {
|
||||
TrackingParams_t("david/data/david.webm", 300, Rect(163,62,47,56)),
|
||||
TrackingParams_t("dudek/data/dudek.webm", 1, Rect(123,87,132,176)),
|
||||
TrackingParams_t("faceocc2/data/faceocc2.webm", 1, Rect(118,57,82,98))
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
class Tracking : public perf::TestBaseWithParam<TrackingParams_t>
|
||||
{
|
||||
public:
|
||||
template<typename ROI_t = Rect2d, typename Tracker>
|
||||
void runTrackingTest(const Ptr<Tracker>& tracker, const TrackingParams_t& params);
|
||||
};
|
||||
|
||||
template<typename ROI_t, typename Tracker>
|
||||
void Tracking::runTrackingTest(const Ptr<Tracker>& tracker, const TrackingParams_t& params)
|
||||
{
|
||||
const int N = 10;
|
||||
string video = get<0>(params);
|
||||
int startFrame = get<1>(params);
|
||||
//int endFrame = startFrame + N;
|
||||
Rect boundingBox = get<2>(params);
|
||||
|
||||
string videoPath = findDataFile(std::string("cv/tracking/") + video);
|
||||
|
||||
VideoCapture c;
|
||||
c.open(videoPath);
|
||||
ASSERT_TRUE(c.isOpened()) << videoPath;
|
||||
#if 0
|
||||
// c.set(CAP_PROP_POS_FRAMES, startFrame);
|
||||
#else
|
||||
if (startFrame)
|
||||
std::cout << "startFrame = " << startFrame << std::endl;
|
||||
for (int i = 0; i < startFrame; i++)
|
||||
{
|
||||
Mat dummy_frame;
|
||||
c >> dummy_frame;
|
||||
ASSERT_FALSE(dummy_frame.empty()) << i << ": " << videoPath;
|
||||
}
|
||||
#endif
|
||||
|
||||
// decode frames into memory (don't measure decoding performance)
|
||||
std::vector<Mat> frames;
|
||||
for (int i = 0; i < N; ++i)
|
||||
{
|
||||
Mat frame;
|
||||
c >> frame;
|
||||
ASSERT_FALSE(frame.empty()) << "i=" << i;
|
||||
frames.push_back(frame);
|
||||
}
|
||||
|
||||
std::cout << "frame size = " << frames[0].size() << std::endl;
|
||||
|
||||
PERF_SAMPLE_BEGIN();
|
||||
{
|
||||
tracker->init(frames[0], (ROI_t)boundingBox);
|
||||
for (int i = 1; i < N; ++i)
|
||||
{
|
||||
ROI_t rc;
|
||||
tracker->update(frames[i], rc);
|
||||
ASSERT_FALSE(rc.empty());
|
||||
}
|
||||
}
|
||||
PERF_SAMPLE_END();
|
||||
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
|
||||
//==================================================================================================
|
||||
|
||||
PERF_TEST_P(Tracking, Boosting, testing::ValuesIn(getTrackingParams()))
|
||||
{
|
||||
auto tracker = legacy::TrackerBoosting::create();
|
||||
runTrackingTest(tracker, GetParam());
|
||||
}
|
||||
|
||||
PERF_TEST_P(Tracking, TLD, testing::ValuesIn(getTrackingParams()))
|
||||
{
|
||||
auto tracker = legacy::TrackerTLD::create();
|
||||
runTrackingTest(tracker, GetParam());
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,357 @@
|
||||
#include "opencv2/core/utility.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/tracking.hpp"
|
||||
#include "opencv2/videoio.hpp"
|
||||
#include "opencv2/plot.hpp"
|
||||
#include "samples_utility.hpp"
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
// TODO: do normalization ala Kalal's assessment protocol for TLD
|
||||
|
||||
static const Scalar gtColor = Scalar(0, 255, 0);
|
||||
|
||||
static Scalar getNextColor()
|
||||
{
|
||||
const int num = 6;
|
||||
static Scalar colors[num] = {Scalar(160, 0, 0), Scalar(0, 0, 160), Scalar(0, 160, 160),
|
||||
Scalar(160, 160, 0), Scalar(160, 0, 160), Scalar(20, 50, 160)};
|
||||
static int id = 0;
|
||||
return colors[id < num ? id++ : num - 1];
|
||||
}
|
||||
|
||||
inline vector<Rect2d> readGT(const string &filename, const string &omitname)
|
||||
{
|
||||
vector<Rect2d> res;
|
||||
{
|
||||
ifstream input(filename.c_str());
|
||||
if (!input.is_open())
|
||||
CV_Error(Error::StsError, "Failed to open file");
|
||||
while (input)
|
||||
{
|
||||
Rect2d one;
|
||||
input >> one.x;
|
||||
input.ignore(numeric_limits<std::streamsize>::max(), ',');
|
||||
input >> one.y;
|
||||
input.ignore(numeric_limits<std::streamsize>::max(), ',');
|
||||
input >> one.width;
|
||||
input.ignore(numeric_limits<std::streamsize>::max(), ',');
|
||||
input >> one.height;
|
||||
input.ignore(numeric_limits<std::streamsize>::max(), '\n');
|
||||
if (input.good())
|
||||
res.push_back(one);
|
||||
}
|
||||
}
|
||||
if (!omitname.empty())
|
||||
{
|
||||
ifstream input(omitname.c_str());
|
||||
if (!input.is_open())
|
||||
CV_Error(Error::StsError, "Failed to open file");
|
||||
while (input)
|
||||
{
|
||||
unsigned int a = 0, b = 0;
|
||||
input >> a >> b;
|
||||
input.ignore(numeric_limits<std::streamsize>::max(), '\n');
|
||||
if (a > 0 && b > 0 && a < res.size() && b < res.size())
|
||||
{
|
||||
if (a > b)
|
||||
swap(a, b);
|
||||
for (vector<Rect2d>::iterator i = res.begin() + a; i != res.begin() + b; ++i)
|
||||
{
|
||||
*i = Rect2d();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
inline bool isGoodBox(const Rect2d &box) { return box.width > 0. && box.height > 0.; }
|
||||
const int LTRC_COUNT = 100;
|
||||
|
||||
struct AlgoWrap
|
||||
{
|
||||
AlgoWrap(const string &name_)
|
||||
: lastState(NotFound), name(name_), color(getNextColor()),
|
||||
numTotal(0), numResponse(0), numPresent(0), numCorrect_0(0), numCorrect_0_5(0),
|
||||
timeTotal(0), auc(LTRC_COUNT + 1, 0)
|
||||
{
|
||||
tracker = createTrackerByName(name);
|
||||
}
|
||||
|
||||
enum State
|
||||
{
|
||||
NotFound,
|
||||
Overlap_None,
|
||||
Overlap_0,
|
||||
Overlap_0_5,
|
||||
};
|
||||
|
||||
Ptr<Tracker> tracker;
|
||||
bool lastRes;
|
||||
Rect lastBox;
|
||||
State lastState;
|
||||
|
||||
// visual
|
||||
string name;
|
||||
Scalar color;
|
||||
|
||||
// results
|
||||
int numTotal; // frames passed to tracker
|
||||
int numResponse; // frames where tracker had response
|
||||
int numPresent; // frames where ground truth result present
|
||||
int numCorrect_0; // frames where overlap with GT > 0
|
||||
int numCorrect_0_5; // frames where overlap with GT > 0.5
|
||||
int64 timeTotal; // ticks
|
||||
vector<int> auc; // number of frames for each overlap percent
|
||||
|
||||
void eval(const Mat &frame, const Rect2d >Box, bool isVerbose)
|
||||
{
|
||||
// RUN
|
||||
lastBox = Rect();
|
||||
int64 frameTime = getTickCount();
|
||||
lastRes = tracker->update(frame, lastBox);
|
||||
frameTime = getTickCount() - frameTime;
|
||||
|
||||
// RESULTS
|
||||
double intersectArea = (gtBox & (Rect2d)lastBox).area();
|
||||
double unionArea = (gtBox | (Rect2d)lastBox).area();
|
||||
numTotal++;
|
||||
numResponse += (lastRes && isGoodBox(lastBox)) ? 1 : 0;
|
||||
numPresent += isGoodBox(gtBox) ? 1 : 0;
|
||||
double overlap = unionArea > 0. ? intersectArea / unionArea : 0.;
|
||||
numCorrect_0 += overlap > 0. ? 1 : 0;
|
||||
numCorrect_0_5 += overlap > 0.5 ? 1 : 0;
|
||||
auc[std::min(std::max((size_t)(overlap * LTRC_COUNT), (size_t)0), (size_t)LTRC_COUNT)]++;
|
||||
timeTotal += frameTime;
|
||||
|
||||
if (isVerbose)
|
||||
cout << name << " - " << overlap << endl;
|
||||
|
||||
if (isGoodBox(gtBox) != isGoodBox(lastBox)) lastState = NotFound;
|
||||
else if (overlap > 0.5) lastState = Overlap_0_5;
|
||||
else if (overlap > 0.0001) lastState = Overlap_0;
|
||||
else lastState = Overlap_None;
|
||||
}
|
||||
|
||||
void draw(Mat &image, const Point &textPoint) const
|
||||
{
|
||||
if (lastRes)
|
||||
rectangle(image, lastBox, color, 2, LINE_8);
|
||||
string suf;
|
||||
switch (lastState)
|
||||
{
|
||||
case AlgoWrap::NotFound: suf = " X"; break;
|
||||
case AlgoWrap::Overlap_None: suf = " ~"; break;
|
||||
case AlgoWrap::Overlap_0: suf = " +"; break;
|
||||
case AlgoWrap::Overlap_0_5: suf = " ++"; break;
|
||||
}
|
||||
putText(image, name + suf, textPoint, FONT_HERSHEY_PLAIN, 1, color, 1, LINE_AA);
|
||||
}
|
||||
|
||||
// calculates "lost track ratio" curve - row of values growing from 0 to 1
|
||||
// number of elements is LTRC_COUNT + 2
|
||||
Mat getLTRC() const
|
||||
{
|
||||
Mat t, res;
|
||||
Mat(auc).convertTo(t, CV_64F); // integral does not support CV_32S input
|
||||
integral(t.t(), res, CV_64F); // t is a column of values
|
||||
return res.row(1) / (double)numTotal;
|
||||
}
|
||||
|
||||
void plotLTRC(Mat &img) const
|
||||
{
|
||||
Ptr<plot::Plot2d> p_ = plot::Plot2d::create(getLTRC());
|
||||
p_->render(img);
|
||||
}
|
||||
|
||||
double calcAUC() const
|
||||
{
|
||||
return cv::sum(getLTRC())[0] / (double)LTRC_COUNT;
|
||||
}
|
||||
|
||||
void stat(ostream &out) const
|
||||
{
|
||||
out << name << endl;
|
||||
out << setw(20) << "Overlap > 0 " << setw(20) << (double)numCorrect_0 / numTotal * 100
|
||||
<< "%" << setw(20) << numCorrect_0 << endl;
|
||||
out << setw(20) << "Overlap > 0.5" << setw(20) << (double)numCorrect_0_5 / numTotal * 100
|
||||
<< "%" << setw(20) << numCorrect_0_5 << endl;
|
||||
|
||||
double p = (double)numCorrect_0_5 / numResponse;
|
||||
double r = (double)numCorrect_0_5 / numPresent;
|
||||
double f = 2 * p * r / (p + r);
|
||||
out << setw(20) << "Precision" << setw(20) << p * 100 << "%" << endl;
|
||||
out << setw(20) << "Recall " << setw(20) << r * 100 << "%" << endl;
|
||||
out << setw(20) << "f-measure" << setw(20) << f * 100 << "%" << endl;
|
||||
out << setw(20) << "AUC" << setw(20) << calcAUC() << endl;
|
||||
|
||||
double s = (timeTotal / getTickFrequency()) / numTotal;
|
||||
out << setw(20) << "Performance" << setw(20) << s * 1000 << " ms/frame" << setw(20) << 1 / s
|
||||
<< " fps" << endl;
|
||||
}
|
||||
};
|
||||
|
||||
inline ostream &operator<<(ostream &out, const AlgoWrap &w) { w.stat(out); return out; }
|
||||
|
||||
inline vector<AlgoWrap> initAlgorithms(const string &algList)
|
||||
{
|
||||
vector<AlgoWrap> res;
|
||||
istringstream input(algList);
|
||||
for (;;)
|
||||
{
|
||||
char one[30];
|
||||
input.getline(one, 30, ',');
|
||||
if (!input)
|
||||
break;
|
||||
cout << " " << one << " - ";
|
||||
AlgoWrap a(one);
|
||||
if (a.tracker)
|
||||
{
|
||||
res.push_back(a);
|
||||
cout << "OK";
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "FAILED";
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static const string &window = "Tracking API";
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const string keys =
|
||||
"{help h||show help}"
|
||||
"{video||video file to process}"
|
||||
"{gt||ground truth file (each line describes rectangle in format: '<x>,<y>,<w>,<h>')}"
|
||||
"{start|0|starting frame}"
|
||||
"{num|0|frame number (0 for all)}"
|
||||
"{omit||file with omit ranges (each line describes occluded frames: '<start> <end>')}"
|
||||
"{plot|false|plot LTR curves at the end}"
|
||||
"{v|false|print each frame info}"
|
||||
"{@algos||comma-separated algorithm names}";
|
||||
CommandLineParser p(argc, argv, keys);
|
||||
if (p.has("help"))
|
||||
{
|
||||
p.printMessage();
|
||||
return 0;
|
||||
}
|
||||
int startFrame = p.get<int>("start");
|
||||
int frameCount = p.get<int>("num");
|
||||
string videoFile = p.get<string>("video");
|
||||
string gtFile = p.get<string>("gt");
|
||||
string omitFile = p.get<string>("omit");
|
||||
string algList = p.get<string>("@algos");
|
||||
bool doPlot = p.get<bool>("plot");
|
||||
bool isVerbose = p.get<bool>("v");
|
||||
if (!p.check())
|
||||
{
|
||||
p.printErrors();
|
||||
return 0;
|
||||
}
|
||||
|
||||
cout << "Reading GT from " << gtFile << " ... ";
|
||||
vector<Rect2d> gt = readGT(gtFile, omitFile);
|
||||
if (gt.empty())
|
||||
CV_Error(Error::StsError, "Failed to read GT file");
|
||||
cout << gt.size() << " boxes" << endl;
|
||||
|
||||
cout << "Opening video " << videoFile << " ... ";
|
||||
VideoCapture cap;
|
||||
cap.open(videoFile);
|
||||
if (!cap.isOpened())
|
||||
CV_Error(Error::StsError, "Failed to open video file");
|
||||
cap.set(CAP_PROP_POS_FRAMES, startFrame);
|
||||
cout << "at frame " << startFrame << endl;
|
||||
|
||||
// INIT
|
||||
vector<AlgoWrap> algos = initAlgorithms(algList);
|
||||
Mat frame, image;
|
||||
cap >> frame;
|
||||
for (vector<AlgoWrap>::iterator i = algos.begin(); i != algos.end(); ++i)
|
||||
i->tracker->init(frame, gt[0]);
|
||||
|
||||
// DRAW
|
||||
{
|
||||
namedWindow(window, WINDOW_AUTOSIZE);
|
||||
frame.copyTo(image);
|
||||
rectangle(image, gt[0], gtColor, 2, LINE_8);
|
||||
imshow(window, image);
|
||||
}
|
||||
|
||||
bool paused = false;
|
||||
int frameId = 0;
|
||||
cout << "Hot keys:" << endl << " q - exit" << endl << " p - pause" << endl;
|
||||
for (;;)
|
||||
{
|
||||
if (!paused)
|
||||
{
|
||||
cap >> frame;
|
||||
if (frame.empty())
|
||||
{
|
||||
cout << "Done - video end" << endl;
|
||||
break;
|
||||
}
|
||||
frameId++;
|
||||
if (isVerbose)
|
||||
cout << endl << "Frame " << frameId << endl;
|
||||
// EVAL
|
||||
for (vector<AlgoWrap>::iterator i = algos.begin(); i != algos.end(); ++i)
|
||||
i->eval(frame, gt[frameId], isVerbose);
|
||||
// DRAW
|
||||
{
|
||||
Point textPoint(1, 16);
|
||||
frame.copyTo(image);
|
||||
rectangle(image, gt[frameId], gtColor, 2, LINE_8);
|
||||
putText(image, "GROUND TRUTH", textPoint, FONT_HERSHEY_PLAIN, 1, gtColor, 1, LINE_AA);
|
||||
for (vector<AlgoWrap>::iterator i = algos.begin(); i != algos.end(); ++i)
|
||||
{
|
||||
textPoint.y += 14;
|
||||
i->draw(image, textPoint);
|
||||
}
|
||||
imshow(window, image);
|
||||
}
|
||||
}
|
||||
|
||||
char c = (char)waitKey(1);
|
||||
if (c == 'q')
|
||||
{
|
||||
cout << "Done - manual exit" << endl;
|
||||
break;
|
||||
}
|
||||
else if (c == 'p')
|
||||
{
|
||||
paused = !paused;
|
||||
}
|
||||
if (frameCount && frameId >= frameCount)
|
||||
{
|
||||
cout << "Done - max frame count" << endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// STAT
|
||||
for (vector<AlgoWrap>::iterator i = algos.begin(); i != algos.end(); ++i)
|
||||
cout << "==========" << endl << *i << endl;
|
||||
|
||||
if (doPlot)
|
||||
{
|
||||
Mat img(300, 300, CV_8UC3);
|
||||
for (vector<AlgoWrap>::iterator i = algos.begin(); i != algos.end(); ++i)
|
||||
{
|
||||
i->plotLTRC(img);
|
||||
imshow("LTR curve for " + i->name, img);
|
||||
}
|
||||
waitKey(0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
#include <opencv2/core/utility.hpp>
|
||||
#include <opencv2/tracking.hpp>
|
||||
#include <opencv2/videoio.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include "samples_utility.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
// show help
|
||||
if (argc<2) {
|
||||
cout <<
|
||||
" Usage: example_tracking_csrt <video_name>\n"
|
||||
" examples:\n"
|
||||
" example_tracking_csrt Bolt/img/%04.jpg\n"
|
||||
" example_tracking_csrt Bolt/img/%04.jpg Bolt/grouondtruth.txt\n"
|
||||
" example_tracking_csrt faceocc2.webm\n"
|
||||
<< endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// create the tracker
|
||||
Ptr<TrackerCSRT> tracker = TrackerCSRT::create();
|
||||
|
||||
// const char* param_file_path = "/home/amuhic/Workspace/3_dip/params.yml";
|
||||
// FileStorage fs(params_file_path, FileStorage::WRITE);
|
||||
// tracker->write(fs);
|
||||
// FileStorage fs(param_file_path, FileStorage::READ);
|
||||
// tracker->read( fs.root());
|
||||
|
||||
// set input video
|
||||
std::string video = argv[1];
|
||||
VideoCapture cap(video);
|
||||
// and read first frame
|
||||
Mat frame;
|
||||
cap >> frame;
|
||||
|
||||
// target bounding box
|
||||
Rect roi;
|
||||
if (argc > 2) {
|
||||
// read first line of ground-truth file
|
||||
std::string groundtruthPath = argv[2];
|
||||
std::ifstream gtIfstream(groundtruthPath.c_str());
|
||||
std::string gtLine;
|
||||
getline(gtIfstream, gtLine);
|
||||
gtIfstream.close();
|
||||
|
||||
// parse the line by elements
|
||||
std::stringstream gtStream(gtLine);
|
||||
std::string element;
|
||||
std::vector<int> elements;
|
||||
while (std::getline(gtStream, element, ','))
|
||||
{
|
||||
elements.push_back(cvRound(std::atof(element.c_str())));
|
||||
}
|
||||
|
||||
if (elements.size() == 4) {
|
||||
// ground-truth is rectangle
|
||||
roi = cv::Rect(elements[0], elements[1], elements[2], elements[3]);
|
||||
}
|
||||
else if (elements.size() == 8) {
|
||||
// ground-truth is polygon
|
||||
int xMin = cvRound(min(elements[0], min(elements[2], min(elements[4], elements[6]))));
|
||||
int yMin = cvRound(min(elements[1], min(elements[3], min(elements[5], elements[7]))));
|
||||
int xMax = cvRound(max(elements[0], max(elements[2], max(elements[4], elements[6]))));
|
||||
int yMax = cvRound(max(elements[1], max(elements[3], max(elements[5], elements[7]))));
|
||||
roi = cv::Rect(xMin, yMin, xMax - xMin, yMax - yMin);
|
||||
|
||||
// create mask from polygon and set it to the tracker
|
||||
cv::Rect aaRect = cv::Rect(xMin, yMin, xMax - xMin, yMax - yMin);
|
||||
cout << aaRect.size() << endl;
|
||||
Mat mask = Mat::zeros(aaRect.size(), CV_8UC1);
|
||||
const int n = 4;
|
||||
std::vector<cv::Point> poly_points(n);
|
||||
//Translate x and y to rects start position
|
||||
int sx = aaRect.x;
|
||||
int sy = aaRect.y;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
poly_points[i] = Point(elements[2 * i] - sx, elements[2 * i + 1] - sy);
|
||||
}
|
||||
cv::fillConvexPoly(mask, poly_points, Scalar(1.0), 8);
|
||||
mask.convertTo(mask, CV_32FC1);
|
||||
tracker->setInitialMask(mask);
|
||||
}
|
||||
else {
|
||||
std::cout << "Number of ground-truth elements is not 4 or 8." << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
// second argument is not given - user selects target
|
||||
roi = selectROI("tracker", frame, true, false);
|
||||
}
|
||||
|
||||
//quit if ROI was not selected
|
||||
if (roi.width == 0 || roi.height == 0)
|
||||
return 0;
|
||||
|
||||
// initialize the tracker
|
||||
int64 t1 = cv::getTickCount();
|
||||
tracker->init(frame, roi);
|
||||
int64 t2 = cv::getTickCount();
|
||||
int64 tick_counter = t2 - t1;
|
||||
|
||||
// do the tracking
|
||||
printf("Start the tracking process, press ESC to quit.\n");
|
||||
int frame_idx = 1;
|
||||
for (;;) {
|
||||
// get frame from the video
|
||||
cap >> frame;
|
||||
|
||||
// stop the program if no more images
|
||||
if (frame.rows == 0 || frame.cols == 0)
|
||||
break;
|
||||
|
||||
// update the tracking result
|
||||
t1 = cv::getTickCount();
|
||||
bool isfound = tracker->update(frame, roi);
|
||||
t2 = cv::getTickCount();
|
||||
tick_counter += t2 - t1;
|
||||
frame_idx++;
|
||||
|
||||
if (!isfound) {
|
||||
cout << "The target has been lost...\n";
|
||||
waitKey(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// draw the tracked object and show the image
|
||||
rectangle(frame, roi, Scalar(255, 0, 0), 2, 1);
|
||||
imshow("tracker", frame);
|
||||
|
||||
//quit on ESC button
|
||||
if (waitKey(1) == 27)break;
|
||||
}
|
||||
|
||||
cout << "Elapsed sec: " << static_cast<double>(tick_counter) / cv::getTickFrequency() << endl;
|
||||
cout << "FPS: " << ((double)(frame_idx)) / (static_cast<double>(tick_counter) / cv::getTickFrequency()) << endl;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*----------------------------------------------
|
||||
* Usage:
|
||||
* example_tracking_kcf <video_name>
|
||||
*
|
||||
* example:
|
||||
* example_tracking_kcf Bolt/img/%04.jpg
|
||||
* example_tracking_kcf faceocc2.webm
|
||||
*--------------------------------------------------*/
|
||||
|
||||
#include <opencv2/core/utility.hpp>
|
||||
#include <opencv2/tracking.hpp>
|
||||
#include <opencv2/videoio.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include "samples_utility.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
int main( int argc, char** argv ){
|
||||
// show help
|
||||
if(argc<2){
|
||||
cout<<
|
||||
" Usage: example_tracking_kcf <video_name>\n"
|
||||
" examples:\n"
|
||||
" example_tracking_kcf Bolt/img/%04.jpg\n"
|
||||
" example_tracking_kcf faceocc2.webm\n"
|
||||
<< endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// create the tracker
|
||||
Ptr<Tracker> tracker = TrackerKCF::create();
|
||||
|
||||
// set input video
|
||||
std::string video = argv[1];
|
||||
VideoCapture cap(video);
|
||||
|
||||
Mat frame;
|
||||
|
||||
// get bounding box
|
||||
cap >> frame;
|
||||
Rect roi = selectROI("tracker", frame, true, false);
|
||||
|
||||
//quit if ROI was not selected
|
||||
if(roi.width==0 || roi.height==0)
|
||||
return 0;
|
||||
|
||||
// initialize the tracker
|
||||
tracker->init(frame,roi);
|
||||
|
||||
// do the tracking
|
||||
printf("Start the tracking process, press ESC to quit.\n");
|
||||
for ( ;; ){
|
||||
// get frame from the video
|
||||
cap >> frame;
|
||||
|
||||
// stop the program if no more images
|
||||
if(frame.rows==0 || frame.cols==0)
|
||||
break;
|
||||
|
||||
// update the tracking result
|
||||
bool isfound = tracker->update(frame,roi);
|
||||
if(!isfound)
|
||||
{
|
||||
cout << "The target has been lost...\n";
|
||||
waitKey(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// draw the tracked object
|
||||
rectangle( frame, roi, Scalar( 255, 0, 0 ), 2, 1 );
|
||||
|
||||
// show image with the tracked object
|
||||
imshow("tracker",frame);
|
||||
|
||||
//quit on ESC button
|
||||
if(waitKey(1)==27)break;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "opencv2/opencv_modules.hpp"
|
||||
#include "opencv2/core.hpp"
|
||||
#ifdef HAVE_OPENCV_DATASETS
|
||||
|
||||
#include "opencv2/datasets/track_vot.hpp"
|
||||
#include <opencv2/core/utility.hpp>
|
||||
#include <opencv2/tracking.hpp>
|
||||
#include <opencv2/videoio.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include "samples_utility.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
using namespace cv::datasets;
|
||||
|
||||
#define NUM_TEST_FRAMES 1000
|
||||
|
||||
static Mat image;
|
||||
static bool paused;
|
||||
static bool selectObjects = false;
|
||||
static bool startSelection = false;
|
||||
vector<Rect2d> boundingBoxes;
|
||||
int targetsCnt = 0;
|
||||
int targetsNum = 0;
|
||||
Rect2d boundingBox;
|
||||
|
||||
static const char* keys =
|
||||
{ "{@tracker_algorithm | | Tracker algorithm }"
|
||||
"{@target_num |1| Number of targets }"
|
||||
"{@dataset_path |true| Dataset path }"
|
||||
"{@dataset_id |1| Dataset ID }"
|
||||
};
|
||||
|
||||
static void onMouse(int event, int x, int y, int, void*)
|
||||
{
|
||||
if (!selectObjects)
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case EVENT_LBUTTONDOWN:
|
||||
//set origin of the bounding box
|
||||
startSelection = true;
|
||||
boundingBox.x = x;
|
||||
boundingBox.y = y;
|
||||
boundingBox.width = boundingBox.height = 0;
|
||||
break;
|
||||
case EVENT_LBUTTONUP:
|
||||
//sei with and height of the bounding box
|
||||
boundingBox.width = std::abs(x - boundingBox.x);
|
||||
boundingBox.height = std::abs(y - boundingBox.y);
|
||||
boundingBoxes.push_back(boundingBox);
|
||||
targetsCnt++;
|
||||
if (targetsCnt == targetsNum)
|
||||
{
|
||||
paused = false;
|
||||
selectObjects = true;
|
||||
}
|
||||
startSelection = false;
|
||||
break;
|
||||
case EVENT_MOUSEMOVE:
|
||||
|
||||
if (startSelection && !selectObjects)
|
||||
{
|
||||
//draw the bounding box
|
||||
Mat currentFrame;
|
||||
image.copyTo(currentFrame);
|
||||
for (int i = 0; i < (int)boundingBoxes.size(); i++)
|
||||
rectangle(currentFrame, boundingBoxes[i], Scalar(255, 0, 0), 2, 1);
|
||||
rectangle(currentFrame, Point((int)boundingBox.x, (int)boundingBox.y), Point(x, y), Scalar(255, 0, 0), 2, 1);
|
||||
imshow("Tracking API", currentFrame);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void help()
|
||||
{
|
||||
cout << "\nThis example shows the functionality of \"Long-term optical tracking API\""
|
||||
"TLD dataset ID: 1~10, VOT2015 dataset ID: 1~60\n"
|
||||
"-- pause video [p] and draw a bounding boxes around the targets to start the tracker\n"
|
||||
"Example:\n"
|
||||
"./example_tracking_multiTracker_dataset<tracker_algorithm> <number_of_targets> <dataset_path> <dataset_id>\n"
|
||||
<< endl;
|
||||
|
||||
cout << "\n\nHot keys: \n"
|
||||
"\tq - quit the program\n"
|
||||
"\tp - pause video\n";
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
CommandLineParser parser(argc, argv, keys);
|
||||
string tracker_algorithm = parser.get<string>(0);
|
||||
targetsNum = parser.get<int>(1);
|
||||
string datasetRootPath = parser.get<string>(2);
|
||||
int datasetID = parser.get<int>(3);
|
||||
if (tracker_algorithm.empty() || datasetRootPath.empty() || targetsNum < 1)
|
||||
{
|
||||
help();
|
||||
return -1;
|
||||
}
|
||||
|
||||
Mat frame;
|
||||
paused = false;
|
||||
namedWindow("Tracking API", 0);
|
||||
setMouseCallback("Tracking API", onMouse, 0);
|
||||
|
||||
legacy::MultiTrackerTLD mt;
|
||||
//Init Dataset
|
||||
Ptr<TRACK_vot> dataset = TRACK_vot::create();
|
||||
dataset->load(datasetRootPath);
|
||||
dataset->initDataset(datasetID);
|
||||
|
||||
//Read first frame
|
||||
dataset->getNextFrame(frame);
|
||||
frame.copyTo(image);
|
||||
for (int i = 0; i < (int)boundingBoxes.size(); i++)
|
||||
rectangle(image, boundingBoxes[i], Scalar(255, 0, 0), 2, 1);
|
||||
imshow("Tracking API", image);
|
||||
|
||||
bool initialized = false;
|
||||
paused = true;
|
||||
int frameCounter = 0;
|
||||
|
||||
//Time measurment
|
||||
int64 e3 = getTickCount();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (!paused)
|
||||
{
|
||||
//Time measurment
|
||||
int64 e1 = getTickCount();
|
||||
if (initialized){
|
||||
if (!dataset->getNextFrame(frame))
|
||||
break;
|
||||
frame.copyTo(image);
|
||||
}
|
||||
|
||||
if (!initialized && selectObjects)
|
||||
{
|
||||
//Initialize the tracker and add targets
|
||||
for (int i = 0; i < (int)boundingBoxes.size(); i++)
|
||||
{
|
||||
if (!mt.addTarget(frame, boundingBoxes[i], createTrackerByName_legacy(tracker_algorithm)))
|
||||
{
|
||||
cout << "Trackers Init Error!!!";
|
||||
return 0;
|
||||
}
|
||||
rectangle(frame, boundingBoxes[i], mt.colors[0], 2, 1);
|
||||
}
|
||||
initialized = true;
|
||||
}
|
||||
else if (initialized)
|
||||
{
|
||||
//Update all targets
|
||||
if (mt.update(frame))
|
||||
{
|
||||
for (int i = 0; i < mt.targetNum; i++)
|
||||
{
|
||||
rectangle(frame, mt.boundingBoxes[i], mt.colors[i], 2, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
imshow("Tracking API", frame);
|
||||
frameCounter++;
|
||||
//Time measurment
|
||||
int64 e2 = getTickCount();
|
||||
double t1 = (e2 - e1) / getTickFrequency();
|
||||
cout << frameCounter << "\tframe : " << t1 * 1000.0 << "ms" << endl;
|
||||
}
|
||||
|
||||
char c = (char)waitKey(2);
|
||||
if (c == 'q')
|
||||
break;
|
||||
if (c == 'p')
|
||||
paused = !paused;
|
||||
|
||||
//waitKey(0);
|
||||
}
|
||||
|
||||
//Time measurment
|
||||
int64 e4 = getTickCount();
|
||||
double t2 = (e4 - e3) / getTickFrequency();
|
||||
cout << "Average Time for Frame: " << t2 * 1000.0 / frameCounter << "ms" << endl;
|
||||
cout << "Average FPS: " << 1.0 / t2*frameCounter << endl;
|
||||
|
||||
|
||||
waitKey(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else // ! HAVE_OPENCV_DATASETS
|
||||
#include <opencv2/core.hpp>
|
||||
int main() {
|
||||
CV_Error(cv::Error::StsNotImplemented , "this sample needs to be built with opencv_datasets !");
|
||||
return -1;
|
||||
}
|
||||
#endif // HAVE_OPENCV_DATASETS
|
||||
@@ -0,0 +1,154 @@
|
||||
/*----------------------------------------------
|
||||
* Usage:
|
||||
* example_tracking_multitracker <video_name> [algorithm]
|
||||
*
|
||||
* example:
|
||||
* example_tracking_multitracker Bolt/img/%04d.jpg
|
||||
* example_tracking_multitracker faceocc2.webm KCF
|
||||
*
|
||||
* Note: after the OpenCV library is installed,
|
||||
* please re-compile this code with "HAVE_OPENCV" parameter activated
|
||||
* to enable the high precission of fps computation
|
||||
*--------------------------------------------------*/
|
||||
|
||||
/* after the OpenCV library is installed
|
||||
* please uncomment the the line below and re-compile this code
|
||||
* to enable high precission of fps computation
|
||||
*/
|
||||
//#define HAVE_OPENCV
|
||||
|
||||
#include <opencv2/core/utility.hpp>
|
||||
#include <opencv2/tracking.hpp>
|
||||
#include <opencv2/videoio.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include "samples_utility.hpp"
|
||||
|
||||
#ifdef HAVE_OPENCV
|
||||
#include <opencv2/flann.hpp>
|
||||
#endif
|
||||
|
||||
#define RESET "\033[0m"
|
||||
#define RED "\033[31m" /* Red */
|
||||
#define GREEN "\033[32m" /* Green */
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
int main( int argc, char** argv ){
|
||||
// show help
|
||||
if(argc<2){
|
||||
cout<<
|
||||
" Usage: example_tracking_multitracker <video_name> [algorithm]\n"
|
||||
" examples:\n"
|
||||
" example_tracking_multitracker Bolt/img/%04d.jpg\n"
|
||||
" example_tracking_multitracker faceocc2.webm MEDIANFLOW\n"
|
||||
" \n"
|
||||
" Note: after the OpenCV library is installed,\n"
|
||||
" please re-compile with the HAVE_OPENCV parameter activated\n"
|
||||
" to enable the high precission of fps computation.\n"
|
||||
<< endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// timer
|
||||
#ifdef HAVE_OPENCV
|
||||
cvflann::StartStopTimer timer;
|
||||
#else
|
||||
clock_t timer;
|
||||
#endif
|
||||
|
||||
// for showing the speed
|
||||
double fps;
|
||||
String text;
|
||||
char buffer [50];
|
||||
|
||||
// set the default tracking algorithm
|
||||
String trackingAlg = "KCF";
|
||||
|
||||
// set the tracking algorithm from parameter
|
||||
if(argc>2)
|
||||
trackingAlg = argv[2];
|
||||
|
||||
// create the tracker
|
||||
legacy::MultiTracker trackers;
|
||||
|
||||
// container of the tracked objects
|
||||
vector<Rect> ROIs;
|
||||
vector<Rect2d> objects;
|
||||
|
||||
// set input video
|
||||
String video = argv[1];
|
||||
VideoCapture cap(video);
|
||||
|
||||
Mat frame;
|
||||
|
||||
// get bounding box
|
||||
cap >> frame;
|
||||
selectROIs("tracker",frame,ROIs);
|
||||
|
||||
//quit when the tracked object(s) is not provided
|
||||
if(ROIs.size()<1)
|
||||
return 0;
|
||||
|
||||
std::vector<Ptr<legacy::Tracker> > algorithms;
|
||||
for (size_t i = 0; i < ROIs.size(); i++)
|
||||
{
|
||||
algorithms.push_back(createTrackerByName_legacy(trackingAlg));
|
||||
objects.push_back(ROIs[i]);
|
||||
}
|
||||
|
||||
// initialize the tracker
|
||||
trackers.add(algorithms,frame,objects);
|
||||
|
||||
// do the tracking
|
||||
printf(GREEN "Start the tracking process, press ESC to quit.\n" RESET);
|
||||
for ( ;; ){
|
||||
// get frame from the video
|
||||
cap >> frame;
|
||||
|
||||
// stop the program if no more images
|
||||
if(frame.rows==0 || frame.cols==0)
|
||||
break;
|
||||
|
||||
// start the timer
|
||||
#ifdef HAVE_OPENCV
|
||||
timer.start();
|
||||
#else
|
||||
timer=clock();
|
||||
#endif
|
||||
|
||||
//update the tracking result
|
||||
trackers.update(frame);
|
||||
|
||||
// calculate the processing speed
|
||||
#ifdef HAVE_OPENCV
|
||||
timer.stop();
|
||||
fps=1.0/timer.value;
|
||||
timer.reset();
|
||||
#else
|
||||
timer=clock();
|
||||
trackers.update(frame);
|
||||
timer=clock()-timer;
|
||||
fps=(double)CLOCKS_PER_SEC/(double)timer;
|
||||
#endif
|
||||
|
||||
// draw the tracked object
|
||||
for(unsigned i=0;i<trackers.getObjects().size();i++)
|
||||
rectangle( frame, trackers.getObjects()[i], Scalar( 255, 0, 0 ), 2, 1 );
|
||||
|
||||
// draw the processing speed
|
||||
sprintf (buffer, "speed: %.0f fps", fps);
|
||||
text = buffer;
|
||||
putText(frame, text, Point(20,20), FONT_HERSHEY_PLAIN, 1, Scalar(255,255,255));
|
||||
|
||||
// show image with the tracked object
|
||||
imshow("tracker",frame);
|
||||
|
||||
//quit on ESC button
|
||||
if(waitKey(1)==27)break;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import numpy as np
|
||||
import cv2 as cv
|
||||
import sys
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
print('Input video name is missing')
|
||||
exit()
|
||||
|
||||
print('Select 3 tracking targets')
|
||||
|
||||
cv.namedWindow("tracking")
|
||||
camera = cv.VideoCapture(sys.argv[1])
|
||||
tracker = cv.legacy.MultiTracker_create()
|
||||
init_once = False
|
||||
|
||||
ok, image=camera.read()
|
||||
if not ok:
|
||||
print('Failed to read video')
|
||||
exit()
|
||||
|
||||
bbox1 = cv.selectROI('tracking', image)
|
||||
bbox2 = cv.selectROI('tracking', image)
|
||||
bbox3 = cv.selectROI('tracking', image)
|
||||
|
||||
while camera.isOpened():
|
||||
ok, image=camera.read()
|
||||
if not ok:
|
||||
print('no image to read')
|
||||
break
|
||||
|
||||
if not init_once:
|
||||
ok = tracker.add(cv.legacy.TrackerMIL_create(), image, bbox1)
|
||||
ok = tracker.add(cv.legacy.TrackerMIL_create(), image, bbox2)
|
||||
ok = tracker.add(cv.legacy.TrackerMIL_create(), image, bbox3)
|
||||
init_once = True
|
||||
|
||||
ok, boxes = tracker.update(image)
|
||||
print(ok, boxes)
|
||||
|
||||
for newbox in boxes:
|
||||
p1 = (int(newbox[0]), int(newbox[1]))
|
||||
p2 = (int(newbox[0] + newbox[2]), int(newbox[1] + newbox[3]))
|
||||
cv.rectangle(image, p1, p2, (200,0,0))
|
||||
|
||||
cv.imshow('tracking', image)
|
||||
k = cv.waitKey(1)
|
||||
if k == 27 : break # esc pressed
|
||||
@@ -0,0 +1,59 @@
|
||||
#ifndef _SAMPLES_UTILITY_HPP_
|
||||
#define _SAMPLES_UTILITY_HPP_
|
||||
|
||||
#include <opencv2/tracking.hpp>
|
||||
#include <opencv2/tracking/tracking_legacy.hpp>
|
||||
|
||||
inline cv::Ptr<cv::Tracker> createTrackerByName(const std::string& name)
|
||||
{
|
||||
using namespace cv;
|
||||
|
||||
cv::Ptr<cv::Tracker> tracker;
|
||||
|
||||
if (name == "KCF")
|
||||
tracker = cv::TrackerKCF::create();
|
||||
else if (name == "TLD")
|
||||
tracker = legacy::upgradeTrackingAPI(legacy::TrackerTLD::create());
|
||||
else if (name == "BOOSTING")
|
||||
tracker = legacy::upgradeTrackingAPI(legacy::TrackerBoosting::create());
|
||||
else if (name == "MEDIAN_FLOW")
|
||||
tracker = legacy::upgradeTrackingAPI(legacy::TrackerMedianFlow::create());
|
||||
else if (name == "MIL")
|
||||
tracker = cv::TrackerMIL::create();
|
||||
else if (name == "MOSSE")
|
||||
tracker = legacy::upgradeTrackingAPI(legacy::TrackerMOSSE::create());
|
||||
else if (name == "CSRT")
|
||||
tracker = cv::TrackerCSRT::create();
|
||||
else
|
||||
CV_Error(cv::Error::StsBadArg, "Invalid tracking algorithm name\n");
|
||||
|
||||
return tracker;
|
||||
}
|
||||
|
||||
inline cv::Ptr<cv::legacy::Tracker> createTrackerByName_legacy(const std::string& name)
|
||||
{
|
||||
using namespace cv;
|
||||
|
||||
cv::Ptr<cv::legacy::Tracker> tracker;
|
||||
|
||||
if (name == "KCF")
|
||||
tracker = legacy::TrackerKCF::create();
|
||||
else if (name == "TLD")
|
||||
tracker = legacy::TrackerTLD::create();
|
||||
else if (name == "BOOSTING")
|
||||
tracker = legacy::TrackerBoosting::create();
|
||||
else if (name == "MEDIAN_FLOW")
|
||||
tracker = legacy::TrackerMedianFlow::create();
|
||||
else if (name == "MIL")
|
||||
tracker = legacy::TrackerMIL::create();
|
||||
else if (name == "MOSSE")
|
||||
tracker = legacy::TrackerMOSSE::create();
|
||||
else if (name == "CSRT")
|
||||
tracker = legacy::TrackerCSRT::create();
|
||||
else
|
||||
CV_Error(cv::Error::StsBadArg, "Invalid tracking algorithm name\n");
|
||||
|
||||
return tracker;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,166 @@
|
||||
#include <opencv2/core/utility.hpp>
|
||||
#include <opencv2/tracking.hpp>
|
||||
#include <opencv2/videoio.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include "samples_utility.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
static const char* keys =
|
||||
{ "{@tracker_algorithm | | Tracker algorithm }"
|
||||
"{@video_name | | video name }"
|
||||
"{@start_frame |0| Start frame }"
|
||||
"{@bounding_frame |0,0,0,0| Initial bounding frame}"};
|
||||
|
||||
static void help()
|
||||
{
|
||||
cout << "\nThis example shows the functionality of \"Long-term optical tracking API\""
|
||||
"-- pause video [p] and draw a bounding box around the target to start the tracker\n"
|
||||
"Example of <video_name> is in opencv_extra/testdata/cv/tracking/\n"
|
||||
"Call:\n"
|
||||
"./tracker <tracker_algorithm> <video_name> <start_frame> [<bounding_frame>]\n"
|
||||
"tracker_algorithm can be: MIL, BOOSTING, MEDIANFLOW, TLD, KCF, MOSSE.\n"
|
||||
<< endl;
|
||||
|
||||
cout << "\n\nHot keys: \n"
|
||||
"\tq - quit the program\n"
|
||||
"\tp - pause video\n";
|
||||
}
|
||||
|
||||
int main( int argc, char** argv ){
|
||||
CommandLineParser parser( argc, argv, keys );
|
||||
|
||||
String tracker_algorithm = parser.get<String>( 0 );
|
||||
String video_name = parser.get<String>( 1 );
|
||||
int start_frame = parser.get<int>( 2 );
|
||||
|
||||
if( tracker_algorithm.empty() || video_name.empty() )
|
||||
{
|
||||
help();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int coords[4]={0,0,0,0};
|
||||
bool initBoxWasGivenInCommandLine=false;
|
||||
{
|
||||
String initBoundingBox=parser.get<String>(3);
|
||||
for(size_t npos=0,pos=0,ctr=0;ctr<4;ctr++){
|
||||
npos=initBoundingBox.find_first_of(',',pos);
|
||||
if(npos==string::npos && ctr<3){
|
||||
printf("bounding box should be given in format \"x1,y1,x2,y2\",where x's and y's are integer coordinates of opposed corners of bdd box\n");
|
||||
printf("got: %s\n",initBoundingBox.substr(pos,string::npos).c_str());
|
||||
printf("manual selection of bounding box will be employed\n");
|
||||
break;
|
||||
}
|
||||
int num=atoi(initBoundingBox.substr(pos,(ctr==3)?(string::npos):(npos-pos)).c_str());
|
||||
if(num<=0){
|
||||
printf("bounding box should be given in format \"x1,y1,x2,y2\",where x's and y's are integer coordinates of opposed corners of bdd box\n");
|
||||
printf("got: %s\n",initBoundingBox.substr(pos,npos-pos).c_str());
|
||||
printf("manual selection of bounding box will be employed\n");
|
||||
break;
|
||||
}
|
||||
coords[ctr]=num;
|
||||
pos=npos+1;
|
||||
}
|
||||
if(coords[0]>0 && coords[1]>0 && coords[2]>0 && coords[3]>0){
|
||||
initBoxWasGivenInCommandLine=true;
|
||||
}
|
||||
}
|
||||
|
||||
//open the capture
|
||||
VideoCapture cap;
|
||||
cap.open( video_name );
|
||||
cap.set( CAP_PROP_POS_FRAMES, start_frame );
|
||||
|
||||
if( !cap.isOpened() )
|
||||
{
|
||||
help();
|
||||
cout << "***Could not initialize capturing...***\n";
|
||||
cout << "Current parameter's value: \n";
|
||||
parser.printMessage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
Mat frame;
|
||||
namedWindow( "Tracking API", 1 );
|
||||
|
||||
Mat image;
|
||||
Rect boundingBox;
|
||||
bool paused = false;
|
||||
|
||||
//instantiates the specific Tracker
|
||||
Ptr<Tracker> tracker = createTrackerByName(tracker_algorithm);
|
||||
if (!tracker)
|
||||
{
|
||||
cout << "***Error in the instantiation of the tracker...***\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
//get the first frame
|
||||
cap >> frame;
|
||||
frame.copyTo( image );
|
||||
if(initBoxWasGivenInCommandLine){
|
||||
boundingBox.x = coords[0];
|
||||
boundingBox.y = coords[1];
|
||||
boundingBox.width = std::abs( coords[2] - coords[0] );
|
||||
boundingBox.height = std::abs( coords[3]-coords[1]);
|
||||
printf("bounding box with vertices (%d,%d) and (%d,%d) was given in command line\n",coords[0],coords[1],coords[2],coords[3]);
|
||||
rectangle( image, boundingBox, Scalar( 255, 0, 0 ), 2, 1 );
|
||||
}
|
||||
else
|
||||
boundingBox = selectROI("Tracking API", image);
|
||||
|
||||
imshow( "Tracking API", image );
|
||||
|
||||
bool initialized = false;
|
||||
int frameCounter = 0;
|
||||
int64 timeTotal = 0;
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
if( !paused )
|
||||
{
|
||||
if(initialized){
|
||||
cap >> frame;
|
||||
if(frame.empty()){
|
||||
break;
|
||||
}
|
||||
frame.copyTo( image );
|
||||
}
|
||||
|
||||
if( !initialized )
|
||||
{
|
||||
//initializes the tracker
|
||||
tracker->init(frame, boundingBox);
|
||||
initialized = true;
|
||||
}
|
||||
else if( initialized )
|
||||
{
|
||||
int64 frameTime = getTickCount();
|
||||
//updates the tracker
|
||||
if( tracker->update( frame, boundingBox ) )
|
||||
{
|
||||
rectangle( image, boundingBox, Scalar( 255, 0, 0 ), 2, 1 );
|
||||
}
|
||||
frameTime = getTickCount() - frameTime;
|
||||
timeTotal += frameTime;
|
||||
}
|
||||
imshow( "Tracking API", image );
|
||||
frameCounter++;
|
||||
}
|
||||
|
||||
char c = (char) waitKey( 2 );
|
||||
if( c == 'q' )
|
||||
break;
|
||||
if( c == 'p' )
|
||||
paused = !paused;
|
||||
}
|
||||
|
||||
double s = frameCounter / (timeTotal / getTickFrequency());
|
||||
printf("FPS: %f\n", s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import numpy as np
|
||||
import cv2 as cv
|
||||
import sys
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
print('Input video name is missing')
|
||||
exit()
|
||||
|
||||
cv.namedWindow("tracking")
|
||||
camera = cv.VideoCapture(sys.argv[1])
|
||||
ok, image=camera.read()
|
||||
if not ok:
|
||||
print('Failed to read video')
|
||||
exit()
|
||||
bbox = cv.selectROI("tracking", image)
|
||||
tracker = cv.TrackerMIL_create()
|
||||
init_once = False
|
||||
|
||||
while camera.isOpened():
|
||||
ok, image=camera.read()
|
||||
if not ok:
|
||||
print('no image to read')
|
||||
break
|
||||
|
||||
if not init_once:
|
||||
ok = tracker.init(image, bbox)
|
||||
init_once = True
|
||||
|
||||
ok, newbox = tracker.update(image)
|
||||
print(ok, newbox)
|
||||
|
||||
if ok:
|
||||
p1 = (int(newbox[0]), int(newbox[1]))
|
||||
p2 = (int(newbox[0] + newbox[2]), int(newbox[1] + newbox[3]))
|
||||
cv.rectangle(image, p1, p2, (200,0,0))
|
||||
|
||||
cv.imshow("tracking", image)
|
||||
k = cv.waitKey(1) & 0xff
|
||||
if k == 27 : break # esc pressed
|
||||
@@ -0,0 +1,239 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
|
||||
//
|
||||
// !!! this sample requires the opencv_datasets module !!!
|
||||
//
|
||||
|
||||
#include "opencv2/opencv_modules.hpp"
|
||||
#ifdef HAVE_OPENCV_DATASETS
|
||||
|
||||
#include "opencv2/datasets/track_vot.hpp"
|
||||
#include <opencv2/core/utility.hpp>
|
||||
#include <opencv2/tracking.hpp>
|
||||
#include <opencv2/videoio.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include "samples_utility.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
using namespace cv::datasets;
|
||||
|
||||
#define NUM_TEST_FRAMES 300
|
||||
#define TEST_VIDEO_INDEX 1 //TLD Dataset Video Index from 1-10
|
||||
//#define RECORD_VIDEO_FLG
|
||||
|
||||
static Mat image;
|
||||
static Rect boundingBox;
|
||||
static bool paused;
|
||||
static bool selectObject = false;
|
||||
static bool startSelection = false;
|
||||
|
||||
static const char* keys =
|
||||
{ "{@tracker_algorithm | | Tracker algorithm }"
|
||||
"{@dataset_path |true| Dataset path }"
|
||||
"{@dataset_id |1| Dataset ID }"
|
||||
};
|
||||
|
||||
static void onMouse(int event, int x, int y, int, void*)
|
||||
{
|
||||
if (!selectObject)
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case EVENT_LBUTTONDOWN:
|
||||
//set origin of the bounding box
|
||||
startSelection = true;
|
||||
boundingBox.x = x;
|
||||
boundingBox.y = y;
|
||||
boundingBox.width = boundingBox.height = 0;
|
||||
break;
|
||||
case EVENT_LBUTTONUP:
|
||||
//sei with and height of the bounding box
|
||||
boundingBox.width = std::abs(x - boundingBox.x);
|
||||
boundingBox.height = std::abs(y - boundingBox.y);
|
||||
paused = false;
|
||||
selectObject = true;
|
||||
break;
|
||||
case EVENT_MOUSEMOVE:
|
||||
|
||||
if (startSelection && !selectObject)
|
||||
{
|
||||
//draw the bounding box
|
||||
Mat currentFrame;
|
||||
image.copyTo(currentFrame);
|
||||
rectangle(currentFrame, Point((int)boundingBox.x, (int)boundingBox.y), Point(x, y), Scalar(255, 0, 0), 2, 1);
|
||||
imshow("Tracking API", currentFrame);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void help()
|
||||
{
|
||||
cout << "\nThis example shows the functionality of \"Long-term optical tracking API\""
|
||||
"TLD dataset ID: 1~10, VOT2015 dataset ID: 1~60\n"
|
||||
"-- pause video [p] and draw a bounding box around the target to start the tracker\n"
|
||||
"Example:\n"
|
||||
"./example_tracking_tracker_dataset <tracker_algorithm> <dataset_path> <dataset_id>\n"
|
||||
<< endl;
|
||||
|
||||
cout << "\n\nHot keys: \n"
|
||||
"\tq - quit the program\n"
|
||||
"\tp - pause video\n";
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
CommandLineParser parser(argc, argv, keys);
|
||||
string tracker_algorithm = parser.get<string>(0);
|
||||
string datasetRootPath = parser.get<string>(1);
|
||||
int datasetID = parser.get<int>(2);
|
||||
if (tracker_algorithm.empty() || datasetRootPath.empty())
|
||||
{
|
||||
help();
|
||||
return -1;
|
||||
}
|
||||
|
||||
Mat frame;
|
||||
paused = false;
|
||||
namedWindow("Tracking API", 0);
|
||||
setMouseCallback("Tracking API", onMouse, 0);
|
||||
|
||||
//Create Tracker
|
||||
Ptr<Tracker> tracker = createTrackerByName(tracker_algorithm);
|
||||
if (!tracker)
|
||||
{
|
||||
cout << "***Error in the instantiation of the tracker...***\n";
|
||||
getchar();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//Init Dataset
|
||||
Ptr<TRACK_vot> dataset = TRACK_vot::create();
|
||||
dataset->load(datasetRootPath);
|
||||
dataset->initDataset(datasetID);
|
||||
|
||||
//Read first frame
|
||||
dataset->getNextFrame(frame);
|
||||
frame.copyTo(image);
|
||||
|
||||
rectangle(image, boundingBox, Scalar(255, 0, 0), 2, 1);
|
||||
imshow("Tracking API", image);
|
||||
|
||||
|
||||
bool initialized = false;
|
||||
paused = true;
|
||||
int frameCounter = 0;
|
||||
|
||||
//Time measurment
|
||||
int64 e3 = getTickCount();
|
||||
for (;;)
|
||||
{
|
||||
if (!paused)
|
||||
{
|
||||
//Time measurment
|
||||
int64 e1 = getTickCount();
|
||||
if (initialized){
|
||||
if (!dataset->getNextFrame(frame))
|
||||
break;
|
||||
frame.copyTo(image);
|
||||
}
|
||||
|
||||
if (!initialized && selectObject)
|
||||
{
|
||||
//initializes the tracker
|
||||
tracker->init(frame, boundingBox);
|
||||
initialized = true;
|
||||
}
|
||||
else if (initialized)
|
||||
{
|
||||
//updates the tracker
|
||||
if (tracker->update(frame, boundingBox))
|
||||
{
|
||||
rectangle(image, boundingBox, Scalar(255, 0, 0), 2, 1);
|
||||
}
|
||||
}
|
||||
imshow("Tracking API", image);
|
||||
frameCounter++;
|
||||
//Time measurment
|
||||
int64 e2 = getTickCount();
|
||||
double t1 = (e2 - e1) / getTickFrequency();
|
||||
cout << frameCounter << "\tframe : " << t1 * 1000.0 << "ms" << endl;
|
||||
}
|
||||
|
||||
char c = (char)waitKey(2);
|
||||
if (c == 'q')
|
||||
break;
|
||||
if (c == 'p')
|
||||
paused = !paused;
|
||||
|
||||
|
||||
|
||||
//waitKey(0);
|
||||
}
|
||||
|
||||
//Time measurment
|
||||
int64 e4 = getTickCount();
|
||||
double t2 = (e4 - e3) / getTickFrequency();
|
||||
cout << "Average Time for Frame: " << t2 * 1000.0 / frameCounter << "ms" << endl;
|
||||
cout << "Average FPS: " << 1.0 / t2*frameCounter << endl;
|
||||
|
||||
|
||||
waitKey(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#else // ! HAVE_OPENCV_DATASETS
|
||||
#include <opencv2/core.hpp>
|
||||
int main() {
|
||||
CV_Error(cv::Error::StsNotImplemented , "this sample needs to be built with opencv_datasets !");
|
||||
return -1;
|
||||
}
|
||||
#endif // HAVE_OPENCV_DATASETS
|
||||
@@ -0,0 +1,255 @@
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <opencv2/tracking/tracking_by_matching.hpp>
|
||||
#include <iostream>
|
||||
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
#include <opencv2/dnn.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
using namespace cv::detail::tracking;
|
||||
using namespace cv::detail::tracking::tbm;
|
||||
|
||||
static const char* keys =
|
||||
{ "{video_name | | video name }"
|
||||
"{start_frame |0| Start frame }"
|
||||
"{frame_step |1| Frame step }"
|
||||
"{detector_model | | Path to detector's Caffe model }"
|
||||
"{detector_weights | | Path to detector's Caffe weights }"
|
||||
"{desired_class_id |-1| The desired class that should be tracked }"
|
||||
};
|
||||
|
||||
static void help()
|
||||
{
|
||||
cout << "\nThis example shows the functionality of \"Tracking-by-Matching\" approach:"
|
||||
" detector is used to detect objects on frames, \n"
|
||||
"matching is used to find correspondences between new detections and tracked objects.\n"
|
||||
"Detection is made by DNN detection network every `--frame_step` frame.\n"
|
||||
"Point a .prototxt file of the network as the parameter `--detector_model`, and a .caffemodel file"
|
||||
" as the parameter `--detector_weights`.\n"
|
||||
"(As an example of such detection network is a popular MobileNet_SSD network trained on VOC dataset.)\n"
|
||||
"If `--desired_class_id` parameter is set, the detection result is filtered by class id,"
|
||||
" returned by the detection network.\n"
|
||||
"(That is, if a detection net was trained on VOC dataset, then to track pedestrians point --desired_class_id=15)\n"
|
||||
"Example of <video_name> is in opencv_extra/testdata/cv/tracking/\n"
|
||||
"Call:\n"
|
||||
"./example_tracking_tracking_by_matching --video_name=<video_name> --detector_model=<detector_model_path> --detector_weights=<detector_weights_path> \\\n"
|
||||
" [--start_frame=<start_frame>] \\\n"
|
||||
" [--frame_step=<frame_step>] \\\n"
|
||||
" [--desired_class_id=<desired_class_id>]\n"
|
||||
<< endl;
|
||||
|
||||
cout << "\n\nHot keys: \n"
|
||||
"\tq - quit the program\n"
|
||||
"\tp - pause/resume video\n";
|
||||
}
|
||||
|
||||
cv::Ptr<ITrackerByMatching> createTrackerByMatchingWithFastDescriptor();
|
||||
|
||||
class DnnObjectDetector
|
||||
{
|
||||
public:
|
||||
DnnObjectDetector(const String& net_caffe_model_path, const String& net_caffe_weights_path,
|
||||
int desired_class_id=-1,
|
||||
float confidence_threshold = 0.2,
|
||||
//the following parameters are default for popular MobileNet_SSD caffe model
|
||||
const String& net_input_name="data",
|
||||
const String& net_output_name="detection_out",
|
||||
double net_scalefactor=0.007843,
|
||||
const Size& net_size = Size(300,300),
|
||||
const Scalar& net_mean = Scalar(127.5, 127.5, 127.5),
|
||||
bool net_swapRB=false)
|
||||
:desired_class_id(desired_class_id),
|
||||
confidence_threshold(confidence_threshold),
|
||||
net_input_name(net_input_name),
|
||||
net_output_name(net_output_name),
|
||||
net_scalefactor(net_scalefactor),
|
||||
net_size(net_size),
|
||||
net_mean(net_mean),
|
||||
net_swapRB(net_swapRB)
|
||||
{
|
||||
net = dnn::readNet(net_caffe_weights_path, net_caffe_model_path);
|
||||
if (net.empty())
|
||||
CV_Error(Error::StsError, "Cannot read Caffe net");
|
||||
}
|
||||
TrackedObjects detect(const cv::Mat& frame, int frame_idx)
|
||||
{
|
||||
Mat resized_frame;
|
||||
resize(frame, resized_frame, net_size);
|
||||
Mat inputBlob = cv::dnn::blobFromImage(resized_frame, net_scalefactor, net_size, net_mean, net_swapRB);
|
||||
|
||||
net.setInput(inputBlob, net_input_name);
|
||||
Mat detection = net.forward(net_output_name);
|
||||
Mat detection_as_mat(detection.size[2], detection.size[3], CV_32F, detection.ptr<float>());
|
||||
|
||||
TrackedObjects res;
|
||||
for (int i = 0; i < detection_as_mat.rows; i++)
|
||||
{
|
||||
float cur_confidence = detection_as_mat.at<float>(i, 2);
|
||||
int cur_class_id = static_cast<int>(detection_as_mat.at<float>(i, 1));
|
||||
int x_left = static_cast<int>(detection_as_mat.at<float>(i, 3) * frame.cols);
|
||||
int y_bottom = static_cast<int>(detection_as_mat.at<float>(i, 4) * frame.rows);
|
||||
int x_right = static_cast<int>(detection_as_mat.at<float>(i, 5) * frame.cols);
|
||||
int y_top = static_cast<int>(detection_as_mat.at<float>(i, 6) * frame.rows);
|
||||
|
||||
Rect cur_rect(x_left, y_bottom, (x_right - x_left), (y_top - y_bottom));
|
||||
|
||||
if (cur_confidence < confidence_threshold)
|
||||
continue;
|
||||
if ((desired_class_id >= 0) && (cur_class_id != desired_class_id))
|
||||
continue;
|
||||
|
||||
//clipping by frame size
|
||||
cur_rect = cur_rect & Rect(Point(), frame.size());
|
||||
if (cur_rect.empty())
|
||||
continue;
|
||||
|
||||
TrackedObject cur_obj(cur_rect, cur_confidence, frame_idx, -1);
|
||||
res.push_back(cur_obj);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
private:
|
||||
cv::dnn::Net net;
|
||||
int desired_class_id;
|
||||
float confidence_threshold;
|
||||
String net_input_name;
|
||||
String net_output_name;
|
||||
double net_scalefactor;
|
||||
Size net_size;
|
||||
Scalar net_mean;
|
||||
bool net_swapRB;
|
||||
};
|
||||
|
||||
cv::Ptr<ITrackerByMatching>
|
||||
createTrackerByMatchingWithFastDescriptor() {
|
||||
tbm::TrackerParams params;
|
||||
|
||||
cv::Ptr<ITrackerByMatching> tracker = createTrackerByMatching(params);
|
||||
|
||||
std::shared_ptr<IImageDescriptor> descriptor_fast =
|
||||
std::make_shared<ResizedImageDescriptor>(
|
||||
cv::Size(16, 32), cv::InterpolationFlags::INTER_LINEAR);
|
||||
std::shared_ptr<IDescriptorDistance> distance_fast =
|
||||
std::make_shared<MatchTemplateDistance>();
|
||||
|
||||
tracker->setDescriptorFast(descriptor_fast);
|
||||
tracker->setDistanceFast(distance_fast);
|
||||
|
||||
return tracker;
|
||||
}
|
||||
int main( int argc, char** argv ){
|
||||
CommandLineParser parser( argc, argv, keys );
|
||||
cv::Ptr<ITrackerByMatching> tracker = createTrackerByMatchingWithFastDescriptor();
|
||||
|
||||
String video_name = parser.get<String>("video_name");
|
||||
int start_frame = parser.get<int>("start_frame");
|
||||
int frame_step = parser.get<int>("frame_step");
|
||||
String detector_model = parser.get<String>("detector_model");
|
||||
String detector_weights = parser.get<String>("detector_weights");
|
||||
int desired_class_id = parser.get<int>("desired_class_id");
|
||||
|
||||
if( video_name.empty() || detector_model.empty() || detector_weights.empty() )
|
||||
{
|
||||
help();
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
//open the capture
|
||||
VideoCapture cap;
|
||||
cap.open( video_name );
|
||||
cap.set( CAP_PROP_POS_FRAMES, start_frame );
|
||||
|
||||
if( !cap.isOpened() )
|
||||
{
|
||||
help();
|
||||
cout << "***Could not initialize capturing...***\n";
|
||||
cout << "Current parameter's value: \n";
|
||||
parser.printMessage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// If you use the popular MobileNet_SSD detector, the default parameters may be used.
|
||||
// Otherwise, set your own parameters (net_mean, net_scalefactor, etc).
|
||||
DnnObjectDetector detector(detector_model, detector_weights, desired_class_id);
|
||||
|
||||
Mat frame;
|
||||
namedWindow( "Tracking by Matching", 1 );
|
||||
|
||||
int frame_counter = -1;
|
||||
int64 time_total = 0;
|
||||
bool paused = false;
|
||||
for ( ;; )
|
||||
{
|
||||
if( paused )
|
||||
{
|
||||
char c = (char) waitKey(30);
|
||||
if (c == 'p')
|
||||
paused = !paused;
|
||||
if (c == 'q')
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
cap >> frame;
|
||||
if(frame.empty()){
|
||||
break;
|
||||
}
|
||||
frame_counter++;
|
||||
if (frame_counter < start_frame)
|
||||
continue;
|
||||
if (frame_counter % frame_step != 0)
|
||||
continue;
|
||||
|
||||
|
||||
int64 frame_time = getTickCount();
|
||||
|
||||
TrackedObjects detections = detector.detect(frame, frame_counter);
|
||||
|
||||
// timestamp in milliseconds
|
||||
uint64_t cur_timestamp = static_cast<uint64_t>(1000.0 / 30 * frame_counter);
|
||||
tracker->process(frame, detections, cur_timestamp);
|
||||
|
||||
frame_time = getTickCount() - frame_time;
|
||||
time_total += frame_time;
|
||||
|
||||
// Drawing colored "worms" (tracks).
|
||||
frame = tracker->drawActiveTracks(frame);
|
||||
|
||||
|
||||
// Drawing all detected objects on a frame by BLUE COLOR
|
||||
for (const auto &detection : detections) {
|
||||
cv::rectangle(frame, detection.rect, cv::Scalar(255, 0, 0), 3);
|
||||
}
|
||||
|
||||
// Drawing tracked detections only by RED color and print ID and detection
|
||||
// confidence level.
|
||||
for (const auto &detection : tracker->trackedDetections()) {
|
||||
cv::rectangle(frame, detection.rect, cv::Scalar(0, 0, 255), 3);
|
||||
std::string text = std::to_string(detection.object_id) +
|
||||
" conf: " + std::to_string(detection.confidence);
|
||||
cv::putText(frame, text, detection.rect.tl(), cv::FONT_HERSHEY_COMPLEX,
|
||||
1.0, cv::Scalar(0, 0, 255), 3);
|
||||
}
|
||||
|
||||
imshow( "Tracking by Matching", frame );
|
||||
|
||||
char c = (char) waitKey( 2 );
|
||||
if (c == 'q')
|
||||
break;
|
||||
if (c == 'p')
|
||||
paused = !paused;
|
||||
}
|
||||
|
||||
double s = frame_counter / (time_total / getTickFrequency());
|
||||
printf("FPS: %f\n", s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else // #ifdef HAVE_OPENCV_DNN
|
||||
int main(int, char**){
|
||||
CV_Error(cv::Error::StsNotImplemented, "At the moment the sample 'tracking_by_matching' can work only when opencv_dnn module is built.");
|
||||
}
|
||||
#endif // #ifdef HAVE_OPENCV_DNN
|
||||
@@ -0,0 +1,128 @@
|
||||
#include <opencv2/core/utility.hpp>
|
||||
#include <opencv2/tracking.hpp>
|
||||
#include <opencv2/videoio.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include "samples_utility.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
// prototype of the functino for feature extractor
|
||||
void sobelExtractor(const Mat img, const Rect roi, Mat& feat);
|
||||
|
||||
int main( int argc, char** argv ){
|
||||
// show help
|
||||
if(argc<2){
|
||||
cout<<
|
||||
" Usage: tracker <video_name>\n"
|
||||
" examples:\n"
|
||||
" example_tracking_kcf Bolt/img/%04d.jpg\n"
|
||||
" example_tracking_kcf faceocc2.webm\n"
|
||||
<< endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// declares all required variables
|
||||
Rect roi;
|
||||
Mat frame;
|
||||
|
||||
//! [param]
|
||||
TrackerKCF::Params param;
|
||||
param.desc_pca = TrackerKCF::GRAY | TrackerKCF::CN;
|
||||
param.desc_npca = 0;
|
||||
param.compress_feature = true;
|
||||
param.compressed_size = 2;
|
||||
//! [param]
|
||||
|
||||
// create a tracker object
|
||||
//! [create]
|
||||
Ptr<TrackerKCF> tracker = TrackerKCF::create(param);
|
||||
//! [create]
|
||||
|
||||
//! [setextractor]
|
||||
tracker->setFeatureExtractor(sobelExtractor);
|
||||
//! [setextractor]
|
||||
|
||||
// set input video
|
||||
std::string video = argv[1];
|
||||
VideoCapture cap(video);
|
||||
|
||||
// get bounding box
|
||||
cap >> frame;
|
||||
roi=selectROI("tracker",frame);
|
||||
|
||||
//quit if ROI was not selected
|
||||
if(roi.width==0 || roi.height==0)
|
||||
return 0;
|
||||
|
||||
// initialize the tracker
|
||||
tracker->init(frame,roi);
|
||||
|
||||
// perform the tracking process
|
||||
printf("Start the tracking process, press ESC to quit.\n");
|
||||
for ( ;; ){
|
||||
// get frame from the video
|
||||
cap >> frame;
|
||||
|
||||
// stop the program if no more images
|
||||
if(frame.rows==0 || frame.cols==0)
|
||||
break;
|
||||
|
||||
// update the tracking result
|
||||
tracker->update(frame,roi);
|
||||
|
||||
// draw the tracked object
|
||||
rectangle( frame, roi, Scalar( 255, 0, 0 ), 2, 1 );
|
||||
|
||||
// show image with the tracked object
|
||||
imshow("tracker",frame);
|
||||
|
||||
//quit on ESC button
|
||||
if(waitKey(1)==27)break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sobelExtractor(const Mat img, const Rect roi, Mat& feat){
|
||||
Mat sobel[2];
|
||||
Mat patch;
|
||||
Rect region=roi;
|
||||
|
||||
//! [insideimage]
|
||||
// extract patch inside the image
|
||||
if(roi.x<0){region.x=0;region.width+=roi.x;}
|
||||
if(roi.y<0){region.y=0;region.height+=roi.y;}
|
||||
if(roi.x+roi.width>img.cols)region.width=img.cols-roi.x;
|
||||
if(roi.y+roi.height>img.rows)region.height=img.rows-roi.y;
|
||||
if(region.width>img.cols)region.width=img.cols;
|
||||
if(region.height>img.rows)region.height=img.rows;
|
||||
//! [insideimage]
|
||||
|
||||
patch=img(region).clone();
|
||||
cvtColor(patch,patch, COLOR_BGR2GRAY);
|
||||
|
||||
//! [padding]
|
||||
// add some padding to compensate when the patch is outside image border
|
||||
int addTop,addBottom, addLeft, addRight;
|
||||
addTop=region.y-roi.y;
|
||||
addBottom=(roi.height+roi.y>img.rows?roi.height+roi.y-img.rows:0);
|
||||
addLeft=region.x-roi.x;
|
||||
addRight=(roi.width+roi.x>img.cols?roi.width+roi.x-img.cols:0);
|
||||
|
||||
copyMakeBorder(patch,patch,addTop,addBottom,addLeft,addRight,BORDER_REPLICATE);
|
||||
//! [padding]
|
||||
|
||||
//! [sobel]
|
||||
Sobel(patch, sobel[0], CV_32F,1,0,1);
|
||||
Sobel(patch, sobel[1], CV_32F,0,1,1);
|
||||
|
||||
merge(sobel,2,feat);
|
||||
//! [sobel]
|
||||
|
||||
//! [postprocess]
|
||||
feat=feat/255.0-0.5; // normalize to range -0.5 .. 0.5
|
||||
//! [postprocess]
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
#include <opencv2/core/utility.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/tracking.hpp>
|
||||
#include <opencv2/videoio.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
int main( int argc, char** argv ){
|
||||
// show help
|
||||
//! [help]
|
||||
if(argc<2){
|
||||
cout<<
|
||||
" Usage: tracker <video_name>\n"
|
||||
" examples:\n"
|
||||
" example_tracking_kcf Bolt/img/%04d.jpg\n"
|
||||
" example_tracking_kcf faceocc2.webm\n"
|
||||
<< endl;
|
||||
return 0;
|
||||
}
|
||||
//! [help]
|
||||
|
||||
// declares all required variables
|
||||
//! [vars]
|
||||
Rect roi;
|
||||
Mat frame;
|
||||
//! [vars]
|
||||
|
||||
// create a tracker object
|
||||
//! [create]
|
||||
Ptr<Tracker> tracker = TrackerKCF::create();
|
||||
//! [create]
|
||||
|
||||
// set input video
|
||||
//! [setvideo]
|
||||
std::string video = argv[1];
|
||||
VideoCapture cap(video);
|
||||
//! [setvideo]
|
||||
|
||||
// get bounding box
|
||||
//! [getframe]
|
||||
cap >> frame;
|
||||
//! [getframe]
|
||||
//! [selectroi]
|
||||
roi=selectROI("tracker",frame);
|
||||
//! [selectroi]
|
||||
|
||||
//quit if ROI was not selected
|
||||
if(roi.width==0 || roi.height==0)
|
||||
return 0;
|
||||
|
||||
// initialize the tracker
|
||||
//! [init]
|
||||
tracker->init(frame,roi);
|
||||
//! [init]
|
||||
|
||||
// perform the tracking process
|
||||
printf("Start the tracking process, press ESC to quit.\n");
|
||||
for ( ;; ){
|
||||
// get frame from the video
|
||||
cap >> frame;
|
||||
|
||||
// stop the program if no more images
|
||||
if(frame.rows==0 || frame.cols==0)
|
||||
break;
|
||||
|
||||
// update the tracking result
|
||||
//! [update]
|
||||
tracker->update(frame,roi);
|
||||
//! [update]
|
||||
|
||||
//! [visualization]
|
||||
// draw the tracked object
|
||||
rectangle( frame, roi, Scalar( 255, 0, 0 ), 2, 1 );
|
||||
|
||||
// show image with the tracked object
|
||||
imshow("tracker",frame);
|
||||
//! [visualization]
|
||||
|
||||
//quit on ESC button
|
||||
if(waitKey(1)==27)break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*----------------------------------------------
|
||||
* Usage:
|
||||
* example_tracking_multitracker <video_name> [algorithm]
|
||||
*
|
||||
* example:
|
||||
* example_tracking_multitracker Bolt/img/%04d.jpg
|
||||
* example_tracking_multitracker faceocc2.webm KCF
|
||||
*--------------------------------------------------*/
|
||||
|
||||
#include <opencv2/core/utility.hpp>
|
||||
#include <opencv2/tracking.hpp>
|
||||
#include <opencv2/videoio.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include "samples_utility.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
int main( int argc, char** argv ){
|
||||
// show help
|
||||
if(argc<2){
|
||||
cout<<
|
||||
" Usage: example_tracking_multitracker <video_name> [algorithm]\n"
|
||||
" examples:\n"
|
||||
" example_tracking_multitracker Bolt/img/%04d.jpg\n"
|
||||
" example_tracking_multitracker faceocc2.webm MEDIANFLOW\n"
|
||||
<< endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// set the default tracking algorithm
|
||||
std::string trackingAlg = "KCF";
|
||||
|
||||
// set the tracking algorithm from parameter
|
||||
if(argc>2)
|
||||
trackingAlg = argv[2];
|
||||
|
||||
// create the tracker
|
||||
//! [create]
|
||||
legacy::MultiTracker trackers;
|
||||
//! [create]
|
||||
|
||||
// container of the tracked objects
|
||||
//! [roi]
|
||||
vector<Rect2d> objects;
|
||||
//! [roi]
|
||||
|
||||
// set input video
|
||||
std::string video = argv[1];
|
||||
VideoCapture cap(video);
|
||||
|
||||
Mat frame;
|
||||
|
||||
// get bounding box
|
||||
cap >> frame;
|
||||
//! [selectmulti]
|
||||
vector<Rect> ROIs;
|
||||
selectROIs("tracker",frame,ROIs);
|
||||
//! [selectmulti]
|
||||
|
||||
//quit when the tracked object(s) is not provided
|
||||
if(ROIs.size()<1)
|
||||
return 0;
|
||||
|
||||
// initialize the tracker
|
||||
//! [init]
|
||||
std::vector<Ptr<legacy::Tracker> > algorithms;
|
||||
for (size_t i = 0; i < ROIs.size(); i++)
|
||||
{
|
||||
algorithms.push_back(createTrackerByName_legacy(trackingAlg));
|
||||
objects.push_back(ROIs[i]);
|
||||
}
|
||||
|
||||
trackers.add(algorithms,frame,objects);
|
||||
//! [init]
|
||||
|
||||
// do the tracking
|
||||
printf("Start the tracking process, press ESC to quit.\n");
|
||||
for ( ;; ){
|
||||
// get frame from the video
|
||||
cap >> frame;
|
||||
|
||||
// stop the program if no more images
|
||||
if(frame.rows==0 || frame.cols==0)
|
||||
break;
|
||||
|
||||
//update the tracking result
|
||||
//! [update]
|
||||
trackers.update(frame);
|
||||
//! [update]
|
||||
|
||||
//! [result]
|
||||
// draw the tracked object
|
||||
for(unsigned i=0;i<trackers.getObjects().size();i++)
|
||||
rectangle( frame, trackers.getObjects()[i], Scalar( 255, 0, 0 ), 2, 1 );
|
||||
//! [result]
|
||||
|
||||
// show image with the tracked object
|
||||
imshow("tracker",frame);
|
||||
|
||||
//quit on ESC button
|
||||
if(waitKey(1)==27)break;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
#include <algorithm>
|
||||
#include <typeinfo>
|
||||
#include <cmath>
|
||||
#define WEIGHTED
|
||||
|
||||
namespace cv{
|
||||
|
||||
//!particle filtering class
|
||||
class PFSolver : public MinProblemSolver{
|
||||
public:
|
||||
class Function : public MinProblemSolver::Function
|
||||
{
|
||||
public:
|
||||
//!if parameters have no sense due to some reason (e.g. lie outside of function domain), this function "corrects" them,
|
||||
//!that is brings to the function domain
|
||||
virtual void correctParams(double* /*optParams*/)const{}
|
||||
//!is used when there is a dependence on the number of iterations done in calc(), note that levels are counted starting from 1
|
||||
virtual void setLevel(int /*level*/, int /*levelsNum*/){}
|
||||
};
|
||||
PFSolver();
|
||||
void getOptParam(OutputArray params)const;
|
||||
int iteration();
|
||||
double minimize(InputOutputArray x) CV_OVERRIDE;
|
||||
|
||||
void setParticlesNum(int num);
|
||||
int getParticlesNum();
|
||||
void setAlpha(double AlphaM);
|
||||
double getAlpha();
|
||||
void getParamsSTD(OutputArray std)const;
|
||||
void setParamsSTD(InputArray std);
|
||||
|
||||
Ptr<MinProblemSolver::Function> getFunction() const CV_OVERRIDE;
|
||||
void setFunction(const Ptr<MinProblemSolver::Function>& f) CV_OVERRIDE;
|
||||
TermCriteria getTermCriteria() const CV_OVERRIDE;
|
||||
void setTermCriteria(const TermCriteria& termcrit) CV_OVERRIDE;
|
||||
private:
|
||||
Mat_<double> _std,_particles,_logweight;
|
||||
Ptr<MinProblemSolver::Function> _Function;
|
||||
PFSolver::Function* _real_function;
|
||||
TermCriteria _termcrit;
|
||||
int _maxItNum,_iter,_particlesNum;
|
||||
double _alpha;
|
||||
inline void normalize(Mat_<double>& row);
|
||||
RNG rng;
|
||||
};
|
||||
|
||||
CV_EXPORTS_W Ptr<PFSolver> createPFSolver(const Ptr<MinProblemSolver::Function>& f=Ptr<MinProblemSolver::Function>(),InputArray std=Mat(),
|
||||
TermCriteria termcrit=TermCriteria(TermCriteria::MAX_ITER,5,0.0),int particlesNum=100,double alpha=0.6);
|
||||
|
||||
PFSolver::PFSolver(){
|
||||
_Function=Ptr<MinProblemSolver::Function>();
|
||||
_real_function=NULL;
|
||||
_std=Mat_<double>();
|
||||
rng=RNG(getTickCount());
|
||||
}
|
||||
void PFSolver::getOptParam(OutputArray params)const{
|
||||
params.create(1,_std.rows,CV_64FC1);
|
||||
Mat mat(1,_std.rows,CV_64FC1);
|
||||
#ifdef WEIGHTED
|
||||
mat.setTo(0.0);
|
||||
for(int i=0;i<_particles.rows;i++){
|
||||
mat+=_particles.row(i)/exp(-_logweight(0,i));
|
||||
}
|
||||
_real_function->correctParams((double*)mat.data);
|
||||
mat.copyTo(params);
|
||||
#else
|
||||
params.create(1,_std.rows,CV_64FC1);
|
||||
Mat optimus=_particles.row(std::max_element(_logweight.begin(),_logweight.end())-_logweight.begin());
|
||||
_real_function->correctParams(optimus.data);
|
||||
optimus.copyTo(params);
|
||||
#endif
|
||||
}
|
||||
int PFSolver::iteration(){
|
||||
if(_iter>=_maxItNum){
|
||||
return _maxItNum+1;
|
||||
}
|
||||
|
||||
_real_function->setLevel(_iter+1,_maxItNum);
|
||||
|
||||
//perturb
|
||||
for(int j=0;j<_particles.cols;j++){
|
||||
double sigma=_std(0,j);
|
||||
for(int i=0;i<_particles.rows;i++){
|
||||
_particles(i,j)+=rng.gaussian(sigma);
|
||||
}
|
||||
}
|
||||
|
||||
//measure
|
||||
for(int i=0;i<_particles.rows;i++){
|
||||
_real_function->correctParams((double*)_particles.row(i).data);
|
||||
_logweight(0,i)=-(_real_function->calc((double*)_particles.row(i).data));
|
||||
}
|
||||
//normalize
|
||||
normalize(_logweight);
|
||||
//replicate
|
||||
Mat_<double> new_particles(_particlesNum,_std.cols);
|
||||
int num_particles=0;
|
||||
for(int i=0;i<_particles.rows;i++){
|
||||
int num_replicons=cvFloor(new_particles.rows/exp(-_logweight(0,i)));
|
||||
for(int j=0;j<num_replicons;j++,num_particles++){
|
||||
_particles.row(i).copyTo(new_particles.row(num_particles));
|
||||
}
|
||||
}
|
||||
//Mat_<double> maxrow=_particles.row(std::max_element(_logweight.begin(),_logweight.end())-_logweight.begin());
|
||||
double max_element;
|
||||
minMaxLoc(_logweight, 0, &max_element);
|
||||
Mat_<double> maxrow=_particles.row((int)max_element);
|
||||
for(;num_particles<new_particles.rows;num_particles++){
|
||||
maxrow.copyTo(new_particles.row(num_particles));
|
||||
}
|
||||
|
||||
if(_particles.rows!=new_particles.rows){
|
||||
_particles=new_particles;
|
||||
}else{
|
||||
new_particles.copyTo(_particles);
|
||||
}
|
||||
_std=_std*_alpha;
|
||||
_iter++;
|
||||
return _iter;
|
||||
}
|
||||
double PFSolver::minimize(InputOutputArray x){
|
||||
CV_Assert(_Function.empty()==false);
|
||||
CV_Assert(_std.rows==1 && _std.cols>0);
|
||||
Mat mat_x=x.getMat();
|
||||
CV_Assert(mat_x.type()==CV_64FC1 && MIN(mat_x.rows,mat_x.cols)==1 && MAX(mat_x.rows,mat_x.cols)==_std.cols);
|
||||
|
||||
_iter=0;
|
||||
_particles=Mat_<double>(_particlesNum,_std.cols);
|
||||
if(mat_x.rows>1){
|
||||
mat_x=mat_x.t();
|
||||
}
|
||||
for(int i=0;i<_particles.rows;i++){
|
||||
mat_x.copyTo(_particles.row(i));
|
||||
}
|
||||
|
||||
_logweight.create(1,_particles.rows);
|
||||
_logweight.setTo(-log((double)_particles.rows));
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
void PFSolver::setParticlesNum(int num){
|
||||
CV_Assert(num>0);
|
||||
_particlesNum=num;
|
||||
}
|
||||
int PFSolver::getParticlesNum(){
|
||||
return _particlesNum;
|
||||
}
|
||||
void PFSolver::setAlpha(double AlphaM){
|
||||
CV_Assert(0<AlphaM && AlphaM<=1);
|
||||
_alpha=AlphaM;
|
||||
}
|
||||
double PFSolver::getAlpha(){
|
||||
return _alpha;
|
||||
}
|
||||
Ptr<MinProblemSolver::Function> PFSolver::getFunction() const{
|
||||
return _Function;
|
||||
}
|
||||
void PFSolver::setFunction(const Ptr<MinProblemSolver::Function>& f){
|
||||
CV_Assert(f.empty()==false);
|
||||
|
||||
Ptr<MinProblemSolver::Function> non_const_f(f);
|
||||
MinProblemSolver::Function* f_ptr=static_cast<MinProblemSolver::Function*>(non_const_f);
|
||||
|
||||
PFSolver::Function *pff=dynamic_cast<PFSolver::Function*>(f_ptr);
|
||||
CV_Assert(pff!=NULL);
|
||||
_Function=f;
|
||||
_real_function=pff;
|
||||
}
|
||||
TermCriteria PFSolver::getTermCriteria() const{
|
||||
return TermCriteria(TermCriteria::MAX_ITER,_maxItNum,0.0);
|
||||
}
|
||||
void PFSolver::setTermCriteria(const TermCriteria& termcrit){
|
||||
CV_Assert(termcrit.type==TermCriteria::MAX_ITER && termcrit.maxCount>0);
|
||||
_maxItNum=termcrit.maxCount;
|
||||
}
|
||||
void PFSolver::getParamsSTD(OutputArray std)const{
|
||||
std.create(1,_std.cols,CV_64FC1);
|
||||
_std.copyTo(std);
|
||||
}
|
||||
void PFSolver::setParamsSTD(InputArray std){
|
||||
Mat m=std.getMat();
|
||||
CV_Assert(MIN(m.cols,m.rows)==1 && m.type()==CV_64FC1);
|
||||
int ndim=MAX(m.cols,m.rows);
|
||||
if(ndim!=_std.cols){
|
||||
_std=Mat_<double>(1,ndim);
|
||||
}
|
||||
if(m.rows==1){
|
||||
m.copyTo(_std);
|
||||
}else{
|
||||
Mat std_t=Mat_<double>(ndim,1,(double*)_std.data);
|
||||
m.copyTo(std_t);
|
||||
}
|
||||
}
|
||||
|
||||
Ptr<PFSolver> createPFSolver(const Ptr<MinProblemSolver::Function>& f,InputArray std,TermCriteria termcrit,int particlesNum,double alpha){
|
||||
Ptr<PFSolver> ptr(new PFSolver());
|
||||
|
||||
if(f.empty()==false){
|
||||
ptr->setFunction(f);
|
||||
}
|
||||
Mat mystd=std.getMat();
|
||||
if(mystd.cols!=0 || mystd.rows!=0){
|
||||
ptr->setParamsSTD(std);
|
||||
}
|
||||
ptr->setTermCriteria(termcrit);
|
||||
ptr->setParticlesNum(particlesNum);
|
||||
ptr->setAlpha(alpha);
|
||||
return ptr;
|
||||
}
|
||||
void PFSolver::normalize(Mat_<double>& row){
|
||||
double logsum=0.0;
|
||||
//double max=*(std::max_element(row.begin(),row.end()));
|
||||
double max;
|
||||
minMaxLoc(row, 0, &max);
|
||||
row-=max;
|
||||
for(int i=0;i<row.cols;i++){
|
||||
logsum+=exp(row(0,i));
|
||||
}
|
||||
logsum=log(logsum);
|
||||
row-=logsum;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
#include <cmath>
|
||||
#define CLIP(x,a,b) MIN(MAX((x),(a)),(b))
|
||||
#define HIST_SIZE 50
|
||||
|
||||
namespace cv{
|
||||
|
||||
class TrackingFunctionPF : public PFSolver::Function{
|
||||
public:
|
||||
TrackingFunctionPF(const Mat& chosenRect);
|
||||
void update(const Mat& image);
|
||||
int getDims() const CV_OVERRIDE { return 4; }
|
||||
double calc(const double* x) const CV_OVERRIDE;
|
||||
void correctParams(double* pt)const CV_OVERRIDE;
|
||||
private:
|
||||
Mat _image;
|
||||
static inline Rect rectFromRow(const double* row);
|
||||
const int _nh,_ns,_nv;
|
||||
class TrackingHistogram{
|
||||
public:
|
||||
TrackingHistogram(const Mat& img,int nh,int ns,int nv);
|
||||
double dist(const TrackingHistogram& hist)const;
|
||||
private:
|
||||
Mat_<double> HShist, Vhist;
|
||||
};
|
||||
TrackingHistogram _origHist;
|
||||
const TrackingFunctionPF & operator = (const TrackingFunctionPF &);
|
||||
};
|
||||
|
||||
TrackingFunctionPF::TrackingHistogram::TrackingHistogram(const Mat& img,int nh,int ns,int nv){
|
||||
|
||||
Mat hsv;
|
||||
img.convertTo(hsv,CV_32F,1.0/255.0);
|
||||
cvtColor(hsv,hsv,COLOR_BGR2HSV);
|
||||
|
||||
HShist=Mat_<double>(nh,ns,0.0);
|
||||
Vhist=Mat_<double>(1,nv,0.0);
|
||||
|
||||
for(int i=0;i<img.rows;i++){
|
||||
for(int j=0;j<img.cols;j++){
|
||||
const Vec3f& pt=hsv.at<Vec3f>(i,j);
|
||||
|
||||
if(pt.val[1]>0.1 && pt.val[2]>0.2){
|
||||
HShist(MIN(nh-1,(int)(nh*pt.val[0]/360.0)),MIN(ns-1,(int)(ns*pt.val[1])))++;
|
||||
}else{
|
||||
Vhist(0,MIN(nv-1,(int)(nv*pt.val[2])))++;
|
||||
}
|
||||
}}
|
||||
|
||||
double total=*(sum(HShist)+sum(Vhist)).val;
|
||||
HShist/=total;
|
||||
Vhist/=total;
|
||||
}
|
||||
double TrackingFunctionPF::TrackingHistogram::dist(const TrackingHistogram& hist)const{
|
||||
double res=1.0;
|
||||
|
||||
for(int i=0;i<HShist.rows;i++){
|
||||
for(int j=0;j<HShist.cols;j++){
|
||||
res-=sqrt(HShist(i,j)*hist.HShist(i,j));
|
||||
}}
|
||||
for(int j=0;j<Vhist.cols;j++){
|
||||
res-=sqrt(Vhist(0,j)*hist.Vhist(0,j));
|
||||
}
|
||||
|
||||
return sqrt(res);
|
||||
}
|
||||
double TrackingFunctionPF::calc(const double* x) const{
|
||||
Rect rect=rectFromRow(x);
|
||||
if(rect.empty()){
|
||||
return 2.0;
|
||||
}
|
||||
return _origHist.dist(TrackingHistogram(_image(rect),_nh,_ns,_nv));
|
||||
}
|
||||
TrackingFunctionPF::TrackingFunctionPF(const Mat& chosenRect):_nh(HIST_SIZE),_ns(HIST_SIZE),_nv(HIST_SIZE),_origHist(chosenRect,_nh,_ns,_nv){
|
||||
}
|
||||
void TrackingFunctionPF::update(const Mat& image){
|
||||
_image=image;
|
||||
|
||||
TrackingHistogram hist(image,_nh,_ns,_nv);
|
||||
}
|
||||
void TrackingFunctionPF::correctParams(double* pt)const{
|
||||
pt[0]=CLIP(pt[0],0.0,_image.cols+0.9);
|
||||
pt[1]=CLIP(pt[1],0.0,_image.rows+0.9);
|
||||
pt[2]=CLIP(pt[2],0.0,_image.cols+0.9);
|
||||
pt[3]=CLIP(pt[3],0.0,_image.rows+0.9);
|
||||
if(pt[0]>pt[2]){
|
||||
double tmp=pt[0];
|
||||
pt[0]=pt[2];
|
||||
pt[2]=tmp;
|
||||
}
|
||||
if(pt[1]>pt[3]){
|
||||
double tmp=pt[1];
|
||||
pt[1]=pt[3];
|
||||
pt[3]=tmp;
|
||||
}
|
||||
}
|
||||
Rect TrackingFunctionPF::rectFromRow(const double* row){
|
||||
return Rect(Point_<int>((int)row[0],(int)row[1]),Point_<int>((int)row[2],(int)row[3]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,397 @@
|
||||
/*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) 2015, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "opencv2/tracking/kalman_filters.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
inline namespace kalman_filters {
|
||||
|
||||
void AugmentedUnscentedKalmanFilterParams::
|
||||
init( int dp, int mp, int cp, double processNoiseCovDiag, double measurementNoiseCovDiag,
|
||||
Ptr<UkfSystemModel> dynamicalSystem, int type )
|
||||
{
|
||||
CV_Assert( dp > 0 && mp > 0 );
|
||||
DP = dp;
|
||||
MP = mp;
|
||||
CP = std::max( cp, 0 );
|
||||
CV_Assert( type == CV_32F || type == CV_64F );
|
||||
dataType = type;
|
||||
|
||||
this->model = dynamicalSystem;
|
||||
|
||||
stateInit = Mat::zeros(DP, 1, type);
|
||||
errorCovInit = Mat::eye(DP, DP, type);
|
||||
|
||||
processNoiseCov = processNoiseCovDiag*Mat::eye(DP, DP, type);
|
||||
measurementNoiseCov = measurementNoiseCovDiag*Mat::eye(MP, MP, type);
|
||||
|
||||
alpha = 1e-3;
|
||||
k = 0.0;
|
||||
beta = 2.0;
|
||||
}
|
||||
|
||||
AugmentedUnscentedKalmanFilterParams::
|
||||
AugmentedUnscentedKalmanFilterParams( int dp, int mp, int cp, double processNoiseCovDiag, double measurementNoiseCovDiag,
|
||||
Ptr<UkfSystemModel> dynamicalSystem, int type )
|
||||
{
|
||||
init( dp, mp, cp, processNoiseCovDiag, measurementNoiseCovDiag, dynamicalSystem, type );
|
||||
}
|
||||
|
||||
|
||||
class AugmentedUnscentedKalmanFilterImpl: public UnscentedKalmanFilter
|
||||
{
|
||||
|
||||
int DP; // dimensionality of the state vector
|
||||
int MP; // dimensionality of the measurement vector
|
||||
int CP; // dimensionality of the control vector
|
||||
int DAug; // dimensionality of the augmented vector, DAug = 2*DP + MP
|
||||
int dataType; // type of elements of vectors and matrices
|
||||
|
||||
Mat state; // estimate of the system state (x*), DP x 1
|
||||
Mat errorCov; // estimate of the state cross-covariance matrix (P), DP x DP
|
||||
|
||||
Mat stateAug; // augmented state vector (xa*), DAug x 1,
|
||||
// xa* = ( x*
|
||||
// 0
|
||||
// ...
|
||||
// 0 )
|
||||
Mat errorCovAug; // estimate of the state cross-covariance matrix (Pa), DAug x DAug
|
||||
// Pa = ( P, 0, 0
|
||||
// 0, Q, 0
|
||||
// 0, 0, R )
|
||||
|
||||
Mat processNoiseCov; // process noise cross-covariance matrix (Q), DP x DP
|
||||
Mat measurementNoiseCov; // measurement noise cross-covariance matrix (R), MP x MP
|
||||
|
||||
Ptr<UkfSystemModel> model; // object of the class containing functions for computing the next state and the measurement.
|
||||
|
||||
// Parameters of algorithm
|
||||
double alpha; // parameter, default is 1e-3
|
||||
double k; // parameter, default is 0
|
||||
double beta; // parameter, default is 2.0
|
||||
|
||||
double lambda; // internal parameter, lambda = alpha*alpha*( DP + k ) - DP;
|
||||
double tmpLambda; // internal parameter, tmpLambda = alpha*alpha*( DP + k );
|
||||
|
||||
// Auxillary members
|
||||
Mat measurementEstimate; // estimate of current measurement (y*), MP x 1
|
||||
|
||||
Mat sigmaPoints; // set of sigma points ( x_i, i = 1..2*DP+1 ), DP x 2*DP+1
|
||||
|
||||
Mat transitionSPFuncVals; // set of state function values at sigma points ( f_i, i = 1..2*DP+1 ), DP x 2*DP+1
|
||||
Mat measurementSPFuncVals; // set of measurement function values at sigma points ( h_i, i = 1..2*DP+1 ), MP x 2*DP+1
|
||||
|
||||
Mat transitionSPFuncValsCenter; // set of state function values at sigma points minus estimate of state ( fc_i, i = 1..2*DP+1 ), DP x 2*DP+1
|
||||
Mat measurementSPFuncValsCenter; // set of measurement function values at sigma points minus estimate of measurement ( hc_i, i = 1..2*DP+1 ), MP x 2*DP+1
|
||||
|
||||
Mat Wm; // vector of weights for estimate mean, 2*DP+1 x 1
|
||||
Mat Wc; // matrix of weights for estimate covariance, 2*DP+1 x 2*DP+1
|
||||
|
||||
Mat gain; // Kalman gain matrix (K), DP x MP
|
||||
Mat xyCov; // estimate of the covariance between x* and y* (Sxy), DP x MP
|
||||
Mat yyCov; // estimate of the y* cross-covariance matrix (Syy), MP x MP
|
||||
|
||||
Mat r; // zero vector of process noise for getting transitionSPFuncVals,
|
||||
Mat q; // zero vector of measurement noise for getting measurementSPFuncVals
|
||||
|
||||
Mat getSigmaPoints(const Mat& mean, const Mat& covMatrix, double coef);
|
||||
|
||||
public:
|
||||
|
||||
AugmentedUnscentedKalmanFilterImpl(const AugmentedUnscentedKalmanFilterParams& params);
|
||||
~AugmentedUnscentedKalmanFilterImpl() CV_OVERRIDE;
|
||||
|
||||
Mat predict(InputArray control) CV_OVERRIDE;
|
||||
Mat correct(InputArray measurement) CV_OVERRIDE;
|
||||
|
||||
Mat getProcessNoiseCov() const CV_OVERRIDE;
|
||||
Mat getMeasurementNoiseCov() const CV_OVERRIDE;
|
||||
Mat getErrorCov() const CV_OVERRIDE;
|
||||
|
||||
Mat getState() const CV_OVERRIDE;
|
||||
|
||||
};
|
||||
|
||||
AugmentedUnscentedKalmanFilterImpl::AugmentedUnscentedKalmanFilterImpl(const AugmentedUnscentedKalmanFilterParams& params)
|
||||
{
|
||||
alpha = params.alpha;
|
||||
beta = params.beta;
|
||||
k = params.k;
|
||||
|
||||
CV_Assert( params.DP > 0 && params.MP > 0 );
|
||||
CV_Assert( params.dataType == CV_32F || params.dataType == CV_64F );
|
||||
DP = params.DP;
|
||||
MP = params.MP;
|
||||
CP = std::max( params.CP, 0 );
|
||||
dataType = params.dataType;
|
||||
|
||||
DAug = DP + DP + MP;
|
||||
|
||||
model = params.model;
|
||||
|
||||
stateAug = Mat::zeros( DAug, 1, dataType );
|
||||
state = stateAug( Rect( 0, 0, 1, DP ));
|
||||
|
||||
CV_Assert( params.stateInit.cols == 1 && params.stateInit.rows == DP );
|
||||
params.stateInit.copyTo(state);
|
||||
|
||||
CV_Assert( params.processNoiseCov.cols == DP && params.processNoiseCov.rows == DP );
|
||||
CV_Assert( params.measurementNoiseCov.cols == MP && params.measurementNoiseCov.rows == MP );
|
||||
processNoiseCov = params.processNoiseCov.clone();
|
||||
measurementNoiseCov = params.measurementNoiseCov.clone();
|
||||
|
||||
errorCovAug = Mat::zeros( DAug, DAug, dataType );
|
||||
errorCov = errorCovAug( Rect( 0, 0, DP, DP ) );
|
||||
Mat Q = errorCovAug( Rect( DP, DP, DP, DP ) );
|
||||
Mat R = errorCovAug( Rect( 2*DP, 2*DP, MP, MP ) );
|
||||
processNoiseCov.copyTo( Q );
|
||||
measurementNoiseCov.copyTo( R );
|
||||
|
||||
CV_Assert( params.errorCovInit.cols == DP && params.errorCovInit.rows == DP );
|
||||
params.errorCovInit.copyTo( errorCov );
|
||||
|
||||
measurementEstimate = Mat::zeros( MP, 1, dataType);
|
||||
|
||||
gain = Mat::zeros( DAug, DAug, dataType );
|
||||
|
||||
transitionSPFuncVals = Mat::zeros( DP, 2*DAug+1, dataType );
|
||||
measurementSPFuncVals = Mat::zeros( MP, 2*DAug+1, dataType );
|
||||
|
||||
transitionSPFuncValsCenter = Mat::zeros( DP, 2*DAug+1, dataType );
|
||||
measurementSPFuncValsCenter = Mat::zeros( MP, 2*DAug+1, dataType );
|
||||
|
||||
lambda = alpha*alpha*( DAug + k ) - DAug;
|
||||
tmpLambda = lambda + DAug;
|
||||
|
||||
double tmp2Lambda = 0.5/tmpLambda;
|
||||
|
||||
Wm = tmp2Lambda * Mat::ones( 2*DAug+1, 1, dataType );
|
||||
Wc = tmp2Lambda * Mat::eye( 2*DAug+1, 2*DAug+1, dataType );
|
||||
|
||||
if ( dataType == CV_64F )
|
||||
{
|
||||
Wm.at<double>(0,0) = lambda/tmpLambda;
|
||||
Wc.at<double>(0,0) = lambda/tmpLambda + 1.0 - alpha*alpha + beta;
|
||||
}
|
||||
else
|
||||
{
|
||||
Wm.at<float>(0,0) = (float)(lambda/tmpLambda);
|
||||
Wc.at<float>(0,0) = (float)(lambda/tmpLambda + 1.0 - alpha*alpha + beta);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
AugmentedUnscentedKalmanFilterImpl::~AugmentedUnscentedKalmanFilterImpl()
|
||||
{
|
||||
stateAug.release();
|
||||
errorCovAug.release();
|
||||
|
||||
state.release();
|
||||
errorCov.release();
|
||||
|
||||
processNoiseCov.release();
|
||||
measurementNoiseCov.release();
|
||||
|
||||
measurementEstimate.release();
|
||||
|
||||
sigmaPoints.release();
|
||||
|
||||
transitionSPFuncVals.release();
|
||||
measurementSPFuncVals.release();
|
||||
|
||||
transitionSPFuncValsCenter.release();
|
||||
measurementSPFuncValsCenter.release();
|
||||
|
||||
Wm.release();
|
||||
Wc.release();
|
||||
|
||||
gain.release();
|
||||
xyCov.release();
|
||||
yyCov.release();
|
||||
|
||||
r.release();
|
||||
q.release();
|
||||
|
||||
}
|
||||
|
||||
Mat AugmentedUnscentedKalmanFilterImpl::getSigmaPoints(const Mat &mean, const Mat &covMatrix, double coef)
|
||||
{
|
||||
// x_0 = mean
|
||||
// x_i = mean + coef * cholesky( covMatrix ), i = 1..n
|
||||
// x_(i+n) = mean - coef * cholesky( covMatrix ), i = 1..n
|
||||
|
||||
int n = mean.rows;
|
||||
Mat points = repeat(mean, 1, 2*n+1);
|
||||
|
||||
Mat covMatrixL = covMatrix.clone();
|
||||
|
||||
// covMatrixL = cholesky( covMatrix )
|
||||
if ( dataType == CV_64F )
|
||||
choleskyDecomposition<double>(
|
||||
covMatrix.ptr<double>(), covMatrix.step, covMatrix.rows,
|
||||
covMatrixL.ptr<double>(), covMatrixL.step );
|
||||
else if ( dataType == CV_32F )
|
||||
choleskyDecomposition<float>(
|
||||
covMatrix.ptr<float>(), covMatrix.step, covMatrix.rows,
|
||||
covMatrixL.ptr<float>(), covMatrixL.step );
|
||||
|
||||
covMatrixL = coef * covMatrixL;
|
||||
|
||||
Mat p_plus = points( Rect( 1, 0, n, n ) );
|
||||
Mat p_minus = points( Rect( n+1, 0, n, n ) );
|
||||
|
||||
add(p_plus, covMatrixL, p_plus);
|
||||
subtract(p_minus, covMatrixL, p_minus);
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
Mat AugmentedUnscentedKalmanFilterImpl::predict(InputArray _control)
|
||||
{
|
||||
Mat control = _control.getMat();
|
||||
// get sigma points from xa* and Pa
|
||||
sigmaPoints = getSigmaPoints( stateAug, errorCovAug, sqrt( tmpLambda ) );
|
||||
|
||||
// compute f-function values at sigma points
|
||||
// f_i = f(x_i[0:DP-1], control, x_i[DP:2*DP-1]), i = 0..2*DAug
|
||||
Mat x, fx;
|
||||
for ( int i = 0; i<2*DAug+1; i++)
|
||||
{
|
||||
x = sigmaPoints( Rect( i, 0, 1, DP) );
|
||||
q = sigmaPoints( Rect( i, DP, 1, DP) );
|
||||
fx = transitionSPFuncVals( Rect( i, 0, 1, DP) );
|
||||
model->stateConversionFunction( x, control, q, fx );
|
||||
}
|
||||
|
||||
// compute the estimate of state as mean f-function value at sigma point
|
||||
// x* = SUM_{i=0}^{2*DAug}( Wm[i]*f_i )
|
||||
state = transitionSPFuncVals * Wm;
|
||||
|
||||
// compute f-function values at sigma points minus estimate of state
|
||||
// fc_i = f_i - x*, i = 0..2*DAug
|
||||
subtract(transitionSPFuncVals, repeat( state, 1, 2*DAug+1 ), transitionSPFuncValsCenter);
|
||||
|
||||
// compute the estimate of the state cross-covariance matrix
|
||||
// P = SUM_{i=0}^{2*DAug}( Wc[i]*fc_i*fc_i.t )
|
||||
errorCov = transitionSPFuncValsCenter * Wc * transitionSPFuncValsCenter.t();
|
||||
|
||||
return state.clone();
|
||||
}
|
||||
|
||||
Mat AugmentedUnscentedKalmanFilterImpl::correct(InputArray _measurement)
|
||||
{
|
||||
Mat measurement = _measurement.getMat();
|
||||
// get sigma points from xa* and Pa
|
||||
sigmaPoints = getSigmaPoints( stateAug, errorCovAug, sqrt( tmpLambda ) );
|
||||
|
||||
// compute h-function values at sigma points
|
||||
// h_i = h(x_i[0:DP-1], x_i[2*DP:DAug-1]), i = 0..2*DAug
|
||||
Mat x, hx;
|
||||
measurementEstimate.setTo(0);
|
||||
for ( int i = 0; i<2*DAug+1; i++)
|
||||
{
|
||||
x = transitionSPFuncVals( Rect( i, 0, 1, DP) );
|
||||
r = sigmaPoints( Rect( i, 2*DP, 1, MP) );
|
||||
hx = measurementSPFuncVals( Rect( i, 0, 1, MP) );
|
||||
model->measurementFunction( x, r, hx );
|
||||
}
|
||||
|
||||
// compute the estimate of measurement as mean h-function value at sigma point
|
||||
// y* = SUM_{i=0}^{2*DAug}( Wm[i]*h_i )
|
||||
measurementEstimate = measurementSPFuncVals * Wm;
|
||||
|
||||
// compute h-function values at sigma points minus estimate of state
|
||||
// hc_i = h_i - y*, i = 0..2*DAug
|
||||
subtract(measurementSPFuncVals, repeat( measurementEstimate, 1, 2*DAug+1 ), measurementSPFuncValsCenter);
|
||||
|
||||
// compute the estimate of the y* cross-covariance matrix
|
||||
// Syy = SUM_{i=0}^{2*DAug}( Wc[i]*hc_i*hc_i.t )
|
||||
yyCov = measurementSPFuncValsCenter * Wc * measurementSPFuncValsCenter.t();
|
||||
|
||||
// compute the estimate of the covariance between x* and y*
|
||||
// Sxy = SUM_{i=0}^{2*DAug}( Wc[i]*fc_i*hc_i.t )
|
||||
xyCov = transitionSPFuncValsCenter * Wc * measurementSPFuncValsCenter.t();
|
||||
|
||||
// compute the Kalman gain matrix
|
||||
// K = Sxy * Syy^(-1)
|
||||
gain = xyCov * yyCov.inv(DECOMP_SVD);
|
||||
|
||||
// compute the corrected estimate of state
|
||||
// x* = x* + K*(y - y*), y - current measurement
|
||||
state = state + gain * ( measurement - measurementEstimate );
|
||||
|
||||
// compute the corrected estimate of the state cross-covariance matrix
|
||||
// P = P - K*Sxy.t
|
||||
errorCov = errorCov - gain * xyCov.t();
|
||||
|
||||
return state.clone();
|
||||
}
|
||||
|
||||
Mat AugmentedUnscentedKalmanFilterImpl::getProcessNoiseCov() const
|
||||
{
|
||||
return processNoiseCov.clone();
|
||||
}
|
||||
|
||||
Mat AugmentedUnscentedKalmanFilterImpl::getMeasurementNoiseCov() const
|
||||
{
|
||||
return measurementNoiseCov.clone();
|
||||
}
|
||||
|
||||
Mat AugmentedUnscentedKalmanFilterImpl::getErrorCov() const
|
||||
{
|
||||
return errorCov.clone();
|
||||
}
|
||||
|
||||
Mat AugmentedUnscentedKalmanFilterImpl::getState() const
|
||||
{
|
||||
return state.clone();
|
||||
}
|
||||
|
||||
Ptr<UnscentedKalmanFilter> createAugmentedUnscentedKalmanFilter(const AugmentedUnscentedKalmanFilterParams ¶ms)
|
||||
{
|
||||
Ptr<UnscentedKalmanFilter> kfu( new AugmentedUnscentedKalmanFilterImpl(params) );
|
||||
return kfu;
|
||||
}
|
||||
|
||||
}}}} // namespace
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,147 @@
|
||||
/*///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "gtrUtils.hpp"
|
||||
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace gtr
|
||||
{
|
||||
|
||||
double generateRandomLaplacian(double b, double m)
|
||||
{
|
||||
double t = (double)rand() / (RAND_MAX);
|
||||
double n = (double)rand() / (RAND_MAX);
|
||||
|
||||
if (t > 0.5)
|
||||
return m + b*log(n);
|
||||
else
|
||||
return m - b*log(n);
|
||||
}
|
||||
|
||||
Rect2f anno2rect(std::vector<Point2f> annoBB)
|
||||
{
|
||||
Rect2f rectBB;
|
||||
rectBB.x = min(annoBB[0].x, annoBB[1].x);
|
||||
rectBB.y = min(annoBB[0].y, annoBB[2].y);
|
||||
rectBB.width = fabs(annoBB[0].x - annoBB[1].x);
|
||||
rectBB.height = fabs(annoBB[0].y - annoBB[2].y);
|
||||
|
||||
return rectBB;
|
||||
}
|
||||
|
||||
std::vector <TrainingSample> gatherFrameSamples(Mat prevFrame, Mat currFrame, Rect2f prevBB, Rect2f currBB)
|
||||
{
|
||||
std::vector <TrainingSample> trainingSamples;
|
||||
Point2f currCenter, prevCenter;
|
||||
Rect2f targetPatchRect, searchPatchRect;
|
||||
Mat targetPatch, searchPatch;
|
||||
Mat prevFramePadded, currFramePadded;
|
||||
|
||||
//Crop Target Patch
|
||||
|
||||
//Padding
|
||||
|
||||
//Previous frame GTBBs center
|
||||
prevCenter.x = prevBB.x + prevBB.width / 2;
|
||||
prevCenter.y = prevBB.y + prevBB.height / 2;
|
||||
|
||||
targetPatchRect.width = (float)(prevBB.width*padTarget);
|
||||
targetPatchRect.height = (float)(prevBB.height*padTarget);
|
||||
targetPatchRect.x = (float)(prevCenter.x - prevBB.width*padTarget / 2.0 + targetPatchRect.width);
|
||||
targetPatchRect.y = (float)(prevCenter.y - prevBB.height*padTarget / 2.0 + targetPatchRect.height);
|
||||
|
||||
copyMakeBorder(prevFrame, prevFramePadded, (int)targetPatchRect.height, (int)targetPatchRect.height, (int)targetPatchRect.width, (int)targetPatchRect.width, BORDER_REPLICATE);
|
||||
|
||||
targetPatch = prevFramePadded(targetPatchRect);
|
||||
|
||||
|
||||
for (int i = 0; i < samplesInFrame; i++)
|
||||
{
|
||||
TrainingSample sample;
|
||||
|
||||
//Current frame GTBBs center
|
||||
currCenter.x = (float)(currBB.x + currBB.width / 2.0);
|
||||
currCenter.y = (float)(currBB.y + currBB.height / 2.0);
|
||||
|
||||
//Generate and add random Laplacian distribution (Scaling from target size)
|
||||
double dx, dy, ds;
|
||||
dx = generateRandomLaplacian(bX, 0)*prevBB.width;
|
||||
dy = generateRandomLaplacian(bY, 0)*prevBB.height;
|
||||
ds = generateRandomLaplacian(bS, 1);
|
||||
|
||||
//Limit coefficients
|
||||
dx = min(dx, (double)prevBB.width);
|
||||
dx = max(dx, (double)-prevBB.width);
|
||||
dy = min(dy, (double)prevBB.height);
|
||||
dy = max(dy, (double)-prevBB.height);
|
||||
ds = min(ds, Ymax);
|
||||
ds = max(ds, Ymin);
|
||||
|
||||
searchPatchRect.width = (float)(prevBB.width*padSearch*ds);
|
||||
searchPatchRect.height =(float)(prevBB.height*padSearch*ds);
|
||||
searchPatchRect.x = (float)(currCenter.x + dx - searchPatchRect.width / 2.0 + searchPatchRect.width);
|
||||
searchPatchRect.y = (float)(currCenter.y + dy - searchPatchRect.height / 2.0 + searchPatchRect.height);
|
||||
copyMakeBorder(currFrame, currFramePadded, (int)searchPatchRect.height, (int)searchPatchRect.height, (int)searchPatchRect.width, (int)searchPatchRect.width, BORDER_REPLICATE);
|
||||
searchPatch = currFramePadded(searchPatchRect);
|
||||
|
||||
//Calculate Relative GTBB in search patch
|
||||
Rect2f relGTBB;
|
||||
relGTBB.width = currBB.width;
|
||||
relGTBB.height = currBB.height;
|
||||
relGTBB.x = currBB.x - searchPatchRect.x + searchPatchRect.width;
|
||||
relGTBB.y = currBB.y - searchPatchRect.y + searchPatchRect.height;
|
||||
|
||||
//Link to the sample struct
|
||||
sample.targetPatch = targetPatch.clone();
|
||||
sample.searchPatch = searchPatch.clone();
|
||||
sample.targetBB = relGTBB;
|
||||
|
||||
trainingSamples.push_back(sample);
|
||||
}
|
||||
|
||||
return trainingSamples;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
#ifndef OPENCV_GTR_UTILS
|
||||
#define OPENCV_GTR_UTILS
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace gtr
|
||||
{
|
||||
|
||||
//Number of samples in batch
|
||||
const int samplesInBatch = 50;
|
||||
|
||||
//Number of samples to mine from video frame
|
||||
const int samplesInFrame = 10;
|
||||
|
||||
//Number of samples to mine from still image
|
||||
const int samplesInImage = 10;
|
||||
|
||||
//Padding coefficients for Target/Search Region
|
||||
const double padTarget = 2.0;
|
||||
const double padSearch = 2.0;
|
||||
|
||||
//Scale parameters for Laplace distribution for Translation/Scale
|
||||
const double bX = 1.0/10;
|
||||
const double bY = 1.0/10;
|
||||
const double bS = 1.0/15;
|
||||
|
||||
//Limits of scale changes
|
||||
const double Ymax = 1.4;
|
||||
const double Ymin = 0.6;
|
||||
|
||||
//Lower boundary constraints for random samples (sample should include X% of target BB)
|
||||
const double minX = 0.5;
|
||||
const double minY = 0.5;
|
||||
|
||||
//Structure of sample for training
|
||||
struct TrainingSample
|
||||
{
|
||||
Mat targetPatch;
|
||||
Mat searchPatch;
|
||||
//Output bounding box on search patch
|
||||
Rect2f targetBB;
|
||||
};
|
||||
|
||||
//Laplacian distribution
|
||||
double generateRandomLaplacian(double b, double m);
|
||||
|
||||
//Convert ALOV300++ anno coordinates to Rectangle BB
|
||||
Rect2f anno2rect(std::vector<Point2f> annoBB);
|
||||
|
||||
//Gather samples from random video frame
|
||||
std::vector <TrainingSample> gatherFrameSamples(Mat prevFrame, Mat currFrame, Rect2f prevBB, Rect2f currBB);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,176 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "kuhn_munkres.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
KuhnMunkres::KuhnMunkres() : n_() {}
|
||||
|
||||
std::vector<size_t> KuhnMunkres::Solve(const cv::Mat& dissimilarity_matrix) {
|
||||
CV_Assert(dissimilarity_matrix.type() == CV_32F);
|
||||
double min_val;
|
||||
cv::minMaxLoc(dissimilarity_matrix, &min_val);
|
||||
CV_Assert(min_val >= 0);
|
||||
|
||||
n_ = std::max(dissimilarity_matrix.rows, dissimilarity_matrix.cols);
|
||||
dm_ = cv::Mat(n_, n_, CV_32F, cv::Scalar(0));
|
||||
marked_ = cv::Mat(n_, n_, CV_8S, cv::Scalar(0));
|
||||
points_ = std::vector<cv::Point>(n_ * 2);
|
||||
|
||||
dissimilarity_matrix.copyTo(dm_(
|
||||
cv::Rect(0, 0, dissimilarity_matrix.cols, dissimilarity_matrix.rows)));
|
||||
|
||||
is_row_visited_ = std::vector<int>(n_, 0);
|
||||
is_col_visited_ = std::vector<int>(n_, 0);
|
||||
|
||||
Run();
|
||||
|
||||
std::vector<size_t> results(static_cast<size_t>(marked_.rows), static_cast<size_t>(-1));
|
||||
for (int i = 0; i < marked_.rows; i++) {
|
||||
const auto ptr = marked_.ptr<char>(i);
|
||||
for (int j = 0; j < marked_.cols; j++) {
|
||||
if (ptr[j] == kStar) {
|
||||
results[i] = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
void KuhnMunkres::TrySimpleCase() {
|
||||
auto is_row_visited = std::vector<int>(n_, 0);
|
||||
auto is_col_visited = std::vector<int>(n_, 0);
|
||||
|
||||
for (int row = 0; row < n_; row++) {
|
||||
auto ptr = dm_.ptr<float>(row);
|
||||
auto marked_ptr = marked_.ptr<char>(row);
|
||||
auto min_val = *std::min_element(ptr, ptr + n_);
|
||||
for (int col = 0; col < n_; col++) {
|
||||
ptr[col] -= min_val;
|
||||
if (ptr[col] == 0 && !is_col_visited[col] && !is_row_visited[row]) {
|
||||
marked_ptr[col] = kStar;
|
||||
is_col_visited[col] = 1;
|
||||
is_row_visited[row] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool KuhnMunkres::CheckIfOptimumIsFound() {
|
||||
int count = 0;
|
||||
for (int i = 0; i < n_; i++) {
|
||||
const auto marked_ptr = marked_.ptr<char>(i);
|
||||
for (int j = 0; j < n_; j++) {
|
||||
if (marked_ptr[j] == kStar) {
|
||||
is_col_visited_[j] = 1;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count >= n_;
|
||||
}
|
||||
|
||||
cv::Point KuhnMunkres::FindUncoveredMinValPos() {
|
||||
auto min_val = std::numeric_limits<float>::max();
|
||||
cv::Point min_val_pos(-1, -1);
|
||||
for (int i = 0; i < n_; i++) {
|
||||
if (!is_row_visited_[i]) {
|
||||
auto dm_ptr = dm_.ptr<float>(i);
|
||||
for (int j = 0; j < n_; j++) {
|
||||
if (!is_col_visited_[j] && dm_ptr[j] < min_val) {
|
||||
min_val = dm_ptr[j];
|
||||
min_val_pos = cv::Point(j, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return min_val_pos;
|
||||
}
|
||||
|
||||
void KuhnMunkres::UpdateDissimilarityMatrix(float val) {
|
||||
for (int i = 0; i < n_; i++) {
|
||||
auto dm_ptr = dm_.ptr<float>(i);
|
||||
for (int j = 0; j < n_; j++) {
|
||||
if (is_row_visited_[i]) dm_ptr[j] += val;
|
||||
if (!is_col_visited_[j]) dm_ptr[j] -= val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int KuhnMunkres::FindInRow(int row, int what) {
|
||||
for (int j = 0; j < n_; j++) {
|
||||
if (marked_.at<char>(row, j) == what) {
|
||||
return j;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int KuhnMunkres::FindInCol(int col, int what) {
|
||||
for (int i = 0; i < n_; i++) {
|
||||
if (marked_.at<char>(i, col) == what) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void KuhnMunkres::Run() {
|
||||
TrySimpleCase();
|
||||
while (!CheckIfOptimumIsFound()) {
|
||||
while (true) {
|
||||
auto point = FindUncoveredMinValPos();
|
||||
auto min_val = dm_.at<float>(point.y, point.x);
|
||||
if (min_val > 0) {
|
||||
UpdateDissimilarityMatrix(min_val);
|
||||
} else {
|
||||
marked_.at<char>(point.y, point.x) = kPrime;
|
||||
int col = FindInRow(point.y, kStar);
|
||||
if (col >= 0) {
|
||||
is_row_visited_[point.y] = 1;
|
||||
is_col_visited_[col] = 0;
|
||||
} else {
|
||||
int count = 0;
|
||||
points_[count] = point;
|
||||
|
||||
while (true) {
|
||||
int row = FindInCol(points_[count].x, kStar);
|
||||
if (row >= 0) {
|
||||
count++;
|
||||
points_[count] = cv::Point(points_[count - 1].x, row);
|
||||
int col1 = FindInRow(points_[count].y, kPrime);
|
||||
count++;
|
||||
points_[count] = cv::Point(col1, points_[count - 1].y);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < count + 1; i++) {
|
||||
auto& mark = marked_.at<char>(points_[i].y, points_[i].x);
|
||||
mark = mark == kStar ? 0 : kStar;
|
||||
}
|
||||
|
||||
is_row_visited_ = std::vector<int>(n_, 0);
|
||||
is_col_visited_ = std::vector<int>(n_, 0);
|
||||
|
||||
marked_.setTo(0, marked_ == kPrime);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}}} // namespace
|
||||
@@ -0,0 +1,60 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef __OPENCV_TRACKING_KUHN_MUNKRES_HPP__
|
||||
#define __OPENCV_TRACKING_KUHN_MUNKRES_HPP__
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
///
|
||||
/// \brief The KuhnMunkres class
|
||||
///
|
||||
/// Solves the assignment problem.
|
||||
///
|
||||
class KuhnMunkres {
|
||||
public:
|
||||
KuhnMunkres();
|
||||
|
||||
///
|
||||
/// \brief Solves the assignment problem for given dissimilarity matrix.
|
||||
/// It returns a vector that where each element is a column index for
|
||||
/// corresponding row (e.g. result[0] stores optimal column index for very
|
||||
/// first row in the dissimilarity matrix).
|
||||
/// \param dissimilarity_matrix CV_32F dissimilarity matrix.
|
||||
/// \return Optimal column index for each row. -1 means that there is no
|
||||
/// column for row.
|
||||
///
|
||||
std::vector<size_t> Solve(const cv::Mat &dissimilarity_matrix);
|
||||
|
||||
private:
|
||||
static constexpr int kStar = 1;
|
||||
static constexpr int kPrime = 2;
|
||||
|
||||
cv::Mat dm_;
|
||||
cv::Mat marked_;
|
||||
std::vector<cv::Point> points_;
|
||||
|
||||
std::vector<int> is_row_visited_;
|
||||
std::vector<int> is_col_visited_;
|
||||
|
||||
int n_;
|
||||
|
||||
void TrySimpleCase();
|
||||
bool CheckIfOptimumIsFound();
|
||||
cv::Point FindUncoveredMinValPos();
|
||||
void UpdateDissimilarityMatrix(float val);
|
||||
int FindInRow(int row, int what);
|
||||
int FindInCol(int col, int what);
|
||||
void Run();
|
||||
};
|
||||
|
||||
}}} // namespace
|
||||
#endif // #ifndef __OPENCV_TRACKING_KUHN_MUNKRES_HPP__
|
||||
@@ -0,0 +1,134 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "opencv2/tracking/tracking_legacy.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace legacy {
|
||||
inline namespace tracking {
|
||||
|
||||
Tracker::Tracker()
|
||||
{
|
||||
isInit = false;
|
||||
}
|
||||
|
||||
Tracker::~Tracker()
|
||||
{
|
||||
}
|
||||
|
||||
bool Tracker::init( InputArray image, const Rect2d& boundingBox )
|
||||
{
|
||||
|
||||
if( isInit )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( image.empty() )
|
||||
return false;
|
||||
|
||||
sampler = Ptr<TrackerContribSampler>( new TrackerContribSampler() );
|
||||
featureSet = Ptr<TrackerContribFeatureSet>( new TrackerContribFeatureSet() );
|
||||
model = Ptr<TrackerModel>();
|
||||
|
||||
bool initTracker = initImpl( image.getMat(), boundingBox );
|
||||
|
||||
if (initTracker)
|
||||
{
|
||||
isInit = true;
|
||||
}
|
||||
|
||||
return initTracker;
|
||||
}
|
||||
|
||||
bool Tracker::update( InputArray image, Rect2d& boundingBox )
|
||||
{
|
||||
|
||||
if( !isInit )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( image.empty() )
|
||||
return false;
|
||||
|
||||
return updateImpl( image.getMat(), boundingBox );
|
||||
}
|
||||
|
||||
|
||||
|
||||
class LegacyTrackerWrapper : public cv::Tracker
|
||||
{
|
||||
const Ptr<legacy::Tracker> legacy_tracker_;
|
||||
public:
|
||||
LegacyTrackerWrapper(const Ptr<legacy::Tracker>& legacy_tracker) : legacy_tracker_(legacy_tracker)
|
||||
{
|
||||
CV_Assert(legacy_tracker_);
|
||||
}
|
||||
virtual ~LegacyTrackerWrapper() CV_OVERRIDE {};
|
||||
|
||||
void init(InputArray image, const Rect& boundingBox) CV_OVERRIDE
|
||||
{
|
||||
CV_DbgAssert(legacy_tracker_);
|
||||
legacy_tracker_->init(image, (Rect2d)boundingBox);
|
||||
}
|
||||
|
||||
bool update(InputArray image, CV_OUT Rect& boundingBox) CV_OVERRIDE
|
||||
{
|
||||
CV_DbgAssert(legacy_tracker_);
|
||||
Rect2d boundingBox2d;
|
||||
bool res = legacy_tracker_->update(image, boundingBox2d);
|
||||
int x1 = cvRound(boundingBox2d.x);
|
||||
int y1 = cvRound(boundingBox2d.y);
|
||||
int x2 = cvRound(boundingBox2d.x + boundingBox2d.width);
|
||||
int y2 = cvRound(boundingBox2d.y + boundingBox2d.height);
|
||||
boundingBox = Rect(x1, y1, x2 - x1, y2 - y1) & Rect(Point(0, 0), image.size());
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
CV_EXPORTS_W Ptr<cv::Tracker> upgradeTrackingAPI(const Ptr<legacy::Tracker>& legacy_tracker)
|
||||
{
|
||||
return makePtr<LegacyTrackerWrapper>(legacy_tracker);
|
||||
}
|
||||
|
||||
}}} // namespace
|
||||
@@ -0,0 +1,159 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "opencv2/tracking/tracking_legacy.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace legacy {
|
||||
inline namespace tracking {
|
||||
namespace impl {
|
||||
|
||||
class TrackerCSRTImpl CV_FINAL : public legacy::TrackerCSRT
|
||||
{
|
||||
public:
|
||||
cv::tracking::impl::TrackerCSRTImpl impl;
|
||||
|
||||
TrackerCSRTImpl(const legacy::TrackerCSRT::Params ¶meters)
|
||||
: impl(parameters)
|
||||
{
|
||||
isInit = false;
|
||||
}
|
||||
|
||||
void read(const FileNode& fn) CV_OVERRIDE
|
||||
{
|
||||
static_cast<legacy::TrackerCSRT::Params&>(impl.params).read(fn);
|
||||
}
|
||||
void write(FileStorage& fs) const CV_OVERRIDE
|
||||
{
|
||||
static_cast<const legacy::TrackerCSRT::Params&>(impl.params).write(fs);
|
||||
}
|
||||
|
||||
bool initImpl(const Mat& image, const Rect2d& boundingBox) CV_OVERRIDE
|
||||
{
|
||||
impl.init(image, boundingBox);
|
||||
model = impl.model;
|
||||
sampler = makePtr<TrackerContribSampler>();
|
||||
featureSet = makePtr<TrackerContribFeatureSet>();
|
||||
isInit = true;
|
||||
return true;
|
||||
}
|
||||
bool updateImpl(const Mat& image, Rect2d& boundingBox) CV_OVERRIDE
|
||||
{
|
||||
Rect bb;
|
||||
bool res = impl.update(image, bb);
|
||||
boundingBox = bb;
|
||||
return res;
|
||||
}
|
||||
|
||||
virtual void setInitialMask(InputArray mask) CV_OVERRIDE
|
||||
{
|
||||
impl.setInitialMask(mask);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void legacy::TrackerCSRT::Params::read(const FileNode& fn)
|
||||
{
|
||||
*this = TrackerCSRT::Params();
|
||||
if(!fn["padding"].empty())
|
||||
fn["padding"] >> padding;
|
||||
if(!fn["template_size"].empty())
|
||||
fn["template_size"] >> template_size;
|
||||
if(!fn["gsl_sigma"].empty())
|
||||
fn["gsl_sigma"] >> gsl_sigma;
|
||||
if(!fn["hog_orientations"].empty())
|
||||
fn["hog_orientations"] >> hog_orientations;
|
||||
if(!fn["num_hog_channels_used"].empty())
|
||||
fn["num_hog_channels_used"] >> num_hog_channels_used;
|
||||
if(!fn["hog_clip"].empty())
|
||||
fn["hog_clip"] >> hog_clip;
|
||||
if(!fn["use_hog"].empty())
|
||||
fn["use_hog"] >> use_hog;
|
||||
if(!fn["use_color_names"].empty())
|
||||
fn["use_color_names"] >> use_color_names;
|
||||
if(!fn["use_gray"].empty())
|
||||
fn["use_gray"] >> use_gray;
|
||||
if(!fn["use_rgb"].empty())
|
||||
fn["use_rgb"] >> use_rgb;
|
||||
if(!fn["window_function"].empty())
|
||||
fn["window_function"] >> window_function;
|
||||
if(!fn["kaiser_alpha"].empty())
|
||||
fn["kaiser_alpha"] >> kaiser_alpha;
|
||||
if(!fn["cheb_attenuation"].empty())
|
||||
fn["cheb_attenuation"] >> cheb_attenuation;
|
||||
if(!fn["filter_lr"].empty())
|
||||
fn["filter_lr"] >> filter_lr;
|
||||
if(!fn["admm_iterations"].empty())
|
||||
fn["admm_iterations"] >> admm_iterations;
|
||||
if(!fn["number_of_scales"].empty())
|
||||
fn["number_of_scales"] >> number_of_scales;
|
||||
if(!fn["scale_sigma_factor"].empty())
|
||||
fn["scale_sigma_factor"] >> scale_sigma_factor;
|
||||
if(!fn["scale_model_max_area"].empty())
|
||||
fn["scale_model_max_area"] >> scale_model_max_area;
|
||||
if(!fn["scale_lr"].empty())
|
||||
fn["scale_lr"] >> scale_lr;
|
||||
if(!fn["scale_step"].empty())
|
||||
fn["scale_step"] >> scale_step;
|
||||
if(!fn["use_channel_weights"].empty())
|
||||
fn["use_channel_weights"] >> use_channel_weights;
|
||||
if(!fn["weights_lr"].empty())
|
||||
fn["weights_lr"] >> weights_lr;
|
||||
if(!fn["use_segmentation"].empty())
|
||||
fn["use_segmentation"] >> use_segmentation;
|
||||
if(!fn["histogram_bins"].empty())
|
||||
fn["histogram_bins"] >> histogram_bins;
|
||||
if(!fn["background_ratio"].empty())
|
||||
fn["background_ratio"] >> background_ratio;
|
||||
if(!fn["histogram_lr"].empty())
|
||||
fn["histogram_lr"] >> histogram_lr;
|
||||
if(!fn["psr_threshold"].empty())
|
||||
fn["psr_threshold"] >> psr_threshold;
|
||||
CV_Assert(number_of_scales % 2 == 1);
|
||||
CV_Assert(use_gray || use_color_names || use_hog || use_rgb);
|
||||
}
|
||||
void legacy::TrackerCSRT::Params::write(FileStorage& fs) const
|
||||
{
|
||||
fs << "padding" << padding;
|
||||
fs << "template_size" << template_size;
|
||||
fs << "gsl_sigma" << gsl_sigma;
|
||||
fs << "hog_orientations" << hog_orientations;
|
||||
fs << "num_hog_channels_used" << num_hog_channels_used;
|
||||
fs << "hog_clip" << hog_clip;
|
||||
fs << "use_hog" << use_hog;
|
||||
fs << "use_color_names" << use_color_names;
|
||||
fs << "use_gray" << use_gray;
|
||||
fs << "use_rgb" << use_rgb;
|
||||
fs << "window_function" << window_function;
|
||||
fs << "kaiser_alpha" << kaiser_alpha;
|
||||
fs << "cheb_attenuation" << cheb_attenuation;
|
||||
fs << "filter_lr" << filter_lr;
|
||||
fs << "admm_iterations" << admm_iterations;
|
||||
fs << "number_of_scales" << number_of_scales;
|
||||
fs << "scale_sigma_factor" << scale_sigma_factor;
|
||||
fs << "scale_model_max_area" << scale_model_max_area;
|
||||
fs << "scale_lr" << scale_lr;
|
||||
fs << "scale_step" << scale_step;
|
||||
fs << "use_channel_weights" << use_channel_weights;
|
||||
fs << "weights_lr" << weights_lr;
|
||||
fs << "use_segmentation" << use_segmentation;
|
||||
fs << "histogram_bins" << histogram_bins;
|
||||
fs << "background_ratio" << background_ratio;
|
||||
fs << "histogram_lr" << histogram_lr;
|
||||
fs << "psr_threshold" << psr_threshold;
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
|
||||
Ptr<legacy::TrackerCSRT> legacy::TrackerCSRT::create(const legacy::TrackerCSRT::Params ¶meters)
|
||||
{
|
||||
return makePtr<legacy::tracking::impl::TrackerCSRTImpl>(parameters);
|
||||
}
|
||||
Ptr<legacy::TrackerCSRT> legacy::TrackerCSRT::create()
|
||||
{
|
||||
return create(legacy::TrackerCSRT::Params());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,173 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "opencv2/tracking/tracking_legacy.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace legacy {
|
||||
inline namespace tracking {
|
||||
namespace impl {
|
||||
|
||||
/*---------------------------
|
||||
| TrackerKCF
|
||||
|---------------------------*/
|
||||
class TrackerKCFImpl CV_FINAL : public legacy::TrackerKCF
|
||||
{
|
||||
public:
|
||||
cv::tracking::impl::TrackerKCFImpl impl;
|
||||
|
||||
TrackerKCFImpl(const legacy::TrackerKCF::Params ¶meters)
|
||||
: impl(parameters)
|
||||
{
|
||||
isInit = false;
|
||||
}
|
||||
void read(const FileNode& fn) CV_OVERRIDE
|
||||
{
|
||||
static_cast<legacy::TrackerKCF::Params&>(impl.params).read(fn);
|
||||
}
|
||||
void write(FileStorage& fs) const CV_OVERRIDE
|
||||
{
|
||||
static_cast<const legacy::TrackerKCF::Params&>(impl.params).write(fs);
|
||||
}
|
||||
|
||||
bool initImpl(const Mat& image, const Rect2d& boundingBox) CV_OVERRIDE
|
||||
{
|
||||
impl.init(image, boundingBox);
|
||||
model = impl.model;
|
||||
sampler = makePtr<TrackerContribSampler>();
|
||||
featureSet = makePtr<TrackerContribFeatureSet>();
|
||||
isInit = true;
|
||||
return true;
|
||||
}
|
||||
bool updateImpl(const Mat& image, Rect2d& boundingBox) CV_OVERRIDE
|
||||
{
|
||||
Rect bb;
|
||||
bool res = impl.update(image, bb);
|
||||
boundingBox = bb;
|
||||
return res;
|
||||
}
|
||||
void setFeatureExtractor(void (*f)(const Mat, const Rect, Mat&), bool pca_func = false) CV_OVERRIDE
|
||||
{
|
||||
impl.setFeatureExtractor(f, pca_func);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void legacy::TrackerKCF::Params::read(const cv::FileNode& fn)
|
||||
{
|
||||
*this = TrackerKCF::Params();
|
||||
|
||||
if (!fn["detect_thresh"].empty())
|
||||
fn["detect_thresh"] >> detect_thresh;
|
||||
|
||||
if (!fn["sigma"].empty())
|
||||
fn["sigma"] >> sigma;
|
||||
|
||||
if (!fn["lambda"].empty())
|
||||
fn["lambda"] >> lambda;
|
||||
|
||||
if (!fn["interp_factor"].empty())
|
||||
fn["interp_factor"] >> interp_factor;
|
||||
|
||||
if (!fn["output_sigma_factor"].empty())
|
||||
fn["output_sigma_factor"] >> output_sigma_factor;
|
||||
|
||||
if (!fn["resize"].empty())
|
||||
fn["resize"] >> resize;
|
||||
|
||||
if (!fn["max_patch_size"].empty())
|
||||
fn["max_patch_size"] >> max_patch_size;
|
||||
|
||||
if (!fn["split_coeff"].empty())
|
||||
fn["split_coeff"] >> split_coeff;
|
||||
|
||||
if (!fn["wrap_kernel"].empty())
|
||||
fn["wrap_kernel"] >> wrap_kernel;
|
||||
|
||||
|
||||
if (!fn["desc_npca"].empty())
|
||||
fn["desc_npca"] >> desc_npca;
|
||||
|
||||
if (!fn["desc_pca"].empty())
|
||||
fn["desc_pca"] >> desc_pca;
|
||||
|
||||
if (!fn["compress_feature"].empty())
|
||||
fn["compress_feature"] >> compress_feature;
|
||||
|
||||
if (!fn["compressed_size"].empty())
|
||||
fn["compressed_size"] >> compressed_size;
|
||||
|
||||
if (!fn["pca_learning_rate"].empty())
|
||||
fn["pca_learning_rate"] >> pca_learning_rate;
|
||||
}
|
||||
|
||||
void legacy::TrackerKCF::Params::write(cv::FileStorage& fs) const
|
||||
{
|
||||
fs << "detect_thresh" << detect_thresh;
|
||||
fs << "sigma" << sigma;
|
||||
fs << "lambda" << lambda;
|
||||
fs << "interp_factor" << interp_factor;
|
||||
fs << "output_sigma_factor" << output_sigma_factor;
|
||||
fs << "resize" << resize;
|
||||
fs << "max_patch_size" << max_patch_size;
|
||||
fs << "split_coeff" << split_coeff;
|
||||
fs << "wrap_kernel" << wrap_kernel;
|
||||
fs << "desc_npca" << desc_npca;
|
||||
fs << "desc_pca" << desc_pca;
|
||||
fs << "compress_feature" << compress_feature;
|
||||
fs << "compressed_size" << compressed_size;
|
||||
fs << "pca_learning_rate" << pca_learning_rate;
|
||||
}
|
||||
|
||||
|
||||
}} // namespace legacy::tracking
|
||||
|
||||
Ptr<legacy::TrackerKCF> legacy::TrackerKCF::create(const legacy::TrackerKCF::Params ¶meters)
|
||||
{
|
||||
return makePtr<legacy::tracking::impl::TrackerKCFImpl>(parameters);
|
||||
}
|
||||
Ptr<legacy::TrackerKCF> legacy::TrackerKCF::create()
|
||||
{
|
||||
return create(legacy::TrackerKCF::Params());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
// This file is part of the OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
//
|
||||
//[1] David S. Bolme et al. "Visual Object Tracking using Adaptive Correlation Filters"
|
||||
// http://www.cs.colostate.edu/~draper/papers/bolme_cvpr10.pdf
|
||||
//
|
||||
|
||||
//
|
||||
// credits:
|
||||
// Kun-Hsin Chen: for initial c++ code
|
||||
// Cracki: for the idea of only converting the used patch to gray
|
||||
//
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
#include "opencv2/tracking/tracking_legacy.hpp"
|
||||
|
||||
namespace cv {
|
||||
inline namespace tracking {
|
||||
namespace impl {
|
||||
namespace {
|
||||
|
||||
struct DummyModel : detail::tracking::TrackerModel
|
||||
{
|
||||
virtual void modelUpdateImpl() CV_OVERRIDE {}
|
||||
virtual void modelEstimationImpl( const std::vector<Mat>& ) CV_OVERRIDE {}
|
||||
};
|
||||
|
||||
const double eps=0.00001; // for normalization
|
||||
const double rate=0.2; // learning rate
|
||||
const double psrThreshold=5.7; // no detection, if PSR is smaller than this
|
||||
|
||||
} // namespace
|
||||
|
||||
struct MosseImpl CV_FINAL : legacy::TrackerMOSSE
|
||||
{
|
||||
protected:
|
||||
|
||||
Point2d center; //center of the bounding box
|
||||
Size size; //size of the bounding box
|
||||
Mat hanWin;
|
||||
Mat G; //goal
|
||||
Mat H, A, B; //state
|
||||
|
||||
// Element-wise division of complex numbers in src1 and src2
|
||||
Mat divDFTs( const Mat &src1, const Mat &src2 ) const
|
||||
{
|
||||
Mat c1[2],c2[2],a1,a2,s1,s2,denom,re,im;
|
||||
|
||||
// split into re and im per src
|
||||
cv::split(src1, c1);
|
||||
cv::split(src2, c2);
|
||||
|
||||
// (Re2*Re2 + Im2*Im2) = denom
|
||||
// denom is same for both channels
|
||||
cv::multiply(c2[0], c2[0], s1);
|
||||
cv::multiply(c2[1], c2[1], s2);
|
||||
cv::add(s1, s2, denom);
|
||||
|
||||
// (Re1*Re2 + Im1*Im1)/(Re2*Re2 + Im2*Im2) = Re
|
||||
cv::multiply(c1[0], c2[0], a1);
|
||||
cv::multiply(c1[1], c2[1], a2);
|
||||
cv::divide(a1+a2, denom, re, 1.0 );
|
||||
|
||||
// (Im1*Re2 - Re1*Im2)/(Re2*Re2 + Im2*Im2) = Im
|
||||
cv::multiply(c1[1], c2[0], a1);
|
||||
cv::multiply(c1[0], c2[1], a2);
|
||||
cv::divide(a1+a2, denom, im, -1.0);
|
||||
|
||||
// Merge Re and Im back into a complex matrix
|
||||
Mat dst, chn[] = {re,im};
|
||||
cv::merge(chn, 2, dst);
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
void preProcess( Mat &window ) const
|
||||
{
|
||||
window.convertTo(window, CV_32F);
|
||||
log(window + 1.0f, window);
|
||||
|
||||
//normalize
|
||||
Scalar mean,StdDev;
|
||||
meanStdDev(window, mean, StdDev);
|
||||
window = (window-mean[0]) / (StdDev[0]+eps);
|
||||
|
||||
//Gaussain weighting
|
||||
window = window.mul(hanWin);
|
||||
}
|
||||
|
||||
|
||||
double correlate( const Mat &image_sub, Point &delta_xy ) const
|
||||
{
|
||||
Mat IMAGE_SUB, RESPONSE, response;
|
||||
// filter in dft space
|
||||
dft(image_sub, IMAGE_SUB, DFT_COMPLEX_OUTPUT);
|
||||
mulSpectrums(IMAGE_SUB, H, RESPONSE, 0, true );
|
||||
idft(RESPONSE, response, DFT_SCALE|DFT_REAL_OUTPUT);
|
||||
// update center position
|
||||
double maxVal; Point maxLoc;
|
||||
minMaxLoc(response, 0, &maxVal, 0, &maxLoc);
|
||||
delta_xy.x = maxLoc.x - int(response.size().width/2);
|
||||
delta_xy.y = maxLoc.y - int(response.size().height/2);
|
||||
// normalize response
|
||||
Scalar mean,std;
|
||||
meanStdDev(response, mean, std);
|
||||
return (maxVal-mean[0]) / (std[0]+eps); // PSR
|
||||
}
|
||||
|
||||
|
||||
Mat randWarp( const Mat& a ) const
|
||||
{
|
||||
cv::RNG rng(8031965);
|
||||
|
||||
// random rotation
|
||||
double C=0.1;
|
||||
double ang = rng.uniform(-C,C);
|
||||
double c=cos(ang), s=sin(ang);
|
||||
// affine warp matrix
|
||||
Mat_<float> W(2,3);
|
||||
W << c + rng.uniform(-C,C), -s + rng.uniform(-C,C), 0,
|
||||
s + rng.uniform(-C,C), c + rng.uniform(-C,C), 0;
|
||||
|
||||
// random translation
|
||||
Mat_<float> center_warp(2, 1);
|
||||
center_warp << a.cols/2, a.rows/2;
|
||||
W.col(2) = center_warp - (W.colRange(0, 2))*center_warp;
|
||||
|
||||
Mat warped;
|
||||
warpAffine(a, warped, W, a.size(), BORDER_REFLECT);
|
||||
return warped;
|
||||
}
|
||||
|
||||
|
||||
virtual bool initImpl( const Mat& image, const Rect2d& boundingBox ) CV_OVERRIDE
|
||||
{
|
||||
model = makePtr<DummyModel>();
|
||||
|
||||
Mat img;
|
||||
if (image.channels() == 1)
|
||||
img = image;
|
||||
else
|
||||
cvtColor(image, img, COLOR_BGR2GRAY);
|
||||
|
||||
int w = getOptimalDFTSize(int(boundingBox.width));
|
||||
int h = getOptimalDFTSize(int(boundingBox.height));
|
||||
|
||||
//Get the center position
|
||||
int x1 = int(floor((2*boundingBox.x+boundingBox.width-w)/2));
|
||||
int y1 = int(floor((2*boundingBox.y+boundingBox.height-h)/2));
|
||||
center.x = x1 + (w)/2;
|
||||
center.y = y1 + (h)/2;
|
||||
size.width = w;
|
||||
size.height = h;
|
||||
|
||||
Mat window;
|
||||
getRectSubPix(img, size, center, window);
|
||||
createHanningWindow(hanWin, size, CV_32F);
|
||||
|
||||
// goal
|
||||
Mat g=Mat::zeros(size,CV_32F);
|
||||
g.at<float>(h/2, w/2) = 1;
|
||||
GaussianBlur(g, g, Size(-1,-1), 2.0);
|
||||
double maxVal;
|
||||
minMaxLoc(g, 0, &maxVal);
|
||||
g = g / maxVal;
|
||||
dft(g, G, DFT_COMPLEX_OUTPUT);
|
||||
|
||||
// initial A,B and H
|
||||
A = Mat::zeros(G.size(), G.type());
|
||||
B = Mat::zeros(G.size(), G.type());
|
||||
for(int i=0; i<8; i++)
|
||||
{
|
||||
Mat window_warp = randWarp(window);
|
||||
preProcess(window_warp);
|
||||
|
||||
Mat WINDOW_WARP, A_i, B_i;
|
||||
dft(window_warp, WINDOW_WARP, DFT_COMPLEX_OUTPUT);
|
||||
mulSpectrums(G , WINDOW_WARP, A_i, 0, true);
|
||||
mulSpectrums(WINDOW_WARP, WINDOW_WARP, B_i, 0, true);
|
||||
A+=A_i;
|
||||
B+=B_i;
|
||||
}
|
||||
H = divDFTs(A,B);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool updateImpl( const Mat& image, Rect2d& boundingBox ) CV_OVERRIDE
|
||||
{
|
||||
if (H.empty()) // not initialized
|
||||
return false;
|
||||
|
||||
Mat image_sub;
|
||||
getRectSubPix(image, size, center, image_sub);
|
||||
|
||||
if (image_sub.channels() != 1)
|
||||
cvtColor(image_sub, image_sub, COLOR_BGR2GRAY);
|
||||
preProcess(image_sub);
|
||||
|
||||
Point delta_xy;
|
||||
double PSR = correlate(image_sub, delta_xy);
|
||||
if (PSR < psrThreshold)
|
||||
return false;
|
||||
|
||||
//update location
|
||||
center.x += delta_xy.x;
|
||||
center.y += delta_xy.y;
|
||||
|
||||
Mat img_sub_new;
|
||||
getRectSubPix(image, size, center, img_sub_new);
|
||||
if (img_sub_new.channels() != 1)
|
||||
cvtColor(img_sub_new, img_sub_new, COLOR_BGR2GRAY);
|
||||
preProcess(img_sub_new);
|
||||
|
||||
// new state for A and B
|
||||
Mat F, A_new, B_new;
|
||||
dft(img_sub_new, F, DFT_COMPLEX_OUTPUT);
|
||||
mulSpectrums(G, F, A_new, 0, true );
|
||||
mulSpectrums(F, F, B_new, 0, true );
|
||||
|
||||
// update A ,B, and H
|
||||
A = A*(1-rate) + A_new*rate;
|
||||
B = B*(1-rate) + B_new*rate;
|
||||
H = divDFTs(A, B);
|
||||
|
||||
// return tracked rect
|
||||
double x=center.x, y=center.y;
|
||||
int w = size.width, h=size.height;
|
||||
boundingBox = Rect2d(Point2d(x-0.5*w, y-0.5*h), Point2d(x+0.5*w, y+0.5*h));
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
MosseImpl() { isInit = 0; }
|
||||
|
||||
// dummy implementation.
|
||||
virtual void read( const FileNode& ) CV_OVERRIDE {}
|
||||
virtual void write( FileStorage& ) const CV_OVERRIDE {}
|
||||
|
||||
}; // MosseImpl
|
||||
|
||||
}} // namespace
|
||||
|
||||
Ptr<legacy::tracking::TrackerMOSSE> legacy::tracking::TrackerMOSSE::create()
|
||||
{
|
||||
return makePtr<impl::MosseImpl>();
|
||||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,649 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "multiTracker.hpp"
|
||||
|
||||
#include "opencv2/tracking/tracking_legacy.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace legacy {
|
||||
inline namespace tracking {
|
||||
|
||||
using namespace impl;
|
||||
|
||||
//Multitracker
|
||||
bool MultiTracker_Alt::addTarget(InputArray image, const Rect2d& boundingBox, Ptr<Tracker> tracker_algorithm)
|
||||
{
|
||||
Ptr<Tracker> tracker = tracker_algorithm;
|
||||
if (!tracker)
|
||||
return false;
|
||||
|
||||
if (!tracker->init(image, boundingBox))
|
||||
return false;
|
||||
|
||||
//Add BB of target
|
||||
boundingBoxes.push_back(boundingBox);
|
||||
|
||||
//Add Tracker to stack
|
||||
trackers.push_back(tracker);
|
||||
|
||||
//Assign a random color to target
|
||||
if (targetNum == 1)
|
||||
colors.push_back(Scalar(0, 0, 255));
|
||||
else
|
||||
colors.push_back(Scalar(rand() % 256, rand() % 256, rand() % 256));
|
||||
|
||||
|
||||
|
||||
//Target counter
|
||||
targetNum++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MultiTracker_Alt::update(InputArray image)
|
||||
{
|
||||
for (int i = 0; i < (int)trackers.size(); i++)
|
||||
if (!trackers[i]->update(image, boundingBoxes[i]))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//Multitracker TLD
|
||||
/*Optimized update method for TLD Multitracker */
|
||||
bool MultiTrackerTLD::update_opt(InputArray _image)
|
||||
{
|
||||
Mat image = _image.getMat();
|
||||
//Get parameters from first object
|
||||
//TLD Tracker data extraction
|
||||
Tracker* trackerPtr = trackers[0];
|
||||
tld::TrackerTLDImpl* tracker = static_cast<tld::TrackerTLDImpl*>(trackerPtr);
|
||||
//TLD Model Extraction
|
||||
tld::TrackerTLDModel* tldModel = ((tld::TrackerTLDModel*)static_cast<TrackerModel*>(tracker->getModel()));
|
||||
Ptr<tld::Data> data = tracker->data;
|
||||
double scale = data->getScale();
|
||||
|
||||
Mat image_gray, image_blurred, imageForDetector;
|
||||
cvtColor(image, image_gray, COLOR_BGR2GRAY);
|
||||
|
||||
if (scale > 1.0)
|
||||
resize(image_gray, imageForDetector, Size(cvRound(image_gray.cols*scale), cvRound(image_gray.rows*scale)), 0, 0, tld::DOWNSCALE_MODE);
|
||||
else
|
||||
imageForDetector = image_gray;
|
||||
GaussianBlur(imageForDetector, image_blurred, tld::GaussBlurKernelSize, 0.0);
|
||||
|
||||
//best overlap around 92%
|
||||
Mat_<uchar> standardPatch(tld::STANDARD_PATCH_SIZE, tld::STANDARD_PATCH_SIZE);
|
||||
|
||||
std::vector<std::vector<tld::TLDDetector::LabeledPatch> > detectorResults(targetNum);
|
||||
std::vector<std::vector<Rect2d> > candidates(targetNum);
|
||||
std::vector<std::vector<double> > candidatesRes(targetNum);
|
||||
std::vector<Rect2d> tmpCandidates(targetNum);
|
||||
std::vector<bool> detect_flgs(targetNum);
|
||||
std::vector<bool> trackerNeedsReInit(targetNum);
|
||||
|
||||
bool DETECT_FLG = false;
|
||||
|
||||
//Detect all
|
||||
for (int k = 0; k < targetNum; k++)
|
||||
tmpCandidates[k] = boundingBoxes[k];
|
||||
#ifdef HAVE_OPENCL
|
||||
if (ocl::haveOpenCL())
|
||||
ocl_detect_all(imageForDetector, image_blurred, tmpCandidates, detectorResults, detect_flgs, trackers);
|
||||
else
|
||||
#endif
|
||||
detect_all(imageForDetector, image_blurred, tmpCandidates, detectorResults, detect_flgs, trackers);
|
||||
|
||||
bool success = false;
|
||||
for (int k = 0; k < targetNum; k++)
|
||||
{
|
||||
//TLD Tracker data extraction
|
||||
trackerPtr = trackers[k];
|
||||
tracker = static_cast<tld::TrackerTLDImpl*>(trackerPtr);
|
||||
//TLD Model Extraction
|
||||
tldModel = ((tld::TrackerTLDModel*)static_cast<TrackerModel*>(tracker->getModel()));
|
||||
data = tracker->data;
|
||||
|
||||
data->frameNum++;
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
Rect2d tmpCandid = boundingBoxes[k];
|
||||
|
||||
//if (i == 1)
|
||||
{
|
||||
DETECT_FLG = detect_flgs[k];
|
||||
tmpCandid = tmpCandidates[k];
|
||||
}
|
||||
|
||||
if (((i == 0) && !data->failedLastTime && tracker->trackerProxy->update(image, tmpCandid)) || (DETECT_FLG))
|
||||
{
|
||||
candidates[k].push_back(tmpCandid);
|
||||
if (i == 0)
|
||||
tld::resample(image_gray, tmpCandid, standardPatch);
|
||||
else
|
||||
tld::resample(imageForDetector, tmpCandid, standardPatch);
|
||||
candidatesRes[k].push_back(tldModel->detector->Sc(standardPatch));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i == 0)
|
||||
trackerNeedsReInit[k] = true;
|
||||
else
|
||||
trackerNeedsReInit[k] = false;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<double>::iterator it = std::max_element(candidatesRes[k].begin(), candidatesRes[k].end());
|
||||
|
||||
|
||||
if (it == candidatesRes[k].end())
|
||||
{
|
||||
|
||||
data->confident = false;
|
||||
data->failedLastTime = true;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
success = true;
|
||||
boundingBoxes[k] = candidates[k][it - candidatesRes[k].begin()];
|
||||
data->failedLastTime = false;
|
||||
if (trackerNeedsReInit[k] || it != candidatesRes[k].begin())
|
||||
tracker->trackerProxy->init(image, boundingBoxes[k]);
|
||||
}
|
||||
|
||||
#if 1
|
||||
if (it != candidatesRes[k].end())
|
||||
tld::resample(imageForDetector, candidates[k][it - candidatesRes[k].begin()], standardPatch);
|
||||
#endif
|
||||
|
||||
if (*it > tld::CORE_THRESHOLD)
|
||||
data->confident = true;
|
||||
|
||||
if (data->confident)
|
||||
{
|
||||
tld::TrackerTLDImpl::Pexpert pExpert(imageForDetector, image_blurred, boundingBoxes[k], tldModel->detector, tracker->params, data->getMinSize());
|
||||
tld::TrackerTLDImpl::Nexpert nExpert(imageForDetector, boundingBoxes[k], tldModel->detector, tracker->params);
|
||||
std::vector<Mat_<uchar> > examplesForModel, examplesForEnsemble;
|
||||
examplesForModel.reserve(100); examplesForEnsemble.reserve(100);
|
||||
for (int i = 0; i < (int)detectorResults[k].size(); i++)
|
||||
{
|
||||
bool expertResult;
|
||||
if (detectorResults[k][i].isObject)
|
||||
{
|
||||
expertResult = nExpert(detectorResults[k][i].rect);
|
||||
}
|
||||
else
|
||||
{
|
||||
expertResult = pExpert(detectorResults[k][i].rect);
|
||||
}
|
||||
|
||||
detectorResults[k][i].shouldBeIntegrated = detectorResults[k][i].shouldBeIntegrated || (detectorResults[k][i].isObject != expertResult);
|
||||
detectorResults[k][i].isObject = expertResult;
|
||||
}
|
||||
tldModel->integrateRelabeled(imageForDetector, image_blurred, detectorResults[k]);
|
||||
pExpert.additionalExamples(examplesForModel, examplesForEnsemble);
|
||||
#ifdef HAVE_OPENCL
|
||||
if (ocl::haveOpenCL())
|
||||
tldModel->ocl_integrateAdditional(examplesForModel, examplesForEnsemble, true);
|
||||
else
|
||||
#endif
|
||||
tldModel->integrateAdditional(examplesForModel, examplesForEnsemble, true);
|
||||
examplesForModel.clear(); examplesForEnsemble.clear();
|
||||
nExpert.additionalExamples(examplesForModel, examplesForEnsemble);
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
if (ocl::haveOpenCL())
|
||||
tldModel->ocl_integrateAdditional(examplesForModel, examplesForEnsemble, false);
|
||||
else
|
||||
#endif
|
||||
tldModel->integrateAdditional(examplesForModel, examplesForEnsemble, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef CLOSED_LOOP
|
||||
tldModel->integrateRelabeled(imageForDetector, image_blurred, detectorResults);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
|
||||
|
||||
inline namespace tracking {
|
||||
namespace impl {
|
||||
|
||||
void detect_all(const Mat& img, const Mat& imgBlurred, std::vector<Rect2d>& res, std::vector < std::vector < tld::TLDDetector::LabeledPatch > > &patches, std::vector<bool> &detect_flgs,
|
||||
std::vector<Ptr<legacy::Tracker> > &trackers)
|
||||
{
|
||||
//TLD Tracker data extraction
|
||||
legacy::Tracker* trackerPtr = trackers[0];
|
||||
tld::TrackerTLDImpl* tracker = static_cast<tld::TrackerTLDImpl*>(trackerPtr);
|
||||
//TLD Model Extraction
|
||||
tld::TrackerTLDModel* tldModel = ((tld::TrackerTLDModel*)static_cast<TrackerModel*>(tracker->getModel()));
|
||||
Size initSize = tldModel->getMinSize();
|
||||
|
||||
for (int k = 0; k < (int)trackers.size(); k++)
|
||||
patches[k].clear();
|
||||
|
||||
Mat_<uchar> standardPatch(tld::STANDARD_PATCH_SIZE, tld::STANDARD_PATCH_SIZE);
|
||||
Mat tmp;
|
||||
int dx = initSize.width / 10, dy = initSize.height / 10;
|
||||
Size2d size = img.size();
|
||||
double maxSc = -5.0;
|
||||
Rect2d maxScRect;
|
||||
int scaleID;
|
||||
std::vector <Mat> resized_imgs, blurred_imgs;
|
||||
|
||||
std::vector <std::vector <Point> > varBuffer(trackers.size()), ensBuffer(trackers.size());
|
||||
std::vector <std::vector <int> > varScaleIDs(trackers.size()), ensScaleIDs(trackers.size());
|
||||
|
||||
std::vector <Point> tmpP;
|
||||
std::vector <int> tmpI;
|
||||
|
||||
//Detection part
|
||||
//Generate windows and filter by variance
|
||||
scaleID = 0;
|
||||
resized_imgs.push_back(img);
|
||||
blurred_imgs.push_back(imgBlurred);
|
||||
do
|
||||
{
|
||||
Mat_<double> intImgP, intImgP2;
|
||||
tld::TLDDetector::computeIntegralImages(resized_imgs[scaleID], intImgP, intImgP2);
|
||||
for (int i = 0, imax = cvFloor((0.0 + resized_imgs[scaleID].cols - initSize.width) / dx); i < imax; i++)
|
||||
{
|
||||
for (int j = 0, jmax = cvFloor((0.0 + resized_imgs[scaleID].rows - initSize.height) / dy); j < jmax; j++)
|
||||
{
|
||||
//Optimized variance calculation
|
||||
int x = dx * i,
|
||||
y = dy * j,
|
||||
width = initSize.width,
|
||||
height = initSize.height;
|
||||
double p = 0, p2 = 0;
|
||||
double A, B, C, D;
|
||||
|
||||
A = intImgP(y, x);
|
||||
B = intImgP(y, x + width);
|
||||
C = intImgP(y + height, x);
|
||||
D = intImgP(y + height, x + width);
|
||||
p = (A + D - B - C) / (width * height);
|
||||
|
||||
A = intImgP2(y, x);
|
||||
B = intImgP2(y, x + width);
|
||||
C = intImgP2(y + height, x);
|
||||
D = intImgP2(y + height, x + width);
|
||||
p2 = (A + D - B - C) / (width * height);
|
||||
double windowVar = p2 - p * p;
|
||||
|
||||
//Loop for on all objects
|
||||
for (int k = 0; k < (int)trackers.size(); k++)
|
||||
{
|
||||
//TLD Tracker data extraction
|
||||
trackerPtr = trackers[k];
|
||||
tracker = static_cast<tld::TrackerTLDImpl*>(trackerPtr);
|
||||
//TLD Model Extraction
|
||||
tldModel = ((tld::TrackerTLDModel*)static_cast<TrackerModel*>(tracker->getModel()));
|
||||
|
||||
//Optimized variance calculation
|
||||
bool varPass = (windowVar > tld::VARIANCE_THRESHOLD * *tldModel->detector->originalVariancePtr);
|
||||
|
||||
if (!varPass)
|
||||
continue;
|
||||
varBuffer[k].push_back(Point(dx * i, dy * j));
|
||||
varScaleIDs[k].push_back(scaleID);
|
||||
}
|
||||
}
|
||||
}
|
||||
scaleID++;
|
||||
size.width /= tld::SCALE_STEP;
|
||||
size.height /= tld::SCALE_STEP;
|
||||
resize(img, tmp, size, 0, 0, tld::DOWNSCALE_MODE);
|
||||
resized_imgs.push_back(tmp);
|
||||
GaussianBlur(resized_imgs[scaleID], tmp, tld::GaussBlurKernelSize, 0.0f);
|
||||
blurred_imgs.push_back(tmp);
|
||||
} while (size.width >= initSize.width && size.height >= initSize.height);
|
||||
|
||||
//Encsemble classification
|
||||
for (int k = 0; k < (int)trackers.size(); k++)
|
||||
{
|
||||
//TLD Tracker data extraction
|
||||
trackerPtr = trackers[k];
|
||||
tracker = static_cast<tld::TrackerTLDImpl*>(trackerPtr);
|
||||
//TLD Model Extraction
|
||||
tldModel = ((tld::TrackerTLDModel*)static_cast<TrackerModel*>(tracker->getModel()));
|
||||
|
||||
|
||||
for (int i = 0; i < (int)varBuffer[k].size(); i++)
|
||||
{
|
||||
tldModel->detector->prepareClassifiers(static_cast<int> (blurred_imgs[varScaleIDs[k][i]].step[0]));
|
||||
|
||||
double ensRes = 0;
|
||||
uchar* data = &blurred_imgs[varScaleIDs[k][i]].at<uchar>(varBuffer[k][i].y, varBuffer[k][i].x);
|
||||
for (int x = 0; x < (int)tldModel->detector->classifiers.size(); x++)
|
||||
{
|
||||
int position = 0;
|
||||
for (int n = 0; n < (int)tldModel->detector->classifiers[x].measurements.size(); n++)
|
||||
{
|
||||
position = position << 1;
|
||||
if (data[tldModel->detector->classifiers[x].offset[n].x] < data[tldModel->detector->classifiers[x].offset[n].y])
|
||||
position++;
|
||||
}
|
||||
double posNum = (double)tldModel->detector->classifiers[x].posAndNeg[position].x;
|
||||
double negNum = (double)tldModel->detector->classifiers[x].posAndNeg[position].y;
|
||||
if (posNum == 0.0 && negNum == 0.0)
|
||||
continue;
|
||||
else
|
||||
ensRes += posNum / (posNum + negNum);
|
||||
}
|
||||
ensRes /= tldModel->detector->classifiers.size();
|
||||
ensRes = tldModel->detector->ensembleClassifierNum(&blurred_imgs[varScaleIDs[k][i]].at<uchar>(varBuffer[k][i].y, varBuffer[k][i].x));
|
||||
|
||||
if ( ensRes <= tld::ENSEMBLE_THRESHOLD)
|
||||
continue;
|
||||
ensBuffer[k].push_back(varBuffer[k][i]);
|
||||
ensScaleIDs[k].push_back(varScaleIDs[k][i]);
|
||||
}
|
||||
}
|
||||
|
||||
//NN classification
|
||||
for (int k = 0; k < (int)trackers.size(); k++)
|
||||
{
|
||||
//TLD Tracker data extraction
|
||||
trackerPtr = trackers[k];
|
||||
tracker = static_cast<tld::TrackerTLDImpl*>(trackerPtr);
|
||||
//TLD Model Extraction
|
||||
tldModel = ((tld::TrackerTLDModel*)static_cast<TrackerModel*>(tracker->getModel()));
|
||||
|
||||
maxSc = -5.0;
|
||||
|
||||
for (int i = 0; i < (int)ensBuffer[k].size(); i++)
|
||||
{
|
||||
tld::TLDDetector::LabeledPatch labPatch;
|
||||
double curScale = pow(tld::SCALE_STEP, ensScaleIDs[k][i]);
|
||||
labPatch.rect = Rect2d(ensBuffer[k][i].x*curScale, ensBuffer[k][i].y*curScale, initSize.width * curScale, initSize.height * curScale);
|
||||
tld::resample(resized_imgs[ensScaleIDs[k][i]], Rect2d(ensBuffer[k][i], initSize), standardPatch);
|
||||
|
||||
double srValue, scValue;
|
||||
srValue = tldModel->detector->Sr(standardPatch);
|
||||
|
||||
////To fix: Check the paper, probably this cause wrong learning
|
||||
//
|
||||
labPatch.isObject = srValue > tld::THETA_NN;
|
||||
labPatch.shouldBeIntegrated = abs(srValue - tld::THETA_NN) < tld::CLASSIFIER_MARGIN;
|
||||
patches[k].push_back(labPatch);
|
||||
//
|
||||
|
||||
if (!labPatch.isObject)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
scValue = tldModel->detector->Sc(standardPatch);
|
||||
if (scValue > maxSc)
|
||||
{
|
||||
maxSc = scValue;
|
||||
maxScRect = labPatch.rect;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (maxSc < 0)
|
||||
detect_flgs[k] = false;
|
||||
else
|
||||
{
|
||||
res[k] = maxScRect;
|
||||
detect_flgs[k] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
void ocl_detect_all(const Mat& img, const Mat& imgBlurred, std::vector<Rect2d>& res, std::vector < std::vector < tld::TLDDetector::LabeledPatch > > &patches, std::vector<bool> &detect_flgs,
|
||||
std::vector<Ptr<legacy::Tracker> > &trackers)
|
||||
{
|
||||
//TLD Tracker data extraction
|
||||
legacy::Tracker* trackerPtr = trackers[0];
|
||||
tld::TrackerTLDImpl* tracker = static_cast<tld::TrackerTLDImpl*>(trackerPtr);
|
||||
//TLD Model Extraction
|
||||
tld::TrackerTLDModel* tldModel = ((tld::TrackerTLDModel*)static_cast<TrackerModel*>(tracker->getModel()));
|
||||
Size initSize = tldModel->getMinSize();
|
||||
|
||||
for (int k = 0; k < (int)trackers.size(); k++)
|
||||
patches[k].clear();
|
||||
|
||||
Mat_<uchar> standardPatch(tld::STANDARD_PATCH_SIZE, tld::STANDARD_PATCH_SIZE);
|
||||
Mat tmp;
|
||||
int dx = initSize.width / 10, dy = initSize.height / 10;
|
||||
Size2d size = img.size();
|
||||
double maxSc = -5.0;
|
||||
Rect2d maxScRect;
|
||||
int scaleID;
|
||||
std::vector <Mat> resized_imgs, blurred_imgs;
|
||||
|
||||
std::vector <std::vector <Point> > varBuffer(trackers.size()), ensBuffer(trackers.size());
|
||||
std::vector <std::vector <int> > varScaleIDs(trackers.size()), ensScaleIDs(trackers.size());
|
||||
|
||||
std::vector <Point> tmpP;
|
||||
std::vector <int> tmpI;
|
||||
|
||||
//Detection part
|
||||
//Generate windows and filter by variance
|
||||
scaleID = 0;
|
||||
resized_imgs.push_back(img);
|
||||
blurred_imgs.push_back(imgBlurred);
|
||||
do
|
||||
{
|
||||
Mat_<double> intImgP, intImgP2;
|
||||
tld::TLDDetector::computeIntegralImages(resized_imgs[scaleID], intImgP, intImgP2);
|
||||
for (int i = 0, imax = cvFloor((0.0 + resized_imgs[scaleID].cols - initSize.width) / dx); i < imax; i++)
|
||||
{
|
||||
for (int j = 0, jmax = cvFloor((0.0 + resized_imgs[scaleID].rows - initSize.height) / dy); j < jmax; j++)
|
||||
{
|
||||
//Optimized variance calculation
|
||||
int x = dx * i,
|
||||
y = dy * j,
|
||||
width = initSize.width,
|
||||
height = initSize.height;
|
||||
double p = 0, p2 = 0;
|
||||
double A, B, C, D;
|
||||
|
||||
A = intImgP(y, x);
|
||||
B = intImgP(y, x + width);
|
||||
C = intImgP(y + height, x);
|
||||
D = intImgP(y + height, x + width);
|
||||
p = (A + D - B - C) / (width * height);
|
||||
|
||||
A = intImgP2(y, x);
|
||||
B = intImgP2(y, x + width);
|
||||
C = intImgP2(y + height, x);
|
||||
D = intImgP2(y + height, x + width);
|
||||
p2 = (A + D - B - C) / (width * height);
|
||||
double windowVar = p2 - p * p;
|
||||
|
||||
//Loop for on all objects
|
||||
for (int k = 0; k < (int)trackers.size(); k++)
|
||||
{
|
||||
//TLD Tracker data extraction
|
||||
trackerPtr = trackers[k];
|
||||
tracker = static_cast<tld::TrackerTLDImpl*>(trackerPtr);
|
||||
//TLD Model Extraction
|
||||
tldModel = ((tld::TrackerTLDModel*)static_cast<TrackerModel*>(tracker->getModel()));
|
||||
|
||||
//Optimized variance calculation
|
||||
bool varPass = (windowVar > tld::VARIANCE_THRESHOLD * *tldModel->detector->originalVariancePtr);
|
||||
|
||||
if (!varPass)
|
||||
continue;
|
||||
varBuffer[k].push_back(Point(dx * i, dy * j));
|
||||
varScaleIDs[k].push_back(scaleID);
|
||||
}
|
||||
}
|
||||
}
|
||||
scaleID++;
|
||||
size.width /= tld::SCALE_STEP;
|
||||
size.height /= tld::SCALE_STEP;
|
||||
resize(img, tmp, size, 0, 0, tld::DOWNSCALE_MODE);
|
||||
resized_imgs.push_back(tmp);
|
||||
GaussianBlur(resized_imgs[scaleID], tmp, tld::GaussBlurKernelSize, 0.0f);
|
||||
blurred_imgs.push_back(tmp);
|
||||
} while (size.width >= initSize.width && size.height >= initSize.height);
|
||||
|
||||
//Encsemble classification
|
||||
for (int k = 0; k < (int)trackers.size(); k++)
|
||||
{
|
||||
//TLD Tracker data extraction
|
||||
trackerPtr = trackers[k];
|
||||
tracker = static_cast<tld::TrackerTLDImpl*>(trackerPtr);
|
||||
//TLD Model Extraction
|
||||
tldModel = ((tld::TrackerTLDModel*)static_cast<TrackerModel*>(tracker->getModel()));
|
||||
|
||||
|
||||
for (int i = 0; i < (int)varBuffer[k].size(); i++)
|
||||
{
|
||||
tldModel->detector->prepareClassifiers(static_cast<int> (blurred_imgs[varScaleIDs[k][i]].step[0]));
|
||||
|
||||
double ensRes = 0;
|
||||
uchar* data = &blurred_imgs[varScaleIDs[k][i]].at<uchar>(varBuffer[k][i].y, varBuffer[k][i].x);
|
||||
for (int x = 0; x < (int)tldModel->detector->classifiers.size(); x++)
|
||||
{
|
||||
int position = 0;
|
||||
for (int n = 0; n < (int)tldModel->detector->classifiers[x].measurements.size(); n++)
|
||||
{
|
||||
position = position << 1;
|
||||
if (data[tldModel->detector->classifiers[x].offset[n].x] < data[tldModel->detector->classifiers[x].offset[n].y])
|
||||
position++;
|
||||
}
|
||||
double posNum = (double)tldModel->detector->classifiers[x].posAndNeg[position].x;
|
||||
double negNum = (double)tldModel->detector->classifiers[x].posAndNeg[position].y;
|
||||
if (posNum == 0.0 && negNum == 0.0)
|
||||
continue;
|
||||
else
|
||||
ensRes += posNum / (posNum + negNum);
|
||||
}
|
||||
ensRes /= tldModel->detector->classifiers.size();
|
||||
ensRes = tldModel->detector->ensembleClassifierNum(&blurred_imgs[varScaleIDs[k][i]].at<uchar>(varBuffer[k][i].y, varBuffer[k][i].x));
|
||||
|
||||
if (ensRes <= tld::ENSEMBLE_THRESHOLD)
|
||||
continue;
|
||||
ensBuffer[k].push_back(varBuffer[k][i]);
|
||||
ensScaleIDs[k].push_back(varScaleIDs[k][i]);
|
||||
}
|
||||
}
|
||||
|
||||
//NN classification
|
||||
for (int k = 0; k < (int)trackers.size(); k++)
|
||||
{
|
||||
//TLD Tracker data extraction
|
||||
trackerPtr = trackers[k];
|
||||
tracker = static_cast<tld::TrackerTLDImpl*>(trackerPtr);
|
||||
//TLD Model Extraction
|
||||
tldModel = ((tld::TrackerTLDModel*)static_cast<TrackerModel*>(tracker->getModel()));
|
||||
maxSc = -5.0;
|
||||
|
||||
//Prepare batch of patches
|
||||
int numOfPatches = (int)ensBuffer[k].size();
|
||||
Mat_<uchar> stdPatches(numOfPatches, 225);
|
||||
double *resultSr = new double[numOfPatches];
|
||||
double *resultSc = new double[numOfPatches];
|
||||
|
||||
uchar *patchesData = stdPatches.data;
|
||||
for (int i = 0; i < (int)ensBuffer.size(); i++)
|
||||
{
|
||||
tld::resample(resized_imgs[ensScaleIDs[k][i]], Rect2d(ensBuffer[k][i], initSize), standardPatch);
|
||||
uchar *stdPatchData = standardPatch.data;
|
||||
for (int j = 0; j < 225; j++)
|
||||
patchesData[225 * i + j] = stdPatchData[j];
|
||||
}
|
||||
//Calculate Sr and Sc batches
|
||||
tldModel->detector->ocl_batchSrSc(stdPatches, resultSr, resultSc, numOfPatches);
|
||||
|
||||
for (int i = 0; i < (int)ensBuffer[k].size(); i++)
|
||||
{
|
||||
tld::TLDDetector::LabeledPatch labPatch;
|
||||
standardPatch.data = &stdPatches.data[225 * i];
|
||||
double curScale = pow(tld::SCALE_STEP, ensScaleIDs[k][i]);
|
||||
labPatch.rect = Rect2d(ensBuffer[k][i].x*curScale, ensBuffer[k][i].y*curScale, initSize.width * curScale, initSize.height * curScale);
|
||||
tld::resample(resized_imgs[ensScaleIDs[k][i]], Rect2d(ensBuffer[k][i], initSize), standardPatch);
|
||||
|
||||
double srValue, scValue;
|
||||
srValue = resultSr[i];
|
||||
|
||||
////To fix: Check the paper, probably this cause wrong learning
|
||||
//
|
||||
labPatch.isObject = srValue > tld::THETA_NN;
|
||||
labPatch.shouldBeIntegrated = abs(srValue - tld::THETA_NN) < 0.1;
|
||||
patches[k].push_back(labPatch);
|
||||
//
|
||||
|
||||
if (!labPatch.isObject)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
scValue = resultSc[i];
|
||||
if (scValue > maxSc)
|
||||
{
|
||||
maxSc = scValue;
|
||||
maxScRect = labPatch.rect;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (maxSc < 0)
|
||||
detect_flgs[k] = false;
|
||||
else
|
||||
{
|
||||
res[k] = maxScRect;
|
||||
detect_flgs[k] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}}} // namespace
|
||||
@@ -0,0 +1,59 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef OPENCV_MULTITRACKER
|
||||
#define OPENCV_MULTITRACKER
|
||||
|
||||
#include "tldTracker.hpp"
|
||||
#include "tldUtils.hpp"
|
||||
#include <math.h>
|
||||
|
||||
namespace cv {
|
||||
inline namespace tracking {
|
||||
namespace impl {
|
||||
void detect_all(const Mat& img, const Mat& imgBlurred, std::vector<Rect2d>& res, std::vector < std::vector < tld::TLDDetector::LabeledPatch > > &patches,
|
||||
std::vector<bool>& detect_flgs, std::vector<Ptr<legacy::Tracker> >& trackers);
|
||||
#ifdef HAVE_OPENCL
|
||||
void ocl_detect_all(const Mat& img, const Mat& imgBlurred, std::vector<Rect2d>& res, std::vector < std::vector < tld::TLDDetector::LabeledPatch > > &patches,
|
||||
std::vector<bool>& detect_flgs, std::vector<Ptr<legacy::Tracker> >& trackers);
|
||||
#endif
|
||||
}}} // namespace
|
||||
#endif
|
||||
@@ -0,0 +1,111 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "opencv2/tracking/tracking_legacy.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace legacy {
|
||||
inline namespace tracking {
|
||||
|
||||
// constructor
|
||||
MultiTracker::MultiTracker(){};
|
||||
|
||||
// destructor
|
||||
MultiTracker::~MultiTracker(){};
|
||||
|
||||
// add a new tracked object
|
||||
bool MultiTracker::add( Ptr<Tracker> newTracker, InputArray image, const Rect2d& boundingBox )
|
||||
{
|
||||
// add the tracker algorithm to the trackers list
|
||||
trackerList.push_back(newTracker);
|
||||
|
||||
// add the ROI to the bounding box list
|
||||
objects.push_back(boundingBox);
|
||||
|
||||
// initialize the created tracker
|
||||
return trackerList.back()->init(image, boundingBox);
|
||||
};
|
||||
|
||||
// add a set of objects to be tracked
|
||||
bool MultiTracker::add(std::vector<Ptr<Tracker> > newTrackers, InputArray image, std::vector<Rect2d> boundingBox){
|
||||
// status of the tracker addition
|
||||
bool stat=false;
|
||||
|
||||
// add tracker for all input objects
|
||||
for(unsigned i =0;i<boundingBox.size();i++){
|
||||
stat=add(newTrackers[i],image,boundingBox[i]);
|
||||
if(!stat)break;
|
||||
}
|
||||
|
||||
// return the status
|
||||
return stat;
|
||||
};
|
||||
|
||||
// update position of the tracked objects, the result is stored in internal storage
|
||||
bool MultiTracker::update(InputArray image)
|
||||
{
|
||||
bool status = true;
|
||||
for(unsigned i=0;i< trackerList.size(); i++){
|
||||
status &= trackerList[i]->update(image, objects[i]);
|
||||
}
|
||||
return status;
|
||||
};
|
||||
|
||||
// update position of the tracked objects, the result is copied to external variable
|
||||
bool MultiTracker::update(InputArray image, std::vector<Rect2d> & boundingBox )
|
||||
{
|
||||
bool status = update(image);
|
||||
boundingBox=objects;
|
||||
return status;
|
||||
};
|
||||
|
||||
const std::vector<Rect2d>& MultiTracker::getObjects() const
|
||||
{
|
||||
return objects;
|
||||
}
|
||||
|
||||
Ptr<MultiTracker> MultiTracker::create()
|
||||
{
|
||||
return makePtr<MultiTracker>();
|
||||
}
|
||||
|
||||
}}} // namespace
|
||||
@@ -0,0 +1,736 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "opencv2/tracking/onlineBoosting.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
StrongClassifierDirectSelection::StrongClassifierDirectSelection( int numBaseClf, int numWeakClf, Size patchSz, const Rect& sampleROI,
|
||||
bool useFeatureEx, int iterationInit )
|
||||
{
|
||||
//StrongClassifier
|
||||
numBaseClassifier = numBaseClf;
|
||||
numAllWeakClassifier = numWeakClf + iterationInit;
|
||||
iterInit = iterationInit;
|
||||
numWeakClassifier = numWeakClf;
|
||||
|
||||
alpha.assign( numBaseClf, 0 );
|
||||
|
||||
patchSize = patchSz;
|
||||
useFeatureExchange = useFeatureEx;
|
||||
|
||||
m_errorMask.resize( numAllWeakClassifier );
|
||||
m_errors.resize( numAllWeakClassifier );
|
||||
m_sumErrors.resize( numAllWeakClassifier );
|
||||
|
||||
ROI = sampleROI;
|
||||
detector = new Detector( this );
|
||||
}
|
||||
|
||||
void StrongClassifierDirectSelection::initBaseClassifier()
|
||||
{
|
||||
baseClassifier = new BaseClassifier*[numBaseClassifier];
|
||||
baseClassifier[0] = new BaseClassifier( numWeakClassifier, iterInit );
|
||||
|
||||
for ( int curBaseClassifier = 1; curBaseClassifier < numBaseClassifier; curBaseClassifier++ )
|
||||
baseClassifier[curBaseClassifier] = new BaseClassifier( numWeakClassifier, iterInit, baseClassifier[0]->getReferenceWeakClassifier() );
|
||||
}
|
||||
|
||||
StrongClassifierDirectSelection::~StrongClassifierDirectSelection()
|
||||
{
|
||||
for ( int curBaseClassifier = 0; curBaseClassifier < numBaseClassifier; curBaseClassifier++ )
|
||||
delete baseClassifier[curBaseClassifier];
|
||||
delete[] baseClassifier;
|
||||
alpha.clear();
|
||||
delete detector;
|
||||
}
|
||||
|
||||
Size StrongClassifierDirectSelection::getPatchSize() const
|
||||
{
|
||||
return patchSize;
|
||||
}
|
||||
|
||||
Rect StrongClassifierDirectSelection::getROI() const
|
||||
{
|
||||
return ROI;
|
||||
}
|
||||
|
||||
float StrongClassifierDirectSelection::classifySmooth( const std::vector<Mat>& images, const Rect& sampleROI, int& idx )
|
||||
{
|
||||
ROI = sampleROI;
|
||||
idx = 0;
|
||||
float confidence = 0;
|
||||
//detector->classify (image, patches);
|
||||
detector->classifySmooth( images );
|
||||
|
||||
//move to best detection
|
||||
if( detector->getNumDetections() <= 0 )
|
||||
{
|
||||
confidence = 0;
|
||||
return confidence;
|
||||
}
|
||||
idx = detector->getPatchIdxOfBestDetection();
|
||||
confidence = detector->getConfidenceOfBestDetection();
|
||||
|
||||
return confidence;
|
||||
}
|
||||
|
||||
bool StrongClassifierDirectSelection::getUseFeatureExchange() const
|
||||
{
|
||||
return useFeatureExchange;
|
||||
}
|
||||
|
||||
int StrongClassifierDirectSelection::getReplacedClassifier() const
|
||||
{
|
||||
return replacedClassifier;
|
||||
}
|
||||
|
||||
int StrongClassifierDirectSelection::getSwappedClassifier() const
|
||||
{
|
||||
return swappedClassifier;
|
||||
}
|
||||
|
||||
bool StrongClassifierDirectSelection::update( const Mat& image, int target, float importance )
|
||||
{
|
||||
m_errorMask.assign( (size_t)numAllWeakClassifier, false );
|
||||
m_errors.assign( (size_t)numAllWeakClassifier, 0.0f );
|
||||
m_sumErrors.assign( (size_t)numAllWeakClassifier, 0.0f );
|
||||
|
||||
baseClassifier[0]->trainClassifier( image, target, importance, m_errorMask );
|
||||
for ( int curBaseClassifier = 0; curBaseClassifier < numBaseClassifier; curBaseClassifier++ )
|
||||
{
|
||||
int selectedClassifier = baseClassifier[curBaseClassifier]->selectBestClassifier( m_errorMask, importance, m_errors );
|
||||
|
||||
if( m_errors[selectedClassifier] >= 0.5 )
|
||||
alpha[curBaseClassifier] = 0;
|
||||
else
|
||||
alpha[curBaseClassifier] = logf( ( 1.0f - m_errors[selectedClassifier] ) / m_errors[selectedClassifier] );
|
||||
|
||||
if( m_errorMask[selectedClassifier] )
|
||||
importance *= (float) sqrt( ( 1.0f - m_errors[selectedClassifier] ) / m_errors[selectedClassifier] );
|
||||
else
|
||||
importance *= (float) sqrt( m_errors[selectedClassifier] / ( 1.0f - m_errors[selectedClassifier] ) );
|
||||
|
||||
//weight limitation
|
||||
//if (importance > 100) importance = 100;
|
||||
|
||||
//sum up errors
|
||||
for ( int curWeakClassifier = 0; curWeakClassifier < numAllWeakClassifier; curWeakClassifier++ )
|
||||
{
|
||||
if( m_errors[curWeakClassifier] != FLT_MAX && m_sumErrors[curWeakClassifier] >= 0 )
|
||||
m_sumErrors[curWeakClassifier] += m_errors[curWeakClassifier];
|
||||
}
|
||||
|
||||
//mark feature as used
|
||||
m_sumErrors[selectedClassifier] = -1;
|
||||
m_errors[selectedClassifier] = FLT_MAX;
|
||||
}
|
||||
|
||||
if( useFeatureExchange )
|
||||
{
|
||||
replacedClassifier = baseClassifier[0]->computeReplaceWeakestClassifier( m_sumErrors );
|
||||
swappedClassifier = baseClassifier[0]->getIdxOfNewWeakClassifier();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void StrongClassifierDirectSelection::replaceWeakClassifier( int idx )
|
||||
{
|
||||
if( useFeatureExchange && idx >= 0 )
|
||||
{
|
||||
baseClassifier[0]->replaceWeakClassifier( idx );
|
||||
for ( int curBaseClassifier = 1; curBaseClassifier < numBaseClassifier; curBaseClassifier++ )
|
||||
baseClassifier[curBaseClassifier]->replaceClassifierStatistic( baseClassifier[0]->getIdxOfNewWeakClassifier(), idx );
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> StrongClassifierDirectSelection::getSelectedWeakClassifier()
|
||||
{
|
||||
std::vector<int> selected;
|
||||
int curBaseClassifier = 0;
|
||||
for ( curBaseClassifier = 0; curBaseClassifier < numBaseClassifier; curBaseClassifier++ )
|
||||
{
|
||||
selected.push_back( baseClassifier[curBaseClassifier]->getSelectedClassifier() );
|
||||
}
|
||||
return selected;
|
||||
}
|
||||
|
||||
float StrongClassifierDirectSelection::eval( const Mat& response )
|
||||
{
|
||||
float value = 0.0f;
|
||||
int curBaseClassifier = 0;
|
||||
|
||||
for ( curBaseClassifier = 0; curBaseClassifier < numBaseClassifier; curBaseClassifier++ )
|
||||
value += baseClassifier[curBaseClassifier]->eval( response ) * alpha[curBaseClassifier];
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
int StrongClassifierDirectSelection::getNumBaseClassifier()
|
||||
{
|
||||
return numBaseClassifier;
|
||||
}
|
||||
|
||||
BaseClassifier::BaseClassifier( int numWeakClassifier, int iterationInit )
|
||||
{
|
||||
this->m_numWeakClassifier = numWeakClassifier;
|
||||
this->m_iterationInit = iterationInit;
|
||||
|
||||
weakClassifier = new WeakClassifierHaarFeature*[numWeakClassifier + iterationInit];
|
||||
m_idxOfNewWeakClassifier = numWeakClassifier;
|
||||
|
||||
generateRandomClassifier();
|
||||
|
||||
m_referenceWeakClassifier = false;
|
||||
m_selectedClassifier = 0;
|
||||
|
||||
m_wCorrect.assign( numWeakClassifier + iterationInit, 0 );
|
||||
|
||||
m_wWrong.assign( numWeakClassifier + iterationInit, 0 );
|
||||
|
||||
for ( int curWeakClassifier = 0; curWeakClassifier < numWeakClassifier + iterationInit; curWeakClassifier++ )
|
||||
m_wWrong[curWeakClassifier] = m_wCorrect[curWeakClassifier] = 1;
|
||||
}
|
||||
|
||||
BaseClassifier::BaseClassifier( int numWeakClassifier, int iterationInit, WeakClassifierHaarFeature** weakCls )
|
||||
{
|
||||
m_numWeakClassifier = numWeakClassifier;
|
||||
m_iterationInit = iterationInit;
|
||||
weakClassifier = weakCls;
|
||||
m_referenceWeakClassifier = true;
|
||||
m_selectedClassifier = 0;
|
||||
m_idxOfNewWeakClassifier = numWeakClassifier;
|
||||
|
||||
m_wCorrect.assign( numWeakClassifier + iterationInit, 0 );
|
||||
m_wWrong.assign( numWeakClassifier + iterationInit, 0 );
|
||||
|
||||
for ( int curWeakClassifier = 0; curWeakClassifier < numWeakClassifier + iterationInit; curWeakClassifier++ )
|
||||
m_wWrong[curWeakClassifier] = m_wCorrect[curWeakClassifier] = 1;
|
||||
}
|
||||
|
||||
BaseClassifier::~BaseClassifier()
|
||||
{
|
||||
if( !m_referenceWeakClassifier )
|
||||
{
|
||||
for ( int curWeakClassifier = 0; curWeakClassifier < m_numWeakClassifier + m_iterationInit; curWeakClassifier++ )
|
||||
delete weakClassifier[curWeakClassifier];
|
||||
|
||||
delete[] weakClassifier;
|
||||
}
|
||||
m_wCorrect.clear();
|
||||
m_wWrong.clear();
|
||||
}
|
||||
|
||||
void BaseClassifier::generateRandomClassifier()
|
||||
{
|
||||
for ( int curWeakClassifier = 0; curWeakClassifier < m_numWeakClassifier + m_iterationInit; curWeakClassifier++ )
|
||||
{
|
||||
weakClassifier[curWeakClassifier] = new WeakClassifierHaarFeature();
|
||||
}
|
||||
}
|
||||
|
||||
int BaseClassifier::eval( const Mat& image )
|
||||
{
|
||||
return weakClassifier[m_selectedClassifier]->eval( image.at<float>( m_selectedClassifier ) );
|
||||
}
|
||||
|
||||
int BaseClassifier::getSelectedClassifier() const
|
||||
{
|
||||
return m_selectedClassifier;
|
||||
}
|
||||
|
||||
void BaseClassifier::trainClassifier( const Mat& image, int target, float importance, std::vector<bool>& errorMask )
|
||||
{
|
||||
|
||||
//get poisson value
|
||||
double A = 1;
|
||||
int K = 0;
|
||||
int K_max = 10;
|
||||
for ( ; ; )
|
||||
{
|
||||
double U_k = (double) rand() / RAND_MAX;
|
||||
A *= U_k;
|
||||
if( K > K_max || A < exp( -importance ) )
|
||||
break;
|
||||
K++;
|
||||
}
|
||||
|
||||
for ( int curK = 0; curK <= K; curK++ )
|
||||
{
|
||||
for ( int curWeakClassifier = 0; curWeakClassifier < m_numWeakClassifier + m_iterationInit; curWeakClassifier++ )
|
||||
{
|
||||
errorMask[curWeakClassifier] = weakClassifier[curWeakClassifier]->update( image.at<float>( curWeakClassifier ), target );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
float BaseClassifier::getError( int curWeakClassifier )
|
||||
{
|
||||
if( curWeakClassifier == -1 )
|
||||
curWeakClassifier = m_selectedClassifier;
|
||||
return m_wWrong[curWeakClassifier] / ( m_wWrong[curWeakClassifier] + m_wCorrect[curWeakClassifier] );
|
||||
}
|
||||
|
||||
int BaseClassifier::selectBestClassifier( std::vector<bool>& errorMask, float importance, std::vector<float> & errors )
|
||||
{
|
||||
float minError = FLT_MAX;
|
||||
int tmp_selectedClassifier = m_selectedClassifier;
|
||||
|
||||
for ( int curWeakClassifier = 0; curWeakClassifier < m_numWeakClassifier + m_iterationInit; curWeakClassifier++ )
|
||||
{
|
||||
if( errorMask[curWeakClassifier] )
|
||||
{
|
||||
m_wWrong[curWeakClassifier] += importance;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_wCorrect[curWeakClassifier] += importance;
|
||||
}
|
||||
|
||||
if( errors[curWeakClassifier] == FLT_MAX )
|
||||
continue;
|
||||
|
||||
errors[curWeakClassifier] = m_wWrong[curWeakClassifier] / ( m_wWrong[curWeakClassifier] + m_wCorrect[curWeakClassifier] );
|
||||
|
||||
/*if(errors[curWeakClassifier] < 0.001 || !(errors[curWeakClassifier]>0.0))
|
||||
{
|
||||
errors[curWeakClassifier] = 0.001;
|
||||
}
|
||||
|
||||
if(errors[curWeakClassifier] >= 1.0)
|
||||
errors[curWeakClassifier] = 0.999;
|
||||
|
||||
assert (errors[curWeakClassifier] > 0.0);
|
||||
assert (errors[curWeakClassifier] < 1.0);*/
|
||||
|
||||
if( curWeakClassifier < m_numWeakClassifier )
|
||||
{
|
||||
if( errors[curWeakClassifier] < minError )
|
||||
{
|
||||
minError = errors[curWeakClassifier];
|
||||
tmp_selectedClassifier = curWeakClassifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_selectedClassifier = tmp_selectedClassifier;
|
||||
return m_selectedClassifier;
|
||||
}
|
||||
|
||||
void BaseClassifier::getErrors( float* errors )
|
||||
{
|
||||
for ( int curWeakClassifier = 0; curWeakClassifier < m_numWeakClassifier + m_iterationInit; curWeakClassifier++ )
|
||||
{
|
||||
if( errors[curWeakClassifier] == FLT_MAX )
|
||||
continue;
|
||||
|
||||
errors[curWeakClassifier] = m_wWrong[curWeakClassifier] / ( m_wWrong[curWeakClassifier] + m_wCorrect[curWeakClassifier] );
|
||||
|
||||
CV_Assert( errors[curWeakClassifier] > 0 );
|
||||
}
|
||||
}
|
||||
|
||||
void BaseClassifier::replaceWeakClassifier( int index )
|
||||
{
|
||||
delete weakClassifier[index];
|
||||
weakClassifier[index] = weakClassifier[m_idxOfNewWeakClassifier];
|
||||
m_wWrong[index] = m_wWrong[m_idxOfNewWeakClassifier];
|
||||
m_wWrong[m_idxOfNewWeakClassifier] = 1;
|
||||
m_wCorrect[index] = m_wCorrect[m_idxOfNewWeakClassifier];
|
||||
m_wCorrect[m_idxOfNewWeakClassifier] = 1;
|
||||
|
||||
weakClassifier[m_idxOfNewWeakClassifier] = new WeakClassifierHaarFeature();
|
||||
}
|
||||
|
||||
int BaseClassifier::computeReplaceWeakestClassifier( const std::vector<float> & errors )
|
||||
{
|
||||
float maxError = 0.0f;
|
||||
int index = -1;
|
||||
|
||||
//search the classifier with the largest error
|
||||
for ( int curWeakClassifier = m_numWeakClassifier - 1; curWeakClassifier >= 0; curWeakClassifier-- )
|
||||
{
|
||||
if( errors[curWeakClassifier] > maxError )
|
||||
{
|
||||
maxError = errors[curWeakClassifier];
|
||||
index = curWeakClassifier;
|
||||
}
|
||||
}
|
||||
|
||||
CV_Assert( index > -1 );
|
||||
CV_Assert( index != m_selectedClassifier );
|
||||
|
||||
//replace
|
||||
m_idxOfNewWeakClassifier++;
|
||||
if( m_idxOfNewWeakClassifier == m_numWeakClassifier + m_iterationInit )
|
||||
m_idxOfNewWeakClassifier = m_numWeakClassifier;
|
||||
|
||||
if( maxError > errors[m_idxOfNewWeakClassifier] )
|
||||
{
|
||||
return index;
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
void BaseClassifier::replaceClassifierStatistic( int sourceIndex, int targetIndex )
|
||||
{
|
||||
CV_Assert( targetIndex >= 0 );
|
||||
CV_Assert( targetIndex != m_selectedClassifier );
|
||||
CV_Assert( targetIndex < m_numWeakClassifier );
|
||||
|
||||
//replace
|
||||
m_wWrong[targetIndex] = m_wWrong[sourceIndex];
|
||||
m_wWrong[sourceIndex] = 1.0f;
|
||||
m_wCorrect[targetIndex] = m_wCorrect[sourceIndex];
|
||||
m_wCorrect[sourceIndex] = 1.0f;
|
||||
}
|
||||
|
||||
EstimatedGaussDistribution::EstimatedGaussDistribution()
|
||||
{
|
||||
m_mean = 0;
|
||||
m_sigma = 1;
|
||||
this->m_P_mean = 1000;
|
||||
this->m_R_mean = 0.01f;
|
||||
this->m_P_sigma = 1000;
|
||||
this->m_R_sigma = 0.01f;
|
||||
}
|
||||
|
||||
EstimatedGaussDistribution::EstimatedGaussDistribution( float P_mean, float R_mean, float P_sigma, float R_sigma )
|
||||
{
|
||||
m_mean = 0;
|
||||
m_sigma = 1;
|
||||
this->m_P_mean = P_mean;
|
||||
this->m_R_mean = R_mean;
|
||||
this->m_P_sigma = P_sigma;
|
||||
this->m_R_sigma = R_sigma;
|
||||
}
|
||||
|
||||
EstimatedGaussDistribution::~EstimatedGaussDistribution()
|
||||
{
|
||||
}
|
||||
|
||||
void EstimatedGaussDistribution::update( float value )
|
||||
{
|
||||
//update distribution (mean and sigma) using a kalman filter for each
|
||||
|
||||
float K;
|
||||
float minFactor = 0.001f;
|
||||
|
||||
//mean
|
||||
|
||||
K = m_P_mean / ( m_P_mean + m_R_mean );
|
||||
if( K < minFactor )
|
||||
K = minFactor;
|
||||
|
||||
m_mean = K * value + ( 1.0f - K ) * m_mean;
|
||||
m_P_mean = m_P_mean * m_R_mean / ( m_P_mean + m_R_mean );
|
||||
|
||||
K = m_P_sigma / ( m_P_sigma + m_R_sigma );
|
||||
if( K < minFactor )
|
||||
K = minFactor;
|
||||
|
||||
float tmp_sigma = K * ( m_mean - value ) * ( m_mean - value ) + ( 1.0f - K ) * m_sigma * m_sigma;
|
||||
m_P_sigma = m_P_sigma * m_R_mean / ( m_P_sigma + m_R_sigma );
|
||||
|
||||
m_sigma = static_cast<float>( sqrt( tmp_sigma ) );
|
||||
if( m_sigma <= 1.0f )
|
||||
m_sigma = 1.0f;
|
||||
|
||||
}
|
||||
|
||||
void EstimatedGaussDistribution::setValues( float mean, float sigma )
|
||||
{
|
||||
this->m_mean = mean;
|
||||
this->m_sigma = sigma;
|
||||
}
|
||||
|
||||
float EstimatedGaussDistribution::getMean()
|
||||
{
|
||||
return m_mean;
|
||||
}
|
||||
|
||||
float EstimatedGaussDistribution::getSigma()
|
||||
{
|
||||
return m_sigma;
|
||||
}
|
||||
|
||||
WeakClassifierHaarFeature::WeakClassifierHaarFeature()
|
||||
{
|
||||
sigma = 1;
|
||||
mean = 0;
|
||||
|
||||
EstimatedGaussDistribution* m_posSamples = new EstimatedGaussDistribution();
|
||||
EstimatedGaussDistribution* m_negSamples = new EstimatedGaussDistribution();
|
||||
generateRandomClassifier( m_posSamples, m_negSamples );
|
||||
|
||||
getInitialDistribution( (EstimatedGaussDistribution*) m_classifier->getDistribution( -1 ) );
|
||||
getInitialDistribution( (EstimatedGaussDistribution*) m_classifier->getDistribution( 1 ) );
|
||||
}
|
||||
|
||||
WeakClassifierHaarFeature::~WeakClassifierHaarFeature()
|
||||
{
|
||||
delete m_classifier;
|
||||
}
|
||||
|
||||
void WeakClassifierHaarFeature::getInitialDistribution( EstimatedGaussDistribution* distribution )
|
||||
{
|
||||
distribution->setValues( mean, sigma );
|
||||
}
|
||||
|
||||
void WeakClassifierHaarFeature::generateRandomClassifier( EstimatedGaussDistribution* m_posSamples, EstimatedGaussDistribution* m_negSamples )
|
||||
{
|
||||
m_classifier = new ClassifierThreshold( m_posSamples, m_negSamples );
|
||||
}
|
||||
|
||||
bool WeakClassifierHaarFeature::update( float value, int target )
|
||||
{
|
||||
m_classifier->update( value, target );
|
||||
return ( m_classifier->eval( value ) != target );
|
||||
}
|
||||
|
||||
int WeakClassifierHaarFeature::eval( float value )
|
||||
{
|
||||
return m_classifier->eval( value );
|
||||
}
|
||||
|
||||
Detector::Detector( StrongClassifierDirectSelection* classifier ) :
|
||||
m_sizeDetections( 0 )
|
||||
{
|
||||
this->m_classifier = classifier;
|
||||
|
||||
m_sizeConfidences = 0;
|
||||
m_maxConfidence = -FLT_MAX;
|
||||
m_numDetections = 0;
|
||||
m_idxBestDetection = -1;
|
||||
}
|
||||
|
||||
Detector::~Detector()
|
||||
{
|
||||
}
|
||||
|
||||
void Detector::prepareConfidencesMemory( int numPatches )
|
||||
{
|
||||
if( numPatches <= m_sizeConfidences )
|
||||
return;
|
||||
|
||||
m_sizeConfidences = numPatches;
|
||||
m_confidences.resize( numPatches );
|
||||
}
|
||||
|
||||
void Detector::prepareDetectionsMemory( int numDetections )
|
||||
{
|
||||
if( numDetections <= m_sizeDetections )
|
||||
return;
|
||||
|
||||
m_sizeDetections = numDetections;
|
||||
m_idxDetections.resize( numDetections );
|
||||
}
|
||||
|
||||
void Detector::classifySmooth( const std::vector<Mat>& images, float minMargin )
|
||||
{
|
||||
int numPatches = static_cast<int>(images.size());
|
||||
|
||||
prepareConfidencesMemory( numPatches );
|
||||
|
||||
m_numDetections = 0;
|
||||
m_idxBestDetection = -1;
|
||||
m_maxConfidence = -FLT_MAX;
|
||||
|
||||
//compute grid
|
||||
//TODO 0.99 overlap from params
|
||||
Size patchSz = m_classifier->getPatchSize();
|
||||
int stepCol = (int) floor( ( 1.0f - 0.99f ) * (float) patchSz.width + 0.5f );
|
||||
int stepRow = (int) floor( ( 1.0f - 0.99f ) * (float) patchSz.height + 0.5f );
|
||||
if( stepCol <= 0 )
|
||||
stepCol = 1;
|
||||
if( stepRow <= 0 )
|
||||
stepRow = 1;
|
||||
|
||||
Size patchGrid;
|
||||
Rect ROI = m_classifier->getROI();
|
||||
patchGrid.height = ( (int) ( (float) ( ROI.height - patchSz.height ) / stepRow ) + 1 );
|
||||
patchGrid.width = ( (int) ( (float) ( ROI.width - patchSz.width ) / stepCol ) + 1 );
|
||||
|
||||
if( ( patchGrid.width != m_confMatrix.cols ) || ( patchGrid.height != m_confMatrix.rows ) )
|
||||
{
|
||||
m_confMatrix.create( patchGrid.height, patchGrid.width );
|
||||
m_confMatrixSmooth.create( patchGrid.height, patchGrid.width );
|
||||
m_confImageDisplay.create( patchGrid.height, patchGrid.width );
|
||||
}
|
||||
|
||||
int curPatch = 0;
|
||||
// Eval and filter
|
||||
for ( int row = 0; row < patchGrid.height; row++ )
|
||||
{
|
||||
for ( int col = 0; col < patchGrid.width; col++ )
|
||||
{
|
||||
m_confidences[curPatch] = m_classifier->eval( images[curPatch] );
|
||||
|
||||
// fill matrix
|
||||
m_confMatrix( row, col ) = m_confidences[curPatch];
|
||||
curPatch++;
|
||||
}
|
||||
}
|
||||
|
||||
// Filter
|
||||
//cv::GaussianBlur(m_confMatrix,m_confMatrixSmooth,cv::Size(3,3),0.8);
|
||||
cv::GaussianBlur( m_confMatrix, m_confMatrixSmooth, cv::Size( 3, 3 ), 0 );
|
||||
|
||||
// Make display friendly
|
||||
double min_val, max_val;
|
||||
cv::minMaxLoc( m_confMatrixSmooth, &min_val, &max_val );
|
||||
for ( int y = 0; y < m_confImageDisplay.rows; y++ )
|
||||
{
|
||||
unsigned char* pConfImg = m_confImageDisplay[y];
|
||||
const float* pConfData = m_confMatrixSmooth[y];
|
||||
for ( int x = 0; x < m_confImageDisplay.cols; x++, pConfImg++, pConfData++ )
|
||||
{
|
||||
*pConfImg = static_cast<unsigned char>( 255.0 * ( *pConfData - min_val ) / ( max_val - min_val ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Get best detection
|
||||
curPatch = 0;
|
||||
for ( int row = 0; row < patchGrid.height; row++ )
|
||||
{
|
||||
for ( int col = 0; col < patchGrid.width; col++ )
|
||||
{
|
||||
// fill matrix
|
||||
m_confidences[curPatch] = m_confMatrixSmooth( row, col );
|
||||
|
||||
if( m_confidences[curPatch] > m_maxConfidence )
|
||||
{
|
||||
m_maxConfidence = m_confidences[curPatch];
|
||||
m_idxBestDetection = curPatch;
|
||||
}
|
||||
if( m_confidences[curPatch] > minMargin )
|
||||
{
|
||||
m_numDetections++;
|
||||
}
|
||||
curPatch++;
|
||||
}
|
||||
}
|
||||
|
||||
prepareDetectionsMemory( m_numDetections );
|
||||
int curDetection = -1;
|
||||
for ( int currentPatch = 0; currentPatch < numPatches; currentPatch++ )
|
||||
{
|
||||
if( m_confidences[currentPatch] > minMargin )
|
||||
m_idxDetections[++curDetection] = currentPatch;
|
||||
}
|
||||
}
|
||||
|
||||
int Detector::getNumDetections()
|
||||
{
|
||||
return m_numDetections;
|
||||
}
|
||||
|
||||
float Detector::getConfidence( int patchIdx )
|
||||
{
|
||||
return m_confidences[patchIdx];
|
||||
}
|
||||
|
||||
float Detector::getConfidenceOfDetection( int detectionIdx )
|
||||
{
|
||||
return m_confidences[getPatchIdxOfDetection( detectionIdx )];
|
||||
}
|
||||
|
||||
int Detector::getPatchIdxOfBestDetection()
|
||||
{
|
||||
return m_idxBestDetection;
|
||||
}
|
||||
|
||||
int Detector::getPatchIdxOfDetection( int detectionIdx )
|
||||
{
|
||||
return m_idxDetections[detectionIdx];
|
||||
}
|
||||
|
||||
ClassifierThreshold::ClassifierThreshold( EstimatedGaussDistribution* posSamples, EstimatedGaussDistribution* negSamples )
|
||||
{
|
||||
m_posSamples = posSamples;
|
||||
m_negSamples = negSamples;
|
||||
m_threshold = 0.0f;
|
||||
m_parity = 0;
|
||||
}
|
||||
|
||||
ClassifierThreshold::~ClassifierThreshold()
|
||||
{
|
||||
if( m_posSamples != NULL )
|
||||
delete m_posSamples;
|
||||
if( m_negSamples != NULL )
|
||||
delete m_negSamples;
|
||||
}
|
||||
|
||||
void*
|
||||
ClassifierThreshold::getDistribution( int target )
|
||||
{
|
||||
if( target == 1 )
|
||||
return m_posSamples;
|
||||
else
|
||||
return m_negSamples;
|
||||
}
|
||||
|
||||
void ClassifierThreshold::update( float value, int target )
|
||||
{
|
||||
//update distribution
|
||||
if( target == 1 )
|
||||
m_posSamples->update( value );
|
||||
else
|
||||
m_negSamples->update( value );
|
||||
|
||||
//adapt threshold and parity
|
||||
m_threshold = ( m_posSamples->getMean() + m_negSamples->getMean() ) / 2.0f;
|
||||
m_parity = ( m_posSamples->getMean() > m_negSamples->getMean() ) ? 1 : -1;
|
||||
}
|
||||
|
||||
int ClassifierThreshold::eval( float value )
|
||||
{
|
||||
return ( ( ( m_parity * ( value - m_threshold ) ) > 0 ) ? 1 : -1 );
|
||||
}
|
||||
|
||||
}}} // namespace
|
||||
@@ -0,0 +1,133 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
// Copyright (C) 2014, Advanced Micro Devices, Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
|
||||
|
||||
|
||||
__kernel void NCC(__global const uchar *patch,
|
||||
__global const uchar *positiveSamples,
|
||||
__global const uchar *negativeSamples,
|
||||
__global float *ncc,
|
||||
int posNum,
|
||||
int negNum)
|
||||
{
|
||||
int id = get_global_id(0);
|
||||
if (id >= 1000) return;
|
||||
bool posFlg;
|
||||
|
||||
if (id < 500)
|
||||
posFlg = true;
|
||||
if (id >= 500)
|
||||
{
|
||||
//Negative index
|
||||
id = id - 500;
|
||||
posFlg = false;
|
||||
}
|
||||
|
||||
//Variables
|
||||
int s1 = 0, s2 = 0, n1 = 0, n2 = 0, prod = 0;
|
||||
float sq1 = 0, sq2 = 0, ares = 0;
|
||||
int N = 225;
|
||||
//NCC with positive sample
|
||||
if (posFlg && id < posNum)
|
||||
{
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
s1 += positiveSamples[id * N + i];
|
||||
s2 += patch[i];
|
||||
n1 += positiveSamples[id * N + i] * positiveSamples[id * N + i];
|
||||
n2 += patch[i] * patch[i];
|
||||
prod += positiveSamples[id * N + i] * patch[i];
|
||||
}
|
||||
sq1 = sqrt(max(0.0, n1 - 1.0 * s1 * s1 / N));
|
||||
sq2 = sqrt(max(0.0, n2 - 1.0 * s2 * s2 / N));
|
||||
ares = (sq2 == 0) ? sq1 / fabs(sq1) : (prod - s1 * s2 / N) / sq1 / sq2;
|
||||
ncc[id] = ares;
|
||||
}
|
||||
|
||||
//NCC with negative sample
|
||||
if (!posFlg && id < negNum)
|
||||
{
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
|
||||
s1 += negativeSamples[id * N + i];
|
||||
s2 += patch[i];
|
||||
n1 += negativeSamples[id * N + i] * negativeSamples[id * N + i];
|
||||
n2 += patch[i] * patch[i];
|
||||
prod += negativeSamples[id * N + i] * patch[i];
|
||||
}
|
||||
sq1 = sqrt(max(0.0, n1 - 1.0 * s1 * s1 / N));
|
||||
sq2 = sqrt(max(0.0, n2 - 1.0 * s2 * s2 / N));
|
||||
ares = (sq2 == 0) ? sq1 / fabs(sq1) : (prod - s1 * s2 / N) / sq1 / sq2;
|
||||
ncc[id+500] = ares;
|
||||
}
|
||||
}
|
||||
|
||||
__kernel void batchNCC(__global const uchar *patches,
|
||||
__global const uchar *positiveSamples,
|
||||
__global const uchar *negativeSamples,
|
||||
__global float *posNcc,
|
||||
__global float *negNcc,
|
||||
int posNum,
|
||||
int negNum,
|
||||
int patchNum)
|
||||
{
|
||||
int id = get_global_id(0);
|
||||
bool posFlg;
|
||||
|
||||
if (id < 500*patchNum)
|
||||
posFlg = true;
|
||||
if (id >= 500*patchNum)
|
||||
{
|
||||
//Negative index
|
||||
id = id - 500*patchNum;
|
||||
posFlg = false;
|
||||
}
|
||||
|
||||
int modelSampleID = id % 500;
|
||||
int patchID = id / 500;
|
||||
|
||||
//Variables
|
||||
int s1 = 0, s2 = 0, n1 = 0, n2 = 0, prod = 0;
|
||||
float sq1 = 0, sq2 = 0, ares = 0;
|
||||
int N = 225;
|
||||
|
||||
//NCC with positive sample
|
||||
if (posFlg && modelSampleID < posNum)
|
||||
{
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
s1 += positiveSamples[modelSampleID * N + i];
|
||||
s2 += patches[patchID*N + i];
|
||||
n1 += positiveSamples[modelSampleID * N + i] * positiveSamples[modelSampleID * N + i];
|
||||
n2 += patches[patchID*N + i] * patches[patchID*N + i];
|
||||
prod += positiveSamples[modelSampleID * N + i] * patches[patchID*N + i];
|
||||
}
|
||||
sq1 = sqrt(max(0.0, n1 - 1.0 * s1 * s1 / N));
|
||||
sq2 = sqrt(max(0.0, n2 - 1.0 * s2 * s2 / N));
|
||||
ares = (sq2 == 0) ? sq1 / fabs(sq1) : (prod - s1 * s2 / N) / sq1 / sq2;
|
||||
posNcc[id] = ares;
|
||||
}
|
||||
|
||||
//NCC with negative sample
|
||||
if (!posFlg && modelSampleID < negNum)
|
||||
{
|
||||
for (int i = 0; i < N; i++)
|
||||
{
|
||||
|
||||
s1 += negativeSamples[modelSampleID * N + i];
|
||||
s2 += patches[patchID*N + i];
|
||||
n1 += negativeSamples[modelSampleID * N + i] * negativeSamples[modelSampleID * N + i];
|
||||
n2 += patches[patchID*N + i] * patches[patchID*N + i];
|
||||
prod += negativeSamples[modelSampleID * N + i] * patches[patchID*N + i];
|
||||
}
|
||||
sq1 = sqrt(max(0.0, n1 - 1.0 * s1 * s1 / N));
|
||||
sq2 = sqrt(max(0.0, n2 - 1.0 * s2 * s2 / N));
|
||||
ares = (sq2 == 0) ? sq1 / fabs(sq1) : (prod - s1 * s2 / N) / sq1 / sq2;
|
||||
negNcc[id] = ares;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
// Copyright (C) 2016, Intel, Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
|
||||
#define LOCAL_SIZE_X 64
|
||||
#define BLOCK_SIZE_X 3
|
||||
|
||||
__kernel void tmm(__global float *A, int m, int n, float alpha, __global float *D)
|
||||
{
|
||||
int lidX = get_local_id(0);
|
||||
uint lsizeX = get_local_size(0);
|
||||
|
||||
uint matI = get_group_id(1);
|
||||
uint matJ = get_group_id(0);
|
||||
|
||||
if (matI < matJ)
|
||||
return;
|
||||
|
||||
__local float4 a[LOCAL_SIZE_X], b[LOCAL_SIZE_X];
|
||||
float4 result;
|
||||
__local uint cnt;
|
||||
result = 0;
|
||||
cnt = 0;
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
do {
|
||||
// load block data to SLM.
|
||||
int global_block_base = (lidX + cnt * lsizeX) * BLOCK_SIZE_X;
|
||||
float4 pa[BLOCK_SIZE_X], pb[BLOCK_SIZE_X];
|
||||
|
||||
#pragma unroll
|
||||
for(uint j = 0; j < BLOCK_SIZE_X && (cnt * lsizeX + lidX) * BLOCK_SIZE_X < n / 4; j++) {
|
||||
pa[j] = *(__global float4*)&A[matI * n + (global_block_base + j) * 4];
|
||||
if (matI != matJ)
|
||||
pb[j] = *(__global float4*)&A[matJ * n + (global_block_base + j) * 4];
|
||||
else
|
||||
pb[j] = pa[j];
|
||||
}
|
||||
|
||||
// zero the data out-of-boundary.
|
||||
if (global_block_base + BLOCK_SIZE_X - 1 >= n/4) {
|
||||
#pragma unroll
|
||||
for(int i = 0; i < BLOCK_SIZE_X; i++) {
|
||||
if (global_block_base + i >= n/4)
|
||||
pb[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pb[0] *= pa[0];
|
||||
|
||||
for(int j = 1; j < BLOCK_SIZE_X; j++)
|
||||
pb[0] = fma(pb[j], pa[j], pb[0]);
|
||||
|
||||
b[lidX] = pb[0];
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
// perform reduce add
|
||||
for(int offset = LOCAL_SIZE_X / 2; offset > 0; offset >>= 1) {
|
||||
if (lidX < offset)
|
||||
b[lidX] += b[(lidX + offset)];
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
}
|
||||
if (lidX == 0) {
|
||||
result += b[0];
|
||||
cnt++;
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
} while(cnt * BLOCK_SIZE_X * lsizeX < n / 4);
|
||||
if (lidX == 0) {
|
||||
float ret = (result.s0 + result.s1 + result.s2 + result.s3) * alpha;
|
||||
D[matI * m + matJ] = ret;
|
||||
if (matI != matJ)
|
||||
D[matJ * m + matI] = ret;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef __OPENCV_PRECOMP_H__
|
||||
#define __OPENCV_PRECOMP_H__
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/core/ocl.hpp"
|
||||
#include "opencv2/core/hal/hal.hpp"
|
||||
|
||||
#include "opencv2/video/tracking.hpp"
|
||||
|
||||
#include "opencv2/tracking.hpp"
|
||||
|
||||
|
||||
#include "opencv2/tracking/tracking_internals.hpp"
|
||||
|
||||
namespace cv { inline namespace tracking {
|
||||
namespace impl { }
|
||||
using namespace impl;
|
||||
using namespace cv::detail::tracking;
|
||||
}} // namespace
|
||||
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
extern const float ColorNames[][10];
|
||||
|
||||
/* Cholesky decomposition
|
||||
The function performs Cholesky decomposition <https://en.wikipedia.org/wiki/Cholesky_decomposition>.
|
||||
A - the Hermitian, positive-definite matrix,
|
||||
astep - size of row in A,
|
||||
asize - number of cols and rows in A,
|
||||
L - the lower triangular matrix, A = L*Lt.
|
||||
*/
|
||||
|
||||
template<typename _Tp> bool
|
||||
inline callHalCholesky( _Tp* L, size_t lstep, int lsize );
|
||||
|
||||
template<> bool
|
||||
inline callHalCholesky<float>( float* L, size_t lstep, int lsize )
|
||||
{
|
||||
return hal::Cholesky32f(L, lstep, lsize, NULL, 0, 0);
|
||||
}
|
||||
|
||||
template<> bool
|
||||
inline callHalCholesky<double>( double* L, size_t lstep, int lsize)
|
||||
{
|
||||
return hal::Cholesky64f(L, lstep, lsize, NULL, 0, 0);
|
||||
}
|
||||
|
||||
template<typename _Tp> bool
|
||||
inline choleskyDecomposition( const _Tp* A, size_t astep, int asize, _Tp* L, size_t lstep )
|
||||
{
|
||||
bool success = false;
|
||||
|
||||
astep /= sizeof(_Tp);
|
||||
lstep /= sizeof(_Tp);
|
||||
|
||||
for(int i = 0; i < asize; i++)
|
||||
for(int j = 0; j <= i; j++)
|
||||
L[i*lstep + j] = A[i*astep + j];
|
||||
|
||||
success = callHalCholesky(L, lstep*sizeof(_Tp), asize);
|
||||
|
||||
if(success)
|
||||
{
|
||||
for(int i = 0; i < asize; i++ )
|
||||
for(int j = i + 1; j < asize; j++ )
|
||||
L[i*lstep + j] = 0.0;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
}}} // namespace
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,189 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "opencv2/tracking/tldDataset.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
namespace tld
|
||||
{
|
||||
char tldRootPath[100];
|
||||
int frameNum = 0;
|
||||
bool flagPNG = false;
|
||||
bool flagVOT = false;
|
||||
|
||||
//TLD Dataset Parameters
|
||||
const char* tldFolderName[10] = {
|
||||
"01_david",
|
||||
"02_jumping",
|
||||
"03_pedestrian1",
|
||||
"04_pedestrian2",
|
||||
"05_pedestrian3",
|
||||
"06_car",
|
||||
"07_motocross",
|
||||
"08_volkswagen",
|
||||
"09_carchase",
|
||||
"10_panda"
|
||||
};
|
||||
const char* votFolderName[60] = {
|
||||
"bag", "ball1", "ball2", "basketball", "birds1", "birds2", "blanket", "bmx", "bolt1", "bolt2",
|
||||
"book", "butterfly", "car1", "car2", "crossing", "dinosaur", "fernando", "fish1", "fish2", "fish3",
|
||||
"fish4", "girl", "glove", "godfather", "graduate", "gymnastics1", "gymnastics2 ", "gymnastics3", "gymnastics4", "hand",
|
||||
"handball1", "handball2", "helicopter", "iceskater1", "iceskater2", "leaves", "marching", "matrix", "motocross1", "motocross2",
|
||||
"nature", "octopus", "pedestrian1", "pedestrian2", "rabbit", "racing", "road", "shaking", "sheep", "singer1",
|
||||
"singer2", "singer3", "soccer1", "soccer2", "soldier", "sphere", "tiger", "traffic", "tunnel", "wiper"
|
||||
};
|
||||
|
||||
const Rect2d tldInitBB[10] = {
|
||||
Rect2d(165, 93, 51, 54), Rect2d(147, 110, 33, 32), Rect2d(47, 51, 21, 36), Rect2d(130, 134, 21, 53), Rect2d(154, 102, 24, 52),
|
||||
Rect2d(142, 125, 90, 39), Rect2d(290, 43, 23, 40), Rect2d(273, 77, 27, 25), Rect2d(337, 219, 54, 37), Rect2d(58, 100, 27, 22)
|
||||
};
|
||||
const Rect2d votInitBB[60] = {
|
||||
Rect2d(142, 125, 90, 39), Rect2d(490, 400, 40, 40), Rect2d(273, 77, 27, 25), Rect2d(145, 84, 54, 37), Rect2d(58, 100, 27, 22),
|
||||
Rect2d(450, 380, 60, 60), Rect2d(290, 43, 23, 40), Rect2d(273, 77, 27, 25), Rect2d(225, 175, 50, 50), Rect2d(58, 100, 27, 22),
|
||||
|
||||
Rect2d(142, 125, 90, 39), Rect2d(290, 43, 23, 40), Rect2d(273, 77, 27, 25), Rect2d(145, 84, 54, 37), Rect2d(560, 460, 50, 120),
|
||||
Rect2d(142, 125, 90, 39), Rect2d(290, 43, 23, 40), Rect2d(273, 77, 27, 25), Rect2d(145, 84, 54, 37), Rect2d(58, 100, 27, 22),
|
||||
|
||||
Rect2d(142, 125, 90, 39), Rect2d(290, 43, 23, 40), Rect2d(273, 77, 27, 25), Rect2d(145, 84, 54, 37), Rect2d(58, 100, 27, 22),
|
||||
Rect2d(142, 125, 90, 39), Rect2d(290, 43, 23, 40), Rect2d(273, 77, 27, 25), Rect2d(145, 84, 54, 37), Rect2d(58, 100, 27, 22),
|
||||
|
||||
Rect2d(142, 125, 90, 39), Rect2d(290, 43, 23, 40), Rect2d(273, 77, 27, 25), Rect2d(145, 84, 54, 37), Rect2d(58, 100, 27, 22),
|
||||
Rect2d(142, 125, 90, 39), Rect2d(290, 43, 23, 40), Rect2d(273, 77, 27, 25), Rect2d(145, 84, 54, 37), Rect2d(58, 100, 27, 22),
|
||||
|
||||
Rect2d(142, 125, 90, 39), Rect2d(290, 43, 23, 40), Rect2d(273, 77, 27, 25), Rect2d(145, 84, 54, 37), Rect2d(58, 100, 27, 22),
|
||||
Rect2d(142, 125, 90, 39), Rect2d(290, 43, 23, 40), Rect2d(273, 77, 27, 25), Rect2d(145, 84, 54, 37), Rect2d(58, 100, 27, 22),
|
||||
|
||||
Rect2d(142, 125, 90, 39), Rect2d(290, 43, 23, 40), Rect2d(273, 77, 27, 25), Rect2d(145, 84, 54, 37), Rect2d(58, 100, 27, 22),
|
||||
Rect2d(142, 125, 90, 39), Rect2d(290, 43, 23, 40), Rect2d(273, 77, 27, 25), Rect2d(145, 84, 54, 37), Rect2d(58, 100, 27, 22),
|
||||
};
|
||||
|
||||
int tldFrameOffset[10] = { 100, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
|
||||
int votFrameOffset[60] = {
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1
|
||||
};
|
||||
bool tldFlagPNG[10] = { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 };
|
||||
bool votFlagPNG[60] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
cv::Rect2d tld_InitDataset(int videoInd, const char* rootPath, int datasetInd)
|
||||
{
|
||||
char* folderName = (char *)"";
|
||||
double x = 0,
|
||||
y = 0,
|
||||
w = 0,
|
||||
h = 0;
|
||||
|
||||
//Index range
|
||||
// 1-10 TLD Dataset
|
||||
// 1-60 VOT 2015 Dataset
|
||||
int id = videoInd - 1;
|
||||
|
||||
if (datasetInd == 0)
|
||||
{
|
||||
folderName = (char*)tldFolderName[id];
|
||||
x = tldInitBB[id].x;
|
||||
y = tldInitBB[id].y;
|
||||
w = tldInitBB[id].width;
|
||||
h = tldInitBB[id].height;
|
||||
frameNum = tldFrameOffset[id];
|
||||
flagPNG = tldFlagPNG[id];
|
||||
flagVOT = false;
|
||||
}
|
||||
if (datasetInd == 1)
|
||||
{
|
||||
folderName = (char*)votFolderName[id];
|
||||
x = votInitBB[id].x;
|
||||
y = votInitBB[id].y;
|
||||
w = votInitBB[id].width;
|
||||
h = votInitBB[id].height;
|
||||
frameNum = votFrameOffset[id];
|
||||
flagPNG = votFlagPNG[id];
|
||||
flagVOT = true;
|
||||
}
|
||||
|
||||
strcpy(tldRootPath, rootPath);
|
||||
strcat(tldRootPath, "\\");
|
||||
strcat(tldRootPath, folderName);
|
||||
|
||||
|
||||
return cv::Rect2d(x, y, w, h);
|
||||
}
|
||||
|
||||
cv::String tld_getNextDatasetFrame()
|
||||
{
|
||||
char fullPath[100];
|
||||
char numStr[10];
|
||||
strcpy(fullPath, tldRootPath);
|
||||
strcat(fullPath, "\\");
|
||||
if (flagVOT)
|
||||
strcat(fullPath, "000");
|
||||
if (frameNum < 10) strcat(fullPath, "0000");
|
||||
else if (frameNum < 100) strcat(fullPath, "000");
|
||||
else if (frameNum < 1000) strcat(fullPath, "00");
|
||||
else if (frameNum < 10000) strcat(fullPath, "0");
|
||||
|
||||
sprintf(numStr, "%d", frameNum);
|
||||
strcat(fullPath, numStr);
|
||||
if (flagPNG) strcat(fullPath, ".png");
|
||||
else strcat(fullPath, ".jpg");
|
||||
frameNum++;
|
||||
|
||||
return fullPath;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}}}
|
||||
@@ -0,0 +1,606 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
#include "tldDetector.hpp"
|
||||
#include "tracking_utils.hpp"
|
||||
|
||||
namespace cv {
|
||||
inline namespace tracking {
|
||||
namespace impl {
|
||||
namespace tld {
|
||||
// Calculate offsets for classifiers
|
||||
void TLDDetector::prepareClassifiers(int rowstep)
|
||||
{
|
||||
for (int i = 0; i < (int)classifiers.size(); i++)
|
||||
classifiers[i].prepareClassifier(rowstep);
|
||||
}
|
||||
|
||||
// Calculate posterior probability, that the patch belongs to the current EC model
|
||||
double TLDDetector::ensembleClassifierNum(const uchar* data)
|
||||
{
|
||||
double p = 0;
|
||||
for (int k = 0; k < (int)classifiers.size(); k++)
|
||||
p += classifiers[k].posteriorProbabilityFast(data);
|
||||
p /= classifiers.size();
|
||||
return p;
|
||||
}
|
||||
|
||||
double TLDDetector::computeSminus(const Mat_<uchar>& patch) const
|
||||
{
|
||||
double sminus = 0.0;
|
||||
Mat_<uchar> modelSample(STANDARD_PATCH_SIZE, STANDARD_PATCH_SIZE);
|
||||
for (int i = 0; i < *negNum; i++)
|
||||
{
|
||||
modelSample.data = &(negExp->data[i * 225]);
|
||||
sminus = std::max(sminus, 0.5 * (tracking_internal::computeNCC(modelSample, patch) + 1.0));
|
||||
}
|
||||
return sminus;
|
||||
}
|
||||
|
||||
// Calculate Relative similarity of the patch (NN-Model)
|
||||
double TLDDetector::Sr(const Mat_<uchar>& patch) const
|
||||
{
|
||||
double splus = 0.0, sminus = 0.0;
|
||||
Mat_<uchar> modelSample(STANDARD_PATCH_SIZE, STANDARD_PATCH_SIZE);
|
||||
for (int i = 0; i < *posNum; i++)
|
||||
{
|
||||
modelSample.data = &(posExp->data[i * 225]);
|
||||
splus = std::max(splus, 0.5 * (tracking_internal::computeNCC(modelSample, patch) + 1.0));
|
||||
}
|
||||
sminus = computeSminus(patch);
|
||||
|
||||
if (splus + sminus == 0.0)
|
||||
return 0.0;
|
||||
return splus / (sminus + splus);
|
||||
}
|
||||
|
||||
std::pair<double, double> TLDDetector::SrAndSc(const Mat_<uchar>& patch) const
|
||||
{
|
||||
double splusC = 0.0, sminus = 0.0, splus = 0.0;
|
||||
Mat_<uchar> modelSample(STANDARD_PATCH_SIZE, STANDARD_PATCH_SIZE);
|
||||
int med = tracking_internal::getMedian((*timeStampsPositive));
|
||||
for (int i = 0; i < *posNum; i++)
|
||||
{
|
||||
modelSample.data = &(posExp->data[i * 225]);
|
||||
double s = 0.5 * (tracking_internal::computeNCC(modelSample, patch) + 1.0);
|
||||
|
||||
if ((int)(*timeStampsPositive)[i] <= med)
|
||||
splusC = std::max(splusC, s);
|
||||
|
||||
splus = std::max(splus, s);
|
||||
}
|
||||
sminus = computeSminus(patch);
|
||||
|
||||
double sr = (splus + sminus == 0.0) ? 0. : splus / (sminus + splus);
|
||||
double sc = (splusC + sminus == 0.0) ? 0. : splusC / (sminus + splusC);
|
||||
|
||||
return std::pair<double, double>(sr, sc);
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
double TLDDetector::ocl_Sr(const Mat_<uchar>& patch)
|
||||
{
|
||||
double splus = 0.0, sminus = 0.0;
|
||||
|
||||
|
||||
UMat devPatch = patch.getUMat(ACCESS_READ, USAGE_ALLOCATE_DEVICE_MEMORY);
|
||||
UMat devPositiveSamples = posExp->getUMat(ACCESS_READ, USAGE_ALLOCATE_DEVICE_MEMORY);
|
||||
UMat devNegativeSamples = negExp->getUMat(ACCESS_READ, USAGE_ALLOCATE_DEVICE_MEMORY);
|
||||
UMat devNCC(1, 2*MAX_EXAMPLES_IN_MODEL, CV_32FC1, ACCESS_RW, USAGE_ALLOCATE_DEVICE_MEMORY);
|
||||
|
||||
|
||||
ocl::Kernel k;
|
||||
ocl::ProgramSource src = ocl::tracking::tldDetector_oclsrc;
|
||||
String error;
|
||||
ocl::Program prog(src, String(), error);
|
||||
k.create("NCC", prog);
|
||||
if (k.empty())
|
||||
printf("Kernel create failed!!!\n");
|
||||
k.args(
|
||||
ocl::KernelArg::PtrReadOnly(devPatch),
|
||||
ocl::KernelArg::PtrReadOnly(devPositiveSamples),
|
||||
ocl::KernelArg::PtrReadOnly(devNegativeSamples),
|
||||
ocl::KernelArg::PtrWriteOnly(devNCC),
|
||||
*posNum,
|
||||
*negNum);
|
||||
|
||||
size_t globSize = 1000;
|
||||
|
||||
if (!k.run(1, &globSize, NULL, false))
|
||||
printf("Kernel Run Error!!!");
|
||||
|
||||
Mat resNCC = devNCC.getMat(ACCESS_READ);
|
||||
|
||||
for (int i = 0; i < *posNum; i++)
|
||||
splus = std::max(splus, 0.5 * (resNCC.at<float>(i) + 1.0));
|
||||
|
||||
for (int i = 0; i < *negNum; i++)
|
||||
sminus = std::max(sminus, 0.5 * (resNCC.at<float>(i+500) +1.0));
|
||||
|
||||
if (splus + sminus == 0.0)
|
||||
return 0.0;
|
||||
return splus / (sminus + splus);
|
||||
}
|
||||
|
||||
void TLDDetector::ocl_batchSrSc(const Mat_<uchar>& patches, double *resultSr, double *resultSc, int numOfPatches)
|
||||
{
|
||||
UMat devPatches = patches.getUMat(ACCESS_READ, USAGE_ALLOCATE_DEVICE_MEMORY);
|
||||
UMat devPositiveSamples = posExp->getUMat(ACCESS_READ, USAGE_ALLOCATE_DEVICE_MEMORY);
|
||||
UMat devNegativeSamples = negExp->getUMat(ACCESS_READ, USAGE_ALLOCATE_DEVICE_MEMORY);
|
||||
UMat devPosNCC(MAX_EXAMPLES_IN_MODEL, numOfPatches, CV_32FC1, ACCESS_RW, USAGE_ALLOCATE_DEVICE_MEMORY);
|
||||
UMat devNegNCC(MAX_EXAMPLES_IN_MODEL, numOfPatches, CV_32FC1, ACCESS_RW, USAGE_ALLOCATE_DEVICE_MEMORY);
|
||||
|
||||
ocl::Kernel k;
|
||||
ocl::ProgramSource src = ocl::tracking::tldDetector_oclsrc;
|
||||
String error;
|
||||
ocl::Program prog(src, String(), error);
|
||||
k.create("batchNCC", prog);
|
||||
if (k.empty())
|
||||
printf("Kernel create failed!!!\n");
|
||||
k.args(
|
||||
ocl::KernelArg::PtrReadOnly(devPatches),
|
||||
ocl::KernelArg::PtrReadOnly(devPositiveSamples),
|
||||
ocl::KernelArg::PtrReadOnly(devNegativeSamples),
|
||||
ocl::KernelArg::PtrWriteOnly(devPosNCC),
|
||||
ocl::KernelArg::PtrWriteOnly(devNegNCC),
|
||||
*posNum,
|
||||
*negNum,
|
||||
numOfPatches);
|
||||
|
||||
size_t globSize = 2 * numOfPatches*MAX_EXAMPLES_IN_MODEL;
|
||||
|
||||
if (!k.run(1, &globSize, NULL, true))
|
||||
printf("Kernel Run Error!!!");
|
||||
|
||||
Mat posNCC = devPosNCC.getMat(ACCESS_READ);
|
||||
Mat negNCC = devNegNCC.getMat(ACCESS_READ);
|
||||
|
||||
//Calculate Srs
|
||||
for (int id = 0; id < numOfPatches; id++)
|
||||
{
|
||||
double spr = 0.0, smr = 0.0, spc = 0.0, smc = 0;
|
||||
int med = tracking_internal::getMedian((*timeStampsPositive));
|
||||
for (int i = 0; i < *posNum; i++)
|
||||
{
|
||||
spr = std::max(spr, 0.5 * (posNCC.at<float>(id * 500 + i) + 1.0));
|
||||
if ((int)(*timeStampsPositive)[i] <= med)
|
||||
spc = std::max(spr, 0.5 * (posNCC.at<float>(id * 500 + i) + 1.0));
|
||||
}
|
||||
for (int i = 0; i < *negNum; i++)
|
||||
smc = smr = std::max(smr, 0.5 * (negNCC.at<float>(id * 500 + i) + 1.0));
|
||||
|
||||
if (spr + smr == 0.0)
|
||||
resultSr[id] = 0.0;
|
||||
else
|
||||
resultSr[id] = spr / (smr + spr);
|
||||
|
||||
if (spc + smc == 0.0)
|
||||
resultSc[id] = 0.0;
|
||||
else
|
||||
resultSc[id] = spc / (smc + spc);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Calculate Conservative similarity of the patch (NN-Model)
|
||||
double TLDDetector::Sc(const Mat_<uchar>& patch) const
|
||||
{
|
||||
double splus = 0.0, sminus = 0.0;
|
||||
Mat_<uchar> modelSample(STANDARD_PATCH_SIZE, STANDARD_PATCH_SIZE);
|
||||
int med = tracking_internal::getMedian((*timeStampsPositive));
|
||||
for (int i = 0; i < *posNum; i++)
|
||||
{
|
||||
if ((int)(*timeStampsPositive)[i] <= med)
|
||||
{
|
||||
modelSample.data = &(posExp->data[i * 225]);
|
||||
splus = std::max(splus, 0.5 * (tracking_internal::computeNCC(modelSample, patch) + 1.0));
|
||||
}
|
||||
}
|
||||
sminus = computeSminus(patch);
|
||||
|
||||
if (splus + sminus == 0.0)
|
||||
return 0.0;
|
||||
|
||||
return splus / (sminus + splus);
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
double TLDDetector::ocl_Sc(const Mat_<uchar>& patch)
|
||||
{
|
||||
double splus = 0.0, sminus = 0.0;
|
||||
|
||||
UMat devPatch = patch.getUMat(ACCESS_READ, USAGE_ALLOCATE_DEVICE_MEMORY);
|
||||
UMat devPositiveSamples = posExp->getUMat(ACCESS_READ, USAGE_ALLOCATE_DEVICE_MEMORY);
|
||||
UMat devNegativeSamples = negExp->getUMat(ACCESS_READ, USAGE_ALLOCATE_DEVICE_MEMORY);
|
||||
UMat devNCC(1, 2 * MAX_EXAMPLES_IN_MODEL, CV_32FC1, ACCESS_RW, USAGE_ALLOCATE_DEVICE_MEMORY);
|
||||
|
||||
|
||||
ocl::Kernel k;
|
||||
ocl::ProgramSource src = ocl::tracking::tldDetector_oclsrc;
|
||||
String error;
|
||||
ocl::Program prog(src, String(), error);
|
||||
k.create("NCC", prog);
|
||||
if (k.empty())
|
||||
printf("Kernel create failed!!!\n");
|
||||
k.args(
|
||||
ocl::KernelArg::PtrReadOnly(devPatch),
|
||||
ocl::KernelArg::PtrReadOnly(devPositiveSamples),
|
||||
ocl::KernelArg::PtrReadOnly(devNegativeSamples),
|
||||
ocl::KernelArg::PtrWriteOnly(devNCC),
|
||||
*posNum,
|
||||
*negNum);
|
||||
|
||||
size_t globSize = 1000;
|
||||
|
||||
if (!k.run(1, &globSize, NULL, false))
|
||||
printf("Kernel Run Error!!!");
|
||||
|
||||
Mat resNCC = devNCC.getMat(ACCESS_READ);
|
||||
|
||||
int med = tracking_internal::getMedian((*timeStampsPositive));
|
||||
for (int i = 0; i < *posNum; i++)
|
||||
if ((int)(*timeStampsPositive)[i] <= med)
|
||||
splus = std::max(splus, 0.5 * (resNCC.at<float>(i) +1.0));
|
||||
|
||||
for (int i = 0; i < *negNum; i++)
|
||||
sminus = std::max(sminus, 0.5 * (resNCC.at<float>(i + 500) + 1.0));
|
||||
|
||||
if (splus + sminus == 0.0)
|
||||
return 0.0;
|
||||
return splus / (sminus + splus);
|
||||
}
|
||||
#endif // HAVE_OPENCL
|
||||
|
||||
// Generate Search Windows for detector from aspect ratio of initial BBs
|
||||
void TLDDetector::generateScanGrid(int rows, int cols, Size initBox, std::vector<Rect2d>& res, bool withScaling)
|
||||
{
|
||||
res.clear();
|
||||
//Scales step: SCALE_STEP; Translation steps: 10% of width & 10% of height; minSize: 20pix
|
||||
for (double h = initBox.height, w = initBox.width; h < cols && w < rows;)
|
||||
{
|
||||
for (double x = 0; (x + w + 1.0) <= cols; x += (0.1 * w))
|
||||
{
|
||||
for (double y = 0; (y + h + 1.0) <= rows; y += (0.1 * h))
|
||||
res.push_back(Rect2d(x, y, w, h));
|
||||
}
|
||||
if (withScaling)
|
||||
{
|
||||
if (h <= initBox.height)
|
||||
{
|
||||
h /= SCALE_STEP; w /= SCALE_STEP;
|
||||
if (h < 20 || w < 20)
|
||||
{
|
||||
h = initBox.height * SCALE_STEP; w = initBox.width * SCALE_STEP;
|
||||
CV_Assert(h > initBox.height || w > initBox.width);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
h *= SCALE_STEP; w *= SCALE_STEP;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Detection - returns most probable new target location (Max Sc)
|
||||
|
||||
class CalcScSrParallelLoopBody: public cv::ParallelLoopBody
|
||||
{
|
||||
public:
|
||||
explicit CalcScSrParallelLoopBody (TLDDetector * detector, Size initSize):
|
||||
detectorF (detector),
|
||||
initSizeF (initSize)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void operator () (const cv::Range & r) const CV_OVERRIDE
|
||||
{
|
||||
for (int ind = r.start; ind < r.end; ++ind)
|
||||
{
|
||||
resample(detectorF->resized_imgs[detectorF->ensScaleIDs[ind]],
|
||||
Rect2d(detectorF->ensBuffer[ind], initSizeF),
|
||||
detectorF->standardPatches[ind]);
|
||||
std::pair<double, double> values = detectorF->SrAndSc(detectorF->standardPatches[ind]);
|
||||
detectorF->scValues[ind] = values.second;
|
||||
detectorF->srValues[ind] = values.first;
|
||||
}
|
||||
}
|
||||
|
||||
TLDDetector * detectorF;
|
||||
const Size initSizeF;
|
||||
private:
|
||||
CalcScSrParallelLoopBody (const CalcScSrParallelLoopBody&);
|
||||
CalcScSrParallelLoopBody& operator= (const CalcScSrParallelLoopBody&);
|
||||
};
|
||||
|
||||
bool TLDDetector::detect(const Mat& img, const Mat& imgBlurred, Rect2d& res, std::vector<LabeledPatch>& patches, Size initSize)
|
||||
{
|
||||
patches.clear();
|
||||
Mat tmp;
|
||||
int dx = initSize.width / 10, dy = initSize.height / 10;
|
||||
Size2d size = img.size();
|
||||
double maxSc = -5.0;
|
||||
Rect2d maxScRect;
|
||||
int scaleID;
|
||||
|
||||
resized_imgs.clear ();
|
||||
blurred_imgs.clear ();
|
||||
varBuffer.clear ();
|
||||
ensBuffer.clear ();
|
||||
varScaleIDs.clear ();
|
||||
ensScaleIDs.clear ();
|
||||
|
||||
//Detection part
|
||||
//Generate windows and filter by variance
|
||||
scaleID = 0;
|
||||
resized_imgs.push_back(img);
|
||||
blurred_imgs.push_back(imgBlurred);
|
||||
do
|
||||
{
|
||||
Mat_<double> intImgP, intImgP2;
|
||||
computeIntegralImages(resized_imgs[scaleID], intImgP, intImgP2);
|
||||
for (int i = 0, imax = cvFloor((0.0 + resized_imgs[scaleID].cols - initSize.width) / dx); i < imax; i++)
|
||||
{
|
||||
for (int j = 0, jmax = cvFloor((0.0 + resized_imgs[scaleID].rows - initSize.height) / dy); j < jmax; j++)
|
||||
{
|
||||
if (!patchVariance(intImgP, intImgP2, originalVariancePtr, Point(dx * i, dy * j), initSize))
|
||||
continue;
|
||||
varBuffer.push_back(Point(dx * i, dy * j));
|
||||
varScaleIDs.push_back(scaleID);
|
||||
}
|
||||
}
|
||||
scaleID++;
|
||||
size.width /= SCALE_STEP;
|
||||
size.height /= SCALE_STEP;
|
||||
resize(img, tmp, size, 0, 0, DOWNSCALE_MODE);
|
||||
resized_imgs.push_back(tmp);
|
||||
GaussianBlur(resized_imgs[scaleID], tmp, GaussBlurKernelSize, 0.0f);
|
||||
blurred_imgs.push_back(tmp);
|
||||
} while (size.width >= initSize.width && size.height >= initSize.height);
|
||||
|
||||
//Encsemble classification
|
||||
for (int i = 0; i < (int)varBuffer.size(); i++)
|
||||
{
|
||||
prepareClassifiers(static_cast<int> (blurred_imgs[varScaleIDs[i]].step[0]));
|
||||
if (ensembleClassifierNum(&blurred_imgs[varScaleIDs[i]].at<uchar>(varBuffer[i].y, varBuffer[i].x)) <= ENSEMBLE_THRESHOLD)
|
||||
continue;
|
||||
ensBuffer.push_back(varBuffer[i]);
|
||||
ensScaleIDs.push_back(varScaleIDs[i]);
|
||||
}
|
||||
|
||||
//Batch preparation
|
||||
srValues.resize (ensBuffer.size());
|
||||
scValues.resize (ensBuffer.size());
|
||||
|
||||
//Carefully resize standard patches with reference-counted Mat members
|
||||
const int oldPatchesSize = (int)standardPatches.size();
|
||||
standardPatches.resize (ensBuffer.size());
|
||||
if ((int)ensBuffer.size() > oldPatchesSize)
|
||||
{
|
||||
Mat_<uchar> standardPatch(STANDARD_PATCH_SIZE, STANDARD_PATCH_SIZE);
|
||||
for (int i = oldPatchesSize; i < (int)ensBuffer.size(); ++i)
|
||||
{
|
||||
standardPatches[i] = standardPatch.clone();
|
||||
}
|
||||
}
|
||||
|
||||
//Batch calculation
|
||||
cv::parallel_for_ (cv::Range (0, (int)ensBuffer.size ()), CalcScSrParallelLoopBody (this, initSize));
|
||||
|
||||
//NN classification
|
||||
for (int i = 0; i < (int)ensBuffer.size(); i++)
|
||||
{
|
||||
LabeledPatch labPatch;
|
||||
double curScale = pow(SCALE_STEP, ensScaleIDs[i]);
|
||||
labPatch.rect = Rect2d(ensBuffer[i].x*curScale, ensBuffer[i].y*curScale, initSize.width * curScale, initSize.height * curScale);
|
||||
|
||||
const double srValue = srValues[i];
|
||||
const double scValue = scValues[i];
|
||||
|
||||
////To fix: Check the paper, probably this cause wrong learning
|
||||
//
|
||||
labPatch.isObject = srValue > THETA_NN;
|
||||
labPatch.shouldBeIntegrated = abs(srValue - THETA_NN) < CLASSIFIER_MARGIN;
|
||||
patches.push_back(labPatch);
|
||||
//
|
||||
|
||||
if (!labPatch.isObject)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (scValue > maxSc)
|
||||
{
|
||||
maxSc = scValue;
|
||||
maxScRect = labPatch.rect;
|
||||
}
|
||||
}
|
||||
|
||||
if (maxSc < 0)
|
||||
return false;
|
||||
else
|
||||
{
|
||||
res = maxScRect;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
bool TLDDetector::ocl_detect(const Mat& img, const Mat& imgBlurred, Rect2d& res, std::vector<LabeledPatch>& patches, Size initSize)
|
||||
{
|
||||
patches.clear();
|
||||
Mat_<uchar> standardPatch(STANDARD_PATCH_SIZE, STANDARD_PATCH_SIZE);
|
||||
Mat tmp;
|
||||
int dx = initSize.width / 10, dy = initSize.height / 10;
|
||||
Size2d size = img.size();
|
||||
double maxSc = -5.0;
|
||||
Rect2d maxScRect;
|
||||
int scaleID;
|
||||
std::vector <Mat> resized_imgs, blurred_imgs;
|
||||
std::vector <Point> varBuffer, ensBuffer;
|
||||
std::vector <int> varScaleIDs, ensScaleIDs;
|
||||
|
||||
//Detection part
|
||||
//Generate windows and filter by variance
|
||||
scaleID = 0;
|
||||
resized_imgs.push_back(img);
|
||||
blurred_imgs.push_back(imgBlurred);
|
||||
do
|
||||
{
|
||||
Mat_<double> intImgP, intImgP2;
|
||||
computeIntegralImages(resized_imgs[scaleID], intImgP, intImgP2);
|
||||
for (int i = 0, imax = cvFloor((0.0 + resized_imgs[scaleID].cols - initSize.width) / dx); i < imax; i++)
|
||||
{
|
||||
for (int j = 0, jmax = cvFloor((0.0 + resized_imgs[scaleID].rows - initSize.height) / dy); j < jmax; j++)
|
||||
{
|
||||
if (!patchVariance(intImgP, intImgP2, originalVariancePtr, Point(dx * i, dy * j), initSize))
|
||||
continue;
|
||||
varBuffer.push_back(Point(dx * i, dy * j));
|
||||
varScaleIDs.push_back(scaleID);
|
||||
}
|
||||
}
|
||||
scaleID++;
|
||||
size.width /= SCALE_STEP;
|
||||
size.height /= SCALE_STEP;
|
||||
resize(img, tmp, size, 0, 0, DOWNSCALE_MODE);
|
||||
resized_imgs.push_back(tmp);
|
||||
GaussianBlur(resized_imgs[scaleID], tmp, GaussBlurKernelSize, 0.0f);
|
||||
blurred_imgs.push_back(tmp);
|
||||
} while (size.width >= initSize.width && size.height >= initSize.height);
|
||||
|
||||
//Encsemble classification
|
||||
for (int i = 0; i < (int)varBuffer.size(); i++)
|
||||
{
|
||||
prepareClassifiers((int)blurred_imgs[varScaleIDs[i]].step[0]);
|
||||
if (ensembleClassifierNum(&blurred_imgs[varScaleIDs[i]].at<uchar>(varBuffer[i].y, varBuffer[i].x)) <= ENSEMBLE_THRESHOLD)
|
||||
continue;
|
||||
ensBuffer.push_back(varBuffer[i]);
|
||||
ensScaleIDs.push_back(varScaleIDs[i]);
|
||||
}
|
||||
|
||||
//NN classification
|
||||
//Prepare batch of patches
|
||||
int numOfPatches = (int)ensBuffer.size();
|
||||
Mat_<uchar> stdPatches(numOfPatches, 225);
|
||||
double *resultSr = new double[numOfPatches];
|
||||
double *resultSc = new double[numOfPatches];
|
||||
|
||||
uchar *patchesData = stdPatches.data;
|
||||
for (int i = 0; i < (int)ensBuffer.size(); i++)
|
||||
{
|
||||
resample(resized_imgs[ensScaleIDs[i]], Rect2d(ensBuffer[i], initSize), standardPatch);
|
||||
uchar *stdPatchData = standardPatch.data;
|
||||
for (int j = 0; j < 225; j++)
|
||||
patchesData[225*i+j] = stdPatchData[j];
|
||||
}
|
||||
//Calculate Sr and Sc batches
|
||||
ocl_batchSrSc(stdPatches, resultSr, resultSc, numOfPatches);
|
||||
|
||||
|
||||
for (int i = 0; i < (int)ensBuffer.size(); i++)
|
||||
{
|
||||
LabeledPatch labPatch;
|
||||
standardPatch.data = &stdPatches.data[225 * i];
|
||||
double curScale = pow(SCALE_STEP, ensScaleIDs[i]);
|
||||
labPatch.rect = Rect2d(ensBuffer[i].x*curScale, ensBuffer[i].y*curScale, initSize.width * curScale, initSize.height * curScale);
|
||||
|
||||
double srValue, scValue;
|
||||
|
||||
srValue = resultSr[i];
|
||||
|
||||
////To fix: Check the paper, probably this cause wrong learning
|
||||
//
|
||||
labPatch.isObject = srValue > THETA_NN;
|
||||
labPatch.shouldBeIntegrated = abs(srValue - THETA_NN) < CLASSIFIER_MARGIN;
|
||||
patches.push_back(labPatch);
|
||||
//
|
||||
|
||||
if (!labPatch.isObject)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
scValue = resultSc[i];
|
||||
if (scValue > maxSc)
|
||||
{
|
||||
maxSc = scValue;
|
||||
maxScRect = labPatch.rect;
|
||||
}
|
||||
}
|
||||
|
||||
if (maxSc < 0)
|
||||
return false;
|
||||
res = maxScRect;
|
||||
return true;
|
||||
}
|
||||
#endif // HAVE_OPENCL
|
||||
|
||||
// Computes the variance of subimage given by box, with the help of two integral
|
||||
// images intImgP and intImgP2 (sum of squares), which should be also provided.
|
||||
bool TLDDetector::patchVariance(Mat_<double>& intImgP, Mat_<double>& intImgP2, double *originalVariance, Point pt, Size size)
|
||||
{
|
||||
int x = (pt.x), y = (pt.y), width = (size.width), height = (size.height);
|
||||
CV_Assert(0 <= x && (x + width) < intImgP.cols && (x + width) < intImgP2.cols);
|
||||
CV_Assert(0 <= y && (y + height) < intImgP.rows && (y + height) < intImgP2.rows);
|
||||
double p = 0, p2 = 0;
|
||||
double A, B, C, D;
|
||||
|
||||
A = intImgP(y, x);
|
||||
B = intImgP(y, x + width);
|
||||
C = intImgP(y + height, x);
|
||||
D = intImgP(y + height, x + width);
|
||||
p = (A + D - B - C) / (width * height);
|
||||
|
||||
A = intImgP2(y, x);
|
||||
B = intImgP2(y, x + width);
|
||||
C = intImgP2(y + height, x);
|
||||
D = intImgP2(y + height, x + width);
|
||||
p2 = (A + D - B - C) / (width * height);
|
||||
|
||||
return ((p2 - p * p) > VARIANCE_THRESHOLD * *originalVariance);
|
||||
}
|
||||
|
||||
}}}} // namespace
|
||||
@@ -0,0 +1,121 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef OPENCV_TLD_DETECTOR
|
||||
#define OPENCV_TLD_DETECTOR
|
||||
|
||||
#include "opencl_kernels_tracking.hpp"
|
||||
#include "tldEnsembleClassifier.hpp"
|
||||
#include "tldUtils.hpp"
|
||||
|
||||
namespace cv {
|
||||
inline namespace tracking {
|
||||
namespace impl {
|
||||
namespace tld {
|
||||
|
||||
const int STANDARD_PATCH_SIZE = 15;
|
||||
const int NEG_EXAMPLES_IN_INIT_MODEL = 300;
|
||||
const int MAX_EXAMPLES_IN_MODEL = 500;
|
||||
const int MEASURES_PER_CLASSIFIER = 13;
|
||||
const int GRIDSIZE = 15;
|
||||
const int DOWNSCALE_MODE = cv::INTER_LINEAR_EXACT;
|
||||
const double THETA_NN = 0.5;
|
||||
const double CORE_THRESHOLD = 0.5;
|
||||
const double CLASSIFIER_MARGIN = 0.1;
|
||||
const double SCALE_STEP = 1.2;
|
||||
const double ENSEMBLE_THRESHOLD = 0.5;
|
||||
const double VARIANCE_THRESHOLD = 0.5;
|
||||
const double NEXPERT_THRESHOLD = 0.2;
|
||||
|
||||
static const cv::Size GaussBlurKernelSize(3, 3);
|
||||
|
||||
|
||||
|
||||
class TLDDetector
|
||||
{
|
||||
public:
|
||||
TLDDetector(){}
|
||||
~TLDDetector(){}
|
||||
double ensembleClassifierNum(const uchar* data);
|
||||
void prepareClassifiers(int rowstep);
|
||||
double Sr(const Mat_<uchar>& patch) const;
|
||||
double Sc(const Mat_<uchar>& patch) const;
|
||||
std::pair<double, double> SrAndSc(const Mat_<uchar>& patch) const;
|
||||
#ifdef HAVE_OPENCL
|
||||
double ocl_Sr(const Mat_<uchar>& patch);
|
||||
double ocl_Sc(const Mat_<uchar>& patch);
|
||||
void ocl_batchSrSc(const Mat_<uchar>& patches, double *resultSr, double *resultSc, int numOfPatches);
|
||||
#endif
|
||||
|
||||
std::vector<TLDEnsembleClassifier> classifiers;
|
||||
Mat *posExp, *negExp;
|
||||
int *posNum, *negNum;
|
||||
std::vector<Mat_<uchar> > *positiveExamples, *negativeExamples;
|
||||
std::vector<int> *timeStampsPositive, *timeStampsNegative;
|
||||
double *originalVariancePtr;
|
||||
std::vector<double> scValues, srValues;
|
||||
std::vector<Mat_<uchar> > standardPatches;
|
||||
|
||||
std::vector <Mat> resized_imgs, blurred_imgs;
|
||||
std::vector <Point> varBuffer, ensBuffer;
|
||||
std::vector <int> varScaleIDs, ensScaleIDs;
|
||||
|
||||
static void generateScanGrid(int rows, int cols, Size initBox, std::vector<Rect2d>& res, bool withScaling = false);
|
||||
struct LabeledPatch
|
||||
{
|
||||
Rect2d rect;
|
||||
bool isObject, shouldBeIntegrated;
|
||||
};
|
||||
bool detect(const Mat& img, const Mat& imgBlurred, Rect2d& res, std::vector<LabeledPatch>& patches, Size initSize);
|
||||
bool ocl_detect(const Mat& img, const Mat& imgBlurred, Rect2d& res, std::vector<LabeledPatch>& patches, Size initSize);
|
||||
|
||||
friend class MyMouseCallbackDEBUG;
|
||||
static void computeIntegralImages(const Mat& img, Mat_<double>& intImgP, Mat_<double>& intImgP2){ integral(img, intImgP, intImgP2, CV_64F); }
|
||||
static inline bool patchVariance(Mat_<double>& intImgP, Mat_<double>& intImgP2, double *originalVariance, Point pt, Size size);
|
||||
|
||||
protected:
|
||||
double computeSminus(const Mat_<uchar>& patch) const;
|
||||
};
|
||||
|
||||
|
||||
}}}} // namespace
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,199 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "tldEnsembleClassifier.hpp"
|
||||
|
||||
namespace cv {
|
||||
inline namespace tracking {
|
||||
namespace impl {
|
||||
namespace tld {
|
||||
|
||||
// Constructor
|
||||
TLDEnsembleClassifier::TLDEnsembleClassifier(const std::vector<Vec4b>& meas, int beg, int end) :lastStep_(-1)
|
||||
{
|
||||
int posSize = 1, mpc = end - beg;
|
||||
for (int i = 0; i < mpc; i++)
|
||||
posSize *= 2;
|
||||
posAndNeg.assign(posSize, Point2i(0, 0));
|
||||
measurements.assign(meas.begin() + beg, meas.begin() + end);
|
||||
offset.assign(mpc, Point2i(0, 0));
|
||||
}
|
||||
// Calculate measure locations from 15x15 grid on minSize patches
|
||||
void TLDEnsembleClassifier::stepPrefSuff(std::vector<Vec4b>& arr, int pos, int len, int gridSize)
|
||||
{
|
||||
#if 0
|
||||
int step = len / (gridSize - 1), pref = (len - step * (gridSize - 1)) / 2;
|
||||
for (int i = 0; i < (int)(sizeof(x1) / sizeof(x1[0])); i++)
|
||||
arr[i] = pref + arr[i] * step;
|
||||
#else
|
||||
int total = len - gridSize;
|
||||
int quo = total / (gridSize - 1), rem = total % (gridSize - 1);
|
||||
int smallStep = quo, bigStep = quo + 1;
|
||||
int bigOnes = rem, smallOnes = gridSize - bigOnes - 1;
|
||||
int bigOnes_front = bigOnes / 2, bigOnes_back = bigOnes - bigOnes_front;
|
||||
for (int i = 0; i < (int)arr.size(); i++)
|
||||
{
|
||||
if (arr[i].val[pos] < bigOnes_back)
|
||||
{
|
||||
arr[i].val[pos] = (uchar)(arr[i].val[pos] * bigStep + arr[i].val[pos]);
|
||||
continue;
|
||||
}
|
||||
if (arr[i].val[pos] < (bigOnes_front + smallOnes))
|
||||
{
|
||||
arr[i].val[pos] = (uchar)(bigOnes_front * bigStep + (arr[i].val[pos] - bigOnes_front) * smallStep + arr[i].val[pos]);
|
||||
continue;
|
||||
}
|
||||
if (arr[i].val[pos] < (bigOnes_front + smallOnes + bigOnes_back))
|
||||
{
|
||||
arr[i].val[pos] =
|
||||
(uchar)(bigOnes_front * bigStep + smallOnes * smallStep +
|
||||
(arr[i].val[pos] - (bigOnes_front + smallOnes)) * bigStep + arr[i].val[pos]);
|
||||
continue;
|
||||
}
|
||||
arr[i].val[pos] = (uchar)(len - 1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Calculate offsets for classifier
|
||||
void TLDEnsembleClassifier::prepareClassifier(int rowstep)
|
||||
{
|
||||
if (lastStep_ != rowstep)
|
||||
{
|
||||
lastStep_ = rowstep;
|
||||
for (int i = 0; i < (int)offset.size(); i++)
|
||||
{
|
||||
offset[i].x = rowstep * measurements[i].val[2] + measurements[i].val[0];
|
||||
offset[i].y = rowstep * measurements[i].val[3] + measurements[i].val[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Integrate patch into the Ensemble Classifier model
|
||||
void TLDEnsembleClassifier::integrate(const Mat_<uchar>& patch, bool isPositive)
|
||||
{
|
||||
int position = code(patch.data, (int)patch.step[0]);
|
||||
if (isPositive)
|
||||
posAndNeg[position].x++;
|
||||
else
|
||||
posAndNeg[position].y++;
|
||||
}
|
||||
|
||||
// Calculate posterior probability on the patch
|
||||
double TLDEnsembleClassifier::posteriorProbability(const uchar* data, int rowstep) const
|
||||
{
|
||||
int position = code(data, rowstep);
|
||||
double posNum = (double)posAndNeg[position].x, negNum = (double)posAndNeg[position].y;
|
||||
if (posNum == 0.0 && negNum == 0.0)
|
||||
return 0.0;
|
||||
else
|
||||
return posNum / (posNum + negNum);
|
||||
}
|
||||
double TLDEnsembleClassifier::posteriorProbabilityFast(const uchar* data) const
|
||||
{
|
||||
int position = codeFast(data);
|
||||
double posNum = (double)posAndNeg[position].x, negNum = (double)posAndNeg[position].y;
|
||||
if (posNum == 0.0 && negNum == 0.0)
|
||||
return 0.0;
|
||||
else
|
||||
return posNum / (posNum + negNum);
|
||||
}
|
||||
|
||||
// Calculate the 13-bit fern index
|
||||
int TLDEnsembleClassifier::codeFast(const uchar* data) const
|
||||
{
|
||||
int position = 0;
|
||||
for (int i = 0; i < (int)measurements.size(); i++)
|
||||
{
|
||||
position = position << 1;
|
||||
if (data[offset[i].x] < data[offset[i].y])
|
||||
position++;
|
||||
}
|
||||
return position;
|
||||
}
|
||||
int TLDEnsembleClassifier::code(const uchar* data, int rowstep) const
|
||||
{
|
||||
int position = 0;
|
||||
for (int i = 0; i < (int)measurements.size(); i++)
|
||||
{
|
||||
position = position << 1;
|
||||
if (*(data + rowstep * measurements[i].val[2] + measurements[i].val[0]) <
|
||||
*(data + rowstep * measurements[i].val[3] + measurements[i].val[1]))
|
||||
{
|
||||
position++;
|
||||
}
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
// Create fern classifiers
|
||||
int TLDEnsembleClassifier::makeClassifiers(Size size, int measurePerClassifier, int gridSize,
|
||||
std::vector<TLDEnsembleClassifier>& classifiers)
|
||||
{
|
||||
|
||||
std::vector<Vec4b> measurements;
|
||||
|
||||
//Generate random measures for 10 ferns x 13 measures
|
||||
for (int i = 0; i < 10*measurePerClassifier; i++)
|
||||
{
|
||||
Vec4b m;
|
||||
m.val[0] = rand() % 15;
|
||||
m.val[1] = rand() % 15;
|
||||
m.val[2] = rand() % 15;
|
||||
m.val[3] = rand() % 15;
|
||||
measurements.push_back(m);
|
||||
}
|
||||
|
||||
//Warp measures to minSize patch coordinates
|
||||
stepPrefSuff(measurements, 0, size.width, gridSize);
|
||||
stepPrefSuff(measurements, 1, size.width, gridSize);
|
||||
stepPrefSuff(measurements, 2, size.height, gridSize);
|
||||
stepPrefSuff(measurements, 3, size.height, gridSize);
|
||||
|
||||
//Compile fern classifiers
|
||||
for (int i = 0, howMany = (int)measurements.size() / measurePerClassifier; i < howMany; i++)
|
||||
classifiers.push_back(TLDEnsembleClassifier(measurements, i * measurePerClassifier, (i + 1) * measurePerClassifier));
|
||||
|
||||
return (int)classifiers.size();
|
||||
}
|
||||
|
||||
}}}} // namespace
|
||||
@@ -0,0 +1,68 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace cv {
|
||||
inline namespace tracking {
|
||||
namespace impl {
|
||||
namespace tld {
|
||||
|
||||
class TLDEnsembleClassifier
|
||||
{
|
||||
public:
|
||||
static int makeClassifiers(Size size, int measurePerClassifier, int gridSize, std::vector<TLDEnsembleClassifier>& classifiers);
|
||||
void integrate(const Mat_<uchar>& patch, bool isPositive);
|
||||
double posteriorProbability(const uchar* data, int rowstep) const;
|
||||
double posteriorProbabilityFast(const uchar* data) const;
|
||||
void prepareClassifier(int rowstep);
|
||||
|
||||
TLDEnsembleClassifier(const std::vector<Vec4b>& meas, int beg, int end);
|
||||
static void stepPrefSuff(std::vector<Vec4b> & arr, int pos, int len, int gridSize);
|
||||
int code(const uchar* data, int rowstep) const;
|
||||
int codeFast(const uchar* data) const;
|
||||
std::vector<Point2i> posAndNeg;
|
||||
std::vector<Vec4b> measurements;
|
||||
std::vector<Point2i> offset;
|
||||
int lastStep_;
|
||||
};
|
||||
|
||||
}}}} // namespace
|
||||
@@ -0,0 +1,344 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "tldModel.hpp"
|
||||
|
||||
namespace cv {
|
||||
inline namespace tracking {
|
||||
namespace impl {
|
||||
namespace tld {
|
||||
|
||||
//Constructor
|
||||
TrackerTLDModel::TrackerTLDModel(TrackerTLD::Params params, const Mat& image, const Rect2d& boundingBox, Size minSize):
|
||||
timeStampPositiveNext(0), timeStampNegativeNext(0), minSize_(minSize), params_(params), boundingBox_(boundingBox)
|
||||
{
|
||||
std::vector<Rect2d> closest, scanGrid;
|
||||
Mat scaledImg, blurredImg, image_blurred;
|
||||
|
||||
//Create Detector
|
||||
detector = Ptr<TLDDetector>(new TLDDetector());
|
||||
|
||||
//Propagate data to Detector
|
||||
posNum = 0;
|
||||
negNum = 0;
|
||||
posExp = Mat(Size(225, 500), CV_8UC1);
|
||||
negExp = Mat(Size(225, 500), CV_8UC1);
|
||||
detector->posNum = &posNum;
|
||||
detector->negNum = &negNum;
|
||||
detector->posExp = &posExp;
|
||||
detector->negExp = &negExp;
|
||||
|
||||
detector->positiveExamples = &positiveExamples;
|
||||
detector->negativeExamples = &negativeExamples;
|
||||
detector->timeStampsPositive = &timeStampsPositive;
|
||||
detector->timeStampsNegative = &timeStampsNegative;
|
||||
detector->originalVariancePtr = &originalVariance_;
|
||||
|
||||
//Calculate the variance in initial BB
|
||||
originalVariance_ = variance(image(boundingBox));
|
||||
//Find the scale
|
||||
double scale = scaleAndBlur(image, cvRound(log(1.0 * boundingBox.width / (minSize.width)) / log(SCALE_STEP)),
|
||||
scaledImg, blurredImg, GaussBlurKernelSize, SCALE_STEP);
|
||||
GaussianBlur(image, image_blurred, GaussBlurKernelSize, 0.0);
|
||||
TLDDetector::generateScanGrid(image.rows, image.cols, minSize_, scanGrid);
|
||||
getClosestN(scanGrid, Rect2d(boundingBox.x / scale, boundingBox.y / scale, boundingBox.width / scale, boundingBox.height / scale), 10, closest);
|
||||
Mat_<uchar> blurredPatch(minSize);
|
||||
TLDEnsembleClassifier::makeClassifiers(minSize, MEASURES_PER_CLASSIFIER, GRIDSIZE, detector->classifiers);
|
||||
|
||||
//Generate initial positive samples and put them to the model
|
||||
positiveExamples.reserve(200);
|
||||
|
||||
for (size_t i = 0; i < closest.size(); i++)
|
||||
{
|
||||
for (size_t j = 0; j < 20; j++)
|
||||
{
|
||||
Point2f center;
|
||||
Size2f size;
|
||||
Mat_<uchar> standardPatch(STANDARD_PATCH_SIZE, STANDARD_PATCH_SIZE);
|
||||
center.x = (float)(closest[i].x + closest[i].width * (0.5 + rng.uniform(-0.01, 0.01)));
|
||||
center.y = (float)(closest[i].y + closest[i].height * (0.5 + rng.uniform(-0.01, 0.01)));
|
||||
size.width = (float)(closest[i].width * rng.uniform((double)0.99, (double)1.01));
|
||||
size.height = (float)(closest[i].height * rng.uniform((double)0.99, (double)1.01));
|
||||
float angle = (float)rng.uniform(-10.0, 10.0);
|
||||
|
||||
resample(scaledImg, RotatedRect(center, size, angle), standardPatch);
|
||||
|
||||
for( int y = 0; y < standardPatch.rows; y++ )
|
||||
{
|
||||
uchar* patchRow = standardPatch.ptr(y);
|
||||
for( int x = 0; x < standardPatch.cols; x++ )
|
||||
{
|
||||
int newValue = patchRow[x] + cvRound(rng.gaussian(5.0));
|
||||
patchRow[x] = saturate_cast<uchar>(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef BLUR_AS_VADIM
|
||||
GaussianBlur(standardPatch, blurredPatch, GaussBlurKernelSize, 0.0);
|
||||
resize(blurredPatch, blurredPatch, minSize, 0, 0, INTER_LINEAR_EXACT);
|
||||
#else
|
||||
resample(blurredImg, RotatedRect(center, size, angle), blurredPatch);
|
||||
#endif
|
||||
pushIntoModel(standardPatch, true);
|
||||
for (int k = 0; k < (int)detector->classifiers.size(); k++)
|
||||
detector->classifiers[k].integrate(blurredPatch, true);
|
||||
}
|
||||
}
|
||||
|
||||
//Generate initial negative samples and put them to the model
|
||||
TLDDetector::generateScanGrid(image.rows, image.cols, minSize, scanGrid, true);
|
||||
negativeExamples.clear();
|
||||
negativeExamples.reserve(NEG_EXAMPLES_IN_INIT_MODEL);
|
||||
std::vector<int> indices;
|
||||
indices.reserve(NEG_EXAMPLES_IN_INIT_MODEL);
|
||||
while ((int)negativeExamples.size() < NEG_EXAMPLES_IN_INIT_MODEL)
|
||||
{
|
||||
int i = rng.uniform((int)0, (int)scanGrid.size());
|
||||
if (std::find(indices.begin(), indices.end(), i) == indices.end() && overlap(boundingBox, scanGrid[i]) < NEXPERT_THRESHOLD)
|
||||
{
|
||||
Mat_<uchar> standardPatch(STANDARD_PATCH_SIZE, STANDARD_PATCH_SIZE);
|
||||
resample(image, scanGrid[i], standardPatch);
|
||||
pushIntoModel(standardPatch, false);
|
||||
|
||||
resample(image_blurred, scanGrid[i], blurredPatch);
|
||||
for (int k = 0; k < (int)detector->classifiers.size(); k++)
|
||||
detector->classifiers[k].integrate(blurredPatch, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TrackerTLDModel::integrateRelabeled(Mat& img, Mat& imgBlurred, const std::vector<TLDDetector::LabeledPatch>& patches)
|
||||
{
|
||||
Mat_<uchar> standardPatch(STANDARD_PATCH_SIZE, STANDARD_PATCH_SIZE), blurredPatch(minSize_);
|
||||
for (int k = 0; k < (int)patches.size(); k++)
|
||||
{
|
||||
if (patches[k].shouldBeIntegrated)
|
||||
{
|
||||
resample(img, patches[k].rect, standardPatch);
|
||||
if (patches[k].isObject)
|
||||
{
|
||||
pushIntoModel(standardPatch, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
pushIntoModel(standardPatch, false);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CLOSED_LOOP
|
||||
if (patches[k].shouldBeIntegrated || !patches[k].isPositive)
|
||||
#else
|
||||
if (patches[k].shouldBeIntegrated)
|
||||
#endif
|
||||
{
|
||||
resample(imgBlurred, patches[k].rect, blurredPatch);
|
||||
for (int i = 0; i < (int)detector->classifiers.size(); i++)
|
||||
detector->classifiers[i].integrate(blurredPatch, patches[k].isObject);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class CalcSrParallelLoopBody: public cv::ParallelLoopBody
|
||||
{
|
||||
public:
|
||||
explicit CalcSrParallelLoopBody (TrackerTLDModel * model, const std::vector<Mat_<uchar> >& eForModel):
|
||||
modelF (model),
|
||||
eForModelF (eForModel)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void operator () (const cv::Range & r) const CV_OVERRIDE
|
||||
{
|
||||
for (int ind = r.start; ind < r.end; ++ind)
|
||||
{
|
||||
modelF->srValues[ind] = modelF->detector->Sr (eForModelF[ind]);
|
||||
}
|
||||
}
|
||||
|
||||
TrackerTLDModel * modelF;
|
||||
const std::vector<Mat_<uchar> >& eForModelF;
|
||||
private:
|
||||
CalcSrParallelLoopBody (const CalcSrParallelLoopBody&);
|
||||
CalcSrParallelLoopBody& operator= (const CalcSrParallelLoopBody&);
|
||||
};
|
||||
|
||||
void TrackerTLDModel::integrateAdditional(const std::vector<Mat_<uchar> >& eForModel, const std::vector<Mat_<uchar> >& eForEnsemble, bool isPositive)
|
||||
{
|
||||
if ((int)eForModel.size() == 0) return;
|
||||
|
||||
srValues.resize (eForModel.size ());
|
||||
cv::parallel_for_ (cv::Range (0, (int)eForModel.size ()), CalcSrParallelLoopBody (this, eForModel));
|
||||
|
||||
for (int k = 0; k < (int)eForModel.size(); k++)
|
||||
{
|
||||
const double sr = srValues[k];
|
||||
if ((sr > THETA_NN) != isPositive)
|
||||
{
|
||||
if (isPositive)
|
||||
{
|
||||
pushIntoModel(eForModel[k], true);
|
||||
}
|
||||
else
|
||||
{
|
||||
pushIntoModel(eForModel[k], false);
|
||||
}
|
||||
}
|
||||
double p = 0;
|
||||
for (int i = 0; i < (int)detector->classifiers.size(); i++)
|
||||
p += detector->classifiers[i].posteriorProbability(eForEnsemble[k].data, (int)eForEnsemble[k].step[0]);
|
||||
p /= detector->classifiers.size();
|
||||
if ((p > ENSEMBLE_THRESHOLD) != isPositive)
|
||||
{
|
||||
for (int i = 0; i < (int)detector->classifiers.size(); i++)
|
||||
detector->classifiers[i].integrate(eForEnsemble[k], isPositive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
void TrackerTLDModel::ocl_integrateAdditional(const std::vector<Mat_<uchar> >& eForModel, const std::vector<Mat_<uchar> >& eForEnsemble, bool isPositive)
|
||||
{
|
||||
if ((int)eForModel.size() == 0) return;
|
||||
|
||||
//Prepare batch of patches
|
||||
int numOfPatches = (int)eForModel.size();
|
||||
Mat_<uchar> stdPatches(numOfPatches, 225);
|
||||
double *resultSr = new double[numOfPatches];
|
||||
double *resultSc = new double[numOfPatches];
|
||||
uchar *patchesData = stdPatches.data;
|
||||
for (int i = 0; i < numOfPatches; i++)
|
||||
{
|
||||
uchar *stdPatchData = eForModel[i].data;
|
||||
for (int j = 0; j < 225; j++)
|
||||
patchesData[225 * i + j] = stdPatchData[j];
|
||||
}
|
||||
|
||||
//Calculate Sr and Sc batches
|
||||
detector->ocl_batchSrSc(stdPatches, resultSr, resultSc, numOfPatches);
|
||||
|
||||
for (int k = 0; k < (int)eForModel.size(); k++)
|
||||
{
|
||||
double sr = resultSr[k];
|
||||
if ((sr > THETA_NN) != isPositive)
|
||||
{
|
||||
if (isPositive)
|
||||
{
|
||||
pushIntoModel(eForModel[k], true);
|
||||
}
|
||||
else
|
||||
{
|
||||
pushIntoModel(eForModel[k], false);
|
||||
}
|
||||
}
|
||||
double p = 0;
|
||||
for (int i = 0; i < (int)detector->classifiers.size(); i++)
|
||||
p += detector->classifiers[i].posteriorProbability(eForEnsemble[k].data, (int)eForEnsemble[k].step[0]);
|
||||
p /= detector->classifiers.size();
|
||||
if ((p > ENSEMBLE_THRESHOLD) != isPositive)
|
||||
{
|
||||
for (int i = 0; i < (int)detector->classifiers.size(); i++)
|
||||
detector->classifiers[i].integrate(eForEnsemble[k], isPositive);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // HAVE_OPENCL
|
||||
|
||||
//Push the patch to the model
|
||||
void TrackerTLDModel::pushIntoModel(const Mat_<uchar>& example, bool positive)
|
||||
{
|
||||
std::vector<Mat_<uchar> >* proxyV;
|
||||
int* proxyN;
|
||||
std::vector<int>* proxyT;
|
||||
if (positive)
|
||||
{
|
||||
if (posNum < 500)
|
||||
{
|
||||
uchar *patchPtr = example.data;
|
||||
uchar *modelPtr = posExp.data;
|
||||
for (int i = 0; i < STANDARD_PATCH_SIZE*STANDARD_PATCH_SIZE; i++)
|
||||
modelPtr[posNum*STANDARD_PATCH_SIZE*STANDARD_PATCH_SIZE + i] = patchPtr[i];
|
||||
posNum++;
|
||||
}
|
||||
|
||||
proxyV = &positiveExamples;
|
||||
proxyN = &timeStampPositiveNext;
|
||||
proxyT = &timeStampsPositive;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (negNum < 500)
|
||||
{
|
||||
uchar *patchPtr = example.data;
|
||||
uchar *modelPtr = negExp.data;
|
||||
for (int i = 0; i < STANDARD_PATCH_SIZE*STANDARD_PATCH_SIZE; i++)
|
||||
modelPtr[negNum*STANDARD_PATCH_SIZE*STANDARD_PATCH_SIZE + i] = patchPtr[i];
|
||||
negNum++;
|
||||
}
|
||||
|
||||
proxyV = &negativeExamples;
|
||||
proxyN = &timeStampNegativeNext;
|
||||
proxyT = &timeStampsNegative;
|
||||
}
|
||||
if ((int)proxyV->size() < MAX_EXAMPLES_IN_MODEL)
|
||||
{
|
||||
proxyV->push_back(example);
|
||||
proxyT->push_back(*proxyN);
|
||||
}
|
||||
else
|
||||
{
|
||||
int index = rng.uniform((int)0, (int)proxyV->size());
|
||||
(*proxyV)[index] = example;
|
||||
(*proxyT)[index] = (*proxyN);
|
||||
}
|
||||
(*proxyN)++;
|
||||
}
|
||||
|
||||
void TrackerTLDModel::printme(FILE* port)
|
||||
{
|
||||
dfprintf((port, "TrackerTLDModel:\n"));
|
||||
dfprintf((port, "\tpositiveExamples.size() = %d\n", (int)positiveExamples.size()));
|
||||
dfprintf((port, "\tnegativeExamples.size() = %d\n", (int)negativeExamples.size()));
|
||||
}
|
||||
|
||||
}}}} // namespace
|
||||
@@ -0,0 +1,94 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef OPENCV_TLD_MODEL
|
||||
#define OPENCV_TLD_MODEL
|
||||
|
||||
#include "tldDetector.hpp"
|
||||
#include "tldUtils.hpp"
|
||||
|
||||
#include "opencv2/tracking/tracking_legacy.hpp"
|
||||
|
||||
namespace cv {
|
||||
inline namespace tracking {
|
||||
namespace impl {
|
||||
namespace tld {
|
||||
|
||||
using namespace cv::legacy;
|
||||
|
||||
class TrackerTLDModel : public TrackerModel
|
||||
{
|
||||
public:
|
||||
TrackerTLDModel(TrackerTLD::Params params, const Mat& image, const Rect2d& boundingBox, Size minSize);
|
||||
Rect2d getBoundingBox(){ return boundingBox_; }
|
||||
void setBoudingBox(Rect2d boundingBox){ boundingBox_ = boundingBox; }
|
||||
void integrateRelabeled(Mat& img, Mat& imgBlurred, const std::vector<TLDDetector::LabeledPatch>& patches);
|
||||
void integrateAdditional(const std::vector<Mat_<uchar> >& eForModel, const std::vector<Mat_<uchar> >& eForEnsemble, bool isPositive);
|
||||
#ifdef HAVE_OPENCL
|
||||
void ocl_integrateAdditional(const std::vector<Mat_<uchar> >& eForModel, const std::vector<Mat_<uchar> >& eForEnsemble, bool isPositive);
|
||||
#endif
|
||||
Size getMinSize(){ return minSize_; }
|
||||
void printme(FILE* port = stdout);
|
||||
Ptr<TLDDetector> detector;
|
||||
|
||||
std::vector<Mat_<uchar> > positiveExamples, negativeExamples;
|
||||
Mat posExp, negExp;
|
||||
int posNum, negNum;
|
||||
std::vector<int> timeStampsPositive, timeStampsNegative;
|
||||
int timeStampPositiveNext, timeStampNegativeNext;
|
||||
double originalVariance_;
|
||||
std::vector<double> srValues;
|
||||
|
||||
double getOriginalVariance(){ return originalVariance_; }
|
||||
|
||||
protected:
|
||||
Size minSize_;
|
||||
TrackerTLD::Params params_;
|
||||
void pushIntoModel(const Mat_<uchar>& example, bool positive);
|
||||
void modelEstimationImpl(const std::vector<Mat>& /*responses*/) CV_OVERRIDE {}
|
||||
void modelUpdateImpl() CV_OVERRIDE {}
|
||||
Rect2d boundingBox_;
|
||||
RNG rng;
|
||||
};
|
||||
|
||||
}}}} // namespace
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,331 @@
|
||||
/*///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "opencv2/tracking/tracking_legacy.hpp"
|
||||
#include "tldTracker.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace legacy {
|
||||
inline namespace tracking {
|
||||
using namespace impl;
|
||||
using namespace impl::tld;
|
||||
|
||||
TrackerTLD::Params::Params(){}
|
||||
|
||||
void TrackerTLD::Params::read(const cv::FileNode& /*fn*/){}
|
||||
|
||||
void TrackerTLD::Params::write(cv::FileStorage& /*fs*/) const {}
|
||||
|
||||
|
||||
Ptr<TrackerTLD> TrackerTLD::create(const TrackerTLD::Params ¶meters)
|
||||
{
|
||||
return Ptr<tld::TrackerTLDImpl>(new tld::TrackerTLDImpl(parameters));
|
||||
}
|
||||
Ptr<TrackerTLD> TrackerTLD::create()
|
||||
{
|
||||
return Ptr<tld::TrackerTLDImpl>(new tld::TrackerTLDImpl());
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
|
||||
inline namespace tracking {
|
||||
namespace impl {
|
||||
namespace tld {
|
||||
|
||||
TrackerTLDImpl::TrackerTLDImpl(const TrackerTLD::Params ¶meters) :
|
||||
params( parameters )
|
||||
{
|
||||
isInit = false;
|
||||
trackerProxy = Ptr<TrackerProxyImpl<TrackerMedianFlow, TrackerMedianFlow::Params> >
|
||||
(new TrackerProxyImpl<TrackerMedianFlow, TrackerMedianFlow::Params>());
|
||||
}
|
||||
|
||||
void TrackerTLDImpl::read(const cv::FileNode& fn)
|
||||
{
|
||||
params.read( fn );
|
||||
}
|
||||
|
||||
void TrackerTLDImpl::write(cv::FileStorage& fs) const
|
||||
{
|
||||
params.write( fs );
|
||||
}
|
||||
|
||||
bool TrackerTLDImpl::initImpl(const Mat& image, const Rect2d& boundingBox)
|
||||
{
|
||||
Mat image_gray;
|
||||
trackerProxy->init(image, boundingBox);
|
||||
if(image.channels() > 1)
|
||||
{
|
||||
cvtColor( image, image_gray, COLOR_BGR2GRAY );
|
||||
}
|
||||
else
|
||||
{
|
||||
image_gray = image.clone();
|
||||
}
|
||||
|
||||
data = Ptr<Data>(new Data(boundingBox));
|
||||
double scale = data->getScale();
|
||||
Rect2d myBoundingBox = boundingBox;
|
||||
if( scale > 1.0 )
|
||||
{
|
||||
Mat image_proxy;
|
||||
resize(image_gray, image_proxy, Size(cvRound(image.cols * scale), cvRound(image.rows * scale)), 0, 0, DOWNSCALE_MODE);
|
||||
image_proxy.copyTo(image_gray);
|
||||
myBoundingBox.x *= scale;
|
||||
myBoundingBox.y *= scale;
|
||||
myBoundingBox.width *= scale;
|
||||
myBoundingBox.height *= scale;
|
||||
}
|
||||
model = Ptr<TrackerTLDModel>(new TrackerTLDModel(params, image_gray, myBoundingBox, data->getMinSize()));
|
||||
|
||||
data->confident = false;
|
||||
data->failedLastTime = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TrackerTLDImpl::updateImpl(const Mat& image, Rect2d& boundingBox)
|
||||
{
|
||||
Mat image_gray, image_blurred, imageForDetector;
|
||||
if(image.channels() > 1)
|
||||
{
|
||||
cvtColor( image, image_gray, COLOR_BGR2GRAY );
|
||||
}
|
||||
else
|
||||
{
|
||||
image_gray = image.clone();
|
||||
}
|
||||
double scale = data->getScale();
|
||||
if( scale > 1.0 )
|
||||
resize(image_gray, imageForDetector, Size(cvRound(image.cols*scale), cvRound(image.rows*scale)), 0, 0, DOWNSCALE_MODE);
|
||||
else
|
||||
imageForDetector = image_gray;
|
||||
GaussianBlur(imageForDetector, image_blurred, GaussBlurKernelSize, 0.0);
|
||||
TrackerTLDModel* tldModel = ((TrackerTLDModel*)static_cast<TrackerModel*>(model));
|
||||
data->frameNum++;
|
||||
Mat_<uchar> standardPatch(STANDARD_PATCH_SIZE, STANDARD_PATCH_SIZE);
|
||||
std::vector<TLDDetector::LabeledPatch> detectorResults;
|
||||
//best overlap around 92%
|
||||
std::vector<Rect2d> candidates;
|
||||
std::vector<double> candidatesRes;
|
||||
bool trackerNeedsReInit = false;
|
||||
bool DETECT_FLG = false;
|
||||
|
||||
//run tracker
|
||||
Rect2d tmpCandid = boundingBox;
|
||||
if(!data->failedLastTime && trackerProxy->update(image, tmpCandid))
|
||||
{
|
||||
candidates.push_back(tmpCandid);
|
||||
resample(image_gray, tmpCandid, standardPatch);
|
||||
candidatesRes.push_back(tldModel->detector->Sc(standardPatch));
|
||||
}
|
||||
else
|
||||
trackerNeedsReInit = true;
|
||||
|
||||
//run detector
|
||||
tmpCandid = boundingBox;
|
||||
#ifdef HAVE_OPENCL
|
||||
if (false)//ocl::useOpenCL())
|
||||
DETECT_FLG = tldModel->detector->ocl_detect(imageForDetector, image_blurred, tmpCandid, detectorResults, tldModel->getMinSize());
|
||||
else
|
||||
#endif
|
||||
DETECT_FLG = tldModel->detector->detect(imageForDetector, image_blurred, tmpCandid, detectorResults, tldModel->getMinSize());
|
||||
|
||||
if(DETECT_FLG)
|
||||
{
|
||||
candidates.push_back(tmpCandid);
|
||||
resample(imageForDetector, tmpCandid, standardPatch);
|
||||
candidatesRes.push_back(tldModel->detector->Sc(standardPatch));
|
||||
}
|
||||
|
||||
std::vector<double>::iterator it = std::max_element(candidatesRes.begin(), candidatesRes.end());
|
||||
|
||||
if( it == candidatesRes.end() ) //candidates are empty
|
||||
{
|
||||
data->confident = false;
|
||||
data->failedLastTime = true;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
boundingBox = candidates[it - candidatesRes.begin()];
|
||||
data->failedLastTime = false;
|
||||
if( trackerNeedsReInit || it != candidatesRes.begin() )
|
||||
trackerProxy->init(image, boundingBox);
|
||||
}
|
||||
|
||||
#if 1
|
||||
if( it != candidatesRes.end() )
|
||||
resample(imageForDetector, candidates[it - candidatesRes.begin()], standardPatch);
|
||||
#endif
|
||||
|
||||
if( *it > CORE_THRESHOLD )
|
||||
data->confident = true;
|
||||
|
||||
if( data->confident )
|
||||
{
|
||||
Pexpert pExpert(imageForDetector, image_blurred, boundingBox, tldModel->detector, params, data->getMinSize());
|
||||
Nexpert nExpert(imageForDetector, boundingBox, tldModel->detector, params);
|
||||
std::vector<Mat_<uchar> > examplesForModel, examplesForEnsemble;
|
||||
examplesForModel.reserve(100); examplesForEnsemble.reserve(100);
|
||||
for( int i = 0; i < (int)detectorResults.size(); i++ )
|
||||
{
|
||||
bool expertResult;
|
||||
if( detectorResults[i].isObject )
|
||||
{
|
||||
expertResult = nExpert(detectorResults[i].rect);
|
||||
}
|
||||
else
|
||||
{
|
||||
expertResult = pExpert(detectorResults[i].rect);
|
||||
}
|
||||
|
||||
detectorResults[i].shouldBeIntegrated = detectorResults[i].shouldBeIntegrated || (detectorResults[i].isObject != expertResult);
|
||||
detectorResults[i].isObject = expertResult;
|
||||
}
|
||||
tldModel->integrateRelabeled(imageForDetector, image_blurred, detectorResults);
|
||||
pExpert.additionalExamples(examplesForModel, examplesForEnsemble);
|
||||
#ifdef HAVE_OPENCL
|
||||
if (false)//ocl::useOpenCL())
|
||||
tldModel->ocl_integrateAdditional(examplesForModel, examplesForEnsemble, true);
|
||||
else
|
||||
#endif
|
||||
tldModel->integrateAdditional(examplesForModel, examplesForEnsemble, true);
|
||||
examplesForModel.clear(); examplesForEnsemble.clear();
|
||||
nExpert.additionalExamples(examplesForModel, examplesForEnsemble);
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
if (false)//ocl::useOpenCL())
|
||||
tldModel->ocl_integrateAdditional(examplesForModel, examplesForEnsemble, false);
|
||||
else
|
||||
#endif
|
||||
tldModel->integrateAdditional(examplesForModel, examplesForEnsemble, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef CLOSED_LOOP
|
||||
tldModel->integrateRelabeled(imageForDetector, image_blurred, detectorResults);
|
||||
#endif
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int TrackerTLDImpl::Pexpert::additionalExamples(std::vector<Mat_<uchar> >& examplesForModel, std::vector<Mat_<uchar> >& examplesForEnsemble)
|
||||
{
|
||||
examplesForModel.clear(); examplesForEnsemble.clear();
|
||||
examplesForModel.reserve(100); examplesForEnsemble.reserve(100);
|
||||
|
||||
std::vector<Rect2d> closest, scanGrid;
|
||||
Mat scaledImg, blurredImg;
|
||||
|
||||
double scale = scaleAndBlur(img_, cvRound(log(1.0 * resultBox_.width / (initSize_.width)) / log(SCALE_STEP)),
|
||||
scaledImg, blurredImg, GaussBlurKernelSize, SCALE_STEP);
|
||||
|
||||
TLDDetector::generateScanGrid(img_.rows, img_.cols, initSize_, scanGrid);
|
||||
getClosestN(scanGrid, Rect2d(resultBox_.x / scale, resultBox_.y / scale, resultBox_.width / scale, resultBox_.height / scale), 10, closest);
|
||||
|
||||
for( size_t i = 0; i < closest.size(); i++ )
|
||||
{
|
||||
for( size_t j = 0; j < 10; j++ )
|
||||
{
|
||||
Point2f center;
|
||||
Size2f size;
|
||||
Mat_<uchar> standardPatch(STANDARD_PATCH_SIZE, STANDARD_PATCH_SIZE), blurredPatch(initSize_);
|
||||
center.x = (float)(closest[i].x + closest[i].width * (0.5 + rng.uniform(-0.01, 0.01)));
|
||||
center.y = (float)(closest[i].y + closest[i].height * (0.5 + rng.uniform(-0.01, 0.01)));
|
||||
size.width = (float)(closest[i].width * rng.uniform((double)0.99, (double)1.01));
|
||||
size.height = (float)(closest[i].height * rng.uniform((double)0.99, (double)1.01));
|
||||
float angle = (float)rng.uniform(-5.0, 5.0);
|
||||
|
||||
resample(scaledImg, RotatedRect(center, size, angle), standardPatch);
|
||||
for( int y = 0; y < standardPatch.rows; y++ )
|
||||
{
|
||||
uchar* patchRow = standardPatch.ptr(y);
|
||||
for( int x = 0; x < standardPatch.cols; x++ )
|
||||
{
|
||||
int newValue = patchRow[x] + cvRound(rng.gaussian(5.0));
|
||||
patchRow[x] = saturate_cast<uchar>(newValue);
|
||||
}
|
||||
}
|
||||
examplesForModel.push_back(standardPatch);
|
||||
|
||||
#if defined BLUR_AS_VADIM
|
||||
GaussianBlur(standardPatch, blurredPatch, GaussBlurKernelSize, 0.0);
|
||||
resize(blurredPatch, blurredPatch, initSize_, 0, 0, INTER_LINEAR_EXACT);
|
||||
#else
|
||||
resample(blurredImg, RotatedRect(center, size, angle), blurredPatch);
|
||||
#endif
|
||||
examplesForEnsemble.push_back(blurredPatch);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool TrackerTLDImpl::Nexpert::operator()(Rect2d box)
|
||||
{
|
||||
if( overlap(resultBox_, box) < NEXPERT_THRESHOLD )
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
Data::Data(Rect2d initBox)
|
||||
{
|
||||
double minDim = std::min(initBox.width, initBox.height);
|
||||
scale = 20.0 / minDim;
|
||||
minSize.width = (int)(initBox.width * 20.0 / minDim);
|
||||
minSize.height = (int)(initBox.height * 20.0 / minDim);
|
||||
frameNum = 0;
|
||||
}
|
||||
|
||||
void Data::printme(FILE* port)
|
||||
{
|
||||
dfprintf((port, "Data:\n"));
|
||||
dfprintf((port, "\tframeNum = %d\n", frameNum));
|
||||
dfprintf((port, "\tconfident = %s\n", confident?"true":"false"));
|
||||
dfprintf((port, "\tfailedLastTime = %s\n", failedLastTime?"true":"false"));
|
||||
dfprintf((port, "\tminSize = %dx%d\n", minSize.width, minSize.height));
|
||||
}
|
||||
|
||||
}}}} // namespace
|
||||
@@ -0,0 +1,172 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef OPENCV_TLD_TRACKER
|
||||
#define OPENCV_TLD_TRACKER
|
||||
|
||||
#include "opencv2/video/tracking.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "tldModel.hpp"
|
||||
#include <algorithm>
|
||||
#include <limits.h>
|
||||
|
||||
namespace cv {
|
||||
inline namespace tracking {
|
||||
namespace impl {
|
||||
namespace tld {
|
||||
|
||||
class TrackerProxy
|
||||
{
|
||||
public:
|
||||
virtual bool init(const Mat& image, const Rect2d& boundingBox) = 0;
|
||||
virtual bool update(const Mat& image, Rect2d& boundingBox) = 0;
|
||||
virtual ~TrackerProxy(){}
|
||||
};
|
||||
|
||||
|
||||
class MyMouseCallbackDEBUG
|
||||
{
|
||||
public:
|
||||
MyMouseCallbackDEBUG(Mat& img, Mat& imgBlurred, TLDDetector* detector) :img_(img), imgBlurred_(imgBlurred), detector_(detector){}
|
||||
static void onMouse(int event, int x, int y, int, void* obj){ ((MyMouseCallbackDEBUG*)obj)->onMouse(event, x, y); }
|
||||
MyMouseCallbackDEBUG& operator = (const MyMouseCallbackDEBUG& /*other*/){ return *this; }
|
||||
private:
|
||||
void onMouse(int event, int x, int y);
|
||||
Mat& img_, imgBlurred_;
|
||||
TLDDetector* detector_;
|
||||
};
|
||||
|
||||
|
||||
class Data
|
||||
{
|
||||
public:
|
||||
Data(Rect2d initBox);
|
||||
Size getMinSize(){ return minSize; }
|
||||
double getScale(){ return scale; }
|
||||
bool confident;
|
||||
bool failedLastTime;
|
||||
int frameNum;
|
||||
void printme(FILE* port = stdout);
|
||||
private:
|
||||
double scale;
|
||||
Size minSize;
|
||||
};
|
||||
|
||||
template<class T, class Tparams>
|
||||
class TrackerProxyImpl : public TrackerProxy
|
||||
{
|
||||
public:
|
||||
TrackerProxyImpl(Tparams params = Tparams()) :params_(params){}
|
||||
bool init(const Mat& image, const Rect2d& boundingBox) CV_OVERRIDE
|
||||
{
|
||||
trackerPtr = T::create();
|
||||
return trackerPtr->init(image, boundingBox);
|
||||
}
|
||||
bool update(const Mat& image, Rect2d& boundingBox) CV_OVERRIDE
|
||||
{
|
||||
return trackerPtr->update(image, boundingBox);
|
||||
}
|
||||
private:
|
||||
Ptr<T> trackerPtr;
|
||||
Tparams params_;
|
||||
Rect2d boundingBox_;
|
||||
};
|
||||
|
||||
|
||||
#undef BLUR_AS_VADIM
|
||||
#undef CLOSED_LOOP
|
||||
|
||||
class TrackerTLDImpl : public TrackerTLD
|
||||
{
|
||||
public:
|
||||
TrackerTLDImpl(const TrackerTLD::Params ¶meters = TrackerTLD::Params());
|
||||
void read(const FileNode& fn) CV_OVERRIDE;
|
||||
void write(FileStorage& fs) const CV_OVERRIDE;
|
||||
|
||||
Ptr<TrackerModel> getModel()
|
||||
{
|
||||
return model;
|
||||
}
|
||||
|
||||
class Pexpert
|
||||
{
|
||||
public:
|
||||
Pexpert(const Mat& img_in, const Mat& imgBlurred_in, Rect2d& resultBox_in,
|
||||
const TLDDetector* detector_in, TrackerTLD::Params params_in, Size initSize_in) :
|
||||
img_(img_in), imgBlurred_(imgBlurred_in), resultBox_(resultBox_in), detector_(detector_in), params_(params_in), initSize_(initSize_in){}
|
||||
bool operator()(Rect2d /*box*/){ return false; }
|
||||
int additionalExamples(std::vector<Mat_<uchar> >& examplesForModel, std::vector<Mat_<uchar> >& examplesForEnsemble);
|
||||
protected:
|
||||
Pexpert() : detector_(NULL) {}
|
||||
Mat img_, imgBlurred_;
|
||||
Rect2d resultBox_;
|
||||
const TLDDetector* detector_;
|
||||
TrackerTLD::Params params_;
|
||||
RNG rng;
|
||||
Size initSize_;
|
||||
};
|
||||
|
||||
class Nexpert : public Pexpert
|
||||
{
|
||||
public:
|
||||
Nexpert(const Mat& img_in, Rect2d& resultBox_in, const TLDDetector* detector_in, TrackerTLD::Params params_in)
|
||||
{
|
||||
img_ = img_in; resultBox_ = resultBox_in; detector_ = detector_in; params_ = params_in;
|
||||
}
|
||||
bool operator()(Rect2d box);
|
||||
int additionalExamples(std::vector<Mat_<uchar> >& examplesForModel, std::vector<Mat_<uchar> >& examplesForEnsemble)
|
||||
{
|
||||
examplesForModel.clear(); examplesForEnsemble.clear(); return 0;
|
||||
}
|
||||
};
|
||||
|
||||
bool initImpl(const Mat& image, const Rect2d& boundingBox) CV_OVERRIDE;
|
||||
bool updateImpl(const Mat& image, Rect2d& boundingBox) CV_OVERRIDE;
|
||||
|
||||
TrackerTLD::Params params;
|
||||
Ptr<Data> data;
|
||||
Ptr<TrackerProxy> trackerProxy;
|
||||
|
||||
};
|
||||
|
||||
}}}} // namespace
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,196 @@
|
||||
/*///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "tldUtils.hpp"
|
||||
|
||||
|
||||
namespace cv {
|
||||
inline namespace tracking {
|
||||
namespace impl {
|
||||
namespace tld {
|
||||
|
||||
//Debug functions and variables
|
||||
Rect2d etalon(14.0, 110.0, 20.0, 20.0);
|
||||
void myassert(const Mat& img)
|
||||
{
|
||||
int count = 0;
|
||||
for( int i = 0; i < img.rows; i++ )
|
||||
{
|
||||
for( int j = 0; j < img.cols; j++ )
|
||||
{
|
||||
if( img.at<uchar>(i, j) == 0 )
|
||||
count++;
|
||||
}
|
||||
}
|
||||
dprintf(("black: %d out of %d (%f)\n", count, img.rows * img.cols, 1.0 * count / img.rows / img.cols));
|
||||
}
|
||||
void printPatch(const Mat_<uchar>& standardPatch)
|
||||
{
|
||||
for( int i = 0; i < standardPatch.rows; i++ )
|
||||
{
|
||||
for( int j = 0; j < standardPatch.cols; j++ )
|
||||
dprintf(("%5.2f, ", (double)standardPatch(i, j)));
|
||||
dprintf(("\n"));
|
||||
}
|
||||
}
|
||||
std::string type2str(const Mat& mat)
|
||||
{
|
||||
int type = mat.type();
|
||||
std::string r;
|
||||
|
||||
uchar depth = type & CV_MAT_DEPTH_MASK;
|
||||
uchar chans = (uchar)(1 + (type >> CV_CN_SHIFT));
|
||||
|
||||
switch ( depth ) {
|
||||
case CV_8U: r = "8U"; break;
|
||||
case CV_8S: r = "8S"; break;
|
||||
case CV_16U: r = "16U"; break;
|
||||
case CV_16S: r = "16S"; break;
|
||||
case CV_32S: r = "32S"; break;
|
||||
case CV_32F: r = "32F"; break;
|
||||
case CV_64F: r = "64F"; break;
|
||||
default: r = "User"; break;
|
||||
}
|
||||
|
||||
r += "C";
|
||||
r += (chans + '0');
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
//Scale & Blur image using scale Indx
|
||||
double scaleAndBlur(const Mat& originalImg, int scale, Mat& scaledImg, Mat& blurredImg, Size GaussBlurKernelSize, double scaleStep)
|
||||
{
|
||||
double dScale = 1.0;
|
||||
for( int i = 0; i < scale; i++, dScale *= scaleStep );
|
||||
Size2d size = originalImg.size();
|
||||
size.height /= dScale; size.width /= dScale;
|
||||
resize(originalImg, scaledImg, size, 0, 0, INTER_LINEAR_EXACT);
|
||||
GaussianBlur(scaledImg, blurredImg, GaussBlurKernelSize, 0.0);
|
||||
return dScale;
|
||||
}
|
||||
|
||||
//Find N-closest BB to the target
|
||||
void getClosestN(std::vector<Rect2d>& scanGrid, Rect2d bBox, int n, std::vector<Rect2d>& res)
|
||||
{
|
||||
if( n >= (int)scanGrid.size() )
|
||||
{
|
||||
res.assign(scanGrid.begin(), scanGrid.end());
|
||||
return;
|
||||
}
|
||||
std::vector<double> overlaps;
|
||||
overlaps.assign(n, 0.0);
|
||||
res.assign(scanGrid.begin(), scanGrid.begin() + n);
|
||||
for( int i = 0; i < n; i++ )
|
||||
overlaps[i] = overlap(res[i], bBox);
|
||||
double otmp;
|
||||
Rect2d rtmp;
|
||||
for (int i = 1; i < n; i++)
|
||||
{
|
||||
int j = i;
|
||||
while (j > 0 && overlaps[j - 1] > overlaps[j]) {
|
||||
otmp = overlaps[j]; overlaps[j] = overlaps[j - 1]; overlaps[j - 1] = otmp;
|
||||
rtmp = res[j]; res[j] = res[j - 1]; res[j - 1] = rtmp;
|
||||
j--;
|
||||
}
|
||||
}
|
||||
|
||||
for( int i = n; i < (int)scanGrid.size(); i++ )
|
||||
{
|
||||
double o = 0.0;
|
||||
if( (o = overlap(scanGrid[i], bBox)) <= overlaps[0] )
|
||||
continue;
|
||||
int j = 0;
|
||||
while( j < n && overlaps[j] < o )
|
||||
j++;
|
||||
j--;
|
||||
for( int k = 0; k < j; overlaps[k] = overlaps[k + 1], res[k] = res[k + 1], k++ );
|
||||
overlaps[j] = o; res[j] = scanGrid[i];
|
||||
}
|
||||
}
|
||||
|
||||
//Calculate patch variance
|
||||
double variance(const Mat& img)
|
||||
{
|
||||
double p = 0, p2 = 0;
|
||||
p = sum(img)(0);
|
||||
p2 = norm(img, NORM_L2SQR);
|
||||
p /= (img.cols * img.rows);
|
||||
p2 /= (img.cols * img.rows);
|
||||
|
||||
return p2 - p * p;
|
||||
}
|
||||
|
||||
//Overlap between two BB
|
||||
double overlap(const Rect2d& r1, const Rect2d& r2)
|
||||
{
|
||||
double a1 = r1.area(), a2 = r2.area(), a0 = (r1&r2).area();
|
||||
return a0 / (a1 + a2 - a0);
|
||||
}
|
||||
|
||||
void resample(const Mat& img, const RotatedRect& r2, Mat_<uchar>& samples)
|
||||
{
|
||||
Mat_<float> M(2, 3), R(2, 2), Si(2, 2), s(2, 1), o(2, 1);
|
||||
R(0, 0) = (float)cos(r2.angle * CV_PI / 180); R(0, 1) = (float)(-sin(r2.angle * CV_PI / 180));
|
||||
R(1, 0) = (float)sin(r2.angle * CV_PI / 180); R(1, 1) = (float)cos(r2.angle * CV_PI / 180);
|
||||
Si(0, 0) = (float)(samples.cols / r2.size.width); Si(0, 1) = 0.0f;
|
||||
Si(1, 0) = 0.0f; Si(1, 1) = (float)(samples.rows / r2.size.height);
|
||||
s(0, 0) = (float)samples.cols; s(1, 0) = (float)samples.rows;
|
||||
o(0, 0) = r2.center.x; o(1, 0) = r2.center.y;
|
||||
Mat_<float> A(2, 2), b(2, 1);
|
||||
A = Si * R;
|
||||
b = s / 2.0 - Si * R * o;
|
||||
A.copyTo(M.colRange(Range(0, 2)));
|
||||
b.copyTo(M.colRange(Range(2, 3)));
|
||||
warpAffine(img, samples, M, samples.size());
|
||||
}
|
||||
|
||||
void resample(const Mat& img, const Rect2d& r2, Mat_<uchar>& samples)
|
||||
{
|
||||
Mat_<float> M(2, 3);
|
||||
M(0, 0) = (float)(samples.cols / r2.width); M(0, 1) = 0.0f; M(0, 2) = (float)(-r2.x * samples.cols / r2.width);
|
||||
M(1, 0) = 0.0f; M(1, 1) = (float)(samples.rows / r2.height); M(1, 2) = (float)(-r2.y * samples.rows / r2.height);
|
||||
warpAffine(img, samples, M, samples.size());
|
||||
}
|
||||
|
||||
|
||||
}}}} // namespace
|
||||
@@ -0,0 +1,53 @@
|
||||
#ifndef OPENCV_TLD_UTILS
|
||||
#define OPENCV_TLD_UTILS
|
||||
|
||||
namespace cv {
|
||||
inline namespace tracking {
|
||||
namespace impl {
|
||||
namespace tld {
|
||||
|
||||
//debug functions and variables
|
||||
#define ALEX_DEBUG
|
||||
#ifdef ALEX_DEBUG
|
||||
#define dfprintf(x) fprintf x
|
||||
#define dprintf(x) printf x
|
||||
#else
|
||||
#define dfprintf(x)
|
||||
#define dprintf(x)
|
||||
#endif
|
||||
#define MEASURE_TIME(a)\
|
||||
{\
|
||||
clock_t start; float milisec = 0.0; \
|
||||
start = clock(); {a} milisec = 1000.0 * (clock() - start) / CLOCKS_PER_SEC; \
|
||||
dprintf(("%-90s took %f milis\n", #a, milisec));\
|
||||
}
|
||||
#define HERE dprintf(("line %d\n", __LINE__)); fflush(stderr);
|
||||
#define START_TICK(name)\
|
||||
{ \
|
||||
clock_t start; double milisec = 0.0; start = clock();
|
||||
#define END_TICK(name) milisec = 1000.0 * (clock() - start) / CLOCKS_PER_SEC; \
|
||||
dprintf(("%s took %f milis\n", name, milisec)); \
|
||||
}
|
||||
extern Rect2d etalon;
|
||||
|
||||
void myassert(const Mat& img);
|
||||
void printPatch(const Mat_<uchar>& standardPatch);
|
||||
std::string type2str(const Mat& mat);
|
||||
|
||||
//aux functions and variables
|
||||
template<typename T> inline T CLIP(T x, T a, T b){ return std::min(std::max(x, a), b); }
|
||||
/** Computes overlap between the two given rectangles. Overlap is computed as ratio of rectangles' intersection to that
|
||||
* of their union.*/
|
||||
double overlap(const Rect2d& r1, const Rect2d& r2);
|
||||
/** Resamples the area surrounded by r2 in img so it matches the size of samples, where it is written.*/
|
||||
void resample(const Mat& img, const RotatedRect& r2, Mat_<uchar>& samples);
|
||||
/** Specialization of resample() for rectangles without retation for better performance and simplicity.*/
|
||||
void resample(const Mat& img, const Rect2d& r2, Mat_<uchar>& samples);
|
||||
/** Computes the variance of single given image.*/
|
||||
double variance(const Mat& img);
|
||||
void getClosestN(std::vector<Rect2d>& scanGrid, Rect2d bBox, int n, std::vector<Rect2d>& res);
|
||||
double scaleAndBlur(const Mat& originalImg, int scale, Mat& scaledImg, Mat& blurredImg, Size GaussBlurKernelSize, double scaleStep);
|
||||
|
||||
}}}} // namespace
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
// see modules/video/src/tracking/tracker.cpp
|
||||
|
||||
#include "legacy/tracker.legacy.hpp"
|
||||
@@ -0,0 +1,326 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "trackerBoostingModel.hpp"
|
||||
|
||||
#include "opencv2/tracking/tracking_legacy.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace legacy {
|
||||
inline namespace tracking {
|
||||
using namespace impl;
|
||||
|
||||
class TrackerBoostingImpl : public TrackerBoosting
|
||||
{
|
||||
public:
|
||||
TrackerBoostingImpl( const TrackerBoosting::Params ¶meters = TrackerBoosting::Params() );
|
||||
void read( const FileNode& fn ) CV_OVERRIDE;
|
||||
void write( FileStorage& fs ) const CV_OVERRIDE;
|
||||
|
||||
protected:
|
||||
|
||||
bool initImpl( const Mat& image, const Rect2d& boundingBox ) CV_OVERRIDE;
|
||||
bool updateImpl( const Mat& image, Rect2d& boundingBox ) CV_OVERRIDE;
|
||||
|
||||
TrackerBoosting::Params params;
|
||||
};
|
||||
|
||||
/*
|
||||
* TrackerBoosting
|
||||
*/
|
||||
|
||||
/*
|
||||
* Parameters
|
||||
*/
|
||||
TrackerBoosting::Params::Params()
|
||||
{
|
||||
numClassifiers = 100;
|
||||
samplerOverlap = 0.99f;
|
||||
samplerSearchFactor = 1.8f;
|
||||
iterationInit = 50;
|
||||
featureSetNumFeatures = ( numClassifiers * 10 ) + iterationInit;
|
||||
}
|
||||
|
||||
void TrackerBoosting::Params::read( const cv::FileNode& fn )
|
||||
{
|
||||
numClassifiers = fn["numClassifiers"];
|
||||
samplerOverlap = fn["overlap"];
|
||||
samplerSearchFactor = fn["samplerSearchFactor"];
|
||||
iterationInit = fn["iterationInit"];
|
||||
samplerSearchFactor = fn["searchFactor"];
|
||||
}
|
||||
|
||||
void TrackerBoosting::Params::write( cv::FileStorage& fs ) const
|
||||
{
|
||||
fs << "numClassifiers" << numClassifiers;
|
||||
fs << "overlap" << samplerOverlap;
|
||||
fs << "searchFactor" << samplerSearchFactor;
|
||||
fs << "iterationInit" << iterationInit;
|
||||
fs << "samplerSearchFactor" << samplerSearchFactor;
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructor
|
||||
*/
|
||||
Ptr<TrackerBoosting> TrackerBoosting::create(const TrackerBoosting::Params ¶meters){
|
||||
return Ptr<TrackerBoostingImpl>(new TrackerBoostingImpl(parameters));
|
||||
}
|
||||
Ptr<TrackerBoosting> TrackerBoosting::create(){
|
||||
return Ptr<TrackerBoostingImpl>(new TrackerBoostingImpl());
|
||||
}
|
||||
TrackerBoostingImpl::TrackerBoostingImpl( const TrackerBoostingImpl::Params ¶meters ) :
|
||||
params( parameters )
|
||||
{
|
||||
isInit = false;
|
||||
}
|
||||
|
||||
void TrackerBoostingImpl::read( const cv::FileNode& fn )
|
||||
{
|
||||
params.read( fn );
|
||||
}
|
||||
|
||||
void TrackerBoostingImpl::write( cv::FileStorage& fs ) const
|
||||
{
|
||||
params.write( fs );
|
||||
}
|
||||
|
||||
bool TrackerBoostingImpl::initImpl( const Mat& image, const Rect2d& boundingBox )
|
||||
{
|
||||
srand (1);
|
||||
//sampling
|
||||
Mat_<int> intImage;
|
||||
Mat_<double> intSqImage;
|
||||
Mat image_;
|
||||
cvtColor( image, image_, COLOR_BGR2GRAY );
|
||||
integral( image_, intImage, intSqImage, CV_32S );
|
||||
TrackerSamplerCS::Params CSparameters;
|
||||
CSparameters.overlap = params.samplerOverlap;
|
||||
CSparameters.searchFactor = params.samplerSearchFactor;
|
||||
|
||||
Ptr<TrackerContribSamplerAlgorithm> CSSampler = Ptr<TrackerSamplerCS>( new TrackerSamplerCS( CSparameters ) );
|
||||
|
||||
if( !sampler->addTrackerSamplerAlgorithm( CSSampler ) )
|
||||
return false;
|
||||
|
||||
CSSampler.staticCast<TrackerSamplerCS>()->setMode( TrackerSamplerCS::MODE_POSITIVE );
|
||||
sampler->sampling( intImage, boundingBox );
|
||||
const std::vector<Mat> posSamples = sampler->getSamples();
|
||||
|
||||
CSSampler.staticCast<TrackerSamplerCS>()->setMode( TrackerSamplerCS::MODE_NEGATIVE );
|
||||
sampler->sampling( intImage, boundingBox );
|
||||
const std::vector<Mat> negSamples = sampler->getSamples();
|
||||
|
||||
if( posSamples.empty() || negSamples.empty() )
|
||||
return false;
|
||||
|
||||
Rect ROI = CSSampler.staticCast<TrackerSamplerCS>()->getROI();
|
||||
|
||||
//compute HAAR features
|
||||
TrackerContribFeatureHAAR::Params HAARparameters;
|
||||
HAARparameters.numFeatures = params.featureSetNumFeatures;
|
||||
HAARparameters.isIntegral = true;
|
||||
HAARparameters.rectSize = Size( static_cast<int>(boundingBox.width), static_cast<int>(boundingBox.height) );
|
||||
Ptr<TrackerContribFeature> trackerFeature = Ptr<TrackerContribFeatureHAAR>( new TrackerContribFeatureHAAR( HAARparameters ) );
|
||||
if( !featureSet->addTrackerFeature( trackerFeature ) )
|
||||
return false;
|
||||
|
||||
featureSet->extraction( posSamples );
|
||||
const std::vector<Mat> posResponse = featureSet->getResponses();
|
||||
featureSet->extraction( negSamples );
|
||||
const std::vector<Mat> negResponse = featureSet->getResponses();
|
||||
|
||||
//Model
|
||||
model = Ptr<TrackerBoostingModel>( new TrackerBoostingModel( boundingBox ) );
|
||||
Ptr<TrackerStateEstimatorAdaBoosting> stateEstimator = Ptr<TrackerStateEstimatorAdaBoosting>(
|
||||
new TrackerStateEstimatorAdaBoosting( params.numClassifiers, params.iterationInit, params.featureSetNumFeatures,
|
||||
Size( static_cast<int>(boundingBox.width), static_cast<int>(boundingBox.height) ), ROI ) );
|
||||
model->setTrackerStateEstimator( stateEstimator );
|
||||
|
||||
//Run model estimation and update for iterationInit iterations
|
||||
for ( int i = 0; i < params.iterationInit; i++ )
|
||||
{
|
||||
//compute temp features
|
||||
TrackerContribFeatureHAAR::Params HAARparameters2;
|
||||
HAARparameters2.numFeatures = static_cast<int>( posSamples.size() + negSamples.size() );
|
||||
HAARparameters2.isIntegral = true;
|
||||
HAARparameters2.rectSize = Size( static_cast<int>(boundingBox.width), static_cast<int>(boundingBox.height) );
|
||||
Ptr<TrackerContribFeatureHAAR> trackerFeature2 = Ptr<TrackerContribFeatureHAAR>( new TrackerContribFeatureHAAR( HAARparameters2 ) );
|
||||
|
||||
model.staticCast<TrackerBoostingModel>()->setMode( TrackerBoostingModel::MODE_NEGATIVE, negSamples );
|
||||
model->modelEstimation( negResponse );
|
||||
model.staticCast<TrackerBoostingModel>()->setMode( TrackerBoostingModel::MODE_POSITIVE, posSamples );
|
||||
model->modelEstimation( posResponse );
|
||||
model->modelUpdate();
|
||||
|
||||
//get replaced classifier and change the features
|
||||
std::vector<int> replacedClassifier = stateEstimator->computeReplacedClassifier();
|
||||
std::vector<int> swappedClassified = stateEstimator->computeSwappedClassifier();
|
||||
for ( size_t j = 0; j < replacedClassifier.size(); j++ )
|
||||
{
|
||||
if( replacedClassifier[j] != -1 && swappedClassified[j] != -1 )
|
||||
{
|
||||
trackerFeature.staticCast<TrackerContribFeatureHAAR>()->swapFeature( replacedClassifier[j], swappedClassified[j] );
|
||||
trackerFeature.staticCast<TrackerContribFeatureHAAR>()->swapFeature( swappedClassified[j], trackerFeature2->getFeatureAt( (int)j ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TrackerBoostingImpl::updateImpl( const Mat& image, Rect2d& boundingBox )
|
||||
{
|
||||
Mat_<int> intImage;
|
||||
Mat_<double> intSqImage;
|
||||
Mat image_;
|
||||
cvtColor( image, image_, COLOR_BGR2GRAY );
|
||||
integral( image_, intImage, intSqImage, CV_32S );
|
||||
//get the last location [AAM] X(k-1)
|
||||
Ptr<TrackerTargetState> lastLocation = model->getLastTargetState();
|
||||
Rect lastBoundingBox( (int)lastLocation->getTargetPosition().x, (int)lastLocation->getTargetPosition().y, lastLocation->getTargetWidth(),
|
||||
lastLocation->getTargetHeight() );
|
||||
|
||||
//sampling new frame based on last location
|
||||
( sampler->getSamplers().at( 0 ).second ).staticCast<TrackerSamplerCS>()->setMode( TrackerSamplerCS::MODE_CLASSIFY );
|
||||
sampler->sampling( intImage, lastBoundingBox );
|
||||
const std::vector<Mat> detectSamples = sampler->getSamples();
|
||||
Rect ROI = ( sampler->getSamplers().at( 0 ).second ).staticCast<TrackerSamplerCS>()->getROI();
|
||||
|
||||
if( detectSamples.empty() )
|
||||
return false;
|
||||
|
||||
/*//TODO debug samples
|
||||
Mat f;
|
||||
image.copyTo( f );
|
||||
|
||||
for ( size_t i = 0; i < detectSamples.size(); i = i + 10 )
|
||||
{
|
||||
Size sz;
|
||||
Point off;
|
||||
detectSamples.at( i ).locateROI( sz, off );
|
||||
rectangle( f, Rect( off.x, off.y, detectSamples.at( i ).cols, detectSamples.at( i ).rows ), Scalar( 255, 0, 0 ), 1 );
|
||||
}*/
|
||||
|
||||
std::vector<Mat> responses;
|
||||
Mat response;
|
||||
|
||||
std::vector<int> classifiers = model->getTrackerStateEstimator().staticCast<TrackerStateEstimatorAdaBoosting>()->computeSelectedWeakClassifier();
|
||||
Ptr<TrackerContribFeatureHAAR> extractor = featureSet->getTrackerFeature()[0].second.staticCast<TrackerContribFeatureHAAR>();
|
||||
extractor->extractSelected( classifiers, detectSamples, response );
|
||||
responses.push_back( response );
|
||||
|
||||
//predict new location
|
||||
ConfidenceMap cmap;
|
||||
model.staticCast<TrackerBoostingModel>()->setMode( TrackerBoostingModel::MODE_CLASSIFY, detectSamples );
|
||||
model.staticCast<TrackerBoostingModel>()->responseToConfidenceMap( responses, cmap );
|
||||
model->getTrackerStateEstimator().staticCast<TrackerStateEstimatorAdaBoosting>()->setCurrentConfidenceMap( cmap );
|
||||
model->getTrackerStateEstimator().staticCast<TrackerStateEstimatorAdaBoosting>()->setSampleROI( ROI );
|
||||
|
||||
if( !model->runStateEstimator() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Ptr<TrackerTargetState> currentState = model->getLastTargetState();
|
||||
boundingBox = Rect( (int)currentState->getTargetPosition().x, (int)currentState->getTargetPosition().y, currentState->getTargetWidth(),
|
||||
currentState->getTargetHeight() );
|
||||
|
||||
/*//TODO debug
|
||||
rectangle( f, lastBoundingBox, Scalar( 0, 255, 0 ), 1 );
|
||||
rectangle( f, boundingBox, Scalar( 0, 0, 255 ), 1 );
|
||||
imshow( "f", f );
|
||||
//waitKey( 0 );*/
|
||||
|
||||
//sampling new frame based on new location
|
||||
//Positive sampling
|
||||
( sampler->getSamplers().at( 0 ).second ).staticCast<TrackerSamplerCS>()->setMode( TrackerSamplerCS::MODE_POSITIVE );
|
||||
sampler->sampling( intImage, boundingBox );
|
||||
const std::vector<Mat> posSamples = sampler->getSamples();
|
||||
|
||||
//Negative sampling
|
||||
( sampler->getSamplers().at( 0 ).second ).staticCast<TrackerSamplerCS>()->setMode( TrackerSamplerCS::MODE_NEGATIVE );
|
||||
sampler->sampling( intImage, boundingBox );
|
||||
const std::vector<Mat> negSamples = sampler->getSamples();
|
||||
|
||||
if( posSamples.empty() || negSamples.empty() )
|
||||
return false;
|
||||
|
||||
//extract features
|
||||
featureSet->extraction( posSamples );
|
||||
const std::vector<Mat> posResponse = featureSet->getResponses();
|
||||
|
||||
featureSet->extraction( negSamples );
|
||||
const std::vector<Mat> negResponse = featureSet->getResponses();
|
||||
|
||||
//compute temp features
|
||||
TrackerContribFeatureHAAR::Params HAARparameters2;
|
||||
HAARparameters2.numFeatures = static_cast<int>( posSamples.size() + negSamples.size() );
|
||||
HAARparameters2.isIntegral = true;
|
||||
HAARparameters2.rectSize = Size( static_cast<int>(boundingBox.width), static_cast<int>(boundingBox.height) );
|
||||
Ptr<TrackerContribFeatureHAAR> trackerFeature2 = Ptr<TrackerContribFeatureHAAR>( new TrackerContribFeatureHAAR( HAARparameters2 ) );
|
||||
|
||||
//model estimate
|
||||
model.staticCast<TrackerBoostingModel>()->setMode( TrackerBoostingModel::MODE_NEGATIVE, negSamples );
|
||||
model->modelEstimation( negResponse );
|
||||
model.staticCast<TrackerBoostingModel>()->setMode( TrackerBoostingModel::MODE_POSITIVE, posSamples );
|
||||
model->modelEstimation( posResponse );
|
||||
|
||||
//model update
|
||||
model->modelUpdate();
|
||||
|
||||
//get replaced classifier and change the features
|
||||
std::vector<int> replacedClassifier = model->getTrackerStateEstimator().staticCast<TrackerStateEstimatorAdaBoosting>()->computeReplacedClassifier();
|
||||
std::vector<int> swappedClassified = model->getTrackerStateEstimator().staticCast<TrackerStateEstimatorAdaBoosting>()->computeSwappedClassifier();
|
||||
for ( size_t j = 0; j < replacedClassifier.size(); j++ )
|
||||
{
|
||||
if( replacedClassifier[j] != -1 && swappedClassified[j] != -1 )
|
||||
{
|
||||
featureSet->getTrackerFeature().at( 0 ).second.staticCast<TrackerContribFeatureHAAR>()->swapFeature( replacedClassifier[j], swappedClassified[j] );
|
||||
featureSet->getTrackerFeature().at( 0 ).second.staticCast<TrackerContribFeatureHAAR>()->swapFeature( swappedClassified[j],
|
||||
trackerFeature2->getFeatureAt( (int)j ) );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}}} // namespace
|
||||
@@ -0,0 +1,124 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "trackerBoostingModel.hpp"
|
||||
|
||||
/**
|
||||
* TrackerBoostingModel
|
||||
*/
|
||||
|
||||
namespace cv {
|
||||
inline namespace tracking {
|
||||
namespace impl {
|
||||
|
||||
TrackerBoostingModel::TrackerBoostingModel( const Rect& boundingBox )
|
||||
{
|
||||
|
||||
mode = MODE_POSITIVE;
|
||||
|
||||
Ptr<TrackerStateEstimatorAdaBoosting::TrackerAdaBoostingTargetState> initState =
|
||||
Ptr<TrackerStateEstimatorAdaBoosting::TrackerAdaBoostingTargetState>(
|
||||
new TrackerStateEstimatorAdaBoosting::TrackerAdaBoostingTargetState( Point2f( (float)boundingBox.x, (float)boundingBox.y ), boundingBox.width,
|
||||
boundingBox.height, true, Mat() ) );
|
||||
trajectory.push_back( initState );
|
||||
maxCMLength = 10;
|
||||
}
|
||||
|
||||
void TrackerBoostingModel::modelEstimationImpl( const std::vector<Mat>& responses )
|
||||
{
|
||||
responseToConfidenceMap( responses, currentConfidenceMap );
|
||||
}
|
||||
|
||||
void TrackerBoostingModel::modelUpdateImpl()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TrackerBoostingModel::setMode( int trainingMode, const std::vector<Mat>& samples )
|
||||
{
|
||||
currentSample.clear();
|
||||
currentSample = samples;
|
||||
|
||||
mode = trainingMode;
|
||||
}
|
||||
|
||||
std::vector<int> TrackerBoostingModel::getSelectedWeakClassifier()
|
||||
{
|
||||
return stateEstimator.staticCast<TrackerStateEstimatorAdaBoosting>()->computeSelectedWeakClassifier();
|
||||
}
|
||||
|
||||
void TrackerBoostingModel::responseToConfidenceMap( const std::vector<Mat>& responses, ConfidenceMap& confidenceMap )
|
||||
{
|
||||
if( currentSample.empty() )
|
||||
{
|
||||
CV_Error( cv::Error::StsError, "The samples in Model estimation are empty" );
|
||||
}
|
||||
|
||||
for ( size_t i = 0; i < currentSample.size(); i++ )
|
||||
{
|
||||
|
||||
Size currentSize;
|
||||
Point currentOfs;
|
||||
currentSample.at( i ).locateROI( currentSize, currentOfs );
|
||||
bool foreground = false;
|
||||
if( mode == MODE_POSITIVE || mode == MODE_CLASSIFY )
|
||||
{
|
||||
foreground = true;
|
||||
}
|
||||
else if( mode == MODE_NEGATIVE )
|
||||
{
|
||||
foreground = false;
|
||||
}
|
||||
const Mat resp = responses[0].col( (int)i );
|
||||
|
||||
//create the state
|
||||
Ptr<TrackerStateEstimatorAdaBoosting::TrackerAdaBoostingTargetState> currentState = Ptr<
|
||||
TrackerStateEstimatorAdaBoosting::TrackerAdaBoostingTargetState>(
|
||||
new TrackerStateEstimatorAdaBoosting::TrackerAdaBoostingTargetState( currentOfs, currentSample.at( i ).cols, currentSample.at( i ).rows,
|
||||
foreground, resp ) );
|
||||
|
||||
confidenceMap.push_back( std::make_pair( currentState, 0.0f ) );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}}} // namespace
|
||||
@@ -0,0 +1,106 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef __OPENCV_TRACKER_BOOSTING_MODEL_HPP__
|
||||
#define __OPENCV_TRACKER_BOOSTING_MODEL_HPP__
|
||||
|
||||
namespace cv {
|
||||
inline namespace tracking {
|
||||
namespace impl {
|
||||
|
||||
/**
|
||||
* \brief Implementation of TrackerModel for BOOSTING algorithm
|
||||
*/
|
||||
class TrackerBoostingModel : public TrackerModel
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
MODE_POSITIVE = 1, // mode for positive features
|
||||
MODE_NEGATIVE = 2, // mode for negative features
|
||||
MODE_CLASSIFY = 3 // mode for classify step
|
||||
};
|
||||
/**
|
||||
* \brief Constructor
|
||||
* \param boundingBox The first boundingBox
|
||||
*/
|
||||
TrackerBoostingModel( const Rect& boundingBox );
|
||||
|
||||
/**
|
||||
* \brief Destructor
|
||||
*/
|
||||
~TrackerBoostingModel()
|
||||
{
|
||||
}
|
||||
;
|
||||
|
||||
/**
|
||||
* \brief Set the mode
|
||||
*/
|
||||
void setMode( int trainingMode, const std::vector<Mat>& samples );
|
||||
|
||||
/**
|
||||
* \brief Create the ConfidenceMap from a list of responses
|
||||
* \param responses The list of the responses
|
||||
* \param confidenceMap The output
|
||||
*/
|
||||
void responseToConfidenceMap( const std::vector<Mat>& responses, ConfidenceMap& confidenceMap );
|
||||
|
||||
/**
|
||||
* \brief return the selected weak classifiers for the detect
|
||||
* @return the selected weak classifiers
|
||||
*/
|
||||
std::vector<int> getSelectedWeakClassifier();
|
||||
|
||||
protected:
|
||||
void modelEstimationImpl( const std::vector<Mat>& responses ) CV_OVERRIDE;
|
||||
void modelUpdateImpl() CV_OVERRIDE;
|
||||
|
||||
private:
|
||||
|
||||
std::vector<Mat> currentSample;
|
||||
|
||||
int mode;
|
||||
};
|
||||
|
||||
}}} // namespace
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,655 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
#include "trackerCSRTSegmentation.hpp"
|
||||
#include "trackerCSRTUtils.hpp"
|
||||
#include "trackerCSRTScaleEstimation.hpp"
|
||||
|
||||
namespace cv {
|
||||
inline namespace tracking {
|
||||
namespace impl {
|
||||
|
||||
/**
|
||||
* \brief Implementation of TrackerModel for CSRT algorithm
|
||||
*/
|
||||
class TrackerCSRTModel CV_FINAL : public TrackerModel
|
||||
{
|
||||
public:
|
||||
TrackerCSRTModel(){}
|
||||
~TrackerCSRTModel(){}
|
||||
protected:
|
||||
void modelEstimationImpl(const std::vector<Mat>& /*responses*/) CV_OVERRIDE {}
|
||||
void modelUpdateImpl() CV_OVERRIDE {}
|
||||
};
|
||||
|
||||
class TrackerCSRTImpl CV_FINAL : public TrackerCSRT
|
||||
{
|
||||
public:
|
||||
TrackerCSRTImpl(const Params ¶meters = Params());
|
||||
|
||||
Params params;
|
||||
|
||||
Ptr<TrackerCSRTModel> model;
|
||||
|
||||
// Tracker API
|
||||
virtual void init(InputArray image, const Rect& boundingBox) CV_OVERRIDE;
|
||||
virtual bool update(InputArray image, Rect& boundingBox) CV_OVERRIDE;
|
||||
virtual void setInitialMask(InputArray mask) CV_OVERRIDE;
|
||||
|
||||
protected:
|
||||
void update_csr_filter(const Mat &image, const Mat &my_mask);
|
||||
void update_histograms(const Mat &image, const Rect ®ion);
|
||||
void extract_histograms(const Mat &image, cv::Rect region, Histogram &hf, Histogram &hb);
|
||||
std::vector<Mat> create_csr_filter(const std::vector<cv::Mat>
|
||||
img_features, const cv::Mat Y, const cv::Mat P);
|
||||
Mat calculate_response(const Mat &image, const std::vector<Mat> filter);
|
||||
Mat get_location_prior(const Rect roi, const Size2f target_size, const Size img_sz);
|
||||
Mat segment_region(const Mat &image, const Point2f &object_center,
|
||||
const Size2f &template_size, const Size &target_size, float scale_factor);
|
||||
Point2f estimate_new_position(const Mat &image);
|
||||
std::vector<Mat> get_features(const Mat &patch, const Size2i &feature_size);
|
||||
|
||||
bool check_mask_area(const Mat &mat, const double obj_area);
|
||||
float current_scale_factor;
|
||||
Mat window;
|
||||
Mat yf;
|
||||
Rect2f bounding_box;
|
||||
std::vector<Mat> csr_filter;
|
||||
std::vector<float> filter_weights;
|
||||
Size2f original_target_size;
|
||||
Size2i image_size;
|
||||
Size2f template_size;
|
||||
Size2i rescaled_template_size;
|
||||
float rescale_ratio;
|
||||
Point2f object_center;
|
||||
DSST dsst;
|
||||
Histogram hist_foreground;
|
||||
Histogram hist_background;
|
||||
double p_b;
|
||||
Mat erode_element;
|
||||
Mat filter_mask;
|
||||
Mat preset_mask;
|
||||
Mat default_mask;
|
||||
float default_mask_area;
|
||||
int cell_size;
|
||||
};
|
||||
|
||||
TrackerCSRTImpl::TrackerCSRTImpl(const TrackerCSRT::Params ¶meters) :
|
||||
params(parameters)
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
void TrackerCSRTImpl::setInitialMask(InputArray mask)
|
||||
{
|
||||
preset_mask = mask.getMat();
|
||||
}
|
||||
|
||||
bool TrackerCSRTImpl::check_mask_area(const Mat &mat, const double obj_area)
|
||||
{
|
||||
double threshold = 0.05;
|
||||
double mask_area= sum(mat)[0];
|
||||
if(mask_area < threshold*obj_area) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Mat TrackerCSRTImpl::calculate_response(const Mat &image, const std::vector<Mat> filter)
|
||||
{
|
||||
Mat patch = get_subwindow(image, object_center, cvFloor(current_scale_factor * template_size.width),
|
||||
cvFloor(current_scale_factor * template_size.height));
|
||||
resize(patch, patch, rescaled_template_size, 0, 0, INTER_CUBIC);
|
||||
|
||||
std::vector<Mat> ftrs = get_features(patch, yf.size());
|
||||
std::vector<Mat> Ffeatures = fourier_transform_features(ftrs);
|
||||
Mat resp, res;
|
||||
if(params.use_channel_weights){
|
||||
res = Mat::zeros(Ffeatures[0].size(), CV_32FC2);
|
||||
Mat resp_ch;
|
||||
Mat mul_mat;
|
||||
for(size_t i = 0; i < Ffeatures.size(); ++i) {
|
||||
mulSpectrums(Ffeatures[i], filter[i], resp_ch, 0, true);
|
||||
res += (resp_ch * filter_weights[i]);
|
||||
}
|
||||
idft(res, res, DFT_SCALE | DFT_REAL_OUTPUT);
|
||||
} else {
|
||||
res = Mat::zeros(Ffeatures[0].size(), CV_32FC2);
|
||||
Mat resp_ch;
|
||||
for(size_t i = 0; i < Ffeatures.size(); ++i) {
|
||||
mulSpectrums(Ffeatures[i], filter[i], resp_ch, 0 , true);
|
||||
res = res + resp_ch;
|
||||
}
|
||||
idft(res, res, DFT_SCALE | DFT_REAL_OUTPUT);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void TrackerCSRTImpl::update_csr_filter(const Mat &image, const Mat &mask)
|
||||
{
|
||||
Mat patch = get_subwindow(image, object_center, cvFloor(current_scale_factor * template_size.width),
|
||||
cvFloor(current_scale_factor * template_size.height));
|
||||
resize(patch, patch, rescaled_template_size, 0, 0, INTER_CUBIC);
|
||||
|
||||
std::vector<Mat> ftrs = get_features(patch, yf.size());
|
||||
std::vector<Mat> Fftrs = fourier_transform_features(ftrs);
|
||||
std::vector<Mat> new_csr_filter = create_csr_filter(Fftrs, yf, mask);
|
||||
//calculate per channel weights
|
||||
if(params.use_channel_weights) {
|
||||
Mat current_resp;
|
||||
double max_val;
|
||||
float sum_weights = 0;
|
||||
std::vector<float> new_filter_weights = std::vector<float>(new_csr_filter.size());
|
||||
for(size_t i = 0; i < new_csr_filter.size(); ++i) {
|
||||
mulSpectrums(Fftrs[i], new_csr_filter[i], current_resp, 0, true);
|
||||
idft(current_resp, current_resp, DFT_SCALE | DFT_REAL_OUTPUT);
|
||||
minMaxLoc(current_resp, NULL, &max_val, NULL, NULL);
|
||||
sum_weights += static_cast<float>(max_val);
|
||||
new_filter_weights[i] = static_cast<float>(max_val);
|
||||
}
|
||||
//update filter weights with new values
|
||||
float updated_sum = 0;
|
||||
for(size_t i = 0; i < filter_weights.size(); ++i) {
|
||||
filter_weights[i] = filter_weights[i]*(1.0f - params.weights_lr) +
|
||||
params.weights_lr * (new_filter_weights[i] / sum_weights);
|
||||
updated_sum += filter_weights[i];
|
||||
}
|
||||
//normalize weights
|
||||
for(size_t i = 0; i < filter_weights.size(); ++i) {
|
||||
filter_weights[i] /= updated_sum;
|
||||
}
|
||||
}
|
||||
for(size_t i = 0; i < csr_filter.size(); ++i) {
|
||||
csr_filter[i] = (1.0f - params.filter_lr)*csr_filter[i] + params.filter_lr * new_csr_filter[i];
|
||||
}
|
||||
std::vector<Mat>().swap(ftrs);
|
||||
std::vector<Mat>().swap(Fftrs);
|
||||
}
|
||||
|
||||
|
||||
std::vector<Mat> TrackerCSRTImpl::get_features(const Mat &patch, const Size2i &feature_size)
|
||||
{
|
||||
std::vector<Mat> features;
|
||||
if (params.use_hog) {
|
||||
std::vector<Mat> hog = get_features_hog(patch, cell_size);
|
||||
features.insert(features.end(), hog.begin(),
|
||||
hog.begin()+params.num_hog_channels_used);
|
||||
}
|
||||
if (params.use_color_names) {
|
||||
std::vector<Mat> cn;
|
||||
cn = get_features_cn(patch, feature_size);
|
||||
features.insert(features.end(), cn.begin(), cn.end());
|
||||
}
|
||||
if(params.use_gray) {
|
||||
Mat gray_m;
|
||||
cvtColor(patch, gray_m, COLOR_BGR2GRAY);
|
||||
resize(gray_m, gray_m, feature_size, 0, 0, INTER_CUBIC);
|
||||
gray_m.convertTo(gray_m, CV_32FC1, 1.0/255.0, -0.5);
|
||||
features.push_back(gray_m);
|
||||
}
|
||||
if(params.use_rgb) {
|
||||
std::vector<Mat> rgb_features = get_features_rgb(patch, feature_size);
|
||||
features.insert(features.end(), rgb_features.begin(), rgb_features.end());
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < features.size(); ++i) {
|
||||
features.at(i) = features.at(i).mul(window);
|
||||
}
|
||||
return features;
|
||||
}
|
||||
|
||||
class ParallelCreateCSRFilter : public ParallelLoopBody {
|
||||
public:
|
||||
ParallelCreateCSRFilter(
|
||||
const std::vector<cv::Mat> img_features,
|
||||
const cv::Mat Y,
|
||||
const cv::Mat P,
|
||||
int admm_iterations,
|
||||
std::vector<Mat> &result_filter_):
|
||||
result_filter(result_filter_)
|
||||
{
|
||||
this->img_features = img_features;
|
||||
this->Y = Y;
|
||||
this->P = P;
|
||||
this->admm_iterations = admm_iterations;
|
||||
}
|
||||
virtual void operator ()(const Range& range) const CV_OVERRIDE
|
||||
{
|
||||
for (int i = range.start; i < range.end; i++) {
|
||||
float mu = 5.0f;
|
||||
float beta = 3.0f;
|
||||
float mu_max = 20.0f;
|
||||
float lambda = mu / 100.0f;
|
||||
|
||||
Mat F = img_features[i];
|
||||
|
||||
Mat Sxy, Sxx;
|
||||
mulSpectrums(F, Y, Sxy, 0, true);
|
||||
mulSpectrums(F, F, Sxx, 0, true);
|
||||
|
||||
Mat H;
|
||||
H = divide_complex_matrices(Sxy, (Sxx + lambda));
|
||||
idft(H, H, DFT_SCALE|DFT_REAL_OUTPUT);
|
||||
H = H.mul(P);
|
||||
dft(H, H, DFT_COMPLEX_OUTPUT);
|
||||
Mat L = Mat::zeros(H.size(), H.type()); //Lagrangian multiplier
|
||||
Mat G;
|
||||
for(int iteration = 0; iteration < admm_iterations; ++iteration) {
|
||||
G = divide_complex_matrices((Sxy + (mu * H) - L) , (Sxx + mu));
|
||||
idft((mu * G) + L, H, DFT_SCALE | DFT_REAL_OUTPUT);
|
||||
float lm = 1.0f / (lambda+mu);
|
||||
H = H.mul(P*lm);
|
||||
dft(H, H, DFT_COMPLEX_OUTPUT);
|
||||
|
||||
//Update variables for next iteration
|
||||
L = L + mu * (G - H);
|
||||
mu = min(mu_max, beta*mu);
|
||||
}
|
||||
result_filter[i] = H;
|
||||
}
|
||||
}
|
||||
|
||||
ParallelCreateCSRFilter& operator=(const ParallelCreateCSRFilter &) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
int admm_iterations;
|
||||
Mat Y;
|
||||
Mat P;
|
||||
std::vector<Mat> img_features;
|
||||
std::vector<Mat> &result_filter;
|
||||
};
|
||||
|
||||
|
||||
std::vector<Mat> TrackerCSRTImpl::create_csr_filter(
|
||||
const std::vector<cv::Mat> img_features,
|
||||
const cv::Mat Y,
|
||||
const cv::Mat P)
|
||||
{
|
||||
std::vector<Mat> result_filter;
|
||||
result_filter.resize(img_features.size());
|
||||
ParallelCreateCSRFilter parallelCreateCSRFilter(img_features, Y, P,
|
||||
params.admm_iterations, result_filter);
|
||||
parallel_for_(Range(0, static_cast<int>(result_filter.size())), parallelCreateCSRFilter);
|
||||
|
||||
return result_filter;
|
||||
}
|
||||
|
||||
Mat TrackerCSRTImpl::get_location_prior(
|
||||
const Rect roi,
|
||||
const Size2f target_size,
|
||||
const Size img_sz)
|
||||
{
|
||||
int x1 = cvRound(max(min(roi.x-1, img_sz.width-1) , 0));
|
||||
int y1 = cvRound(max(min(roi.y-1, img_sz.height-1) , 0));
|
||||
|
||||
int x2 = cvRound(min(max(roi.width-1, 0) , img_sz.width-1));
|
||||
int y2 = cvRound(min(max(roi.height-1, 0) , img_sz.height-1));
|
||||
|
||||
Size target_sz;
|
||||
target_sz.width = target_sz.height = cvFloor(min(target_size.width, target_size.height));
|
||||
|
||||
double cx = x1 + (x2-x1)/2.;
|
||||
double cy = y1 + (y2-y1)/2.;
|
||||
double kernel_size_width = 1.0/(0.5*static_cast<double>(target_sz.width)*1.4142+1);
|
||||
double kernel_size_height = 1.0/(0.5*static_cast<double>(target_sz.height)*1.4142+1);
|
||||
|
||||
cv::Mat kernel_weight = Mat::zeros(1 + cvFloor(y2 - y1) , 1+cvFloor(-(x1-cx) + (x2-cx)), CV_64FC1);
|
||||
for (int y = y1; y < y2+1; ++y){
|
||||
double * weightPtr = kernel_weight.ptr<double>(y);
|
||||
double tmp_y = std::pow((cy-y)*kernel_size_height, 2);
|
||||
for (int x = x1; x < x2+1; ++x){
|
||||
weightPtr[x] = kernel_epan(std::pow((cx-x)*kernel_size_width,2) + tmp_y);
|
||||
}
|
||||
}
|
||||
|
||||
double max_val;
|
||||
cv::minMaxLoc(kernel_weight, NULL, &max_val, NULL, NULL);
|
||||
Mat fg_prior = kernel_weight / max_val;
|
||||
fg_prior.setTo(0.5, fg_prior < 0.5);
|
||||
fg_prior.setTo(0.9, fg_prior > 0.9);
|
||||
return fg_prior;
|
||||
}
|
||||
|
||||
Mat TrackerCSRTImpl::segment_region(
|
||||
const Mat &image,
|
||||
const Point2f &object_center,
|
||||
const Size2f &template_size,
|
||||
const Size &target_size,
|
||||
float scale_factor)
|
||||
{
|
||||
Rect valid_pixels;
|
||||
Mat patch = get_subwindow(image, object_center, cvFloor(scale_factor * template_size.width),
|
||||
cvFloor(scale_factor * template_size.height), &valid_pixels);
|
||||
Size2f scaled_target = Size2f(target_size.width * scale_factor,
|
||||
target_size.height * scale_factor);
|
||||
Mat fg_prior = get_location_prior(
|
||||
Rect(0,0, patch.size().width, patch.size().height),
|
||||
scaled_target , patch.size());
|
||||
|
||||
std::vector<Mat> img_channels;
|
||||
split(patch, img_channels);
|
||||
std::pair<Mat, Mat> probs = Segment::computePosteriors2(img_channels, 0, 0, patch.cols, patch.rows,
|
||||
p_b, fg_prior, 1.0-fg_prior, hist_foreground, hist_background);
|
||||
|
||||
Mat mask = Mat::zeros(probs.first.size(), probs.first.type());
|
||||
probs.first(valid_pixels).copyTo(mask(valid_pixels));
|
||||
double max_resp = get_max(mask);
|
||||
threshold(mask, mask, max_resp / 2.0, 1, THRESH_BINARY);
|
||||
mask.convertTo(mask, CV_32FC1, 1.0);
|
||||
return mask;
|
||||
}
|
||||
|
||||
|
||||
void TrackerCSRTImpl::extract_histograms(const Mat &image, cv::Rect region, Histogram &hf, Histogram &hb)
|
||||
{
|
||||
// get coordinates of the region
|
||||
int x1 = std::min(std::max(0, region.x), image.cols-1);
|
||||
int y1 = std::min(std::max(0, region.y), image.rows-1);
|
||||
int x2 = std::min(std::max(0, region.x + region.width), image.cols-1);
|
||||
int y2 = std::min(std::max(0, region.y + region.height), image.rows-1);
|
||||
|
||||
// calculate coordinates of the background region
|
||||
int offsetX = (x2-x1+1) / params.background_ratio;
|
||||
int offsetY = (y2-y1+1) / params.background_ratio;
|
||||
int outer_y1 = std::max(0, (int)(y1-offsetY));
|
||||
int outer_y2 = std::min(image.rows, (int)(y2+offsetY+1));
|
||||
int outer_x1 = std::max(0, (int)(x1-offsetX));
|
||||
int outer_x2 = std::min(image.cols, (int)(x2+offsetX+1));
|
||||
|
||||
// calculate probability for the background
|
||||
p_b = 1.0 - ((x2-x1+1) * (y2-y1+1)) /
|
||||
((double) (outer_x2-outer_x1+1) * (outer_y2-outer_y1+1));
|
||||
|
||||
// split multi-channel image into the std::vector of matrices
|
||||
std::vector<Mat> img_channels(image.channels());
|
||||
split(image, img_channels);
|
||||
for(size_t k=0; k<img_channels.size(); k++) {
|
||||
img_channels.at(k).convertTo(img_channels.at(k), CV_8UC1);
|
||||
}
|
||||
|
||||
hf.extractForegroundHistogram(img_channels, Mat(), false, x1, y1, x2, y2);
|
||||
hb.extractBackGroundHistogram(img_channels, x1, y1, x2, y2,
|
||||
outer_x1, outer_y1, outer_x2, outer_y2);
|
||||
std::vector<Mat>().swap(img_channels);
|
||||
}
|
||||
|
||||
void TrackerCSRTImpl::update_histograms(const Mat &image, const Rect ®ion)
|
||||
{
|
||||
// create temporary histograms
|
||||
Histogram hf(image.channels(), params.histogram_bins);
|
||||
Histogram hb(image.channels(), params.histogram_bins);
|
||||
extract_histograms(image, region, hf, hb);
|
||||
|
||||
// get histogram vectors from temporary histograms
|
||||
std::vector<double> hf_vect_new = hf.getHistogramVector();
|
||||
std::vector<double> hb_vect_new = hb.getHistogramVector();
|
||||
// get histogram vectors from learned histograms
|
||||
std::vector<double> hf_vect = hist_foreground.getHistogramVector();
|
||||
std::vector<double> hb_vect = hist_background.getHistogramVector();
|
||||
|
||||
// update histograms - use learning rate
|
||||
for(size_t i=0; i<hf_vect.size(); i++) {
|
||||
hf_vect_new[i] = (1-params.histogram_lr)*hf_vect[i] +
|
||||
params.histogram_lr*hf_vect_new[i];
|
||||
hb_vect_new[i] = (1-params.histogram_lr)*hb_vect[i] +
|
||||
params.histogram_lr*hb_vect_new[i];
|
||||
}
|
||||
|
||||
// set learned histograms
|
||||
hist_foreground.setHistogramVector(&hf_vect_new[0]);
|
||||
hist_background.setHistogramVector(&hb_vect_new[0]);
|
||||
|
||||
std::vector<double>().swap(hf_vect);
|
||||
std::vector<double>().swap(hb_vect);
|
||||
}
|
||||
|
||||
Point2f TrackerCSRTImpl::estimate_new_position(const Mat &image)
|
||||
{
|
||||
|
||||
Mat resp = calculate_response(image, csr_filter);
|
||||
|
||||
double max_val;
|
||||
Point max_loc;
|
||||
minMaxLoc(resp, NULL, &max_val, NULL, &max_loc);
|
||||
if (max_val < params.psr_threshold)
|
||||
return Point2f(-1,-1); // target "lost"
|
||||
|
||||
// take into account also subpixel accuracy
|
||||
float col = ((float) max_loc.x) + subpixel_peak(resp, "horizontal", max_loc);
|
||||
float row = ((float) max_loc.y) + subpixel_peak(resp, "vertical", max_loc);
|
||||
if(row + 1 > (float)resp.rows / 2.0f) {
|
||||
row = row - resp.rows;
|
||||
}
|
||||
if(col + 1 > (float)resp.cols / 2.0f) {
|
||||
col = col - resp.cols;
|
||||
}
|
||||
// calculate x and y displacements
|
||||
Point2f new_center = object_center + Point2f(current_scale_factor * (1.0f / rescale_ratio) *cell_size*(col),
|
||||
current_scale_factor * (1.0f / rescale_ratio) *cell_size*(row));
|
||||
//sanity checks
|
||||
if(new_center.x < 0)
|
||||
new_center.x = 0;
|
||||
if(new_center.x >= image_size.width)
|
||||
new_center.x = static_cast<float>(image_size.width - 1);
|
||||
if(new_center.y < 0)
|
||||
new_center.y = 0;
|
||||
if(new_center.y >= image_size.height)
|
||||
new_center.y = static_cast<float>(image_size.height - 1);
|
||||
|
||||
return new_center;
|
||||
}
|
||||
|
||||
// *********************************************************************
|
||||
// * Update API function *
|
||||
// *********************************************************************
|
||||
bool TrackerCSRTImpl::update(InputArray image_, Rect& boundingBox)
|
||||
{
|
||||
Mat image;
|
||||
if(image_.channels() == 1) //treat gray image as color image
|
||||
cvtColor(image_, image, COLOR_GRAY2BGR);
|
||||
else
|
||||
image = image_.getMat();
|
||||
|
||||
object_center = estimate_new_position(image);
|
||||
if (object_center.x < 0 && object_center.y < 0)
|
||||
return false;
|
||||
|
||||
current_scale_factor = dsst.getScale(image, object_center);
|
||||
//update bouding_box according to new scale and location
|
||||
bounding_box.x = object_center.x - current_scale_factor * original_target_size.width / 2.0f;
|
||||
bounding_box.y = object_center.y - current_scale_factor * original_target_size.height / 2.0f;
|
||||
bounding_box.width = current_scale_factor * original_target_size.width;
|
||||
bounding_box.height = current_scale_factor * original_target_size.height;
|
||||
|
||||
//update tracker
|
||||
if(params.use_segmentation) {
|
||||
Mat hsv_img = bgr2hsv(image);
|
||||
update_histograms(hsv_img, bounding_box);
|
||||
filter_mask = segment_region(hsv_img, object_center,
|
||||
template_size,original_target_size, current_scale_factor);
|
||||
resize(filter_mask, filter_mask, yf.size(), 0, 0, INTER_NEAREST);
|
||||
if(check_mask_area(filter_mask, default_mask_area)) {
|
||||
dilate(filter_mask , filter_mask, erode_element);
|
||||
} else {
|
||||
filter_mask = default_mask;
|
||||
}
|
||||
} else {
|
||||
filter_mask = default_mask;
|
||||
}
|
||||
update_csr_filter(image, filter_mask);
|
||||
dsst.update(image, object_center);
|
||||
boundingBox = bounding_box;
|
||||
return true;
|
||||
}
|
||||
|
||||
// *********************************************************************
|
||||
// * Init API function *
|
||||
// *********************************************************************
|
||||
void TrackerCSRTImpl::init(InputArray image_, const Rect& boundingBox)
|
||||
{
|
||||
Mat image;
|
||||
if(image_.channels() == 1) //treat gray image as color image
|
||||
cvtColor(image_, image, COLOR_GRAY2BGR);
|
||||
else
|
||||
image = image_.getMat();
|
||||
|
||||
current_scale_factor = 1.0;
|
||||
image_size = image.size();
|
||||
bounding_box = boundingBox;
|
||||
cell_size = cvFloor(std::min(4.0, std::max(1.0, static_cast<double>(
|
||||
cvCeil((bounding_box.width * bounding_box.height)/400.0)))));
|
||||
original_target_size = Size(bounding_box.size());
|
||||
|
||||
template_size.width = static_cast<float>(cvFloor(original_target_size.width + params.padding *
|
||||
sqrt(original_target_size.width * original_target_size.height)));
|
||||
template_size.height = static_cast<float>(cvFloor(original_target_size.height + params.padding *
|
||||
sqrt(original_target_size.width * original_target_size.height)));
|
||||
template_size.width = template_size.height =
|
||||
(template_size.width + template_size.height) / 2.0f;
|
||||
rescale_ratio = sqrt((params.template_size * params.template_size) / (template_size.width * template_size.height));
|
||||
if(rescale_ratio > 1) {
|
||||
rescale_ratio = 1;
|
||||
}
|
||||
rescaled_template_size = Size2i(cvFloor(template_size.width * rescale_ratio),
|
||||
cvFloor(template_size.height * rescale_ratio));
|
||||
object_center = Point2f(static_cast<float>(boundingBox.x) + original_target_size.width / 2.0f,
|
||||
static_cast<float>(boundingBox.y) + original_target_size.height / 2.0f);
|
||||
|
||||
yf = gaussian_shaped_labels(params.gsl_sigma,
|
||||
rescaled_template_size.width / cell_size, rescaled_template_size.height / cell_size);
|
||||
if(params.window_function.compare("hann") == 0) {
|
||||
window = get_hann_win(Size(yf.cols,yf.rows));
|
||||
} else if(params.window_function.compare("cheb") == 0) {
|
||||
window = get_chebyshev_win(Size(yf.cols,yf.rows), params.cheb_attenuation);
|
||||
} else if(params.window_function.compare("kaiser") == 0) {
|
||||
window = get_kaiser_win(Size(yf.cols,yf.rows), params.kaiser_alpha);
|
||||
} else {
|
||||
CV_Error(Error::StsBadArg, "Not a valid window function");
|
||||
}
|
||||
|
||||
Size2i scaled_obj_size = Size2i(cvFloor(original_target_size.width * rescale_ratio / cell_size),
|
||||
cvFloor(original_target_size.height * rescale_ratio / cell_size));
|
||||
//set dummy mask and area;
|
||||
int x0 = std::max((yf.size().width - scaled_obj_size.width)/2 - 1, 0);
|
||||
int y0 = std::max((yf.size().height - scaled_obj_size.height)/2 - 1, 0);
|
||||
default_mask = Mat::zeros(yf.size(), CV_32FC1);
|
||||
default_mask(Rect(x0,y0,scaled_obj_size.width, scaled_obj_size.height)) = 1.0f;
|
||||
default_mask_area = static_cast<float>(sum(default_mask)[0]);
|
||||
|
||||
//initalize segmentation
|
||||
if(params.use_segmentation) {
|
||||
Mat hsv_img = bgr2hsv(image);
|
||||
hist_foreground = Histogram(hsv_img.channels(), params.histogram_bins);
|
||||
hist_background = Histogram(hsv_img.channels(), params.histogram_bins);
|
||||
extract_histograms(hsv_img, bounding_box, hist_foreground, hist_background);
|
||||
filter_mask = segment_region(hsv_img, object_center, template_size,
|
||||
original_target_size, current_scale_factor);
|
||||
//update calculated mask with preset mask
|
||||
if(preset_mask.data){
|
||||
Mat preset_mask_padded = Mat::zeros(filter_mask.size(), filter_mask.type());
|
||||
int sx = std::max((int)cvFloor(preset_mask_padded.cols / 2.0f - preset_mask.cols / 2.0f) - 1, 0);
|
||||
int sy = std::max((int)cvFloor(preset_mask_padded.rows / 2.0f - preset_mask.rows / 2.0f) - 1, 0);
|
||||
preset_mask.copyTo(preset_mask_padded(
|
||||
Rect(sx, sy, preset_mask.cols, preset_mask.rows)));
|
||||
filter_mask = filter_mask.mul(preset_mask_padded);
|
||||
}
|
||||
erode_element = getStructuringElement(MORPH_ELLIPSE, Size(3,3), Point(1,1));
|
||||
resize(filter_mask, filter_mask, yf.size(), 0, 0, INTER_NEAREST);
|
||||
if(check_mask_area(filter_mask, default_mask_area)) {
|
||||
dilate(filter_mask , filter_mask, erode_element);
|
||||
} else {
|
||||
filter_mask = default_mask;
|
||||
}
|
||||
|
||||
} else {
|
||||
filter_mask = default_mask;
|
||||
}
|
||||
|
||||
//initialize filter
|
||||
Mat patch = get_subwindow(image, object_center, cvFloor(current_scale_factor * template_size.width),
|
||||
cvFloor(current_scale_factor * template_size.height));
|
||||
resize(patch, patch, rescaled_template_size, 0, 0, INTER_CUBIC);
|
||||
std::vector<Mat> patch_ftrs = get_features(patch, yf.size());
|
||||
std::vector<Mat> Fftrs = fourier_transform_features(patch_ftrs);
|
||||
csr_filter = create_csr_filter(Fftrs, yf, filter_mask);
|
||||
|
||||
if(params.use_channel_weights) {
|
||||
Mat current_resp;
|
||||
filter_weights = std::vector<float>(csr_filter.size());
|
||||
float chw_sum = 0;
|
||||
for (size_t i = 0; i < csr_filter.size(); ++i) {
|
||||
mulSpectrums(Fftrs[i], csr_filter[i], current_resp, 0, true);
|
||||
idft(current_resp, current_resp, DFT_SCALE | DFT_REAL_OUTPUT);
|
||||
double max_val;
|
||||
minMaxLoc(current_resp, NULL, &max_val, NULL , NULL);
|
||||
chw_sum += static_cast<float>(max_val);
|
||||
filter_weights[i] = static_cast<float>(max_val);
|
||||
}
|
||||
for (size_t i = 0; i < filter_weights.size(); ++i) {
|
||||
filter_weights[i] /= chw_sum;
|
||||
}
|
||||
}
|
||||
|
||||
//initialize scale search
|
||||
dsst = DSST(image, bounding_box, template_size, params.number_of_scales, params.scale_step,
|
||||
params.scale_model_max_area, params.scale_sigma_factor, params.scale_lr);
|
||||
|
||||
model=makePtr<TrackerCSRTModel>();
|
||||
}
|
||||
|
||||
} // namespace impl
|
||||
|
||||
TrackerCSRT::Params::Params()
|
||||
{
|
||||
use_channel_weights = true;
|
||||
use_segmentation = true;
|
||||
use_hog = true;
|
||||
use_color_names = true;
|
||||
use_gray = true;
|
||||
use_rgb = false;
|
||||
window_function = "hann";
|
||||
kaiser_alpha = 3.75f;
|
||||
cheb_attenuation = 45;
|
||||
padding = 3.0f;
|
||||
template_size = 200;
|
||||
gsl_sigma = 1.0f;
|
||||
hog_orientations = 9;
|
||||
hog_clip = 0.2f;
|
||||
num_hog_channels_used = 18;
|
||||
filter_lr = 0.02f;
|
||||
weights_lr = 0.02f;
|
||||
admm_iterations = 4;
|
||||
number_of_scales = 33;
|
||||
scale_sigma_factor = 0.250f;
|
||||
scale_model_max_area = 512.0f;
|
||||
scale_lr = 0.025f;
|
||||
scale_step = 1.020f;
|
||||
histogram_bins = 16;
|
||||
background_ratio = 2;
|
||||
histogram_lr = 0.04f;
|
||||
psr_threshold = 0.035f;
|
||||
}
|
||||
|
||||
TrackerCSRT::TrackerCSRT()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
TrackerCSRT::~TrackerCSRT()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
Ptr<TrackerCSRT> TrackerCSRT::create(const TrackerCSRT::Params ¶meters)
|
||||
{
|
||||
return makePtr<TrackerCSRTImpl>(parameters);
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
|
||||
#include "legacy/trackerCSRT.legacy.hpp"
|
||||
@@ -0,0 +1,211 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
#include "trackerCSRTScaleEstimation.hpp"
|
||||
#include "trackerCSRTUtils.hpp"
|
||||
|
||||
//Discriminative Scale Space Tracking
|
||||
namespace cv
|
||||
{
|
||||
|
||||
class ParallelGetScaleFeatures : public ParallelLoopBody
|
||||
{
|
||||
public:
|
||||
ParallelGetScaleFeatures(
|
||||
Mat img,
|
||||
Point2f pos,
|
||||
Size2f base_target_sz,
|
||||
float current_scale,
|
||||
std::vector<float> &scale_factors,
|
||||
Mat scale_window,
|
||||
Size scale_model_sz,
|
||||
int col_len,
|
||||
Mat &result)
|
||||
{
|
||||
this->img = img;
|
||||
this->pos = pos;
|
||||
this->base_target_sz = base_target_sz;
|
||||
this->current_scale = current_scale;
|
||||
this->scale_factors = scale_factors;
|
||||
this->scale_window = scale_window;
|
||||
this->scale_model_sz = scale_model_sz;
|
||||
this->col_len = col_len;
|
||||
this->result = result;
|
||||
}
|
||||
virtual void operator ()(const Range& range) const CV_OVERRIDE
|
||||
{
|
||||
for (int s = range.start; s < range.end; s++) {
|
||||
Size patch_sz = Size(static_cast<int>(current_scale * scale_factors[s] * base_target_sz.width),
|
||||
static_cast<int>(current_scale * scale_factors[s] * base_target_sz.height));
|
||||
Mat img_patch = get_subwindow(img, pos, patch_sz.width, patch_sz.height);
|
||||
img_patch.convertTo(img_patch, CV_32FC3);
|
||||
resize(img_patch, img_patch, Size(scale_model_sz.width, scale_model_sz.height),0,0,INTER_LINEAR);
|
||||
std::vector<Mat> hog;
|
||||
hog = get_features_hog(img_patch, 4);
|
||||
for (int i = 0; i < static_cast<int>(hog.size()); ++i) {
|
||||
hog[i] = hog[i].t();
|
||||
hog[i] = scale_window.at<float>(0,s) * hog[i].reshape(0, col_len);
|
||||
hog[i].copyTo(result(Rect(Point(s, i*col_len), hog[i].size())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ParallelGetScaleFeatures& operator=(const ParallelGetScaleFeatures &) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
Mat img;
|
||||
Point2f pos;
|
||||
Size2f base_target_sz;
|
||||
float current_scale;
|
||||
std::vector<float> scale_factors;
|
||||
Mat scale_window;
|
||||
Size scale_model_sz;
|
||||
int col_len;
|
||||
Mat result;
|
||||
};
|
||||
|
||||
|
||||
DSST::DSST(const Mat &image,
|
||||
Rect2f bounding_box,
|
||||
Size2f template_size,
|
||||
int numberOfScales,
|
||||
float scaleStep,
|
||||
float maxModelArea,
|
||||
float sigmaFactor,
|
||||
float scaleLearnRate):
|
||||
scales_count(numberOfScales), scale_step(scaleStep), max_model_area(maxModelArea),
|
||||
sigma_factor(sigmaFactor), learn_rate(scaleLearnRate)
|
||||
{
|
||||
original_targ_sz = bounding_box.size();
|
||||
Point2f object_center = Point2f(bounding_box.x + original_targ_sz.width / 2,
|
||||
bounding_box.y + original_targ_sz.height / 2);
|
||||
|
||||
current_scale_factor = 1.0;
|
||||
if(scales_count % 2 == 0)
|
||||
scales_count++;
|
||||
|
||||
scale_sigma = static_cast<float>(sqrt(scales_count) * sigma_factor);
|
||||
|
||||
min_scale_factor = static_cast<float>(pow(scale_step,
|
||||
cvCeil(log(max(5.0 / template_size.width, 5.0 / template_size.height)) / log(scale_step))));
|
||||
max_scale_factor = static_cast<float>(pow(scale_step,
|
||||
cvFloor(log(min((float)image.rows / (float)bounding_box.width,
|
||||
(float)image.cols / (float)bounding_box.height)) / log(scale_step))));
|
||||
ys = Mat(1, scales_count, CV_32FC1);
|
||||
float ss, sf;
|
||||
for(int i = 0; i < ys.cols; ++i) {
|
||||
ss = (float)(i+1) - cvCeil((float)scales_count / 2.0f);
|
||||
ys.at<float>(0,i) = static_cast<float>(exp(-0.5 * pow(ss,2) / pow(scale_sigma,2)));
|
||||
sf = static_cast<float>(i + 1);
|
||||
scale_factors.push_back(pow(scale_step, cvCeil((float)scales_count / 2.0f) - sf));
|
||||
}
|
||||
|
||||
scale_window = get_hann_win(Size(scales_count, 1));
|
||||
|
||||
float scale_model_factor = 1.0;
|
||||
if(template_size.width * template_size.height * pow(scale_model_factor, 2) > max_model_area)
|
||||
{
|
||||
scale_model_factor = sqrt(max_model_area /
|
||||
(template_size.width * template_size.height));
|
||||
}
|
||||
scale_model_sz = Size(cvFloor(template_size.width * scale_model_factor),
|
||||
cvFloor(template_size.height * scale_model_factor));
|
||||
|
||||
Mat scale_resp = get_scale_features(image, object_center, original_targ_sz,
|
||||
current_scale_factor, scale_factors, scale_window, scale_model_sz);
|
||||
|
||||
Mat ysf_row = Mat(ys.size(), CV_32FC2);
|
||||
dft(ys, ysf_row, DFT_ROWS | DFT_COMPLEX_OUTPUT, 0);
|
||||
ysf = repeat(ysf_row, scale_resp.rows, 1);
|
||||
Mat Fscale_resp;
|
||||
dft(scale_resp, Fscale_resp, DFT_ROWS | DFT_COMPLEX_OUTPUT);
|
||||
mulSpectrums(ysf, Fscale_resp, sf_num, 0 , true);
|
||||
Mat sf_den_all;
|
||||
mulSpectrums(Fscale_resp, Fscale_resp, sf_den_all, 0, true);
|
||||
reduce(sf_den_all, sf_den, 0, REDUCE_SUM, -1);
|
||||
}
|
||||
|
||||
DSST::~DSST()
|
||||
{
|
||||
}
|
||||
|
||||
Mat DSST::get_scale_features(
|
||||
Mat img,
|
||||
Point2f pos,
|
||||
Size2f base_target_sz,
|
||||
float current_scale,
|
||||
std::vector<float> &scale_factors,
|
||||
Mat scale_window,
|
||||
Size scale_model_sz)
|
||||
{
|
||||
Mat result;
|
||||
int col_len = 0;
|
||||
Size patch_sz = Size(cvFloor(current_scale * scale_factors[0] * base_target_sz.width),
|
||||
cvFloor(current_scale * scale_factors[0] * base_target_sz.height));
|
||||
Mat img_patch = get_subwindow(img, pos, patch_sz.width, patch_sz.height);
|
||||
img_patch.convertTo(img_patch, CV_32FC3);
|
||||
resize(img_patch, img_patch, Size(scale_model_sz.width, scale_model_sz.height),0,0,INTER_LINEAR);
|
||||
std::vector<Mat> hog;
|
||||
hog = get_features_hog(img_patch, 4);
|
||||
result = Mat(Size((int)scale_factors.size(), hog[0].cols * hog[0].rows * (int)hog.size()), CV_32F);
|
||||
col_len = hog[0].cols * hog[0].rows;
|
||||
for (int i = 0; i < static_cast<int>(hog.size()); ++i) {
|
||||
hog[i] = hog[i].t();
|
||||
hog[i] = scale_window.at<float>(0,0) * hog[i].reshape(0, col_len);
|
||||
hog[i].copyTo(result(Rect(Point(0, i*col_len), hog[i].size())));
|
||||
}
|
||||
|
||||
ParallelGetScaleFeatures parallelGetScaleFeatures(img, pos, base_target_sz,
|
||||
current_scale, scale_factors, scale_window, scale_model_sz, col_len, result);
|
||||
parallel_for_(Range(1, static_cast<int>(scale_factors.size())), parallelGetScaleFeatures);
|
||||
return result;
|
||||
}
|
||||
|
||||
void DSST::update(const Mat &image, const Point2f object_center)
|
||||
{
|
||||
Mat scale_features = get_scale_features(image, object_center, original_targ_sz,
|
||||
current_scale_factor, scale_factors, scale_window, scale_model_sz);
|
||||
Mat Fscale_features;
|
||||
dft(scale_features, Fscale_features, DFT_ROWS | DFT_COMPLEX_OUTPUT);
|
||||
Mat new_sf_num;
|
||||
Mat new_sf_den;
|
||||
Mat new_sf_den_all;
|
||||
mulSpectrums(ysf, Fscale_features, new_sf_num, DFT_ROWS, true);
|
||||
Mat sf_den_all;
|
||||
mulSpectrums(Fscale_features, Fscale_features, new_sf_den_all, DFT_ROWS, true);
|
||||
reduce(new_sf_den_all, new_sf_den, 0, REDUCE_SUM, -1);
|
||||
|
||||
sf_num = (1 - learn_rate) * sf_num + learn_rate * new_sf_num;
|
||||
sf_den = (1 - learn_rate) * sf_den + learn_rate * new_sf_den;
|
||||
}
|
||||
|
||||
float DSST::getScale(const Mat &image, const Point2f object_center)
|
||||
{
|
||||
Mat scale_features = get_scale_features(image, object_center, original_targ_sz,
|
||||
current_scale_factor, scale_factors, scale_window, scale_model_sz);
|
||||
|
||||
Mat Fscale_features;
|
||||
dft(scale_features, Fscale_features, DFT_ROWS | DFT_COMPLEX_OUTPUT);
|
||||
|
||||
mulSpectrums(Fscale_features, sf_num, Fscale_features, 0, false);
|
||||
Mat scale_resp;
|
||||
reduce(Fscale_features, scale_resp, 0, REDUCE_SUM, -1);
|
||||
scale_resp = divide_complex_matrices(scale_resp, sf_den + 0.01f);
|
||||
idft(scale_resp, scale_resp, DFT_REAL_OUTPUT|DFT_SCALE);
|
||||
Point max_loc;
|
||||
minMaxLoc(scale_resp, NULL, NULL, NULL, &max_loc);
|
||||
|
||||
current_scale_factor *= scale_factors[max_loc.x];
|
||||
if(current_scale_factor < min_scale_factor)
|
||||
current_scale_factor = min_scale_factor;
|
||||
else if(current_scale_factor > max_scale_factor)
|
||||
current_scale_factor = max_scale_factor;
|
||||
|
||||
return current_scale_factor;
|
||||
}
|
||||
} /* namespace cv */
|
||||
@@ -0,0 +1,45 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef OPENCV_TRACKER_CSRT_SCALE_ESTIMATION
|
||||
#define OPENCV_TRACKER_CSRT_SCALE_ESTIMATION
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
class DSST {
|
||||
public:
|
||||
DSST() {};
|
||||
DSST(const Mat &image, Rect2f bounding_box, Size2f template_size, int numberOfScales,
|
||||
float scaleStep, float maxModelArea, float sigmaFactor, float scaleLearnRate);
|
||||
~DSST();
|
||||
void update(const Mat &image, const Point2f objectCenter);
|
||||
float getScale(const Mat &image, const Point2f objecCenter);
|
||||
private:
|
||||
Mat get_scale_features(Mat img, Point2f pos, Size2f base_target_sz, float current_scale,
|
||||
std::vector<float> &scale_factors, Mat scale_window, Size scale_model_sz);
|
||||
|
||||
Size scale_model_sz;
|
||||
Mat ys;
|
||||
Mat ysf;
|
||||
Mat scale_window;
|
||||
std::vector<float> scale_factors;
|
||||
Mat sf_num;
|
||||
Mat sf_den;
|
||||
float scale_sigma;
|
||||
float min_scale_factor;
|
||||
float max_scale_factor;
|
||||
float current_scale_factor;
|
||||
int scales_count;
|
||||
float scale_step;
|
||||
float max_model_area;
|
||||
float sigma_factor;
|
||||
float learn_rate;
|
||||
|
||||
Size original_targ_sz;
|
||||
};
|
||||
|
||||
} /* namespace cv */
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,450 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
#include "trackerCSRTSegmentation.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
//-------------------- HISTOGRAM CLASS --------------------
|
||||
namespace cv
|
||||
{
|
||||
|
||||
Histogram::Histogram(int numDimensions, int numBinsPerDimension)
|
||||
{
|
||||
m_numBinsPerDim = numBinsPerDimension;
|
||||
m_numDim = numDimensions;
|
||||
p_size = cvFloor(std::pow(m_numBinsPerDim, m_numDim));
|
||||
p_bins.resize(p_size, 0);
|
||||
p_dimIdCoef.resize(m_numDim, 1);
|
||||
for (int i = 0; i < m_numDim-1; ++i)
|
||||
p_dimIdCoef[i] = static_cast<int>(std::pow(numBinsPerDimension, m_numDim - 1 - i));
|
||||
|
||||
}
|
||||
|
||||
void Histogram::extractForegroundHistogram(std::vector<cv::Mat> & imgChannels,
|
||||
cv::Mat weights, bool useMatWeights, int x1, int y1, int x2, int y2)
|
||||
{
|
||||
//just for code clarity
|
||||
cv::Mat & img = imgChannels[0];
|
||||
|
||||
if (!useMatWeights){
|
||||
//weights are epanechnikov distr. with peek at the center of the image;
|
||||
double cx = x1 + (x2-x1)/2.;
|
||||
double cy = y1 + (y2-y1)/2.;
|
||||
double kernelSize_width = 1.0/(0.5*static_cast<double>(x2-x1)*1.4142+1); //sqrt(2)
|
||||
double kernelSize_height = 1.0/(0.5*static_cast<double>(y2-y1)*1.4142+1);
|
||||
|
||||
cv::Mat kernelWeight(img.rows, img.cols, CV_64FC1);
|
||||
for (int y = y1; y < y2+1; ++y){
|
||||
double * weightPtr = kernelWeight.ptr<double>(y);
|
||||
double tmp_y = std::pow((cy-y)*kernelSize_height, 2);
|
||||
for (int x = x1; x < x2+1; ++x){
|
||||
weightPtr[x] = kernelProfile_Epanechnikov(std::pow((cx-x)*kernelSize_width,2) + tmp_y);
|
||||
}
|
||||
}
|
||||
weights = kernelWeight;
|
||||
}
|
||||
//extract pixel values and compute histogram
|
||||
double rangePerBinInverse = static_cast<double>(m_numBinsPerDim)/256.0; // 1 / (imgRange/numBinsPerDim)
|
||||
double sum = 0;
|
||||
for (int y = y1; y < y2+1; ++y){
|
||||
std::vector<const uchar *> dataPtr(m_numDim);
|
||||
for (int dim = 0; dim < m_numDim; ++dim)
|
||||
dataPtr[dim] = imgChannels[dim].ptr<uchar>(y);
|
||||
const double * weightPtr = weights.ptr<double>(y);
|
||||
|
||||
for (int x = x1; x < x2+1; ++x){
|
||||
int id = 0;
|
||||
for (int dim = 0; dim < m_numDim; ++dim){
|
||||
id += p_dimIdCoef[dim]*cvFloor(rangePerBinInverse*dataPtr[dim][x]);
|
||||
}
|
||||
p_bins[id] += weightPtr[x];
|
||||
sum += weightPtr[x];
|
||||
}
|
||||
}
|
||||
//normalize
|
||||
sum = 1./sum;
|
||||
for(int i = 0; i < p_size; ++i)
|
||||
p_bins[i] *= sum;
|
||||
}
|
||||
|
||||
void Histogram::extractBackGroundHistogram(
|
||||
std::vector<cv::Mat> & imgChannels,
|
||||
int x1, int y1, int x2, int y2,
|
||||
int outer_x1, int outer_y1, int outer_x2, int outer_y2)
|
||||
{
|
||||
//extract pixel values and compute histogram
|
||||
double rangePerBinInverse = static_cast<double>(m_numBinsPerDim)/256.0; // 1 / (imgRange/numBinsPerDim)
|
||||
double sum = 0;
|
||||
for (int y = outer_y1; y < outer_y2; ++y){
|
||||
|
||||
std::vector<const uchar *> dataPtr(m_numDim);
|
||||
for (int dim = 0; dim < m_numDim; ++dim)
|
||||
dataPtr[dim] = imgChannels[dim].ptr<uchar>(y);
|
||||
|
||||
for (int x = outer_x1; x < outer_x2; ++x){
|
||||
if (x >= x1 && x <= x2 && y >= y1 && y <= y2)
|
||||
continue;
|
||||
|
||||
int id = 0;
|
||||
for (int dim = 0; dim < m_numDim; ++dim){
|
||||
id += p_dimIdCoef[dim]*cvFloor(rangePerBinInverse*dataPtr[dim][x]);
|
||||
}
|
||||
p_bins[id] += 1.0;
|
||||
sum += 1.0;
|
||||
}
|
||||
}
|
||||
//normalize
|
||||
sum = 1./sum;
|
||||
for(int i = 0; i < p_size; ++i)
|
||||
p_bins[i] *= sum;
|
||||
}
|
||||
|
||||
cv::Mat Histogram::backProject(std::vector<cv::Mat> & imgChannels)
|
||||
{
|
||||
//just for code clarity
|
||||
cv::Mat & img = imgChannels[0];
|
||||
|
||||
cv::Mat backProject(img.rows, img.cols, CV_64FC1);
|
||||
double rangePerBinInverse = static_cast<double>(m_numBinsPerDim)/256.0; // 1 / (imgRange/numBinsPerDim)
|
||||
|
||||
for (int y = 0; y < img.rows; ++y){
|
||||
double * backProjectPtr = backProject.ptr<double>(y);
|
||||
std::vector<const uchar *> dataPtr(m_numDim);
|
||||
for (int dim = 0; dim < m_numDim; ++dim)
|
||||
dataPtr[dim] = imgChannels[dim].ptr<uchar>(y);
|
||||
|
||||
for (int x = 0; x < img.cols; ++x){
|
||||
int id = 0;
|
||||
for (int dim = 0; dim < m_numDim; ++dim){
|
||||
id += p_dimIdCoef[dim]*cvFloor(rangePerBinInverse*dataPtr[dim][x]);
|
||||
}
|
||||
backProjectPtr[x] = p_bins[id];
|
||||
}
|
||||
}
|
||||
return backProject;
|
||||
}
|
||||
|
||||
// add new methods
|
||||
std::vector<double> Histogram::getHistogramVector() {
|
||||
return p_bins;
|
||||
}
|
||||
|
||||
void Histogram::setHistogramVector(double *vector) {
|
||||
for (size_t i=0; i<p_bins.size(); i++) {
|
||||
p_bins[i] = vector[i];
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------- SEGMENT CLASS --------------------
|
||||
std::pair<cv::Mat, cv::Mat> Segment::computePosteriors(
|
||||
std::vector<cv::Mat> &imgChannels,
|
||||
int x1, int y1, int x2, int y2,
|
||||
cv::Mat weights, cv::Mat fgPrior, cv::Mat bgPrior,
|
||||
const Histogram &fgHistPrior, int numBinsPerChannel)
|
||||
{
|
||||
//preprocess and normalize all data
|
||||
CV_Assert(imgChannels.size() > 0);
|
||||
|
||||
//fit target to the image
|
||||
x1 = std::min(std::max(x1, 0), imgChannels[0].cols-1);
|
||||
y1 = std::min(std::max(y1, 0), imgChannels[0].rows-1);
|
||||
x2 = std::max(std::min(x2, imgChannels[0].cols-1), 0);
|
||||
y2 = std::max(std::min(y2, imgChannels[0].rows-1), 0);
|
||||
|
||||
//enlarge bbox by 1/3 of its size for background area
|
||||
int offsetX = (x2-x1)/3;
|
||||
int offsetY = (y2-y1)/3;
|
||||
int outer_y1 = std::max(0, (int)(y1-offsetY));
|
||||
int outer_y2 = std::min(imgChannels[0].rows, (int)(y2+offsetY+1));
|
||||
int outer_x1 = std::max(0, (int)(x1-offsetX));
|
||||
int outer_x2 = std::min(imgChannels[0].cols, (int)(x2+offsetX+1));
|
||||
|
||||
//extract histogram from original data -> more pixels better representation of distr. by histograms
|
||||
Histogram hist_target =
|
||||
(fgHistPrior.m_numBinsPerDim == numBinsPerChannel && (size_t)fgHistPrior.m_numDim == imgChannels.size())
|
||||
? fgHistPrior : Histogram(static_cast<int>(imgChannels.size()), numBinsPerChannel);
|
||||
Histogram hist_background(static_cast<int>(imgChannels.size()), numBinsPerChannel);
|
||||
if (weights.cols == 0)
|
||||
hist_target.extractForegroundHistogram(imgChannels, cv::Mat(), false, x1, y1, x2, y2);
|
||||
else
|
||||
hist_target.extractForegroundHistogram(imgChannels, weights, true, x1, y1, x2, y2);
|
||||
hist_background.extractBackGroundHistogram(imgChannels, x1, y1, x2, y2,
|
||||
outer_x1, outer_y1, outer_x2, outer_y2);
|
||||
|
||||
//compute resize factor so that the max area is 1000 (=avg. size ~ 32x32)
|
||||
double factor = sqrt(1000.0/((x2-x1)*(y2-y1)));
|
||||
if (factor > 1)
|
||||
factor = 1.0;
|
||||
cv::Size newSize(cvFloor((x2-x1)*factor), cvFloor((y2-y1)*factor));
|
||||
|
||||
//rescale input data
|
||||
cv::Rect roiRect_inner = cv::Rect(x1, y1, x2-x1, y2-y1);
|
||||
std::vector<cv::Mat> imgChannelsROI_inner(imgChannels.size());
|
||||
for (size_t i = 0; i < imgChannels.size(); ++i)
|
||||
cv::resize(imgChannels[i](roiRect_inner), imgChannelsROI_inner[i], newSize);
|
||||
|
||||
//initialize priors if there is no external source and rescale
|
||||
cv::Mat fgPriorScaled;
|
||||
if (fgPrior.cols == 0)
|
||||
fgPriorScaled = 0.5*cv::Mat::ones(newSize, CV_64FC1);
|
||||
else
|
||||
cv::resize(fgPrior(roiRect_inner), fgPriorScaled, newSize);
|
||||
cv::Mat bgPriorScaled;
|
||||
if (bgPrior.cols == 0)
|
||||
bgPriorScaled = 0.5*cv::Mat::ones(newSize, CV_64FC1);
|
||||
else
|
||||
cv::resize(bgPrior(roiRect_inner), bgPriorScaled, newSize);
|
||||
|
||||
//backproject pixels likelihood
|
||||
cv::Mat foregroundLikelihood = hist_target.backProject(imgChannelsROI_inner).mul(fgPriorScaled);
|
||||
cv::Mat backgroundLikelihood = hist_background.backProject(imgChannelsROI_inner).mul(bgPriorScaled);
|
||||
|
||||
double p_b = std::sqrt((std::pow(outer_x2-outer_x1, 2) + std::pow(outer_y2-outer_y1, 2)) /
|
||||
(std::pow(x2-x1, 2) + std::pow(y2-y1, 2))) ;
|
||||
double p_o = 1./(p_b + 1);
|
||||
|
||||
//convert likelihoods to posterior prob. (Bayes rule)
|
||||
cv::Mat prob_o(newSize, foregroundLikelihood.type());
|
||||
prob_o = p_o*foregroundLikelihood / (p_o*foregroundLikelihood + p_b*backgroundLikelihood);
|
||||
cv::Mat prob_b = 1.0 - prob_o;
|
||||
|
||||
std::pair<cv::Mat, cv::Mat> sizedProbs = getRegularizedSegmentation(prob_o, prob_b, fgPriorScaled, bgPriorScaled);
|
||||
|
||||
//resize probs to original size
|
||||
std::pair<cv::Mat, cv::Mat> probs;
|
||||
cv::resize(sizedProbs.first, probs.first, cv::Size(roiRect_inner.width, roiRect_inner.height));
|
||||
cv::resize(sizedProbs.second, probs.second, cv::Size(roiRect_inner.width, roiRect_inner.height));
|
||||
|
||||
return probs;
|
||||
}
|
||||
|
||||
std::pair<cv::Mat, cv::Mat> Segment::computePosteriors2(
|
||||
std::vector<cv::Mat> &imgChannels, int x1, int y1, int x2, int y2, double p_b,
|
||||
cv::Mat fgPrior, cv::Mat bgPrior, Histogram hist_target, Histogram hist_background)
|
||||
{
|
||||
//preprocess and normalize all data
|
||||
CV_Assert(imgChannels.size() > 0);
|
||||
|
||||
//fit target to the image
|
||||
x1 = std::min(std::max(x1, 0), imgChannels[0].cols-1);
|
||||
y1 = std::min(std::max(y1, 0), imgChannels[0].rows-1);
|
||||
x2 = std::max(std::min(x2, imgChannels[0].cols-1), 0);
|
||||
y2 = std::max(std::min(y2, imgChannels[0].rows-1), 0);
|
||||
|
||||
// calculate width and height of the region
|
||||
int w = x2 - x1 + 1;
|
||||
int h = y2 - y1 + 1;
|
||||
w = std::min(std::max(w, 1), imgChannels[0].cols);
|
||||
h = std::min(std::max(h, 1), imgChannels[0].rows);
|
||||
|
||||
//double p_o = 1./(p_b + 1);
|
||||
double p_o = 1. - p_b;
|
||||
|
||||
//compute resize factor so that the max area is 1000 (=avg. size ~ 32x32)
|
||||
double factor = sqrt(1000.0/(w*h));
|
||||
if (factor > 1)
|
||||
factor = 1.0;
|
||||
cv::Size newSize(cvFloor(w*factor), cvFloor(h*factor));
|
||||
|
||||
//rescale input data
|
||||
cv::Rect roiRect_inner = cv::Rect(x1, y1, w, h);
|
||||
std::vector<cv::Mat> imgChannelsROI_inner(imgChannels.size());
|
||||
for (size_t i = 0; i < imgChannels.size(); ++i)
|
||||
cv::resize(imgChannels[i](roiRect_inner), imgChannelsROI_inner[i], newSize);
|
||||
|
||||
//initialize priors if there is no external source and rescale
|
||||
cv::Mat fgPriorScaled;
|
||||
if (fgPrior.cols == 0)
|
||||
fgPriorScaled = 0.5*cv::Mat::ones(newSize, CV_64FC1);
|
||||
else
|
||||
cv::resize(fgPrior(roiRect_inner), fgPriorScaled, newSize);
|
||||
cv::Mat bgPriorScaled;
|
||||
if (bgPrior.cols == 0)
|
||||
bgPriorScaled = 0.5*cv::Mat::ones(newSize, CV_64FC1);
|
||||
else
|
||||
cv::resize(bgPrior(roiRect_inner), bgPriorScaled, newSize);
|
||||
|
||||
//backproject pixels likelihood
|
||||
cv::Mat foregroundLikelihood = hist_target.backProject(imgChannelsROI_inner).mul(fgPriorScaled);
|
||||
cv::Mat backgroundLikelihood = hist_background.backProject(imgChannelsROI_inner).mul(bgPriorScaled);
|
||||
|
||||
//convert likelihoods to posterior prob. (Bayes rule)
|
||||
cv::Mat prob_o(newSize, foregroundLikelihood.type());
|
||||
prob_o = p_o*foregroundLikelihood / (p_o*foregroundLikelihood + p_b*backgroundLikelihood);
|
||||
cv::Mat prob_b = 1.0 - prob_o;
|
||||
|
||||
std::pair<cv::Mat, cv::Mat> sizedProbs = getRegularizedSegmentation(prob_o, prob_b,
|
||||
fgPriorScaled, bgPriorScaled);
|
||||
//std::pair<cv::Mat, cv::Mat> sizedProbs = std::pair<cv::Mat, cv::Mat>(prob_o, prob_b);
|
||||
|
||||
//resize probs to original size
|
||||
std::pair<cv::Mat, cv::Mat> probs;
|
||||
cv::resize(sizedProbs.first, probs.first, cv::Size(roiRect_inner.width, roiRect_inner.height));
|
||||
cv::resize(sizedProbs.second, probs.second, cv::Size(roiRect_inner.width, roiRect_inner.height));
|
||||
|
||||
return probs;
|
||||
}
|
||||
|
||||
std::pair<cv::Mat, cv::Mat> Segment::computePosteriors2(std::vector<cv::Mat> &imgChannels,
|
||||
cv::Mat fgPrior, cv::Mat bgPrior, Histogram hist_target, Histogram hist_background)
|
||||
{
|
||||
//preprocess and normalize all data
|
||||
CV_Assert(imgChannels.size() > 0);
|
||||
|
||||
//fit target to the image
|
||||
int x1 = 0;
|
||||
int y1 = 0;
|
||||
int x2 = imgChannels[0].cols-1;
|
||||
int y2 = imgChannels[0].rows-1;
|
||||
|
||||
//compute resize factor so that we control the max area ~32^2
|
||||
double factor = sqrt(1000./((x2-x1)*(y2-y1)));
|
||||
//double factor = 1;
|
||||
if (factor > 1)
|
||||
factor = 1.0;
|
||||
cv::Size newSize(cvFloor((x2-x1)*factor), cvFloor((y2-y1)*factor));
|
||||
|
||||
//rescale input data
|
||||
cv::Rect roiRect_inner = cv::Rect(x1, y1, x2-x1+1, y2-y1+1);
|
||||
std::vector<cv::Mat> imgChannelsROI_inner(imgChannels.size());
|
||||
for (size_t i = 0; i < imgChannels.size(); ++i)
|
||||
cv::resize(imgChannels[i](roiRect_inner), imgChannelsROI_inner[i], newSize);
|
||||
|
||||
//initialize priors if there is no external source and rescale
|
||||
cv::Mat fgPriorScaled;
|
||||
if (fgPrior.cols == 0)
|
||||
fgPriorScaled = 0.5*cv::Mat::ones(newSize, CV_64FC1);
|
||||
else
|
||||
cv::resize(fgPrior(roiRect_inner), fgPriorScaled, newSize);
|
||||
|
||||
cv::Mat bgPriorScaled;
|
||||
if (bgPrior.cols == 0)
|
||||
bgPriorScaled = 0.5*cv::Mat::ones(newSize, CV_64FC1);
|
||||
else
|
||||
cv::resize(bgPrior(roiRect_inner), bgPriorScaled, newSize);
|
||||
|
||||
//backproject pixels likelihood
|
||||
cv::Mat foregroundLikelihood = hist_target.backProject(imgChannelsROI_inner).mul(fgPriorScaled);
|
||||
cv::Mat backgroundLikelihood = hist_background.backProject(imgChannelsROI_inner).mul(bgPriorScaled);
|
||||
|
||||
//prior for posterior, relative to the number of pixels in bg and fg
|
||||
double p_b = 5./3.;
|
||||
double p_o = 1./(p_b + 1);
|
||||
|
||||
//convert likelihoods to posterior prob. (Bayes rule)
|
||||
cv::Mat prob_o(newSize, foregroundLikelihood.type());
|
||||
prob_o = p_o*foregroundLikelihood / (p_o*foregroundLikelihood + p_b*backgroundLikelihood);
|
||||
cv::Mat prob_b = 1.0 - prob_o;
|
||||
|
||||
std::pair<cv::Mat, cv::Mat> sizedProbs = getRegularizedSegmentation(prob_o, prob_b, fgPriorScaled, bgPriorScaled);
|
||||
|
||||
//resize probs to original size
|
||||
std::pair<cv::Mat, cv::Mat> probs;
|
||||
cv::resize(sizedProbs.first, probs.first, cv::Size(roiRect_inner.width, roiRect_inner.height));
|
||||
cv::resize(sizedProbs.second, probs.second, cv::Size(roiRect_inner.width, roiRect_inner.height));
|
||||
|
||||
return probs;
|
||||
}
|
||||
|
||||
std::pair<cv::Mat, cv::Mat> Segment::getRegularizedSegmentation(
|
||||
cv::Mat &prob_o, cv::Mat &prob_b, cv::Mat & prior_o, cv::Mat & prior_b)
|
||||
{
|
||||
int hsize = cvFloor(std::max(1.0, (double)cvFloor(static_cast<double>(prob_b.cols)*3./50. + 0.5)));
|
||||
int lambdaSize = hsize*2+1;
|
||||
|
||||
//compute gaussian kernel
|
||||
cv::Mat lambda(lambdaSize, lambdaSize, CV_64FC1);
|
||||
double std2 = std::pow(hsize/3.0, 2);
|
||||
double sumLambda = 0.0;
|
||||
for (int y = -hsize; y < hsize + 1; ++y){
|
||||
double * lambdaPtr = lambda.ptr<double>(y+hsize);
|
||||
double tmp_y = y*y;
|
||||
for (int x = -hsize; x < hsize +1; ++x){
|
||||
double tmp_gauss = gaussian(x*x, tmp_y, std2);
|
||||
lambdaPtr[x+hsize] = tmp_gauss;
|
||||
sumLambda += tmp_gauss;
|
||||
}
|
||||
}
|
||||
sumLambda -= lambda.at<double>(hsize, hsize);
|
||||
//set center of kernel to 0
|
||||
lambda.at<double>(hsize, hsize) = 0.0;
|
||||
sumLambda = 1.0/sumLambda;
|
||||
//normalize kernel to sum to 1
|
||||
lambda = lambda*sumLambda;
|
||||
|
||||
//create lambda2 kernel
|
||||
cv::Mat lambda2 = lambda.clone();
|
||||
lambda2.at<double>(hsize, hsize) = 1.0;
|
||||
|
||||
double terminateThr = 1e-1;
|
||||
double logLike = std::numeric_limits<double>::max();
|
||||
int maxIter = 50;
|
||||
|
||||
//return values
|
||||
cv::Mat Qsum_o(prior_o.rows, prior_o.cols, prior_o.type());
|
||||
cv::Mat Qsum_b(prior_o.rows, prior_o.cols, prior_o.type());
|
||||
|
||||
//algorithm temporal
|
||||
cv::Mat Si_o(prior_o.rows, prior_o.cols, prior_o.type());
|
||||
cv::Mat Si_b(prior_o.rows, prior_o.cols, prior_o.type());
|
||||
cv::Mat Ssum_o(prior_o.rows, prior_o.cols, prior_o.type());
|
||||
cv::Mat Ssum_b(prior_o.rows, prior_o.cols, prior_o.type());
|
||||
cv::Mat Qi_o(prior_o.rows, prior_o.cols, prior_o.type());
|
||||
cv::Mat Qi_b(prior_o.rows, prior_o.cols, prior_o.type());
|
||||
cv::Mat logQo(prior_o.rows, prior_o.cols, prior_o.type());
|
||||
cv::Mat logQb(prior_o.rows, prior_o.cols, prior_o.type());
|
||||
|
||||
int i;
|
||||
for (i = 0; i < maxIter; ++i){
|
||||
//follows the equations from Kristan et al. ACCV2014 paper
|
||||
//"A graphical model for rapid obstacle image-map estimation from unmanned surface vehicles"
|
||||
cv::Mat P_Io = prior_o.mul(prob_o) + std::numeric_limits<double>::epsilon();
|
||||
cv::Mat P_Ib = prior_b.mul(prob_b) + std::numeric_limits<double>::epsilon();
|
||||
|
||||
cv::filter2D(prior_o, Si_o, -1, lambda, cv::Point(-1, -1), 0, cv::BORDER_REFLECT);
|
||||
cv::filter2D(prior_b, Si_b, -1, lambda, cv::Point(-1, -1), 0, cv::BORDER_REFLECT);
|
||||
Si_o = Si_o.mul(prior_o);
|
||||
Si_b = Si_b.mul(prior_b);
|
||||
cv::Mat normSi = 1.0/(Si_o + Si_b);
|
||||
Si_o = Si_o.mul(normSi);
|
||||
Si_b = Si_b.mul(normSi);
|
||||
cv::filter2D(Si_o, Ssum_o, -1, lambda2, cv::Point(-1, -1), 0, cv::BORDER_REFLECT);
|
||||
cv::filter2D(Si_b, Ssum_b, -1, lambda2, cv::Point(-1, -1), 0, cv::BORDER_REFLECT);
|
||||
|
||||
cv::filter2D(P_Io, Qi_o, -1, lambda, cv::Point(-1, -1), 0, cv::BORDER_REFLECT);
|
||||
cv::filter2D(P_Ib, Qi_b, -1, lambda, cv::Point(-1, -1), 0, cv::BORDER_REFLECT);
|
||||
Qi_o = Qi_o.mul(P_Io);
|
||||
Qi_b = Qi_b.mul(P_Ib);
|
||||
cv::Mat normQi = 1.0/(Qi_o + Qi_b);
|
||||
Qi_o = Qi_o.mul(normQi);
|
||||
Qi_b = Qi_b.mul(normQi);
|
||||
cv::filter2D(Qi_o, Qsum_o, -1, lambda2, cv::Point(-1, -1), 0, cv::BORDER_REFLECT);
|
||||
cv::filter2D(Qi_b, Qsum_b, -1, lambda2, cv::Point(-1, -1), 0, cv::BORDER_REFLECT);
|
||||
|
||||
prior_o = (Qsum_o + Ssum_o)*0.25;
|
||||
prior_b = (Qsum_b + Ssum_b)*0.25;
|
||||
cv::Mat normPI = 1.0/(prior_o + prior_b);
|
||||
prior_o = prior_o.mul(normPI);
|
||||
prior_b = prior_b.mul(normPI);
|
||||
|
||||
//converge ?
|
||||
cv::log(Qsum_o, logQo);
|
||||
cv::log(Qsum_b, logQb);
|
||||
cv::Scalar mean = cv::sum(logQo+logQb);
|
||||
double logLikeNew = -mean.val[0]/(2*Qsum_o.rows*Qsum_o.cols);
|
||||
if (std::abs(logLike - logLikeNew) < terminateThr)
|
||||
break;
|
||||
logLike = logLikeNew;
|
||||
}
|
||||
return std::pair<cv::Mat, cv::Mat>(Qsum_o, Qsum_b);
|
||||
}
|
||||
|
||||
} //cv namespace
|
||||
//---------------------------------------------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,59 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef OPENCV_TRACKER_CSRT_SEGMENTATION
|
||||
#define OPENCV_TRACKER_CSRT_SEGMENTATION
|
||||
|
||||
namespace cv
|
||||
{
|
||||
class Histogram
|
||||
{
|
||||
public:
|
||||
int m_numBinsPerDim;
|
||||
int m_numDim;
|
||||
|
||||
Histogram() : m_numBinsPerDim(0), m_numDim(0) {}
|
||||
Histogram(int numDimensions, int numBinsPerDimension = 8);
|
||||
void extractForegroundHistogram(std::vector<cv::Mat> & imgChannels,
|
||||
cv::Mat weights, bool useMatWeights, int x1, int y1, int x2, int y2);
|
||||
void extractBackGroundHistogram(std::vector<cv::Mat> & imgChannels,
|
||||
int x1, int y1, int x2, int y2, int outer_x1, int outer_y1,
|
||||
int outer_x2, int outer_y2);
|
||||
cv::Mat backProject(std::vector<cv::Mat> & imgChannels);
|
||||
std::vector<double> getHistogramVector();
|
||||
void setHistogramVector(double *vector);
|
||||
|
||||
private:
|
||||
int p_size;
|
||||
std::vector<double> p_bins;
|
||||
std::vector<int> p_dimIdCoef;
|
||||
|
||||
inline double kernelProfile_Epanechnikov(double x)
|
||||
{ return (x <= 1) ? (2.0/CV_PI)*(1-x) : 0; }
|
||||
};
|
||||
|
||||
|
||||
class Segment
|
||||
{
|
||||
public:
|
||||
static std::pair<cv::Mat, cv::Mat> computePosteriors(std::vector<cv::Mat> & imgChannels,
|
||||
int x1, int y1, int x2, int y2, cv::Mat weights, cv::Mat fgPrior,
|
||||
cv::Mat bgPrior, const Histogram &fgHistPrior, int numBinsPerChannel = 16);
|
||||
static std::pair<cv::Mat, cv::Mat> computePosteriors2(std::vector<cv::Mat> & imgChannels,
|
||||
int x1, int y1, int x2, int y2, double p_b, cv::Mat fgPrior,
|
||||
cv::Mat bgPrior, Histogram hist_target, Histogram hist_background);
|
||||
static std::pair<cv::Mat, cv::Mat> computePosteriors2(std::vector<cv::Mat> &imgChannels,
|
||||
cv::Mat fgPrior, cv::Mat bgPrior, Histogram hist_target, Histogram hist_background);
|
||||
|
||||
private:
|
||||
static std::pair<cv::Mat, cv::Mat> getRegularizedSegmentation(cv::Mat & prob_o,
|
||||
cv::Mat & prob_b, cv::Mat &prior_o, cv::Mat &prior_b);
|
||||
|
||||
inline static double gaussian(double x2, double y2, double std2){
|
||||
return exp(-(x2 + y2)/(2*std2))/(2*CV_PI*std2);
|
||||
}
|
||||
};
|
||||
|
||||
}//cv namespace
|
||||
#endif
|
||||
@@ -0,0 +1,563 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
#include "trackerCSRTUtils.hpp"
|
||||
|
||||
namespace cv {
|
||||
|
||||
Mat circshift(Mat matrix, int dx, int dy)
|
||||
{
|
||||
Mat matrix_out = matrix.clone();
|
||||
int idx_y = 0;
|
||||
int idx_x = 0;
|
||||
for(int i=0; i<matrix.rows; i++) {
|
||||
for(int j=0; j<matrix.cols; j++) {
|
||||
idx_y = modul(i+dy+1, matrix.rows);
|
||||
idx_x = modul(j+dx+1, matrix.cols);
|
||||
matrix_out.at<float>(idx_y, idx_x) = matrix.at<float>(i,j);
|
||||
}
|
||||
}
|
||||
return matrix_out;
|
||||
}
|
||||
|
||||
Mat gaussian_shaped_labels(const float sigma, const int w, const int h)
|
||||
{
|
||||
// create 2D Gaussian peak, convert to Fourier space and stores it into the yf
|
||||
Mat y = Mat::zeros(h, w, CV_32F);
|
||||
float w2 = static_cast<float>(cvFloor(w / 2));
|
||||
float h2 = static_cast<float>(cvFloor(h / 2));
|
||||
|
||||
// calculate for each pixel separatelly
|
||||
for(int i=0; i<y.rows; i++) {
|
||||
for(int j=0; j<y.cols; j++) {
|
||||
y.at<float>(i,j) = (float)exp((-0.5 / pow(sigma, 2)) * (pow((i+1-h2), 2) + pow((j+1-w2), 2)));
|
||||
}
|
||||
}
|
||||
// wrap-around with the circulat shifting
|
||||
y = circshift(y, -cvFloor(y.cols / 2), -cvFloor(y.rows / 2));
|
||||
Mat yf;
|
||||
dft(y, yf, DFT_COMPLEX_OUTPUT);
|
||||
return yf;
|
||||
}
|
||||
|
||||
std::vector<Mat> fourier_transform_features(const std::vector<Mat> &M)
|
||||
{
|
||||
std::vector<Mat> out(M.size());
|
||||
Mat channel;
|
||||
// iterate over channels and convert them to Fourier domain
|
||||
for(size_t k = 0; k < M.size(); k++) {
|
||||
M[k].convertTo(channel, CV_32F);
|
||||
dft(channel, channel, DFT_COMPLEX_OUTPUT);
|
||||
out[k] = (channel);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Mat divide_complex_matrices(const Mat &A, const Mat &B)
|
||||
{
|
||||
std::vector<Mat> va,vb;
|
||||
split(A, va);
|
||||
split(B, vb);
|
||||
|
||||
Mat a = va.at(0);
|
||||
Mat b = va.at(1);
|
||||
Mat c = vb.at(0);
|
||||
Mat d = vb.at(1);
|
||||
|
||||
Mat div = c.mul(c) + d.mul(d);
|
||||
Mat real_part = (a.mul(c) + b.mul(d));
|
||||
Mat im_part = (b.mul(c) - a.mul(d));
|
||||
divide(real_part, div, real_part);
|
||||
divide(im_part, div, im_part);
|
||||
|
||||
std::vector<Mat> tmp(2);
|
||||
tmp[0] = real_part;
|
||||
tmp[1] = im_part;
|
||||
Mat res;
|
||||
merge(tmp, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
Mat get_subwindow(
|
||||
const Mat &image,
|
||||
const Point2f center,
|
||||
const int w,
|
||||
const int h,
|
||||
Rect *valid_pixels)
|
||||
{
|
||||
int startx = cvFloor(center.x) + 1 - (cvFloor(w/2));
|
||||
int starty = cvFloor(center.y) + 1 - (cvFloor(h/2));
|
||||
Rect roi(startx, starty, w, h);
|
||||
int padding_left = 0, padding_right = 0, padding_top = 0, padding_bottom = 0;
|
||||
if(roi.x < 0) {
|
||||
padding_left = -roi.x;
|
||||
roi.x = 0;
|
||||
}
|
||||
if(roi.y < 0) {
|
||||
padding_top = -roi.y;
|
||||
roi.y = 0;
|
||||
}
|
||||
roi.width -= padding_left;
|
||||
roi.height-= padding_top;
|
||||
if(roi.x + roi.width >= image.cols) {
|
||||
padding_right = roi.x + roi.width - image.cols;
|
||||
roi.width = image.cols - roi.x;
|
||||
}
|
||||
if(roi.y + roi.height >= image.rows) {
|
||||
padding_bottom = roi.y + roi.height - image.rows;
|
||||
roi.height = image.rows - roi.y;
|
||||
}
|
||||
Mat subwin = image(roi).clone();
|
||||
copyMakeBorder(subwin, subwin, padding_top, padding_bottom, padding_left, padding_right, BORDER_REPLICATE);
|
||||
|
||||
if(valid_pixels != NULL) {
|
||||
*valid_pixels = Rect(padding_left, padding_top, roi.width, roi.height);
|
||||
}
|
||||
return subwin;
|
||||
}
|
||||
|
||||
float subpixel_peak(const Mat &response, const std::string &s, const Point2f &p)
|
||||
{
|
||||
int i_p0, i_p_l, i_p_r; // indexes in response
|
||||
float p0, p_l, p_r; // values in response
|
||||
|
||||
if(s.compare("vertical") == 0) {
|
||||
// neighbouring rows
|
||||
i_p0 = cvRound(p.y);
|
||||
i_p_l = modul(cvRound(p.y) - 1, response.rows);
|
||||
i_p_r = modul(cvRound(p.y) + 1, response.rows);
|
||||
int px = static_cast<int>(p.x);
|
||||
p0 = response.at<float>(i_p0, px);
|
||||
p_l = response.at<float>(i_p_l, px);
|
||||
p_r = response.at<float>(i_p_r, px);
|
||||
} else if(s.compare("horizontal") == 0) {
|
||||
// neighbouring cols
|
||||
i_p0 = cvRound(p.x);
|
||||
i_p_l = modul(cvRound(p.x) - 1, response.cols);
|
||||
i_p_r = modul(cvRound(p.x) + 1, response.cols);
|
||||
int py = static_cast<int>(p.y);
|
||||
p0 = response.at<float>(py, i_p0);
|
||||
p_l = response.at<float>(py, i_p_l);
|
||||
p_r = response.at<float>(py, i_p_r);
|
||||
} else {
|
||||
std::cout << "Warning: unknown subpixel peak direction!" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
float delta = 0.5f * (p_r - p_l) / (2*p0 - p_r - p_l);
|
||||
if(!std::isfinite(delta)) {
|
||||
delta = 0;
|
||||
}
|
||||
|
||||
return delta;
|
||||
}
|
||||
|
||||
inline float chebpoly(const int n, const float x)
|
||||
{
|
||||
float res;
|
||||
if (fabs(x) <= 1)
|
||||
res = cos(n*acos(x));
|
||||
else
|
||||
res = cosh(n*acosh(x));
|
||||
return res;
|
||||
}
|
||||
|
||||
static Mat chebwin(int N, const float atten)
|
||||
{
|
||||
Mat out(N , 1, CV_32FC1);
|
||||
int nn, i;
|
||||
float M, n, sum = 0, max=0;
|
||||
float tg = static_cast<float>(pow(10,atten/20.0f)); /* 1/r term [2], 10^gamma [2] */
|
||||
float x0 = cosh((1.0f/(N-1))*acosh(tg));
|
||||
M = (N-1)/2.0f;
|
||||
if(N%2==0)
|
||||
M = M + 0.5f; /* handle even length windows */
|
||||
for(nn=0; nn<(N/2+1); nn++) {
|
||||
n = nn-M;
|
||||
sum = 0;
|
||||
for(i=1; i<=M; i++){
|
||||
sum += chebpoly(N-1,x0*static_cast<float>(cos(CV_PI*i/N))) *
|
||||
static_cast<float>(cos(2.0f*n*CV_PI*i/N));
|
||||
}
|
||||
out.at<float>(nn,0) = tg + 2*sum;
|
||||
out.at<float>(N-nn-1,0) = out.at<float>(nn,0) ;
|
||||
if(out.at<float>(nn,0) > max)
|
||||
max = out.at<float>(nn,0);
|
||||
}
|
||||
for(nn=0; nn<N; nn++)
|
||||
out.at<float>(nn,0) /= max; /* normalize everything */
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
static double modified_bessel(int order, double x)
|
||||
{
|
||||
// sum m=0:inf 1/(m! * Gamma(m + order + 1)) * (x/2)^(2m + order)
|
||||
const double eps = 1e-13;
|
||||
double result = 0;
|
||||
double m = 0;
|
||||
double gamma = 1.0;
|
||||
for(int i = 2; i <= order; ++i)
|
||||
gamma *= i;
|
||||
double term = pow(x,order) / (pow(2,order) * gamma);
|
||||
|
||||
while(term > eps * result) {
|
||||
result += term;
|
||||
//calculate new term in series
|
||||
++m;
|
||||
term *= (x*x) / (4*m*(m+order));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Mat get_hann_win(Size sz)
|
||||
{
|
||||
Mat hann_rows = Mat::ones(sz.height, 1, CV_32F);
|
||||
Mat hann_cols = Mat::ones(1, sz.width, CV_32F);
|
||||
int NN = sz.height - 1;
|
||||
if(NN != 0) {
|
||||
for (int i = 0; i < hann_rows.rows; ++i) {
|
||||
hann_rows.at<float>(i,0) = (float)(1.0/2.0 * (1.0 - cos(2*CV_PI*i/NN)));
|
||||
}
|
||||
}
|
||||
NN = sz.width - 1;
|
||||
if(NN != 0) {
|
||||
for (int i = 0; i < hann_cols.cols; ++i) {
|
||||
hann_cols.at<float>(0,i) = (float)(1.0/2.0 * (1.0 - cos(2*CV_PI*i/NN)));
|
||||
}
|
||||
}
|
||||
return hann_rows * hann_cols;
|
||||
}
|
||||
|
||||
Mat get_kaiser_win(Size sz, float alpha)
|
||||
{
|
||||
Mat kaiser_rows = Mat::ones(sz.height, 1, CV_32F);
|
||||
Mat kaiser_cols = Mat::ones(1, sz.width, CV_32F);
|
||||
|
||||
int N = sz.height - 1;
|
||||
double shape = alpha;
|
||||
double den = 1.0 / modified_bessel(0, shape);
|
||||
|
||||
for(int n = 0; n <= N; ++n) {
|
||||
double K = (2.0 * n * 1.0/N) - 1.0;
|
||||
double x = sqrt(1.0 - (K * K));
|
||||
kaiser_rows.at<float>(n,0) = static_cast<float>(modified_bessel(0, shape * x) * den);
|
||||
}
|
||||
|
||||
N = sz.width - 1;
|
||||
for(int n = 0; n <= N; ++n) {
|
||||
double K = (2.0 * n * 1.0/N) - 1.0;
|
||||
double x = sqrt(1.0 - (K * K));
|
||||
kaiser_cols.at<float>(0,n) = static_cast<float>(modified_bessel(0, shape * x) * den);
|
||||
}
|
||||
|
||||
return kaiser_rows * kaiser_cols;
|
||||
}
|
||||
|
||||
Mat get_chebyshev_win(Size sz, float attenuation)
|
||||
{
|
||||
Mat cheb_rows = chebwin(sz.height, attenuation);
|
||||
Mat cheb_cols = chebwin(sz.width, attenuation).t();
|
||||
return cheb_rows * cheb_cols;
|
||||
}
|
||||
|
||||
static void computeHOG32D(const Mat &imageM, Mat &featM, const int sbin, const int pad_x, const int pad_y)
|
||||
{
|
||||
const int dimHOG = 32;
|
||||
CV_Assert(pad_x >= 0);
|
||||
CV_Assert(pad_y >= 0);
|
||||
CV_Assert(imageM.channels() == 3);
|
||||
CV_Assert(imageM.depth() == CV_64F);
|
||||
|
||||
// epsilon to avoid division by zero
|
||||
const double eps = 0.0001;
|
||||
// number of orientations
|
||||
const int numOrient = 18;
|
||||
// unit vectors to compute gradient orientation
|
||||
const double uu[9] = {1.000, 0.9397, 0.7660, 0.5000, 0.1736, -0.1736, -0.5000, -0.7660, -0.9397};
|
||||
const double vv[9] = {0.000, 0.3420, 0.6428, 0.8660, 0.9848, 0.9848, 0.8660, 0.6428, 0.3420};
|
||||
|
||||
// image size
|
||||
const Size imageSize = imageM.size();
|
||||
// block size
|
||||
// int bW = cvRound((double)imageSize.width/(double)sbin);
|
||||
// int bH = cvRound((double)imageSize.height/(double)sbin);
|
||||
int bW = cvFloor((double)imageSize.width/(double)sbin);
|
||||
int bH = cvFloor((double)imageSize.height/(double)sbin);
|
||||
const Size blockSize(bW, bH);
|
||||
// size of HOG features
|
||||
int oW = max(blockSize.width-2, 0) + 2*pad_x;
|
||||
int oH = max(blockSize.height-2, 0) + 2*pad_y;
|
||||
Size outSize = Size(oW, oH);
|
||||
// size of visible
|
||||
const Size visible = blockSize*sbin;
|
||||
|
||||
// initialize historgram, norm, output feature matrices
|
||||
Mat histM = Mat::zeros(Size(blockSize.width*numOrient, blockSize.height), CV_64F);
|
||||
Mat normM = Mat::zeros(Size(blockSize.width, blockSize.height), CV_64F);
|
||||
featM = Mat::zeros(Size(outSize.width*dimHOG, outSize.height), CV_64F);
|
||||
|
||||
// get the stride of each matrix
|
||||
const size_t imStride = imageM.step1();
|
||||
const size_t histStride = histM.step1();
|
||||
const size_t normStride = normM.step1();
|
||||
const size_t featStride = featM.step1();
|
||||
|
||||
// calculate the zero offset
|
||||
const double* im = imageM.ptr<double>(0);
|
||||
double* const hist = histM.ptr<double>(0);
|
||||
double* const norm = normM.ptr<double>(0);
|
||||
double* const feat = featM.ptr<double>(0);
|
||||
|
||||
for (int y = 1; y < visible.height - 1; y++)
|
||||
{
|
||||
for (int x = 1; x < visible.width - 1; x++)
|
||||
{
|
||||
// OpenCV uses an interleaved format: BGR-BGR-BGR
|
||||
const double* s = im + 3*min(x, imageM.cols-2) + min(y, imageM.rows-2)*imStride;
|
||||
|
||||
// blue image channel
|
||||
double dyb = *(s+imStride) - *(s-imStride);
|
||||
double dxb = *(s+3) - *(s-3);
|
||||
double vb = dxb*dxb + dyb*dyb;
|
||||
|
||||
// green image channel
|
||||
s += 1;
|
||||
double dyg = *(s+imStride) - *(s-imStride);
|
||||
double dxg = *(s+3) - *(s-3);
|
||||
double vg = dxg*dxg + dyg*dyg;
|
||||
|
||||
// red image channel
|
||||
s += 1;
|
||||
double dy = *(s+imStride) - *(s-imStride);
|
||||
double dx = *(s+3) - *(s-3);
|
||||
double v = dx*dx + dy*dy;
|
||||
|
||||
// pick the channel with the strongest gradient
|
||||
if (vg > v) { v = vg; dx = dxg; dy = dyg; }
|
||||
if (vb > v) { v = vb; dx = dxb; dy = dyb; }
|
||||
|
||||
// snap to one of the 18 orientations
|
||||
double best_dot = 0;
|
||||
int best_o = 0;
|
||||
for (int o = 0; o < (int)numOrient/2; o++)
|
||||
{
|
||||
double dot = uu[o]*dx + vv[o]*dy;
|
||||
if (dot > best_dot)
|
||||
{
|
||||
best_dot = dot;
|
||||
best_o = o;
|
||||
}
|
||||
else if (-dot > best_dot)
|
||||
{
|
||||
best_dot = -dot;
|
||||
best_o = o + (int)(numOrient/2);
|
||||
}
|
||||
}
|
||||
|
||||
// add to 4 historgrams around pixel using bilinear interpolation
|
||||
double yp = ((double)y+0.5)/(double)sbin - 0.5;
|
||||
double xp = ((double)x+0.5)/(double)sbin - 0.5;
|
||||
int iyp = (int)cvFloor(yp);
|
||||
int ixp = (int)cvFloor(xp);
|
||||
double vy0 = yp - iyp;
|
||||
double vx0 = xp - ixp;
|
||||
double vy1 = 1.0 - vy0;
|
||||
double vx1 = 1.0 - vx0;
|
||||
v = sqrt(v);
|
||||
|
||||
// fill the value into the 4 neighborhood cells
|
||||
if (iyp >= 0 && ixp >= 0)
|
||||
*(hist + iyp*histStride + ixp*numOrient + best_o) += vy1*vx1*v;
|
||||
|
||||
if (iyp >= 0 && ixp+1 < blockSize.width)
|
||||
*(hist + iyp*histStride + (ixp+1)*numOrient + best_o) += vx0*vy1*v;
|
||||
|
||||
if (iyp+1 < blockSize.height && ixp >= 0)
|
||||
*(hist + (iyp+1)*histStride + ixp*numOrient + best_o) += vy0*vx1*v;
|
||||
|
||||
if (iyp+1 < blockSize.height && ixp+1 < blockSize.width)
|
||||
*(hist + (iyp+1)*histStride + (ixp+1)*numOrient + best_o) += vy0*vx0*v;
|
||||
|
||||
} // for y
|
||||
} // for x
|
||||
|
||||
// compute the energy in each block by summing over orientation
|
||||
for (int y = 0; y < blockSize.height; y++)
|
||||
{
|
||||
const double* src = hist + y*histStride;
|
||||
double* dst = norm + y*normStride;
|
||||
double const* const dst_end = dst + blockSize.width;
|
||||
// for each cell
|
||||
while (dst < dst_end)
|
||||
{
|
||||
*dst = 0;
|
||||
for (int o = 0; o < (int)(numOrient/2); o++)
|
||||
{
|
||||
*dst += (*src + *(src + numOrient/2))*
|
||||
(*src + *(src + numOrient/2));
|
||||
src++;
|
||||
}
|
||||
dst++;
|
||||
src += numOrient/2;
|
||||
}
|
||||
}
|
||||
|
||||
// compute the features
|
||||
for (int y = pad_y; y < outSize.height - pad_y; y++)
|
||||
{
|
||||
for (int x = pad_x; x < outSize.width - pad_x; x++)
|
||||
{
|
||||
double* dst = feat + y*featStride + x*dimHOG;
|
||||
double* p, n1, n2, n3, n4;
|
||||
const double* src;
|
||||
|
||||
p = norm + (y - pad_y + 1)*normStride + (x - pad_x + 1);
|
||||
n1 = 1.0f / sqrt(*p + *(p + 1) + *(p + normStride) + *(p + normStride + 1) + eps);
|
||||
p = norm + (y - pad_y)*normStride + (x - pad_x + 1);
|
||||
n2 = 1.0f / sqrt(*p + *(p + 1) + *(p + normStride) + *(p + normStride + 1) + eps);
|
||||
p = norm + (y- pad_y + 1)*normStride + x - pad_x;
|
||||
n3 = 1.0f / sqrt(*p + *(p + 1) + *(p + normStride) + *(p + normStride + 1) + eps);
|
||||
p = norm + (y - pad_y)*normStride + x - pad_x;
|
||||
n4 = 1.0f / sqrt(*p + *(p + 1) + *(p + normStride) + *(p + normStride + 1) + eps);
|
||||
|
||||
double t1 = 0.0, t2 = 0.0, t3 = 0.0, t4 = 0.0;
|
||||
|
||||
// contrast-sesitive features
|
||||
src = hist + (y - pad_y + 1)*histStride + (x - pad_x + 1)*numOrient;
|
||||
for (int o = 0; o < numOrient; o++)
|
||||
{
|
||||
double val = *src;
|
||||
double h1 = min(val*n1, 0.2);
|
||||
double h2 = min(val*n2, 0.2);
|
||||
double h3 = min(val*n3, 0.2);
|
||||
double h4 = min(val*n4, 0.2);
|
||||
*(dst++) = 0.5 * (h1 + h2 + h3 + h4);
|
||||
|
||||
src++;
|
||||
t1 += h1;
|
||||
t2 += h2;
|
||||
t3 += h3;
|
||||
t4 += h4;
|
||||
}
|
||||
|
||||
// contrast-insensitive features
|
||||
src = hist + (y - pad_y + 1)*histStride + (x - pad_x + 1)*numOrient;
|
||||
for (int o = 0; o < numOrient/2; o++)
|
||||
{
|
||||
double sum = *src + *(src + numOrient/2);
|
||||
double h1 = min(sum * n1, 0.2);
|
||||
double h2 = min(sum * n2, 0.2);
|
||||
double h3 = min(sum * n3, 0.2);
|
||||
double h4 = min(sum * n4, 0.2);
|
||||
*(dst++) = 0.5 * (h1 + h2 + h3 + h4);
|
||||
src++;
|
||||
}
|
||||
|
||||
// texture features
|
||||
*(dst++) = 0.2357 * t1;
|
||||
*(dst++) = 0.2357 * t2;
|
||||
*(dst++) = 0.2357 * t3;
|
||||
*(dst++) = 0.2357 * t4;
|
||||
// truncation feature
|
||||
*dst = 0;
|
||||
}// for x
|
||||
}// for y
|
||||
// Truncation features
|
||||
for (int m = 0; m < featM.rows; m++)
|
||||
{
|
||||
for (int n = 0; n < featM.cols; n += dimHOG)
|
||||
{
|
||||
if (m > pad_y - 1 && m < featM.rows - pad_y && n > pad_x*dimHOG - 1 && n < featM.cols - pad_x*dimHOG)
|
||||
continue;
|
||||
|
||||
featM.at<double>(m, n + dimHOG - 1) = 1;
|
||||
} // for x
|
||||
}// for y
|
||||
}
|
||||
|
||||
std::vector<Mat> get_features_hog(const Mat &im, const int bin_size)
|
||||
{
|
||||
Mat hogmatrix;
|
||||
Mat im_;
|
||||
im.convertTo(im_, CV_64FC3, 1.0/255.0);
|
||||
computeHOG32D(im_,hogmatrix,bin_size,1,1);
|
||||
hogmatrix.convertTo(hogmatrix, CV_32F);
|
||||
Size hog_size = im.size();
|
||||
hog_size.width /= bin_size;
|
||||
hog_size.height /= bin_size;
|
||||
Mat hogc(hog_size, CV_32FC(32), hogmatrix.data);
|
||||
std::vector<Mat> features;
|
||||
split(hogc, features);
|
||||
return features;
|
||||
}
|
||||
|
||||
std::vector<Mat> get_features_cn(const Mat &ppatch_data, const Size &output_size) {
|
||||
Mat patch_data = ppatch_data.clone();
|
||||
Vec3b & pixel = patch_data.at<Vec3b>(0,0);
|
||||
unsigned index;
|
||||
|
||||
Mat cnFeatures = Mat::zeros(patch_data.rows,patch_data.cols,CV_32FC(10));
|
||||
|
||||
for(int i=0;i<patch_data.rows;i++){
|
||||
for(int j=0;j<patch_data.cols;j++){
|
||||
pixel=patch_data.at<Vec3b>(i,j);
|
||||
index=(unsigned)(cvFloor((float)pixel[2]/8)+32*cvFloor((float)pixel[1]/8)+32*32*cvFloor((float)pixel[0]/8));
|
||||
|
||||
//copy the values
|
||||
for(int k=0;k<10;k++){
|
||||
cnFeatures.at<Vec<float,10> >(i,j)[k]=(float)ColorNames[index][k];
|
||||
}
|
||||
}
|
||||
}
|
||||
std::vector<Mat> result;
|
||||
split(cnFeatures, result);
|
||||
for (size_t i = 0; i < result.size(); i++) {
|
||||
if (output_size.width > 0 && output_size.height > 0) {
|
||||
resize(result.at(i), result.at(i), output_size, INTER_CUBIC);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<Mat> get_features_rgb(const Mat &patch, const Size &output_size)
|
||||
{
|
||||
std::vector<Mat> channels;
|
||||
split(patch, channels);
|
||||
for(size_t k=0; k<channels.size(); k++) {
|
||||
channels[k].convertTo(channels[k], CV_32F, 1.0/255.0, -0.5);
|
||||
channels[k] = channels[k] - mean(channels[k])[0];
|
||||
resize(channels[k], channels[k], output_size, INTER_CUBIC);
|
||||
}
|
||||
return channels;
|
||||
}
|
||||
|
||||
double get_max(const Mat &m)
|
||||
{
|
||||
double val;
|
||||
minMaxLoc(m, NULL, &val, NULL, NULL);
|
||||
return val;
|
||||
}
|
||||
|
||||
double get_min(const Mat &m)
|
||||
{
|
||||
double val;
|
||||
minMaxLoc(m, &val, NULL, NULL, NULL);
|
||||
return val;
|
||||
}
|
||||
|
||||
Mat bgr2hsv(const Mat &img)
|
||||
{
|
||||
Mat hsv_img;
|
||||
cvtColor(img, hsv_img, COLOR_BGR2HSV);
|
||||
std::vector<Mat> hsv_img_channels;
|
||||
split(hsv_img, hsv_img_channels);
|
||||
hsv_img_channels.at(0).convertTo(hsv_img_channels.at(0), CV_8UC1, 255.0 / 180.0);
|
||||
merge(hsv_img_channels, hsv_img);
|
||||
return hsv_img;
|
||||
}
|
||||
|
||||
} //cv namespace
|
||||
@@ -0,0 +1,52 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef OPENCV_TRACKER_CSRT_UTILS
|
||||
#define OPENCV_TRACKER_CSRT_UTILS
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
inline int modul(int a, int b)
|
||||
{
|
||||
// function calculates the module of two numbers and it takes into account also negative numbers
|
||||
return ((a % b) + b) % b;
|
||||
}
|
||||
|
||||
inline double kernel_epan(double x)
|
||||
{
|
||||
return (x <= 1) ? (2.0/3.14)*(1-x) : 0;
|
||||
}
|
||||
|
||||
Mat circshift(Mat matrix, int dx, int dy);
|
||||
Mat gaussian_shaped_labels(const float sigma, const int w, const int h);
|
||||
std::vector<Mat> fourier_transform_features(const std::vector<Mat> &M);
|
||||
Mat divide_complex_matrices(const Mat &A, const Mat &B);
|
||||
Mat get_subwindow(const Mat &image, const Point2f center,
|
||||
const int w, const int h,Rect *valid_pixels = NULL);
|
||||
|
||||
float subpixel_peak(const Mat &response, const std::string &s, const Point2f &p);
|
||||
double get_max(const Mat &m);
|
||||
double get_min(const Mat &m);
|
||||
|
||||
Mat get_hann_win(Size sz);
|
||||
Mat get_kaiser_win(Size sz, float alpha);
|
||||
Mat get_chebyshev_win(Size sz, float attenuation);
|
||||
|
||||
std::vector<Mat> get_features_rgb(const Mat &patch, const Size &output_size);
|
||||
std::vector<Mat> get_features_hog(const Mat &im, const int bin_size);
|
||||
std::vector<Mat> get_features_cn(const Mat &im, const Size &output_size);
|
||||
|
||||
Mat bgr2hsv(const Mat &img);
|
||||
|
||||
} //cv namespace
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,317 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
/*
|
||||
* TrackerContribFeature
|
||||
*/
|
||||
|
||||
TrackerContribFeature::~TrackerContribFeature()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Ptr<TrackerContribFeature> TrackerContribFeature::create( const String& trackerFeatureType )
|
||||
{
|
||||
if( trackerFeatureType.find( "FEATURE2D" ) == 0 )
|
||||
{
|
||||
size_t firstSep = trackerFeatureType.find_first_of('.');
|
||||
size_t secondSep = trackerFeatureType.find_last_of('.');
|
||||
|
||||
String detector = trackerFeatureType.substr( firstSep, secondSep - firstSep );
|
||||
String descriptor = trackerFeatureType.substr( secondSep, trackerFeatureType.length() - secondSep );
|
||||
|
||||
return Ptr<TrackerFeatureFeature2d>( new TrackerFeatureFeature2d( detector, descriptor ) );
|
||||
}
|
||||
|
||||
if( trackerFeatureType.find( "HOG" ) == 0 )
|
||||
{
|
||||
return Ptr<TrackerFeatureHOG>( new TrackerFeatureHOG() );
|
||||
}
|
||||
|
||||
if( trackerFeatureType.find( "HAAR" ) == 0 )
|
||||
{
|
||||
return Ptr<TrackerContribFeatureHAAR>( new TrackerContribFeatureHAAR() );
|
||||
}
|
||||
|
||||
if( trackerFeatureType.find( "LBP" ) == 0 )
|
||||
{
|
||||
return Ptr<TrackerFeatureLBP>( new TrackerFeatureLBP() );
|
||||
}
|
||||
|
||||
CV_Error( cv::Error::StsError, "Tracker feature type not supported" );
|
||||
}
|
||||
|
||||
String TrackerContribFeature::getClassName() const
|
||||
{
|
||||
return className;
|
||||
}
|
||||
|
||||
/**
|
||||
* TrackerFeatureFeature2d
|
||||
*/
|
||||
TrackerFeatureFeature2d::TrackerFeatureFeature2d( String /*detectorType*/, String /*descriptorType*/)
|
||||
{
|
||||
className = "FEATURE2D";
|
||||
}
|
||||
|
||||
TrackerFeatureFeature2d::~TrackerFeatureFeature2d()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool TrackerFeatureFeature2d::computeImpl( const std::vector<Mat>& /*images*/, Mat& /*response*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void TrackerFeatureFeature2d::selection( Mat& /*response*/, int /*npoints*/)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* TrackerFeatureHOG
|
||||
*/
|
||||
TrackerFeatureHOG::TrackerFeatureHOG()
|
||||
{
|
||||
className = "HOG";
|
||||
}
|
||||
|
||||
TrackerFeatureHOG::~TrackerFeatureHOG()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool TrackerFeatureHOG::computeImpl( const std::vector<Mat>& /*images*/, Mat& /*response*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void TrackerFeatureHOG::selection( Mat& /*response*/, int /*npoints*/)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* TrackerContribFeatureHAAR
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parameters
|
||||
*/
|
||||
|
||||
TrackerContribFeatureHAAR::Params::Params()
|
||||
{
|
||||
numFeatures = 250;
|
||||
rectSize = Size( 100, 100 );
|
||||
isIntegral = false;
|
||||
}
|
||||
|
||||
TrackerContribFeatureHAAR::TrackerContribFeatureHAAR( const TrackerContribFeatureHAAR::Params ¶meters ) :
|
||||
params( parameters )
|
||||
{
|
||||
className = "HAAR";
|
||||
|
||||
CvHaarFeatureParams haarParams;
|
||||
haarParams.numFeatures = params.numFeatures;
|
||||
haarParams.isIntegral = params.isIntegral;
|
||||
featureEvaluator = CvFeatureEvaluator::create( CvFeatureParams::HAAR ).staticCast<CvHaarEvaluator>();
|
||||
featureEvaluator->init( &haarParams, 1, params.rectSize );
|
||||
}
|
||||
|
||||
TrackerContribFeatureHAAR::~TrackerContribFeatureHAAR()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CvHaarEvaluator::FeatureHaar& TrackerContribFeatureHAAR::getFeatureAt( int id )
|
||||
{
|
||||
return featureEvaluator->getFeatures( id );
|
||||
}
|
||||
|
||||
bool TrackerContribFeatureHAAR::swapFeature( int id, CvHaarEvaluator::FeatureHaar& feature )
|
||||
{
|
||||
featureEvaluator->getFeatures( id ) = feature;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TrackerContribFeatureHAAR::swapFeature( int source, int target )
|
||||
{
|
||||
CvHaarEvaluator::FeatureHaar feature = featureEvaluator->getFeatures( source );
|
||||
featureEvaluator->getFeatures( source ) = featureEvaluator->getFeatures( target );
|
||||
featureEvaluator->getFeatures( target ) = feature;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TrackerContribFeatureHAAR::extractSelected( const std::vector<int> selFeatures, const std::vector<Mat>& images, Mat& response )
|
||||
{
|
||||
if( images.empty() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int numFeatures = featureEvaluator->getNumFeatures();
|
||||
int numSelFeatures = (int)selFeatures.size();
|
||||
|
||||
//response = Mat_<float>( Size( images.size(), numFeatures ) );
|
||||
response.create( Size( (int)images.size(), numFeatures ), CV_32F );
|
||||
response.setTo( 0 );
|
||||
|
||||
//double t = getTickCount();
|
||||
//for each sample compute #n_feature -> put each feature (n Rect) in response
|
||||
for ( size_t i = 0; i < images.size(); i++ )
|
||||
{
|
||||
int c = images[i].cols;
|
||||
int r = images[i].rows;
|
||||
for ( int j = 0; j < numSelFeatures; j++ )
|
||||
{
|
||||
float res = 0;
|
||||
//const feat
|
||||
CvHaarEvaluator::FeatureHaar& feature = featureEvaluator->getFeatures( selFeatures[j] );
|
||||
feature.eval( images[i], Rect( 0, 0, c, r ), &res );
|
||||
//( Mat_<float>( response ) )( j, i ) = res;
|
||||
response.at<float>( selFeatures[j], (int)i ) = res;
|
||||
}
|
||||
}
|
||||
//t = ( (double) getTickCount() - t ) / getTickFrequency();
|
||||
//std::cout << "StrongClassifierDirectSelection time " << t << std::endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class Parallel_compute : public cv::ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
Ptr<CvHaarEvaluator> featureEvaluator;
|
||||
std::vector<Mat> images;
|
||||
Mat response;
|
||||
//std::vector<CvHaarEvaluator::FeatureHaar> features;
|
||||
public:
|
||||
Parallel_compute( Ptr<CvHaarEvaluator>& fe, const std::vector<Mat>& img, Mat& resp ) :
|
||||
featureEvaluator( fe ),
|
||||
images( img ),
|
||||
response( resp )
|
||||
{
|
||||
|
||||
//features = featureEvaluator->getFeatures();
|
||||
}
|
||||
|
||||
virtual void operator()( const cv::Range &r ) const CV_OVERRIDE
|
||||
{
|
||||
for ( int jf = r.start; jf != r.end; ++jf )
|
||||
{
|
||||
int cols = images[jf].cols;
|
||||
int rows = images[jf].rows;
|
||||
for ( int j = 0; j < featureEvaluator->getNumFeatures(); j++ )
|
||||
{
|
||||
float res = 0;
|
||||
featureEvaluator->getFeatures()[j].eval( images[jf], Rect( 0, 0, cols, rows ), &res );
|
||||
( Mat_<float>( response ) )( j, jf ) = res;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool TrackerContribFeatureHAAR::computeImpl( const std::vector<Mat>& images, Mat& response )
|
||||
{
|
||||
if( images.empty() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int numFeatures = featureEvaluator->getNumFeatures();
|
||||
|
||||
response = Mat_<float>( Size( (int)images.size(), numFeatures ) );
|
||||
|
||||
std::vector<CvHaarEvaluator::FeatureHaar> f = featureEvaluator->getFeatures();
|
||||
//for each sample compute #n_feature -> put each feature (n Rect) in response
|
||||
parallel_for_( Range( 0, (int)images.size() ), Parallel_compute( featureEvaluator, images, response ) );
|
||||
|
||||
/*for ( size_t i = 0; i < images.size(); i++ )
|
||||
{
|
||||
int c = images[i].cols;
|
||||
int r = images[i].rows;
|
||||
for ( int j = 0; j < numFeatures; j++ )
|
||||
{
|
||||
float res = 0;
|
||||
featureEvaluator->getFeatures( j ).eval( images[i], Rect( 0, 0, c, r ), &res );
|
||||
( Mat_<float>( response ) )( j, i ) = res;
|
||||
}
|
||||
}*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TrackerContribFeatureHAAR::selection( Mat& /*response*/, int /*npoints*/)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* TrackerFeatureLBP
|
||||
*/
|
||||
TrackerFeatureLBP::TrackerFeatureLBP()
|
||||
{
|
||||
className = "LBP";
|
||||
}
|
||||
|
||||
TrackerFeatureLBP::~TrackerFeatureLBP()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool TrackerFeatureLBP::computeImpl( const std::vector<Mat>& /*images*/, Mat& /*response*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void TrackerFeatureLBP::selection( Mat& /*response*/, int /*npoints*/)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}}} // namespace
|
||||
@@ -0,0 +1,144 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
/*
|
||||
* TrackerContribFeatureSet
|
||||
*/
|
||||
|
||||
/*
|
||||
* Constructor
|
||||
*/
|
||||
TrackerContribFeatureSet::TrackerContribFeatureSet()
|
||||
{
|
||||
blockAddTrackerFeature = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destructor
|
||||
*/
|
||||
TrackerContribFeatureSet::~TrackerContribFeatureSet()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TrackerContribFeatureSet::extraction( const std::vector<Mat>& images )
|
||||
{
|
||||
|
||||
clearResponses();
|
||||
responses.resize( features.size() );
|
||||
|
||||
for ( size_t i = 0; i < features.size(); i++ )
|
||||
{
|
||||
Mat response;
|
||||
features[i].second->compute( images, response );
|
||||
responses[i] = response;
|
||||
}
|
||||
|
||||
if( !blockAddTrackerFeature )
|
||||
{
|
||||
blockAddTrackerFeature = true;
|
||||
}
|
||||
}
|
||||
|
||||
void TrackerContribFeatureSet::selection()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TrackerContribFeatureSet::removeOutliers()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool TrackerContribFeatureSet::addTrackerFeature( String trackerFeatureType )
|
||||
{
|
||||
if( blockAddTrackerFeature )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Ptr<TrackerContribFeature> feature = TrackerContribFeature::create( trackerFeatureType );
|
||||
|
||||
if (!feature)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
features.push_back( std::make_pair( trackerFeatureType, feature ) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TrackerContribFeatureSet::addTrackerFeature( Ptr<TrackerContribFeature>& feature )
|
||||
{
|
||||
if( blockAddTrackerFeature )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
String trackerFeatureType = feature->getClassName();
|
||||
features.push_back( std::make_pair( trackerFeatureType, feature ) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<std::pair<String, Ptr<TrackerContribFeature> > >& TrackerContribFeatureSet::getTrackerFeature() const
|
||||
{
|
||||
return features;
|
||||
}
|
||||
|
||||
const std::vector<Mat>& TrackerContribFeatureSet::getResponses() const
|
||||
{
|
||||
return responses;
|
||||
}
|
||||
|
||||
void TrackerContribFeatureSet::clearResponses()
|
||||
{
|
||||
responses.clear();
|
||||
}
|
||||
|
||||
|
||||
}}} // namespace
|
||||
@@ -0,0 +1,938 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
#include "opencl_kernels_tracking.hpp"
|
||||
#include <complex>
|
||||
#include <cmath>
|
||||
|
||||
namespace cv {
|
||||
inline namespace tracking {
|
||||
namespace impl {
|
||||
|
||||
/*---------------------------
|
||||
| TrackerKCFModel
|
||||
|---------------------------*/
|
||||
/**
|
||||
* \brief Implementation of TrackerModel for KCF algorithm
|
||||
*/
|
||||
class TrackerKCFModel : public TrackerModel{
|
||||
public:
|
||||
TrackerKCFModel(){}
|
||||
~TrackerKCFModel(){}
|
||||
protected:
|
||||
void modelEstimationImpl( const std::vector<Mat>& /*responses*/ ) CV_OVERRIDE {}
|
||||
void modelUpdateImpl() CV_OVERRIDE {}
|
||||
};
|
||||
|
||||
|
||||
/*---------------------------
|
||||
| TrackerKCF
|
||||
|---------------------------*/
|
||||
/*
|
||||
* Prototype
|
||||
*/
|
||||
class TrackerKCFImpl CV_FINAL : public TrackerKCF
|
||||
{
|
||||
public:
|
||||
TrackerKCFImpl(const TrackerKCF::Params ¶meters);
|
||||
|
||||
virtual void init(InputArray image, const Rect& boundingBox) CV_OVERRIDE;
|
||||
virtual bool update(InputArray image, Rect& boundingBox) CV_OVERRIDE;
|
||||
void setFeatureExtractor(void (*f)(const Mat, const Rect, Mat&), bool pca_func = false) CV_OVERRIDE;
|
||||
|
||||
TrackerKCF::Params params;
|
||||
Ptr<TrackerKCFModel> model;
|
||||
|
||||
protected:
|
||||
void createHanningWindow(OutputArray dest, const cv::Size winSize, const int type) const;
|
||||
void inline fft2(const Mat src, std::vector<Mat> & dest, std::vector<Mat> & layers_data) const;
|
||||
void inline fft2(const Mat src, Mat & dest) const;
|
||||
void inline ifft2(const Mat src, Mat & dest) const;
|
||||
void inline pixelWiseMult(const std::vector<Mat> src1, const std::vector<Mat> src2, std::vector<Mat> & dest, const int flags, const bool conjB=false) const;
|
||||
void inline sumChannels(std::vector<Mat> src, Mat & dest) const;
|
||||
void inline updateProjectionMatrix(const Mat src, Mat & old_cov,Mat & proj_matrix,float pca_rate, int compressed_sz,
|
||||
std::vector<Mat> & layers_pca,std::vector<Scalar> & average, Mat pca_data, Mat new_cov, Mat w, Mat u, Mat v);
|
||||
void inline compress(const Mat proj_matrix, const Mat src, Mat & dest, Mat & data, Mat & compressed) const;
|
||||
bool getSubWindow(const Mat img, const Rect roi, Mat& feat, Mat& patch, TrackerKCF::MODE desc = GRAY) const;
|
||||
bool getSubWindow(const Mat img, const Rect roi, Mat& feat, void (*f)(const Mat, const Rect, Mat& )) const;
|
||||
void extractCN(Mat patch_data, Mat & cnFeatures) const;
|
||||
void denseGaussKernel(const float sigma, const Mat , const Mat y_data, Mat & k_data,
|
||||
std::vector<Mat> & layers_data,std::vector<Mat> & xf_data,std::vector<Mat> & yf_data, std::vector<Mat> xyf_v, Mat xy, Mat xyf ) const;
|
||||
void calcResponse(const Mat alphaf_data, const Mat kf_data, Mat & response_data, Mat & spec_data) const;
|
||||
void calcResponse(const Mat alphaf_data, const Mat alphaf_den_data, const Mat kf_data, Mat & response_data, Mat & spec_data, Mat & spec2_data) const;
|
||||
|
||||
void shiftRows(Mat& mat) const;
|
||||
void shiftRows(Mat& mat, int n) const;
|
||||
void shiftCols(Mat& mat, int n) const;
|
||||
#ifdef HAVE_OPENCL
|
||||
bool inline oclTransposeMM(const Mat src, float alpha, UMat &dst);
|
||||
#endif
|
||||
|
||||
private:
|
||||
float output_sigma;
|
||||
Rect2d roi;
|
||||
Mat hann; //hann window filter
|
||||
Mat hann_cn; //10 dimensional hann-window filter for CN features,
|
||||
|
||||
Mat y,yf; // training response and its FFT
|
||||
Mat x; // observation and its FFT
|
||||
Mat k,kf; // dense gaussian kernel and its FFT
|
||||
Mat kf_lambda; // kf+lambda
|
||||
Mat new_alphaf, alphaf; // training coefficients
|
||||
Mat new_alphaf_den, alphaf_den; // for splitted training coefficients
|
||||
Mat z; // model
|
||||
Mat response; // detection result
|
||||
Mat old_cov_mtx, proj_mtx; // for feature compression
|
||||
|
||||
// pre-defined Mat variables for optimization of private functions
|
||||
Mat spec, spec2;
|
||||
std::vector<Mat> layers;
|
||||
std::vector<Mat> vxf,vyf,vxyf;
|
||||
Mat xy_data,xyf_data;
|
||||
Mat data_temp, compress_data;
|
||||
std::vector<Mat> layers_pca_data;
|
||||
std::vector<Scalar> average_data;
|
||||
Mat img_Patch;
|
||||
|
||||
// storage for the extracted features, KRLS model, KRLS compressed model
|
||||
Mat X[2],Z[2],Zc[2];
|
||||
|
||||
// storage of the extracted features
|
||||
std::vector<Mat> features_pca;
|
||||
std::vector<Mat> features_npca;
|
||||
std::vector<MODE> descriptors_pca;
|
||||
std::vector<MODE> descriptors_npca;
|
||||
|
||||
// optimization variables for updateProjectionMatrix
|
||||
Mat data_pca, new_covar,w_data,u_data,vt_data;
|
||||
|
||||
// custom feature extractor
|
||||
bool use_custom_extractor_pca;
|
||||
bool use_custom_extractor_npca;
|
||||
std::vector<void(*)(const Mat img, const Rect roi, Mat& output)> extractor_pca;
|
||||
std::vector<void(*)(const Mat img, const Rect roi, Mat& output)> extractor_npca;
|
||||
|
||||
bool resizeImage; // resize the image whenever needed and the patch size is large
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
ocl::Kernel transpose_mm_ker; // OCL kernel to compute transpose matrix multiply matrix.
|
||||
#endif
|
||||
|
||||
int frame;
|
||||
};
|
||||
|
||||
/*
|
||||
* Constructor
|
||||
*/
|
||||
TrackerKCFImpl::TrackerKCFImpl( const TrackerKCF::Params ¶meters ) :
|
||||
params( parameters )
|
||||
{
|
||||
resizeImage = false;
|
||||
use_custom_extractor_pca = false;
|
||||
use_custom_extractor_npca = false;
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
// For update proj matrix's multiplication
|
||||
if(ocl::useOpenCL())
|
||||
{
|
||||
cv::String err;
|
||||
ocl::ProgramSource tmmSrc = ocl::tracking::tmm_oclsrc;
|
||||
ocl::Program tmmProg(tmmSrc, String(), err);
|
||||
transpose_mm_ker.create("tmm", tmmProg);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialization:
|
||||
* - creating hann window filter
|
||||
* - ROI padding
|
||||
* - creating a gaussian response for the training ground-truth
|
||||
* - perform FFT to the gaussian response
|
||||
*/
|
||||
void TrackerKCFImpl::init(InputArray image, const Rect& boundingBox)
|
||||
{
|
||||
frame=0;
|
||||
roi.x = cvRound(boundingBox.x);
|
||||
roi.y = cvRound(boundingBox.y);
|
||||
roi.width = cvRound(boundingBox.width);
|
||||
roi.height = cvRound(boundingBox.height);
|
||||
|
||||
//calclulate output sigma
|
||||
output_sigma=std::sqrt(static_cast<float>(roi.width*roi.height))*params.output_sigma_factor;
|
||||
output_sigma=-0.5f/(output_sigma*output_sigma);
|
||||
|
||||
//resize the ROI whenever needed
|
||||
if(params.resize && roi.width*roi.height>params.max_patch_size){
|
||||
resizeImage=true;
|
||||
roi.x/=2.0;
|
||||
roi.y/=2.0;
|
||||
roi.width/=2.0;
|
||||
roi.height/=2.0;
|
||||
}
|
||||
|
||||
// add padding to the roi
|
||||
roi.x-=roi.width/2;
|
||||
roi.y-=roi.height/2;
|
||||
roi.width*=2;
|
||||
roi.height*=2;
|
||||
|
||||
// initialize the hann window filter
|
||||
createHanningWindow(hann, roi.size(), CV_32F);
|
||||
|
||||
// hann window filter for CN feature
|
||||
Mat _layer[] = {hann, hann, hann, hann, hann, hann, hann, hann, hann, hann};
|
||||
merge(_layer, 10, hann_cn);
|
||||
|
||||
// create gaussian response
|
||||
y=Mat::zeros((int)roi.height,(int)roi.width,CV_32F);
|
||||
for(int i=0;i<int(roi.height);i++){
|
||||
for(int j=0;j<int(roi.width);j++){
|
||||
y.at<float>(i,j) =
|
||||
static_cast<float>((i-roi.height/2+1)*(i-roi.height/2+1)+(j-roi.width/2+1)*(j-roi.width/2+1));
|
||||
}
|
||||
}
|
||||
|
||||
y*=(float)output_sigma;
|
||||
cv::exp(y,y);
|
||||
|
||||
// perform fourier transfor to the gaussian response
|
||||
fft2(y,yf);
|
||||
|
||||
if (image.channels() == 1) { // disable CN for grayscale images
|
||||
params.desc_pca &= ~(CN);
|
||||
params.desc_npca &= ~(CN);
|
||||
}
|
||||
model = makePtr<TrackerKCFModel>();
|
||||
|
||||
// record the non-compressed descriptors
|
||||
if((params.desc_npca & GRAY) == GRAY)descriptors_npca.push_back(GRAY);
|
||||
if((params.desc_npca & CN) == CN)descriptors_npca.push_back(CN);
|
||||
if(use_custom_extractor_npca)descriptors_npca.push_back(CUSTOM);
|
||||
features_npca.resize(descriptors_npca.size());
|
||||
|
||||
// record the compressed descriptors
|
||||
if((params.desc_pca & GRAY) == GRAY)descriptors_pca.push_back(GRAY);
|
||||
if((params.desc_pca & CN) == CN)descriptors_pca.push_back(CN);
|
||||
if(use_custom_extractor_pca)descriptors_pca.push_back(CUSTOM);
|
||||
features_pca.resize(descriptors_pca.size());
|
||||
|
||||
// accept only the available descriptor modes
|
||||
CV_Assert(
|
||||
(params.desc_pca & GRAY) == GRAY
|
||||
|| (params.desc_npca & GRAY) == GRAY
|
||||
|| (params.desc_pca & CN) == CN
|
||||
|| (params.desc_npca & CN) == CN
|
||||
|| use_custom_extractor_pca
|
||||
|| use_custom_extractor_npca
|
||||
);
|
||||
|
||||
// ensure roi has intersection with the image
|
||||
Rect2d image_roi(0, 0,
|
||||
image.cols() / (resizeImage ? 2 : 1),
|
||||
image.rows() / (resizeImage ? 2 : 1));
|
||||
CV_Assert(!(roi & image_roi).empty());
|
||||
}
|
||||
|
||||
/*
|
||||
* Main part of the KCF algorithm
|
||||
*/
|
||||
bool TrackerKCFImpl::update(InputArray image, Rect& boundingBoxResult)
|
||||
{
|
||||
double minVal, maxVal; // min-max response
|
||||
Point minLoc,maxLoc; // min-max location
|
||||
|
||||
CV_Assert(image.channels() == 1 || image.channels() == 3);
|
||||
|
||||
Mat img;
|
||||
// resize the image whenever needed
|
||||
if (resizeImage)
|
||||
resize(image, img, Size(image.cols()/2, image.rows()/2), 0, 0, INTER_LINEAR_EXACT);
|
||||
else
|
||||
image.copyTo(img);
|
||||
|
||||
// detection part
|
||||
if(frame>0){
|
||||
|
||||
// extract and pre-process the patch
|
||||
// get non compressed descriptors
|
||||
for(unsigned i=0;i<descriptors_npca.size()-extractor_npca.size();i++){
|
||||
if(!getSubWindow(img,roi, features_npca[i], img_Patch, descriptors_npca[i]))return false;
|
||||
}
|
||||
//get non-compressed custom descriptors
|
||||
for(unsigned i=0,j=(unsigned)(descriptors_npca.size()-extractor_npca.size());i<extractor_npca.size();i++,j++){
|
||||
if(!getSubWindow(img,roi, features_npca[j], extractor_npca[i]))return false;
|
||||
}
|
||||
if(features_npca.size()>0)merge(features_npca,X[1]);
|
||||
|
||||
// get compressed descriptors
|
||||
for(unsigned i=0;i<descriptors_pca.size()-extractor_pca.size();i++){
|
||||
if(!getSubWindow(img,roi, features_pca[i], img_Patch, descriptors_pca[i]))return false;
|
||||
}
|
||||
//get compressed custom descriptors
|
||||
for(unsigned i=0,j=(unsigned)(descriptors_pca.size()-extractor_pca.size());i<extractor_pca.size();i++,j++){
|
||||
if(!getSubWindow(img,roi, features_pca[j], extractor_pca[i]))return false;
|
||||
}
|
||||
if(features_pca.size()>0)merge(features_pca,X[0]);
|
||||
|
||||
//compress the features and the KRSL model
|
||||
if(params.desc_pca !=0){
|
||||
compress(proj_mtx,X[0],X[0],data_temp,compress_data);
|
||||
compress(proj_mtx,Z[0],Zc[0],data_temp,compress_data);
|
||||
}
|
||||
|
||||
// copy the compressed KRLS model
|
||||
Zc[1] = Z[1];
|
||||
|
||||
// merge all features
|
||||
if(features_npca.size()==0){
|
||||
x = X[0];
|
||||
z = Zc[0];
|
||||
}else if(features_pca.size()==0){
|
||||
x = X[1];
|
||||
z = Z[1];
|
||||
}else{
|
||||
merge(X,2,x);
|
||||
merge(Zc,2,z);
|
||||
}
|
||||
|
||||
//compute the gaussian kernel
|
||||
denseGaussKernel(params.sigma,x,z,k,layers,vxf,vyf,vxyf,xy_data,xyf_data);
|
||||
|
||||
// compute the fourier transform of the kernel
|
||||
fft2(k,kf);
|
||||
if(frame==1)spec2=Mat_<Vec2f >(kf.rows, kf.cols);
|
||||
|
||||
// calculate filter response
|
||||
if(params.split_coeff)
|
||||
calcResponse(alphaf,alphaf_den,kf,response, spec, spec2);
|
||||
else
|
||||
calcResponse(alphaf,kf,response, spec);
|
||||
|
||||
// extract the maximum response
|
||||
minMaxLoc( response, &minVal, &maxVal, &minLoc, &maxLoc );
|
||||
if (maxVal < params.detect_thresh)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
roi.x+=(maxLoc.x-roi.width/2+1);
|
||||
roi.y+=(maxLoc.y-roi.height/2+1);
|
||||
}
|
||||
|
||||
// update the bounding box
|
||||
Rect2d boundingBox;
|
||||
boundingBox.x=(resizeImage?roi.x*2:roi.x)+(resizeImage?roi.width*2:roi.width)/4;
|
||||
boundingBox.y=(resizeImage?roi.y*2:roi.y)+(resizeImage?roi.height*2:roi.height)/4;
|
||||
boundingBox.width = (resizeImage?roi.width*2:roi.width)/2;
|
||||
boundingBox.height = (resizeImage?roi.height*2:roi.height)/2;
|
||||
|
||||
// extract the patch for learning purpose
|
||||
// get non compressed descriptors
|
||||
for(unsigned i=0;i<descriptors_npca.size()-extractor_npca.size();i++){
|
||||
if(!getSubWindow(img,roi, features_npca[i], img_Patch, descriptors_npca[i]))return false;
|
||||
}
|
||||
//get non-compressed custom descriptors
|
||||
for(unsigned i=0,j=(unsigned)(descriptors_npca.size()-extractor_npca.size());i<extractor_npca.size();i++,j++){
|
||||
if(!getSubWindow(img,roi, features_npca[j], extractor_npca[i]))return false;
|
||||
}
|
||||
if(features_npca.size()>0)merge(features_npca,X[1]);
|
||||
|
||||
// get compressed descriptors
|
||||
for(unsigned i=0;i<descriptors_pca.size()-extractor_pca.size();i++){
|
||||
if(!getSubWindow(img,roi, features_pca[i], img_Patch, descriptors_pca[i]))return false;
|
||||
}
|
||||
//get compressed custom descriptors
|
||||
for(unsigned i=0,j=(unsigned)(descriptors_pca.size()-extractor_pca.size());i<extractor_pca.size();i++,j++){
|
||||
if(!getSubWindow(img,roi, features_pca[j], extractor_pca[i]))return false;
|
||||
}
|
||||
if(features_pca.size()>0)merge(features_pca,X[0]);
|
||||
|
||||
//update the training data
|
||||
if(frame==0){
|
||||
Z[0] = X[0].clone();
|
||||
Z[1] = X[1].clone();
|
||||
}else{
|
||||
Z[0]=(1.0-params.interp_factor)*Z[0]+params.interp_factor*X[0];
|
||||
Z[1]=(1.0-params.interp_factor)*Z[1]+params.interp_factor*X[1];
|
||||
}
|
||||
|
||||
if(params.desc_pca !=0 || use_custom_extractor_pca){
|
||||
// initialize the vector of Mat variables
|
||||
if(frame==0){
|
||||
layers_pca_data.resize(Z[0].channels());
|
||||
average_data.resize(Z[0].channels());
|
||||
}
|
||||
|
||||
// feature compression
|
||||
updateProjectionMatrix(Z[0],old_cov_mtx,proj_mtx,params.pca_learning_rate,params.compressed_size,layers_pca_data,average_data,data_pca, new_covar,w_data,u_data,vt_data);
|
||||
compress(proj_mtx,X[0],X[0],data_temp,compress_data);
|
||||
}
|
||||
|
||||
// merge all features
|
||||
if(features_npca.size()==0)
|
||||
x = X[0];
|
||||
else if(features_pca.size()==0)
|
||||
x = X[1];
|
||||
else
|
||||
merge(X,2,x);
|
||||
|
||||
// initialize some required Mat variables
|
||||
if(frame==0){
|
||||
layers.resize(x.channels());
|
||||
vxf.resize(x.channels());
|
||||
vyf.resize(x.channels());
|
||||
vxyf.resize(vyf.size());
|
||||
new_alphaf=Mat_<Vec2f >(yf.rows, yf.cols);
|
||||
}
|
||||
|
||||
// Kernel Regularized Least-Squares, calculate alphas
|
||||
denseGaussKernel(params.sigma,x,x,k,layers,vxf,vyf,vxyf,xy_data,xyf_data);
|
||||
|
||||
// compute the fourier transform of the kernel and add a small value
|
||||
fft2(k,kf);
|
||||
kf_lambda=kf+params.lambda;
|
||||
|
||||
float den;
|
||||
if(params.split_coeff){
|
||||
mulSpectrums(yf,kf,new_alphaf,0);
|
||||
mulSpectrums(kf,kf_lambda,new_alphaf_den,0);
|
||||
}else{
|
||||
for(int i=0;i<yf.rows;i++){
|
||||
for(int j=0;j<yf.cols;j++){
|
||||
den = 1.0f/(kf_lambda.at<Vec2f>(i,j)[0]*kf_lambda.at<Vec2f>(i,j)[0]+kf_lambda.at<Vec2f>(i,j)[1]*kf_lambda.at<Vec2f>(i,j)[1]);
|
||||
|
||||
new_alphaf.at<Vec2f>(i,j)[0]=
|
||||
(yf.at<Vec2f>(i,j)[0]*kf_lambda.at<Vec2f>(i,j)[0]+yf.at<Vec2f>(i,j)[1]*kf_lambda.at<Vec2f>(i,j)[1])*den;
|
||||
new_alphaf.at<Vec2f>(i,j)[1]=
|
||||
(yf.at<Vec2f>(i,j)[1]*kf_lambda.at<Vec2f>(i,j)[0]-yf.at<Vec2f>(i,j)[0]*kf_lambda.at<Vec2f>(i,j)[1])*den;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update the RLS model
|
||||
if(frame==0){
|
||||
alphaf=new_alphaf.clone();
|
||||
if(params.split_coeff)alphaf_den=new_alphaf_den.clone();
|
||||
}else{
|
||||
alphaf=(1.0-params.interp_factor)*alphaf+params.interp_factor*new_alphaf;
|
||||
if(params.split_coeff)alphaf_den=(1.0-params.interp_factor)*alphaf_den+params.interp_factor*new_alphaf_den;
|
||||
}
|
||||
|
||||
frame++;
|
||||
|
||||
int x1 = cvRound(boundingBox.x);
|
||||
int y1 = cvRound(boundingBox.y);
|
||||
int x2 = cvRound(boundingBox.x + boundingBox.width);
|
||||
int y2 = cvRound(boundingBox.y + boundingBox.height);
|
||||
boundingBoxResult = Rect(x1, y1, x2 - x1, y2 - y1) & Rect(Point(0, 0), image.size());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*-------------------------------------
|
||||
| implementation of the KCF functions
|
||||
|-------------------------------------*/
|
||||
|
||||
/*
|
||||
* hann window filter
|
||||
*/
|
||||
void TrackerKCFImpl::createHanningWindow(OutputArray dest, const cv::Size winSize, const int type) const {
|
||||
CV_Assert( type == CV_32FC1 || type == CV_64FC1 );
|
||||
|
||||
dest.create(winSize, type);
|
||||
Mat dst = dest.getMat();
|
||||
|
||||
int rows = dst.rows, cols = dst.cols;
|
||||
|
||||
AutoBuffer<float> _wc(cols);
|
||||
float * const wc = _wc.data();
|
||||
|
||||
const float coeff0 = 2.0f * (float)CV_PI / (cols - 1);
|
||||
const float coeff1 = 2.0f * (float)CV_PI / (rows - 1);
|
||||
for(int j = 0; j < cols; j++)
|
||||
wc[j] = 0.5f * (1.0f - cos(coeff0 * j));
|
||||
|
||||
if(dst.depth() == CV_32F){
|
||||
for(int i = 0; i < rows; i++){
|
||||
float* dstData = dst.ptr<float>(i);
|
||||
float wr = 0.5f * (1.0f - cos(coeff1 * i));
|
||||
for(int j = 0; j < cols; j++)
|
||||
dstData[j] = (float)(wr * wc[j]);
|
||||
}
|
||||
}else{
|
||||
for(int i = 0; i < rows; i++){
|
||||
double* dstData = dst.ptr<double>(i);
|
||||
double wr = 0.5f * (1.0f - cos(coeff1 * i));
|
||||
for(int j = 0; j < cols; j++)
|
||||
dstData[j] = wr * wc[j];
|
||||
}
|
||||
}
|
||||
|
||||
// perform batch sqrt for SSE performance gains
|
||||
//cv::sqrt(dst, dst); //matlab do not use the square rooted version
|
||||
}
|
||||
|
||||
/*
|
||||
* simplification of fourier transform function in opencv
|
||||
*/
|
||||
void inline TrackerKCFImpl::fft2(const Mat src, Mat & dest) const {
|
||||
dft(src,dest,DFT_COMPLEX_OUTPUT);
|
||||
}
|
||||
|
||||
void inline TrackerKCFImpl::fft2(const Mat src, std::vector<Mat> & dest, std::vector<Mat> & layers_data) const {
|
||||
split(src, layers_data);
|
||||
|
||||
for(int i=0;i<src.channels();i++){
|
||||
dft(layers_data[i],dest[i],DFT_COMPLEX_OUTPUT);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* simplification of inverse fourier transform function in opencv
|
||||
*/
|
||||
void inline TrackerKCFImpl::ifft2(const Mat src, Mat & dest) const {
|
||||
idft(src,dest,DFT_SCALE+DFT_REAL_OUTPUT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Point-wise multiplication of two Multichannel Mat data
|
||||
*/
|
||||
void inline TrackerKCFImpl::pixelWiseMult(const std::vector<Mat> src1, const std::vector<Mat> src2, std::vector<Mat> & dest, const int flags, const bool conjB) const {
|
||||
for(unsigned i=0;i<src1.size();i++){
|
||||
mulSpectrums(src1[i], src2[i], dest[i],flags,conjB);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Combines all channels in a multi-channels Mat data into a single channel
|
||||
*/
|
||||
void inline TrackerKCFImpl::sumChannels(std::vector<Mat> src, Mat & dest) const {
|
||||
dest=src[0].clone();
|
||||
for(unsigned i=1;i<src.size();i++){
|
||||
dest+=src[i];
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
bool inline TrackerKCFImpl::oclTransposeMM(const Mat src, float alpha, UMat &dst){
|
||||
// Current kernel only support matrix's rows is multiple of 4.
|
||||
// And if one line is less than 512KB, CPU will likely be faster.
|
||||
if (transpose_mm_ker.empty() ||
|
||||
src.rows % 4 != 0 ||
|
||||
(src.rows * 10) < (1024 * 1024 / 4))
|
||||
return false;
|
||||
|
||||
Size s(src.rows, src.cols);
|
||||
const Mat tmp = src.t();
|
||||
const UMat uSrc = tmp.getUMat(ACCESS_READ);
|
||||
transpose_mm_ker.args(
|
||||
ocl::KernelArg::PtrReadOnly(uSrc),
|
||||
(int)uSrc.rows,
|
||||
(int)uSrc.cols,
|
||||
alpha,
|
||||
ocl::KernelArg::PtrWriteOnly(dst));
|
||||
size_t globSize[2] = {static_cast<size_t>(src.cols * 64), static_cast<size_t>(src.cols)};
|
||||
size_t localSize[2] = {64, 1};
|
||||
if (!transpose_mm_ker.run(2, globSize, localSize, true))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* obtains the projection matrix using PCA
|
||||
*/
|
||||
void inline TrackerKCFImpl::updateProjectionMatrix(const Mat src, Mat & old_cov,Mat & proj_matrix, float pca_rate, int compressed_sz,
|
||||
std::vector<Mat> & layers_pca,std::vector<Scalar> & average, Mat pca_data, Mat new_cov, Mat w, Mat u, Mat vt) {
|
||||
CV_Assert(compressed_sz<=src.channels());
|
||||
|
||||
split(src,layers_pca);
|
||||
|
||||
for (int i=0;i<src.channels();i++){
|
||||
average[i]=mean(layers_pca[i]);
|
||||
layers_pca[i]-=average[i];
|
||||
}
|
||||
|
||||
// calc covariance matrix
|
||||
merge(layers_pca,pca_data);
|
||||
pca_data=pca_data.reshape(1,src.rows*src.cols);
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
bool oclSucceed = false;
|
||||
Size s(pca_data.cols, pca_data.cols);
|
||||
UMat result(s, pca_data.type());
|
||||
if (oclTransposeMM(pca_data, 1.0f/(float)(src.rows*src.cols-1), result)) {
|
||||
if(old_cov.rows==0) old_cov=result.getMat(ACCESS_READ).clone();
|
||||
SVD::compute((1.0-pca_rate)*old_cov + pca_rate * result.getMat(ACCESS_READ), w, u, vt);
|
||||
oclSucceed = true;
|
||||
}
|
||||
#define TMM_VERIFICATION 0
|
||||
|
||||
if (oclSucceed == false || TMM_VERIFICATION) {
|
||||
new_cov=1.0f/(float)(src.rows*src.cols-1)*(pca_data.t()*pca_data);
|
||||
#if TMM_VERIFICATION
|
||||
for(int i = 0; i < new_cov.rows; i++)
|
||||
for(int j = 0; j < new_cov.cols; j++)
|
||||
if (abs(new_cov.at<float>(i, j) - result.getMat(ACCESS_RW).at<float>(i , j)) > abs(new_cov.at<float>(i, j)) * 1e-3)
|
||||
printf("error @ i %d j %d got %G expected %G \n", i, j, result.getMat(ACCESS_RW).at<float>(i , j), new_cov.at<float>(i, j));
|
||||
#endif
|
||||
if(old_cov.rows==0)old_cov=new_cov.clone();
|
||||
SVD::compute((1.0f - pca_rate) * old_cov + pca_rate * new_cov, w, u, vt);
|
||||
}
|
||||
#else
|
||||
new_cov=1.0/(float)(src.rows*src.cols-1)*(pca_data.t()*pca_data);
|
||||
if(old_cov.rows==0)old_cov=new_cov.clone();
|
||||
|
||||
// calc PCA
|
||||
SVD::compute((1.0-pca_rate)*old_cov+pca_rate*new_cov, w, u, vt);
|
||||
#endif
|
||||
// extract the projection matrix
|
||||
proj_matrix=u(Rect(0,0,compressed_sz,src.channels())).clone();
|
||||
Mat proj_vars=Mat::eye(compressed_sz,compressed_sz,proj_matrix.type());
|
||||
for(int i=0;i<compressed_sz;i++){
|
||||
proj_vars.at<float>(i,i)=w.at<float>(i);
|
||||
}
|
||||
|
||||
// update the covariance matrix
|
||||
old_cov=(1.0-pca_rate)*old_cov+pca_rate*proj_matrix*proj_vars*proj_matrix.t();
|
||||
}
|
||||
|
||||
/*
|
||||
* compress the features
|
||||
*/
|
||||
void inline TrackerKCFImpl::compress(const Mat proj_matrix, const Mat src, Mat & dest, Mat & data, Mat & compressed) const {
|
||||
data=src.reshape(1,src.rows*src.cols);
|
||||
compressed=data*proj_matrix;
|
||||
dest=compressed.reshape(proj_matrix.cols,src.rows).clone();
|
||||
}
|
||||
|
||||
/*
|
||||
* obtain the patch and apply hann window filter to it
|
||||
*/
|
||||
bool TrackerKCFImpl::getSubWindow(const Mat img, const Rect _roi, Mat& feat, Mat& patch, TrackerKCF::MODE desc) const {
|
||||
|
||||
Rect region=_roi;
|
||||
|
||||
// return false if roi is outside the image
|
||||
if ((roi & Rect2d(0, 0, img.cols, img.rows)).empty())
|
||||
return false;
|
||||
|
||||
// extract patch inside the image
|
||||
if(_roi.x<0){region.x=0;region.width+=_roi.x;}
|
||||
if(_roi.y<0){region.y=0;region.height+=_roi.y;}
|
||||
if(_roi.x+_roi.width>img.cols)region.width=img.cols-_roi.x;
|
||||
if(_roi.y+_roi.height>img.rows)region.height=img.rows-_roi.y;
|
||||
if(region.width>img.cols)region.width=img.cols;
|
||||
if(region.height>img.rows)region.height=img.rows;
|
||||
|
||||
// return false if region is empty
|
||||
if (region.empty())
|
||||
return false;
|
||||
|
||||
patch=img(region).clone();
|
||||
|
||||
// add some padding to compensate when the patch is outside image border
|
||||
int addTop,addBottom, addLeft, addRight;
|
||||
addTop=region.y-_roi.y;
|
||||
addBottom=(_roi.height+_roi.y>img.rows?_roi.height+_roi.y-img.rows:0);
|
||||
addLeft=region.x-_roi.x;
|
||||
addRight=(_roi.width+_roi.x>img.cols?_roi.width+_roi.x-img.cols:0);
|
||||
|
||||
copyMakeBorder(patch,patch,addTop,addBottom,addLeft,addRight,BORDER_REPLICATE);
|
||||
if(patch.rows==0 || patch.cols==0)return false;
|
||||
|
||||
// extract the desired descriptors
|
||||
switch(desc){
|
||||
case CN:
|
||||
CV_Assert(img.channels() == 3);
|
||||
extractCN(patch,feat);
|
||||
feat=feat.mul(hann_cn); // hann window filter
|
||||
break;
|
||||
default: // GRAY
|
||||
if(img.channels()>1)
|
||||
cvtColor(patch,feat, COLOR_BGR2GRAY);
|
||||
else
|
||||
feat=patch;
|
||||
//feat.convertTo(feat,CV_32F);
|
||||
feat.convertTo(feat,CV_32F, 1.0/255.0, -0.5);
|
||||
//feat=feat/255.0-0.5; // normalize to range -0.5 .. 0.5
|
||||
feat=feat.mul(hann); // hann window filter
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* get feature using external function
|
||||
*/
|
||||
bool TrackerKCFImpl::getSubWindow(const Mat img, const Rect _roi, Mat& feat, void (*f)(const Mat, const Rect, Mat& )) const{
|
||||
|
||||
// return false if roi is outside the image
|
||||
if((_roi.x+_roi.width<0)
|
||||
||(_roi.y+_roi.height<0)
|
||||
||(_roi.x>=img.cols)
|
||||
||(_roi.y>=img.rows)
|
||||
)return false;
|
||||
|
||||
f(img, _roi, feat);
|
||||
|
||||
if(_roi.width != feat.cols || _roi.height != feat.rows){
|
||||
printf("error in customized function of features extractor!\n");
|
||||
printf("Rules: roi.width==feat.cols && roi.height = feat.rows \n");
|
||||
}
|
||||
|
||||
Mat hann_win;
|
||||
std::vector<Mat> _layers;
|
||||
|
||||
for(int i=0;i<feat.channels();i++)
|
||||
_layers.push_back(hann);
|
||||
|
||||
merge(_layers, hann_win);
|
||||
|
||||
feat=feat.mul(hann_win); // hann window filter
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Convert BGR to ColorNames
|
||||
*/
|
||||
void TrackerKCFImpl::extractCN(Mat patch_data, Mat & cnFeatures) const {
|
||||
Vec3b & pixel = patch_data.at<Vec3b>(0,0);
|
||||
unsigned index;
|
||||
|
||||
if(cnFeatures.type() != CV_32FC(10))
|
||||
cnFeatures = Mat::zeros(patch_data.rows,patch_data.cols,CV_32FC(10));
|
||||
|
||||
for(int i=0;i<patch_data.rows;i++){
|
||||
for(int j=0;j<patch_data.cols;j++){
|
||||
pixel=patch_data.at<Vec3b>(i,j);
|
||||
index=(unsigned)(floor((float)pixel[2]/8)+32*floor((float)pixel[1]/8)+32*32*floor((float)pixel[0]/8));
|
||||
|
||||
//copy the values
|
||||
for(int _k=0;_k<10;_k++){
|
||||
cnFeatures.at<Vec<float,10> >(i,j)[_k]=ColorNames[index][_k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* dense gauss kernel function
|
||||
*/
|
||||
void TrackerKCFImpl::denseGaussKernel(const float sigma, const Mat x_data, const Mat y_data, Mat & k_data,
|
||||
std::vector<Mat> & layers_data,std::vector<Mat> & xf_data,std::vector<Mat> & yf_data, std::vector<Mat> xyf_v, Mat xy, Mat xyf ) const {
|
||||
double normX, normY;
|
||||
|
||||
fft2(x_data,xf_data,layers_data);
|
||||
fft2(y_data,yf_data,layers_data);
|
||||
|
||||
normX=norm(x_data);
|
||||
normX*=normX;
|
||||
normY=norm(y_data);
|
||||
normY*=normY;
|
||||
|
||||
pixelWiseMult(xf_data,yf_data,xyf_v,0,true);
|
||||
sumChannels(xyf_v,xyf);
|
||||
ifft2(xyf,xyf);
|
||||
|
||||
if(params.wrap_kernel){
|
||||
shiftRows(xyf, x_data.rows/2);
|
||||
shiftCols(xyf, x_data.cols/2);
|
||||
}
|
||||
|
||||
//(xx + yy - 2 * xy) / numel(x)
|
||||
xy=(normX+normY-2*xyf)/(x_data.rows*x_data.cols*x_data.channels());
|
||||
|
||||
// TODO: check wether we really need thresholding or not
|
||||
//threshold(xy,xy,0.0,0.0,THRESH_TOZERO);//max(0, (xx + yy - 2 * xy) / numel(x))
|
||||
for(int i=0;i<xy.rows;i++){
|
||||
for(int j=0;j<xy.cols;j++){
|
||||
if(xy.at<float>(i,j)<0.0)xy.at<float>(i,j)=0.0;
|
||||
}
|
||||
}
|
||||
|
||||
float sig=-1.0f/(sigma*sigma);
|
||||
xy=sig*xy;
|
||||
exp(xy,k_data);
|
||||
|
||||
}
|
||||
|
||||
/* CIRCULAR SHIFT Function
|
||||
* http://stackoverflow.com/questions/10420454/shift-like-matlab-function-rows-or-columns-of-a-matrix-in-opencv
|
||||
*/
|
||||
// circular shift one row from up to down
|
||||
void TrackerKCFImpl::shiftRows(Mat& mat) const {
|
||||
|
||||
Mat temp;
|
||||
Mat m;
|
||||
int _k = (mat.rows-1);
|
||||
mat.row(_k).copyTo(temp);
|
||||
for(; _k > 0 ; _k-- ) {
|
||||
m = mat.row(_k);
|
||||
mat.row(_k-1).copyTo(m);
|
||||
}
|
||||
m = mat.row(0);
|
||||
temp.copyTo(m);
|
||||
|
||||
}
|
||||
|
||||
// circular shift n rows from up to down if n > 0, -n rows from down to up if n < 0
|
||||
void TrackerKCFImpl::shiftRows(Mat& mat, int n) const {
|
||||
if( n < 0 ) {
|
||||
n = -n;
|
||||
flip(mat,mat,0);
|
||||
for(int _k=0; _k < n;_k++) {
|
||||
shiftRows(mat);
|
||||
}
|
||||
flip(mat,mat,0);
|
||||
}else{
|
||||
for(int _k=0; _k < n;_k++) {
|
||||
shiftRows(mat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//circular shift n columns from left to right if n > 0, -n columns from right to left if n < 0
|
||||
void TrackerKCFImpl::shiftCols(Mat& mat, int n) const {
|
||||
if(n < 0){
|
||||
n = -n;
|
||||
flip(mat,mat,1);
|
||||
transpose(mat,mat);
|
||||
shiftRows(mat,n);
|
||||
transpose(mat,mat);
|
||||
flip(mat,mat,1);
|
||||
}else{
|
||||
transpose(mat,mat);
|
||||
shiftRows(mat,n);
|
||||
transpose(mat,mat);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* calculate the detection response
|
||||
*/
|
||||
void TrackerKCFImpl::calcResponse(const Mat alphaf_data, const Mat kf_data, Mat & response_data, Mat & spec_data) const {
|
||||
//alpha f--> 2channels ; k --> 1 channel;
|
||||
mulSpectrums(alphaf_data,kf_data,spec_data,0,false);
|
||||
ifft2(spec_data,response_data);
|
||||
}
|
||||
|
||||
/*
|
||||
* calculate the detection response for splitted form
|
||||
*/
|
||||
void TrackerKCFImpl::calcResponse(const Mat alphaf_data, const Mat _alphaf_den, const Mat kf_data, Mat & response_data, Mat & spec_data, Mat & spec2_data) const {
|
||||
|
||||
mulSpectrums(alphaf_data,kf_data,spec_data,0,false);
|
||||
|
||||
//z=(a+bi)/(c+di)=[(ac+bd)+i(bc-ad)]/(c^2+d^2)
|
||||
float den;
|
||||
for(int i=0;i<kf_data.rows;i++){
|
||||
for(int j=0;j<kf_data.cols;j++){
|
||||
den=1.0f/(_alphaf_den.at<Vec2f>(i,j)[0]*_alphaf_den.at<Vec2f>(i,j)[0]+_alphaf_den.at<Vec2f>(i,j)[1]*_alphaf_den.at<Vec2f>(i,j)[1]);
|
||||
spec2_data.at<Vec2f>(i,j)[0]=
|
||||
(spec_data.at<Vec2f>(i,j)[0]*_alphaf_den.at<Vec2f>(i,j)[0]+spec_data.at<Vec2f>(i,j)[1]*_alphaf_den.at<Vec2f>(i,j)[1])*den;
|
||||
spec2_data.at<Vec2f>(i,j)[1]=
|
||||
(spec_data.at<Vec2f>(i,j)[1]*_alphaf_den.at<Vec2f>(i,j)[0]-spec_data.at<Vec2f>(i,j)[0]*_alphaf_den.at<Vec2f>(i,j)[1])*den;
|
||||
}
|
||||
}
|
||||
|
||||
ifft2(spec2_data,response_data);
|
||||
}
|
||||
|
||||
void TrackerKCFImpl::setFeatureExtractor(void (*f)(const Mat, const Rect, Mat&), bool pca_func){
|
||||
if(pca_func){
|
||||
extractor_pca.push_back(f);
|
||||
use_custom_extractor_pca = true;
|
||||
}else{
|
||||
extractor_npca.push_back(f);
|
||||
use_custom_extractor_npca = true;
|
||||
}
|
||||
}
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
TrackerKCF::Params::Params()
|
||||
{
|
||||
detect_thresh = 0.5f;
|
||||
sigma=0.2f;
|
||||
lambda=0.0001f;
|
||||
interp_factor=0.075f;
|
||||
output_sigma_factor=1.0f / 16.0f;
|
||||
resize=true;
|
||||
max_patch_size=80*80;
|
||||
split_coeff=true;
|
||||
wrap_kernel=false;
|
||||
desc_npca = GRAY;
|
||||
desc_pca = CN;
|
||||
|
||||
//feature compression
|
||||
compress_feature=true;
|
||||
compressed_size=2;
|
||||
pca_learning_rate=0.15f;
|
||||
}
|
||||
|
||||
|
||||
TrackerKCF::TrackerKCF()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
TrackerKCF::~TrackerKCF()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
Ptr<TrackerKCF> TrackerKCF::create(const TrackerKCF::Params ¶meters)
|
||||
{
|
||||
return makePtr<TrackerKCFImpl>(parameters);
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
|
||||
#include "legacy/trackerKCF.legacy.hpp"
|
||||
@@ -0,0 +1,128 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "opencv2/tracking/tracking_legacy.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace legacy {
|
||||
inline namespace tracking {
|
||||
namespace impl {
|
||||
|
||||
class TrackerMILImpl CV_FINAL : public legacy::TrackerMIL
|
||||
{
|
||||
public:
|
||||
Ptr<cv::TrackerMIL> impl;
|
||||
legacy::TrackerMIL::Params params;
|
||||
|
||||
TrackerMILImpl(const legacy::TrackerMIL::Params ¶meters)
|
||||
: impl(cv::TrackerMIL::create(parameters))
|
||||
, params(parameters)
|
||||
{
|
||||
isInit = false;
|
||||
}
|
||||
|
||||
void read(const FileNode& fn) CV_OVERRIDE
|
||||
{
|
||||
params.read(fn);
|
||||
CV_Error(Error::StsNotImplemented, "Can't update legacy tracker wrapper");
|
||||
}
|
||||
void write(FileStorage& fs) const CV_OVERRIDE
|
||||
{
|
||||
params.write(fs);
|
||||
}
|
||||
|
||||
bool initImpl(const Mat& image, const Rect2d& boundingBox2d) CV_OVERRIDE
|
||||
{
|
||||
int x1 = cvRound(boundingBox2d.x);
|
||||
int y1 = cvRound(boundingBox2d.y);
|
||||
int x2 = cvRound(boundingBox2d.x + boundingBox2d.width);
|
||||
int y2 = cvRound(boundingBox2d.y + boundingBox2d.height);
|
||||
Rect boundingBox = Rect(x1, y1, x2 - x1, y2 - y1) & Rect(Point(0, 0), image.size());
|
||||
impl->init(image, boundingBox);
|
||||
isInit = true;
|
||||
return true;
|
||||
}
|
||||
bool updateImpl(const Mat& image, Rect2d& boundingBox) CV_OVERRIDE
|
||||
{
|
||||
Rect bb;
|
||||
bool res = impl->update(image, bb);
|
||||
boundingBox = bb;
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void legacy::TrackerMIL::Params::read(const cv::FileNode& fn)
|
||||
{
|
||||
samplerInitInRadius = fn["samplerInitInRadius"];
|
||||
samplerSearchWinSize = fn["samplerSearchWinSize"];
|
||||
samplerInitMaxNegNum = fn["samplerInitMaxNegNum"];
|
||||
samplerTrackInRadius = fn["samplerTrackInRadius"];
|
||||
samplerTrackMaxPosNum = fn["samplerTrackMaxPosNum"];
|
||||
samplerTrackMaxNegNum = fn["samplerTrackMaxNegNum"];
|
||||
featureSetNumFeatures = fn["featureSetNumFeatures"];
|
||||
}
|
||||
|
||||
void legacy::TrackerMIL::Params::write(cv::FileStorage& fs) const
|
||||
{
|
||||
fs << "samplerInitInRadius" << samplerInitInRadius;
|
||||
fs << "samplerSearchWinSize" << samplerSearchWinSize;
|
||||
fs << "samplerInitMaxNegNum" << samplerInitMaxNegNum;
|
||||
fs << "samplerTrackInRadius" << samplerTrackInRadius;
|
||||
fs << "samplerTrackMaxPosNum" << samplerTrackMaxPosNum;
|
||||
fs << "samplerTrackMaxNegNum" << samplerTrackMaxNegNum;
|
||||
fs << "featureSetNumFeatures" << featureSetNumFeatures;
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
|
||||
Ptr<legacy::TrackerMIL> legacy::TrackerMIL::create(const legacy::TrackerMIL::Params ¶meters)
|
||||
{
|
||||
return makePtr<legacy::tracking::impl::TrackerMILImpl>(parameters);
|
||||
}
|
||||
Ptr<legacy::TrackerMIL> legacy::TrackerMIL::create()
|
||||
{
|
||||
return create(legacy::TrackerMIL::Params());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,457 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "opencv2/tracking/tracking_legacy.hpp"
|
||||
|
||||
#include "tracking_utils.hpp"
|
||||
#include <algorithm>
|
||||
#include <limits.h>
|
||||
|
||||
namespace cv {
|
||||
inline namespace tracking {
|
||||
namespace impl {
|
||||
|
||||
#undef MEDIAN_FLOW_TRACKER_DEBUG_LOGS
|
||||
#ifdef MEDIAN_FLOW_TRACKER_DEBUG_LOGS
|
||||
#define dprintf(x) printf x
|
||||
#else
|
||||
#define dprintf(x) do{} while(false)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* TrackerMedianFlow
|
||||
*/
|
||||
/*
|
||||
* TODO:
|
||||
* add "non-detected" answer in algo --> test it with 2 rects --> frame-by-frame debug in TLD --> test it!!
|
||||
* take all parameters out
|
||||
* asessment framework
|
||||
*
|
||||
*
|
||||
* FIXME:
|
||||
* when patch is cut from image to compute NCC, there can be problem with size
|
||||
* optimize (allocation<-->reallocation)
|
||||
*/
|
||||
|
||||
class TrackerMedianFlowImpl : public legacy::TrackerMedianFlow
|
||||
{
|
||||
public:
|
||||
TrackerMedianFlowImpl(TrackerMedianFlow::Params paramsIn = TrackerMedianFlow::Params()) {params=paramsIn;isInit=false;}
|
||||
void read( const FileNode& fn ) CV_OVERRIDE;
|
||||
void write( FileStorage& fs ) const CV_OVERRIDE;
|
||||
private:
|
||||
bool initImpl( const Mat& image, const Rect2d& boundingBox ) CV_OVERRIDE;
|
||||
bool updateImpl( const Mat& image, Rect2d& boundingBox ) CV_OVERRIDE;
|
||||
bool medianFlowImpl(Mat oldImage,Mat newImage,Rect2d& oldBox);
|
||||
Rect2d vote(const std::vector<Point2f>& oldPoints,const std::vector<Point2f>& newPoints,const Rect2d& oldRect,Point2f& mD);
|
||||
float dist(Point2f p1,Point2f p2);
|
||||
std::string type2str(int type);
|
||||
#if 0
|
||||
void computeStatistics(std::vector<float>& data,int size=-1);
|
||||
#endif
|
||||
void check_FB(const std::vector<Mat>& oldImagePyr,const std::vector<Mat>& newImagePyr,
|
||||
const std::vector<Point2f>& oldPoints,const std::vector<Point2f>& newPoints,std::vector<bool>& status);
|
||||
void check_NCC(const Mat& oldImage,const Mat& newImage,
|
||||
const std::vector<Point2f>& oldPoints,const std::vector<Point2f>& newPoints,std::vector<bool>& status);
|
||||
|
||||
TrackerMedianFlow::Params params;
|
||||
};
|
||||
|
||||
static
|
||||
Mat getPatch(Mat image, Size patch_size, Point2f patch_center)
|
||||
{
|
||||
Mat patch;
|
||||
Point2i roi_strat_corner(cvRound(patch_center.x - patch_size.width / 2.),
|
||||
cvRound(patch_center.y - patch_size.height / 2.));
|
||||
|
||||
Rect2i patch_rect(roi_strat_corner, patch_size);
|
||||
|
||||
if(patch_rect == (patch_rect & Rect2i(0, 0, image.cols, image.rows)))
|
||||
{
|
||||
patch = image(patch_rect);
|
||||
}
|
||||
else
|
||||
{
|
||||
getRectSubPix(image, patch_size,
|
||||
Point2f((float)(patch_rect.x + patch_size.width / 2.),
|
||||
(float)(patch_rect.y + patch_size.height / 2.)), patch);
|
||||
}
|
||||
|
||||
return patch;
|
||||
}
|
||||
|
||||
class TrackerMedianFlowModel : public TrackerModel{
|
||||
public:
|
||||
TrackerMedianFlowModel(legacy::TrackerMedianFlow::Params /*params*/){}
|
||||
Rect2d getBoundingBox(){return boundingBox_;}
|
||||
void setBoudingBox(Rect2d boundingBox){boundingBox_=boundingBox;}
|
||||
Mat getImage(){return image_;}
|
||||
void setImage(const Mat& image){image.copyTo(image_);}
|
||||
protected:
|
||||
Rect2d boundingBox_;
|
||||
Mat image_;
|
||||
void modelEstimationImpl( const std::vector<Mat>& /*responses*/ ) CV_OVERRIDE {}
|
||||
void modelUpdateImpl() CV_OVERRIDE {}
|
||||
};
|
||||
|
||||
void TrackerMedianFlowImpl::read( const cv::FileNode& fn )
|
||||
{
|
||||
params.read( fn );
|
||||
}
|
||||
|
||||
void TrackerMedianFlowImpl::write( cv::FileStorage& fs ) const
|
||||
{
|
||||
params.write( fs );
|
||||
}
|
||||
|
||||
bool TrackerMedianFlowImpl::initImpl( const Mat& image, const Rect2d& boundingBox ){
|
||||
model=Ptr<TrackerMedianFlowModel>(new TrackerMedianFlowModel(params));
|
||||
((TrackerMedianFlowModel*)static_cast<TrackerModel*>(model))->setImage(image);
|
||||
((TrackerMedianFlowModel*)static_cast<TrackerModel*>(model))->setBoudingBox(boundingBox);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TrackerMedianFlowImpl::updateImpl( const Mat& image, Rect2d& boundingBox ){
|
||||
Mat oldImage=((TrackerMedianFlowModel*)static_cast<TrackerModel*>(model))->getImage();
|
||||
|
||||
Rect2d oldBox=((TrackerMedianFlowModel*)static_cast<TrackerModel*>(model))->getBoundingBox();
|
||||
if(!medianFlowImpl(oldImage,image,oldBox)){
|
||||
return false;
|
||||
}
|
||||
boundingBox=oldBox;
|
||||
((TrackerMedianFlowModel*)static_cast<TrackerModel*>(model))->setImage(image);
|
||||
((TrackerMedianFlowModel*)static_cast<TrackerModel*>(model))->setBoudingBox(oldBox);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
size_t filterPointsInVectors(std::vector<T>& status, std::vector<Point2f>& vec1, std::vector<Point2f>& vec2, T goodValue)
|
||||
{
|
||||
CV_DbgAssert(status.size() == vec1.size() && status.size() == vec2.size());
|
||||
|
||||
size_t first_bad_idx = 0;
|
||||
while(first_bad_idx < status.size())
|
||||
{
|
||||
if(status[first_bad_idx] != goodValue)
|
||||
break;
|
||||
first_bad_idx++;
|
||||
}
|
||||
|
||||
if (first_bad_idx >= status.size())
|
||||
return first_bad_idx;
|
||||
|
||||
for(size_t i = first_bad_idx + 1; i < status.size(); i++)
|
||||
{
|
||||
if (status[i] != goodValue)
|
||||
continue;
|
||||
|
||||
status[first_bad_idx] = goodValue;
|
||||
vec1[first_bad_idx] = vec1[i];
|
||||
vec2[first_bad_idx] = vec2[i];
|
||||
first_bad_idx++;
|
||||
}
|
||||
vec1.erase(vec1.begin() + first_bad_idx, vec1.end());
|
||||
vec2.erase(vec2.begin() + first_bad_idx, vec2.end());
|
||||
status.erase(status.begin() + first_bad_idx, status.end());
|
||||
|
||||
return first_bad_idx;
|
||||
}
|
||||
|
||||
bool TrackerMedianFlowImpl::medianFlowImpl(Mat oldImage,Mat newImage,Rect2d& oldBox){
|
||||
std::vector<Point2f> pointsToTrackOld,pointsToTrackNew;
|
||||
|
||||
Mat oldImage_gray,newImage_gray;
|
||||
if (oldImage.channels() != 1)
|
||||
cvtColor( oldImage, oldImage_gray, COLOR_BGR2GRAY );
|
||||
else
|
||||
oldImage.copyTo(oldImage_gray);
|
||||
|
||||
if (newImage.channels() != 1)
|
||||
cvtColor( newImage, newImage_gray, COLOR_BGR2GRAY );
|
||||
else
|
||||
newImage.copyTo(newImage_gray);
|
||||
|
||||
//"open ended" grid
|
||||
for(int i=0;i<params.pointsInGrid;i++){
|
||||
for(int j=0;j<params.pointsInGrid;j++){
|
||||
pointsToTrackOld.push_back(
|
||||
Point2f((float)(oldBox.x+((1.0*oldBox.width)/params.pointsInGrid)*j+.5*oldBox.width/params.pointsInGrid),
|
||||
(float)(oldBox.y+((1.0*oldBox.height)/params.pointsInGrid)*i+.5*oldBox.height/params.pointsInGrid)));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uchar> status(pointsToTrackOld.size());
|
||||
std::vector<float> errors(pointsToTrackOld.size());
|
||||
|
||||
std::vector<Mat> oldImagePyr;
|
||||
buildOpticalFlowPyramid(oldImage_gray, oldImagePyr, params.winSize, params.maxLevel, false);
|
||||
|
||||
std::vector<Mat> newImagePyr;
|
||||
buildOpticalFlowPyramid(newImage_gray, newImagePyr, params.winSize, params.maxLevel, false);
|
||||
|
||||
calcOpticalFlowPyrLK(oldImagePyr,newImagePyr,pointsToTrackOld,pointsToTrackNew,status,errors,
|
||||
params.winSize, params.maxLevel, params.termCriteria, 0);
|
||||
|
||||
CV_Assert(pointsToTrackNew.size() == pointsToTrackOld.size());
|
||||
CV_Assert(status.size() == pointsToTrackOld.size());
|
||||
dprintf(("\t%d after LK forward\n",(int)pointsToTrackOld.size()));
|
||||
|
||||
size_t num_good_points_after_optical_flow = filterPointsInVectors(status, pointsToTrackOld, pointsToTrackNew, (uchar)1);
|
||||
|
||||
dprintf(("\t num_good_points_after_optical_flow = %d\n",num_good_points_after_optical_flow));
|
||||
|
||||
if (num_good_points_after_optical_flow == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CV_Assert(pointsToTrackOld.size() == num_good_points_after_optical_flow);
|
||||
CV_Assert(pointsToTrackNew.size() == num_good_points_after_optical_flow);
|
||||
|
||||
dprintf(("\t%d after LK forward after removing points with bad status\n",(int)pointsToTrackOld.size()));
|
||||
|
||||
std::vector<bool> filter_status(pointsToTrackOld.size(), true);
|
||||
check_FB(oldImagePyr, newImagePyr, pointsToTrackOld, pointsToTrackNew, filter_status);
|
||||
check_NCC(oldImage_gray, newImage_gray, pointsToTrackOld, pointsToTrackNew, filter_status);
|
||||
|
||||
// filter
|
||||
size_t num_good_points_after_filtering = filterPointsInVectors(filter_status, pointsToTrackOld, pointsToTrackNew, true);
|
||||
|
||||
dprintf(("\t num_good_points_after_filtering = %d\n",num_good_points_after_filtering));
|
||||
|
||||
if(num_good_points_after_filtering == 0){
|
||||
return false;
|
||||
}
|
||||
|
||||
CV_Assert(pointsToTrackOld.size() == num_good_points_after_filtering);
|
||||
CV_Assert(pointsToTrackNew.size() == num_good_points_after_filtering);
|
||||
|
||||
dprintf(("\t%d after LK backward\n",(int)pointsToTrackOld.size()));
|
||||
|
||||
std::vector<Point2f> di(pointsToTrackOld.size());
|
||||
for(size_t i=0; i<pointsToTrackOld.size(); i++){
|
||||
di[i] = pointsToTrackNew[i]-pointsToTrackOld[i];
|
||||
}
|
||||
|
||||
Point2f mDisplacement;
|
||||
oldBox=vote(pointsToTrackOld,pointsToTrackNew,oldBox,mDisplacement);
|
||||
|
||||
std::vector<float> displacements;
|
||||
for(size_t i=0;i<di.size();i++){
|
||||
di[i]-=mDisplacement;
|
||||
displacements.push_back((float)sqrt(di[i].ddot(di[i])));
|
||||
}
|
||||
float median_displacements = tracking_internal::getMedianAndDoPartition(displacements);
|
||||
dprintf(("\tmedian of length of difference of displacements = %f\n", median_displacements));
|
||||
if(median_displacements > params.maxMedianLengthOfDisplacementDifference){
|
||||
dprintf(("\tmedian flow tracker returns false due to big median length of difference between displacements\n"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Rect2d TrackerMedianFlowImpl::vote(const std::vector<Point2f>& oldPoints,const std::vector<Point2f>& newPoints,const Rect2d& oldRect,Point2f& mD){
|
||||
Rect2d newRect;
|
||||
Point2d newCenter(oldRect.x+oldRect.width/2.0,oldRect.y+oldRect.height/2.0);
|
||||
const size_t n=oldPoints.size();
|
||||
|
||||
if (n==1) {
|
||||
newRect.x=oldRect.x+newPoints[0].x-oldPoints[0].x;
|
||||
newRect.y=oldRect.y+newPoints[0].y-oldPoints[0].y;
|
||||
newRect.width=oldRect.width;
|
||||
newRect.height=oldRect.height;
|
||||
mD.x = newPoints[0].x-oldPoints[0].x;
|
||||
mD.y = newPoints[0].y-oldPoints[0].y;
|
||||
return newRect;
|
||||
}
|
||||
|
||||
float xshift=0,yshift=0;
|
||||
std::vector<float> buf_for_location(n, 0.);
|
||||
for(size_t i=0;i<n;i++){ buf_for_location[i]=newPoints[i].x-oldPoints[i].x; }
|
||||
xshift=tracking_internal::getMedianAndDoPartition(buf_for_location);
|
||||
newCenter.x+=xshift;
|
||||
for(size_t i=0;i<n;i++){ buf_for_location[i]=newPoints[i].y-oldPoints[i].y; }
|
||||
yshift=tracking_internal::getMedianAndDoPartition(buf_for_location);
|
||||
newCenter.y+=yshift;
|
||||
mD=Point2f((float)xshift,(float)yshift);
|
||||
|
||||
std::vector<double> buf_for_scale(n*(n-1)/2, 0.0);
|
||||
for(size_t i=0,ctr=0;i<n;i++){
|
||||
for(size_t j=0;j<i;j++){
|
||||
double nd=norm(newPoints[i] - newPoints[j]);
|
||||
double od=norm(oldPoints[i] - oldPoints[j]);
|
||||
buf_for_scale[ctr]=(od==0.0)?0.0:(nd/od);
|
||||
ctr++;
|
||||
}
|
||||
}
|
||||
|
||||
double scale=tracking_internal::getMedianAndDoPartition(buf_for_scale);
|
||||
dprintf(("xshift, yshift, scale = %f %f %f\n",xshift,yshift,scale));
|
||||
newRect.x=newCenter.x-scale*oldRect.width/2.0;
|
||||
newRect.y=newCenter.y-scale*oldRect.height/2.0;
|
||||
newRect.width=scale*oldRect.width;
|
||||
newRect.height=scale*oldRect.height;
|
||||
dprintf(("rect old [%f %f %f %f]\n",oldRect.x,oldRect.y,oldRect.width,oldRect.height));
|
||||
dprintf(("rect [%f %f %f %f]\n",newRect.x,newRect.y,newRect.width,newRect.height));
|
||||
|
||||
return newRect;
|
||||
}
|
||||
#if 0
|
||||
void TrackerMedianFlowImpl::computeStatistics(std::vector<float>& data,int size){
|
||||
int binnum=10;
|
||||
if(size==-1){
|
||||
size=(int)data.size();
|
||||
}
|
||||
float mini=*std::min_element(data.begin(),data.begin()+size),maxi=*std::max_element(data.begin(),data.begin()+size);
|
||||
std::vector<int> bins(binnum,(int)0);
|
||||
for(int i=0;i<size;i++){
|
||||
bins[std::min((int)(binnum*(data[i]-mini)/(maxi-mini)),binnum-1)]++;
|
||||
}
|
||||
for(int i=0;i<binnum;i++){
|
||||
dprintf(("[%4f,%4f] -- %4d\n",mini+(maxi-mini)/binnum*i,mini+(maxi-mini)/binnum*(i+1),bins[i]));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
void TrackerMedianFlowImpl::check_FB(const std::vector<Mat>& oldImagePyr, const std::vector<Mat>& newImagePyr,
|
||||
const std::vector<Point2f>& oldPoints, const std::vector<Point2f>& newPoints, std::vector<bool>& status){
|
||||
|
||||
if(status.empty()) {
|
||||
status=std::vector<bool>(oldPoints.size(),true);
|
||||
}
|
||||
|
||||
std::vector<uchar> LKstatus(oldPoints.size());
|
||||
std::vector<float> errors(oldPoints.size());
|
||||
std::vector<float> FBerror(oldPoints.size());
|
||||
std::vector<Point2f> pointsToTrackReprojection;
|
||||
calcOpticalFlowPyrLK(newImagePyr, oldImagePyr,newPoints,pointsToTrackReprojection,LKstatus,errors,
|
||||
params.winSize, params.maxLevel, params.termCriteria, 0);
|
||||
|
||||
for(size_t i=0;i<oldPoints.size();i++){
|
||||
FBerror[i]=(float)norm(oldPoints[i]-pointsToTrackReprojection[i]);
|
||||
}
|
||||
float FBerrorMedian=tracking_internal::getMedian(FBerror);
|
||||
dprintf(("point median=%f\n",FBerrorMedian));
|
||||
dprintf(("FBerrorMedian=%f\n",FBerrorMedian));
|
||||
for(size_t i=0;i<oldPoints.size();i++){
|
||||
status[i]=status[i] && (FBerror[i] <= FBerrorMedian);
|
||||
}
|
||||
}
|
||||
void TrackerMedianFlowImpl::check_NCC(const Mat& oldImage,const Mat& newImage,
|
||||
const std::vector<Point2f>& oldPoints,const std::vector<Point2f>& newPoints,std::vector<bool>& status){
|
||||
|
||||
std::vector<float> NCC(oldPoints.size(),0.0);
|
||||
Mat p1,p2;
|
||||
|
||||
for (size_t i = 0; i < oldPoints.size(); i++) {
|
||||
p1 = getPatch(oldImage, params.winSizeNCC, oldPoints[i]);
|
||||
p2 = getPatch(newImage, params.winSizeNCC, newPoints[i]);
|
||||
|
||||
NCC[i] = (float)tracking_internal::computeNCC(p1, p2);
|
||||
}
|
||||
float median = tracking_internal::getMedian(NCC);
|
||||
for(size_t i = 0; i < oldPoints.size(); i++) {
|
||||
status[i] = status[i] && (NCC[i] >= median);
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
|
||||
namespace legacy {
|
||||
inline namespace tracking {
|
||||
|
||||
/*
|
||||
* Parameters
|
||||
*/
|
||||
TrackerMedianFlow::Params::Params() {
|
||||
pointsInGrid=10;
|
||||
winSize = Size(3,3);
|
||||
maxLevel = 5;
|
||||
termCriteria = TermCriteria(TermCriteria::COUNT|TermCriteria::EPS,20,0.3);
|
||||
winSizeNCC = Size(30,30);
|
||||
maxMedianLengthOfDisplacementDifference = 10;
|
||||
}
|
||||
|
||||
void TrackerMedianFlow::Params::read( const cv::FileNode& fn ){
|
||||
*this = TrackerMedianFlow::Params();
|
||||
|
||||
if (!fn["winSize"].empty())
|
||||
fn["winSize"] >> winSize;
|
||||
|
||||
if(!fn["winSizeNCC"].empty())
|
||||
fn["winSizeNCC"] >> winSizeNCC;
|
||||
|
||||
if(!fn["pointsInGrid"].empty())
|
||||
fn["pointsInGrid"] >> pointsInGrid;
|
||||
|
||||
if(!fn["maxLevel"].empty())
|
||||
fn["maxLevel"] >> maxLevel;
|
||||
|
||||
if(!fn["maxMedianLengthOfDisplacementDifference"].empty())
|
||||
fn["maxMedianLengthOfDisplacementDifference"] >> maxMedianLengthOfDisplacementDifference;
|
||||
|
||||
if(!fn["termCriteria_maxCount"].empty())
|
||||
fn["termCriteria_maxCount"] >> termCriteria.maxCount;
|
||||
|
||||
if(!fn["termCriteria_epsilon"].empty())
|
||||
fn["termCriteria_epsilon"] >> termCriteria.epsilon;
|
||||
}
|
||||
|
||||
void TrackerMedianFlow::Params::write( cv::FileStorage& fs ) const{
|
||||
fs << "pointsInGrid" << pointsInGrid;
|
||||
fs << "winSize" << winSize;
|
||||
fs << "maxLevel" << maxLevel;
|
||||
fs << "termCriteria_maxCount" << termCriteria.maxCount;
|
||||
fs << "termCriteria_epsilon" << termCriteria.epsilon;
|
||||
fs << "winSizeNCC" << winSizeNCC;
|
||||
fs << "maxMedianLengthOfDisplacementDifference" << maxMedianLengthOfDisplacementDifference;
|
||||
}
|
||||
|
||||
Ptr<TrackerMedianFlow> TrackerMedianFlow::create(const TrackerMedianFlow::Params ¶meters)
|
||||
{
|
||||
return makePtr<impl::TrackerMedianFlowImpl>(parameters);
|
||||
}
|
||||
Ptr<TrackerMedianFlow> TrackerMedianFlow::create()
|
||||
{
|
||||
return create(TrackerMedianFlow::Params());
|
||||
}
|
||||
|
||||
}}} // namespace
|
||||
@@ -0,0 +1,144 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
/*
|
||||
* TrackerContribSampler
|
||||
*/
|
||||
|
||||
/*
|
||||
* Constructor
|
||||
*/
|
||||
TrackerContribSampler::TrackerContribSampler()
|
||||
{
|
||||
blockAddTrackerSampler = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Destructor
|
||||
*/
|
||||
TrackerContribSampler::~TrackerContribSampler()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TrackerContribSampler::sampling( const Mat& image, Rect boundingBox )
|
||||
{
|
||||
|
||||
clearSamples();
|
||||
|
||||
for ( size_t i = 0; i < samplers.size(); i++ )
|
||||
{
|
||||
std::vector<Mat> current_samples;
|
||||
samplers[i].second->sampling( image, boundingBox, current_samples );
|
||||
|
||||
//push in samples all current_samples
|
||||
for ( size_t j = 0; j < current_samples.size(); j++ )
|
||||
{
|
||||
std::vector<Mat>::iterator it = samples.end();
|
||||
samples.insert( it, current_samples.at( j ) );
|
||||
}
|
||||
}
|
||||
|
||||
if( !blockAddTrackerSampler )
|
||||
{
|
||||
blockAddTrackerSampler = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool TrackerContribSampler::addTrackerSamplerAlgorithm( String trackerSamplerAlgorithmType )
|
||||
{
|
||||
if( blockAddTrackerSampler )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Ptr<TrackerContribSamplerAlgorithm> sampler = TrackerContribSamplerAlgorithm::create( trackerSamplerAlgorithmType );
|
||||
|
||||
if (!sampler)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
samplers.push_back( std::make_pair( trackerSamplerAlgorithmType, sampler ) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TrackerContribSampler::addTrackerSamplerAlgorithm( Ptr<TrackerContribSamplerAlgorithm>& sampler )
|
||||
{
|
||||
if( blockAddTrackerSampler )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!sampler)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
String trackerSamplerAlgorithmType = sampler->getClassName();
|
||||
samplers.push_back( std::make_pair( trackerSamplerAlgorithmType, sampler ) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<std::pair<String, Ptr<TrackerContribSamplerAlgorithm> > >& TrackerContribSampler::getSamplers() const
|
||||
{
|
||||
return samplers;
|
||||
}
|
||||
|
||||
const std::vector<Mat>& TrackerContribSampler::getSamples() const
|
||||
{
|
||||
return samples;
|
||||
}
|
||||
|
||||
void TrackerContribSampler::clearSamples()
|
||||
{
|
||||
samples.clear();
|
||||
}
|
||||
|
||||
|
||||
}}} // namespace
|
||||
@@ -0,0 +1,398 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "PFSolver.hpp"
|
||||
#include "TrackingFunctionPF.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
/*
|
||||
* TrackerContribSamplerAlgorithm
|
||||
*/
|
||||
|
||||
TrackerContribSamplerAlgorithm::~TrackerContribSamplerAlgorithm()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool TrackerContribSamplerAlgorithm::sampling(const Mat& image, const Rect& boundingBox, std::vector<Mat>& sample)
|
||||
{
|
||||
if( image.empty() )
|
||||
return false;
|
||||
|
||||
return samplingImpl( image, boundingBox, sample );
|
||||
}
|
||||
|
||||
Ptr<TrackerContribSamplerAlgorithm> TrackerContribSamplerAlgorithm::create( const String& trackerSamplerType )
|
||||
{
|
||||
if( trackerSamplerType.find( "CSC" ) == 0 )
|
||||
{
|
||||
return Ptr<TrackerContribSamplerCSC>( new TrackerContribSamplerCSC() );
|
||||
}
|
||||
|
||||
if( trackerSamplerType.find( "CS" ) == 0 )
|
||||
{
|
||||
return Ptr<TrackerSamplerCS>( new TrackerSamplerCS() );
|
||||
}
|
||||
|
||||
CV_Error(Error::StsNotImplemented, "Tracker sampler algorithm type not supported" );
|
||||
}
|
||||
|
||||
String TrackerContribSamplerAlgorithm::getClassName() const
|
||||
{
|
||||
return className;
|
||||
}
|
||||
|
||||
/**
|
||||
* TrackerContribSamplerCSC
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parameters
|
||||
*/
|
||||
|
||||
TrackerContribSamplerCSC::Params::Params()
|
||||
{
|
||||
initInRad = 3;
|
||||
initMaxNegNum = 65;
|
||||
searchWinSize = 25;
|
||||
trackInPosRad = 4;
|
||||
trackMaxNegNum = 65;
|
||||
trackMaxPosNum = 100000;
|
||||
|
||||
}
|
||||
|
||||
TrackerContribSamplerCSC::TrackerContribSamplerCSC( const TrackerContribSamplerCSC::Params ¶meters ) :
|
||||
params( parameters )
|
||||
{
|
||||
className = "CSC";
|
||||
mode = MODE_INIT_POS;
|
||||
rng = theRNG();
|
||||
|
||||
}
|
||||
|
||||
TrackerContribSamplerCSC::~TrackerContribSamplerCSC()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool TrackerContribSamplerCSC::samplingImpl( const Mat& image, Rect boundingBox, std::vector<Mat>& sample )
|
||||
{
|
||||
float inrad = 0;
|
||||
float outrad = 0;
|
||||
int maxnum = 0;
|
||||
|
||||
switch ( mode )
|
||||
{
|
||||
case MODE_INIT_POS:
|
||||
inrad = params.initInRad;
|
||||
sample = sampleImage( image, boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height, inrad );
|
||||
break;
|
||||
case MODE_INIT_NEG:
|
||||
inrad = 2.0f * params.searchWinSize;
|
||||
outrad = 1.5f * params.initInRad;
|
||||
maxnum = params.initMaxNegNum;
|
||||
sample = sampleImage( image, boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height, inrad, outrad, maxnum );
|
||||
break;
|
||||
case MODE_TRACK_POS:
|
||||
inrad = params.trackInPosRad;
|
||||
outrad = 0;
|
||||
maxnum = params.trackMaxPosNum;
|
||||
sample = sampleImage( image, boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height, inrad, outrad, maxnum );
|
||||
break;
|
||||
case MODE_TRACK_NEG:
|
||||
inrad = 1.5f * params.searchWinSize;
|
||||
outrad = params.trackInPosRad + 5;
|
||||
maxnum = params.trackMaxNegNum;
|
||||
sample = sampleImage( image, boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height, inrad, outrad, maxnum );
|
||||
break;
|
||||
case MODE_DETECT:
|
||||
inrad = params.searchWinSize;
|
||||
sample = sampleImage( image, boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height, inrad );
|
||||
break;
|
||||
default:
|
||||
inrad = params.initInRad;
|
||||
sample = sampleImage( image, boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height, inrad );
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TrackerContribSamplerCSC::setMode( int samplingMode )
|
||||
{
|
||||
mode = samplingMode;
|
||||
}
|
||||
|
||||
std::vector<Mat> TrackerContribSamplerCSC::sampleImage( const Mat& img, int x, int y, int w, int h, float inrad, float outrad, int maxnum )
|
||||
{
|
||||
int rowsz = img.rows - h - 1;
|
||||
int colsz = img.cols - w - 1;
|
||||
float inradsq = inrad * inrad;
|
||||
float outradsq = outrad * outrad;
|
||||
int dist;
|
||||
|
||||
uint minrow = max( 0, (int) y - (int) inrad );
|
||||
uint maxrow = min( (int) rowsz - 1, (int) y + (int) inrad );
|
||||
uint mincol = max( 0, (int) x - (int) inrad );
|
||||
uint maxcol = min( (int) colsz - 1, (int) x + (int) inrad );
|
||||
|
||||
//fprintf(stderr,"inrad=%f minrow=%d maxrow=%d mincol=%d maxcol=%d\n",inrad,minrow,maxrow,mincol,maxcol);
|
||||
|
||||
std::vector<Mat> samples;
|
||||
samples.resize( ( maxrow - minrow + 1 ) * ( maxcol - mincol + 1 ) );
|
||||
int i = 0;
|
||||
|
||||
float prob = ( (float) ( maxnum ) ) / samples.size();
|
||||
|
||||
for ( int r = minrow; r <= int( maxrow ); r++ )
|
||||
for ( int c = mincol; c <= int( maxcol ); c++ )
|
||||
{
|
||||
dist = ( y - r ) * ( y - r ) + ( x - c ) * ( x - c );
|
||||
if( float( rng.uniform( 0.f, 1.f ) ) < prob && dist < inradsq && dist >= outradsq )
|
||||
{
|
||||
samples[i] = img( Rect( c, r, w, h ) );
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
samples.resize( min( i, maxnum ) );
|
||||
return samples;
|
||||
}
|
||||
;
|
||||
|
||||
/**
|
||||
* TrackerSamplerCS
|
||||
*/
|
||||
TrackerSamplerCS::Params::Params()
|
||||
{
|
||||
overlap = 0.99f;
|
||||
searchFactor = 2;
|
||||
}
|
||||
|
||||
TrackerSamplerCS::TrackerSamplerCS( const TrackerSamplerCS::Params ¶meters ) :
|
||||
params( parameters )
|
||||
{
|
||||
className = "CS";
|
||||
mode = MODE_POSITIVE;
|
||||
}
|
||||
|
||||
void TrackerSamplerCS::setMode( int samplingMode )
|
||||
{
|
||||
mode = samplingMode;
|
||||
}
|
||||
|
||||
TrackerSamplerCS::~TrackerSamplerCS()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool TrackerSamplerCS::samplingImpl( const Mat& image, Rect boundingBox, std::vector<Mat>& sample )
|
||||
{
|
||||
|
||||
trackedPatch = boundingBox;
|
||||
Size imageSize( image.cols, image.rows );
|
||||
validROI = Rect( 0, 0, imageSize.width, imageSize.height );
|
||||
|
||||
Size trackedPatchSize( trackedPatch.width, trackedPatch.height );
|
||||
Rect trackingROI = getTrackingROI( params.searchFactor );
|
||||
|
||||
sample = patchesRegularScan( image, trackingROI, trackedPatchSize );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Rect TrackerSamplerCS::getTrackingROI( float searchFactor )
|
||||
{
|
||||
Rect searchRegion;
|
||||
|
||||
searchRegion = RectMultiply( trackedPatch, searchFactor );
|
||||
//check
|
||||
if( searchRegion.y + searchRegion.height > validROI.height )
|
||||
searchRegion.height = validROI.height - searchRegion.y;
|
||||
if( searchRegion.x + searchRegion.width > validROI.width )
|
||||
searchRegion.width = validROI.width - searchRegion.x;
|
||||
|
||||
return searchRegion;
|
||||
}
|
||||
|
||||
Rect TrackerSamplerCS::RectMultiply( const Rect & rect, float f )
|
||||
{
|
||||
cv::Rect r_tmp;
|
||||
r_tmp.y = (int) ( rect.y - ( (float) rect.height * f - rect.height ) / 2 );
|
||||
if( r_tmp.y < 0 )
|
||||
r_tmp.y = 0;
|
||||
r_tmp.x = (int) ( rect.x - ( (float) rect.width * f - rect.width ) / 2 );
|
||||
if( r_tmp.x < 0 )
|
||||
r_tmp.x = 0;
|
||||
r_tmp.height = (int) ( rect.height * f );
|
||||
r_tmp.width = (int) ( rect.width * f );
|
||||
|
||||
return r_tmp;
|
||||
}
|
||||
|
||||
Rect TrackerSamplerCS::getROI() const
|
||||
{
|
||||
return ROI;
|
||||
}
|
||||
|
||||
void TrackerSamplerCS::setCheckedROI( Rect imageROI )
|
||||
{
|
||||
int dCol, dRow;
|
||||
dCol = imageROI.x - validROI.x;
|
||||
dRow = imageROI.y - validROI.y;
|
||||
ROI.y = ( dRow < 0 ) ? validROI.y : imageROI.y;
|
||||
ROI.x = ( dCol < 0 ) ? validROI.x : imageROI.x;
|
||||
dCol = imageROI.x + imageROI.width - ( validROI.x + validROI.width );
|
||||
dRow = imageROI.y + imageROI.height - ( validROI.y + validROI.height );
|
||||
ROI.height = ( dRow > 0 ) ? validROI.height + validROI.y - ROI.y : imageROI.height + imageROI.y - ROI.y;
|
||||
ROI.width = ( dCol > 0 ) ? validROI.width + validROI.x - ROI.x : imageROI.width + imageROI.x - ROI.x;
|
||||
}
|
||||
|
||||
std::vector<Mat> TrackerSamplerCS::patchesRegularScan( const Mat& image, Rect trackingROI, Size patchSize )
|
||||
{
|
||||
std::vector<Mat> sample;
|
||||
if( ( validROI == trackingROI ) )
|
||||
ROI = trackingROI;
|
||||
else
|
||||
setCheckedROI( trackingROI );
|
||||
|
||||
if( mode == MODE_POSITIVE )
|
||||
{
|
||||
int num = 4;
|
||||
sample.resize( num );
|
||||
Mat singleSample = image( trackedPatch );
|
||||
for ( int i = 0; i < num; i++ )
|
||||
sample[i] = singleSample;
|
||||
return sample;
|
||||
}
|
||||
|
||||
int stepCol = (int) floor( ( 1.0f - params.overlap ) * (float) patchSize.width + 0.5f );
|
||||
int stepRow = (int) floor( ( 1.0f - params.overlap ) * (float) patchSize.height + 0.5f );
|
||||
if( stepCol <= 0 )
|
||||
stepCol = 1;
|
||||
if( stepRow <= 0 )
|
||||
stepRow = 1;
|
||||
|
||||
Size m_patchGrid;
|
||||
Rect m_rectUpperLeft;
|
||||
Rect m_rectUpperRight;
|
||||
Rect m_rectLowerLeft;
|
||||
Rect m_rectLowerRight;
|
||||
int num;
|
||||
|
||||
m_patchGrid.height = ( (int) ( (float) ( ROI.height - patchSize.height ) / stepRow ) + 1 );
|
||||
m_patchGrid.width = ( (int) ( (float) ( ROI.width - patchSize.width ) / stepCol ) + 1 );
|
||||
|
||||
num = m_patchGrid.width * m_patchGrid.height;
|
||||
sample.resize( num );
|
||||
int curPatch = 0;
|
||||
|
||||
m_rectUpperLeft = m_rectUpperRight = m_rectLowerLeft = m_rectLowerRight = cv::Rect( 0, 0, patchSize.width, patchSize.height );
|
||||
m_rectUpperLeft.y = ROI.y;
|
||||
m_rectUpperLeft.x = ROI.x;
|
||||
m_rectUpperRight.y = ROI.y;
|
||||
m_rectUpperRight.x = ROI.x + ROI.width - patchSize.width;
|
||||
m_rectLowerLeft.y = ROI.y + ROI.height - patchSize.height;
|
||||
m_rectLowerLeft.x = ROI.x;
|
||||
m_rectLowerRight.y = ROI.y + ROI.height - patchSize.height;
|
||||
m_rectLowerRight.x = ROI.x + ROI.width - patchSize.width;
|
||||
|
||||
if( mode == MODE_NEGATIVE )
|
||||
{
|
||||
int numSamples = 4;
|
||||
sample.resize( numSamples );
|
||||
sample[0] = image( m_rectUpperLeft );
|
||||
sample[1] = image( m_rectUpperRight );
|
||||
sample[2] = image( m_rectLowerLeft );
|
||||
sample[3] = image( m_rectLowerRight );
|
||||
return sample;
|
||||
}
|
||||
|
||||
for ( int curRow = 0; curRow < ROI.height - patchSize.height + 1; curRow += stepRow )
|
||||
{
|
||||
for ( int curCol = 0; curCol < ROI.width - patchSize.width + 1; curCol += stepCol )
|
||||
{
|
||||
Mat singleSample = image( Rect( curCol + ROI.x, curRow + ROI.y, patchSize.width, patchSize.height ) );
|
||||
sample[curPatch] = singleSample;
|
||||
curPatch++;
|
||||
}
|
||||
}
|
||||
|
||||
CV_Assert( curPatch == num );
|
||||
|
||||
return sample;
|
||||
}
|
||||
|
||||
TrackerSamplerPF::Params::Params(){
|
||||
iterationNum=20;
|
||||
particlesNum=100;
|
||||
alpha=0.9;
|
||||
std=(Mat_<double>(1,4)<<15.0,15.0,15.0,15.0);
|
||||
}
|
||||
TrackerSamplerPF::TrackerSamplerPF(const Mat& chosenRect,const TrackerSamplerPF::Params ¶meters):
|
||||
params( parameters ),_function(new TrackingFunctionPF(chosenRect)){
|
||||
className="PF";
|
||||
_solver=createPFSolver(_function,parameters.std,TermCriteria(TermCriteria::MAX_ITER,parameters.iterationNum,0.0),
|
||||
parameters.particlesNum,parameters.alpha);
|
||||
}
|
||||
bool TrackerSamplerPF::samplingImpl( const Mat& image, Rect boundingBox, std::vector<Mat>& sample ){
|
||||
Ptr<TrackerTargetState> ptr;
|
||||
Mat_<double> _last_guess=(Mat_<double>(1,4)<<(double)boundingBox.x,(double)boundingBox.y,
|
||||
(double)boundingBox.x+boundingBox.width,(double)boundingBox.y+boundingBox.height);
|
||||
PFSolver* promoted_solver=dynamic_cast<PFSolver*>(static_cast<MinProblemSolver*>(_solver));
|
||||
|
||||
promoted_solver->setParamsSTD(params.std);
|
||||
promoted_solver->minimize(_last_guess);
|
||||
dynamic_cast<TrackingFunctionPF*>(static_cast<MinProblemSolver::Function*>(promoted_solver->getFunction()))->update(image);
|
||||
while(promoted_solver->iteration() <= promoted_solver->getTermCriteria().maxCount);
|
||||
promoted_solver->getOptParam(_last_guess);
|
||||
|
||||
Rect res=Rect(Point_<int>((int)_last_guess(0,0),(int)_last_guess(0,1)),Point_<int>((int)_last_guess(0,2),(int)_last_guess(0,3)));
|
||||
sample.clear();
|
||||
sample.push_back(image(res));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}}} // namespace
|
||||
@@ -0,0 +1,268 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
Ptr<TrackerStateEstimator> TrackerStateEstimator::create( const String& trackeStateEstimatorType )
|
||||
{
|
||||
|
||||
if( trackeStateEstimatorType.find( "SVM" ) == 0 )
|
||||
{
|
||||
return Ptr<TrackerStateEstimatorSVM>( new TrackerStateEstimatorSVM() );
|
||||
}
|
||||
|
||||
if( trackeStateEstimatorType.find( "BOOSTING" ) == 0 )
|
||||
{
|
||||
CV_Error(Error::StsNotImplemented, "TrackerStateEstimatorMILBoosting API is not available");
|
||||
//return Ptr<TrackerStateEstimatorMILBoosting>( new TrackerStateEstimatorMILBoosting() );
|
||||
}
|
||||
|
||||
CV_Error( cv::Error::StsError, "Tracker state estimator type not supported" );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* TrackerStateEstimatorAdaBoosting
|
||||
*/
|
||||
TrackerStateEstimatorAdaBoosting::TrackerStateEstimatorAdaBoosting( int numClassifer, int initIterations, int nFeatures, Size patchSize, const Rect& ROI )
|
||||
{
|
||||
className = "ADABOOSTING";
|
||||
numBaseClassifier = numClassifer;
|
||||
numFeatures = nFeatures;
|
||||
iterationInit = initIterations;
|
||||
initPatchSize = patchSize;
|
||||
trained = false;
|
||||
sampleROI = ROI;
|
||||
|
||||
}
|
||||
|
||||
Rect TrackerStateEstimatorAdaBoosting::getSampleROI() const
|
||||
{
|
||||
return sampleROI;
|
||||
}
|
||||
|
||||
void TrackerStateEstimatorAdaBoosting::setSampleROI( const Rect& ROI )
|
||||
{
|
||||
sampleROI = ROI;
|
||||
}
|
||||
|
||||
/**
|
||||
* TrackerAdaBoostingTargetState::TrackerAdaBoostingTargetState
|
||||
*/
|
||||
TrackerStateEstimatorAdaBoosting::TrackerAdaBoostingTargetState::TrackerAdaBoostingTargetState( const Point2f& position, int width, int height,
|
||||
bool foreground, const Mat& responses )
|
||||
{
|
||||
setTargetPosition( position );
|
||||
setTargetWidth( width );
|
||||
setTargetHeight( height );
|
||||
|
||||
setTargetFg( foreground );
|
||||
setTargetResponses( responses );
|
||||
}
|
||||
|
||||
void TrackerStateEstimatorAdaBoosting::TrackerAdaBoostingTargetState::setTargetFg( bool foreground )
|
||||
{
|
||||
isTarget = foreground;
|
||||
}
|
||||
|
||||
bool TrackerStateEstimatorAdaBoosting::TrackerAdaBoostingTargetState::isTargetFg() const
|
||||
{
|
||||
return isTarget;
|
||||
}
|
||||
|
||||
void TrackerStateEstimatorAdaBoosting::TrackerAdaBoostingTargetState::setTargetResponses( const Mat& responses )
|
||||
{
|
||||
targetResponses = responses;
|
||||
}
|
||||
|
||||
Mat TrackerStateEstimatorAdaBoosting::TrackerAdaBoostingTargetState::getTargetResponses() const
|
||||
{
|
||||
return targetResponses;
|
||||
}
|
||||
|
||||
TrackerStateEstimatorAdaBoosting::~TrackerStateEstimatorAdaBoosting()
|
||||
{
|
||||
|
||||
}
|
||||
void TrackerStateEstimatorAdaBoosting::setCurrentConfidenceMap( ConfidenceMap& confidenceMap )
|
||||
{
|
||||
currentConfidenceMap.clear();
|
||||
currentConfidenceMap = confidenceMap;
|
||||
}
|
||||
|
||||
std::vector<int> TrackerStateEstimatorAdaBoosting::computeReplacedClassifier()
|
||||
{
|
||||
return replacedClassifier;
|
||||
}
|
||||
|
||||
std::vector<int> TrackerStateEstimatorAdaBoosting::computeSwappedClassifier()
|
||||
{
|
||||
return swappedClassifier;
|
||||
}
|
||||
|
||||
std::vector<int> TrackerStateEstimatorAdaBoosting::computeSelectedWeakClassifier()
|
||||
{
|
||||
return boostClassifier->getSelectedWeakClassifier();
|
||||
}
|
||||
|
||||
Ptr<TrackerTargetState> TrackerStateEstimatorAdaBoosting::estimateImpl( const std::vector<ConfidenceMap>& /*confidenceMaps*/ )
|
||||
{
|
||||
//run classify in order to compute next location
|
||||
if( currentConfidenceMap.empty() )
|
||||
return Ptr<TrackerTargetState>();
|
||||
|
||||
std::vector<Mat> images;
|
||||
|
||||
for ( size_t i = 0; i < currentConfidenceMap.size(); i++ )
|
||||
{
|
||||
Ptr<TrackerAdaBoostingTargetState> currentTargetState = currentConfidenceMap.at( i ).first.staticCast<TrackerAdaBoostingTargetState>();
|
||||
images.push_back( currentTargetState->getTargetResponses() );
|
||||
}
|
||||
|
||||
int bestIndex;
|
||||
boostClassifier->classifySmooth( images, sampleROI, bestIndex );
|
||||
|
||||
// get bestIndex from classifySmooth
|
||||
return currentConfidenceMap.at( bestIndex ).first;
|
||||
|
||||
}
|
||||
|
||||
void TrackerStateEstimatorAdaBoosting::updateImpl( std::vector<ConfidenceMap>& confidenceMaps )
|
||||
{
|
||||
if( !trained )
|
||||
{
|
||||
//this is the first time that the classifier is built
|
||||
int numWeakClassifier = numBaseClassifier * 10;
|
||||
|
||||
bool useFeatureExchange = true;
|
||||
boostClassifier = Ptr<StrongClassifierDirectSelection>(
|
||||
new StrongClassifierDirectSelection( numBaseClassifier, numWeakClassifier, initPatchSize, sampleROI, useFeatureExchange, iterationInit ) );
|
||||
//init base classifiers
|
||||
boostClassifier->initBaseClassifier();
|
||||
|
||||
trained = true;
|
||||
}
|
||||
|
||||
ConfidenceMap lastConfidenceMap = confidenceMaps.back();
|
||||
bool featureEx = boostClassifier->getUseFeatureExchange();
|
||||
|
||||
replacedClassifier.clear();
|
||||
replacedClassifier.resize( lastConfidenceMap.size(), -1 );
|
||||
swappedClassifier.clear();
|
||||
swappedClassifier.resize( lastConfidenceMap.size(), -1 );
|
||||
|
||||
for ( size_t i = 0; i < lastConfidenceMap.size() / 2; i++ )
|
||||
{
|
||||
Ptr<TrackerAdaBoostingTargetState> currentTargetState = lastConfidenceMap.at( i ).first.staticCast<TrackerAdaBoostingTargetState>();
|
||||
|
||||
int currentFg = 1;
|
||||
if( !currentTargetState->isTargetFg() )
|
||||
currentFg = -1;
|
||||
Mat res = currentTargetState->getTargetResponses();
|
||||
|
||||
boostClassifier->update( res, currentFg );
|
||||
if( featureEx )
|
||||
{
|
||||
replacedClassifier[i] = boostClassifier->getReplacedClassifier();
|
||||
swappedClassifier[i] = boostClassifier->getSwappedClassifier();
|
||||
if( replacedClassifier[i] >= 0 && swappedClassifier[i] >= 0 )
|
||||
boostClassifier->replaceWeakClassifier( replacedClassifier[i] );
|
||||
}
|
||||
else
|
||||
{
|
||||
replacedClassifier[i] = -1;
|
||||
swappedClassifier[i] = -1;
|
||||
}
|
||||
|
||||
int mapPosition = (int)(i + lastConfidenceMap.size() / 2);
|
||||
Ptr<TrackerAdaBoostingTargetState> currentTargetState2 = lastConfidenceMap.at( mapPosition ).first.staticCast<TrackerAdaBoostingTargetState>();
|
||||
|
||||
currentFg = 1;
|
||||
if( !currentTargetState2->isTargetFg() )
|
||||
currentFg = -1;
|
||||
const Mat res2 = currentTargetState2->getTargetResponses();
|
||||
|
||||
boostClassifier->update( res2, currentFg );
|
||||
if( featureEx )
|
||||
{
|
||||
replacedClassifier[mapPosition] = boostClassifier->getReplacedClassifier();
|
||||
swappedClassifier[mapPosition] = boostClassifier->getSwappedClassifier();
|
||||
if( replacedClassifier[mapPosition] >= 0 && swappedClassifier[mapPosition] >= 0 )
|
||||
boostClassifier->replaceWeakClassifier( replacedClassifier[mapPosition] );
|
||||
}
|
||||
else
|
||||
{
|
||||
replacedClassifier[mapPosition] = -1;
|
||||
swappedClassifier[mapPosition] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* TrackerStateEstimatorSVM
|
||||
*/
|
||||
TrackerStateEstimatorSVM::TrackerStateEstimatorSVM()
|
||||
{
|
||||
className = "SVM";
|
||||
}
|
||||
|
||||
TrackerStateEstimatorSVM::~TrackerStateEstimatorSVM()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Ptr<TrackerTargetState> TrackerStateEstimatorSVM::estimateImpl( const std::vector<ConfidenceMap>& confidenceMaps )
|
||||
{
|
||||
return confidenceMaps.back().back().first;
|
||||
}
|
||||
|
||||
void TrackerStateEstimatorSVM::updateImpl( std::vector<ConfidenceMap>& /*confidenceMaps*/)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}}} // namespace
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,72 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "tracking_utils.hpp"
|
||||
|
||||
namespace cv {
|
||||
|
||||
double tracking_internal::computeNCC(const Mat& patch1, const Mat& patch2)
|
||||
{
|
||||
CV_CheckEQ(patch1.rows, patch2.rows, "");
|
||||
CV_CheckEQ(patch1.cols, patch2.cols, "");
|
||||
|
||||
int N = patch1.rows * patch1.cols;
|
||||
|
||||
if(N <= 1000 && patch1.type() == CV_8U && patch2.type() == CV_8U)
|
||||
{
|
||||
unsigned s1 = 0, s2 = 0;
|
||||
unsigned n1 = 0, n2 = 0;
|
||||
unsigned prod = 0;
|
||||
|
||||
if(patch1.isContinuous() && patch2.isContinuous())
|
||||
{
|
||||
const uchar* p1Ptr = patch1.ptr<uchar>(0);
|
||||
const uchar* p2Ptr = patch2.ptr<uchar>(0);
|
||||
|
||||
for(int j = 0; j < N; j++)
|
||||
{
|
||||
s1 += p1Ptr[j];
|
||||
s2 += p2Ptr[j];
|
||||
n1 += p1Ptr[j]*p1Ptr[j];
|
||||
n2 += p2Ptr[j]*p2Ptr[j];
|
||||
prod += p1Ptr[j]*p2Ptr[j];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < patch1.rows; i++)
|
||||
{
|
||||
const uchar* p1Ptr = patch1.ptr<uchar>(i);
|
||||
const uchar* p2Ptr = patch2.ptr<uchar>(i);
|
||||
|
||||
for(int j = 0; j < patch1.cols; j++)
|
||||
{
|
||||
s1 += p1Ptr[j];
|
||||
s2 += p2Ptr[j];
|
||||
n1 += p1Ptr[j]*p1Ptr[j];
|
||||
n2 += p2Ptr[j]*p2Ptr[j];
|
||||
prod += p1Ptr[j]*p2Ptr[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double sq1 = sqrt(std::max(0.0, n1 - 1.0 * s1 * s1 / N));
|
||||
double sq2 = sqrt(std::max(0.0, n2 - 1.0 * s2 * s2 / N));
|
||||
return (sq2 == 0) ? sq1 / abs(sq1) : (prod - 1.0 * s1 * s2 / N) / sq1 / sq2;
|
||||
}
|
||||
else
|
||||
{
|
||||
double s1 = sum(patch1)(0);
|
||||
double s2 = sum(patch2)(0);
|
||||
double n1 = norm(patch1, NORM_L2SQR);
|
||||
double n2 = norm(patch2, NORM_L2SQR);
|
||||
double prod=patch1.dot(patch2);
|
||||
double sq1 = sqrt(std::max(0.0, n1 - 1.0 * s1 * s1 / N));
|
||||
double sq2 = sqrt(std::max(0.0, n2 - 1.0 * s2 * s2 / N));
|
||||
return (sq2 == 0) ? sq1 / abs(sq1) : (prod - s1 * s2 / N) / sq1 / sq2;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,46 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
#ifndef __OPENCV_TRACKING_UTILS_HPP__
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace cv {
|
||||
namespace tracking_internal {
|
||||
|
||||
/** Computes normalized corellation coefficient between the two patches (they should be
|
||||
* of the same size).*/
|
||||
double computeNCC(const Mat& patch1, const Mat& patch2);
|
||||
|
||||
template<typename T>
|
||||
T getMedianAndDoPartition(std::vector<T>& values)
|
||||
{
|
||||
size_t size = values.size();
|
||||
if(size%2==0)
|
||||
{
|
||||
std::nth_element(values.begin(), values.begin() + size/2-1, values.end());
|
||||
T firstMedian = values[size/2-1];
|
||||
|
||||
std::nth_element(values.begin(), values.begin() + size/2, values.end());
|
||||
T secondMedian = values[size/2];
|
||||
|
||||
return (firstMedian + secondMedian) / (T)2;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t medianIndex = (size - 1) / 2;
|
||||
std::nth_element(values.begin(), values.begin() + medianIndex, values.end());
|
||||
|
||||
return values[medianIndex];
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T getMedian(const std::vector<T>& values)
|
||||
{
|
||||
std::vector<T> copy(values);
|
||||
return getMedianAndDoPartition(copy);
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
#endif
|
||||
@@ -0,0 +1,77 @@
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "opencv2/tracking/twist.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
inline namespace tracking
|
||||
{
|
||||
|
||||
void computeInteractionMatrix(const cv::Mat& uv, const cv::Mat& depths, const cv::Mat& K_, cv::Mat& J)
|
||||
{
|
||||
CV_Assert(uv.cols == depths.cols);
|
||||
CV_Assert(depths.type() == CV_32F);
|
||||
CV_Assert(K_.cols == 3 && K_.rows == 3 && K_.type() == CV_32F);
|
||||
|
||||
J.create(depths.cols * 2, 6, CV_32F);
|
||||
J.setTo(0);
|
||||
|
||||
Matx33f K, Kinv;
|
||||
K_.copyTo(K);
|
||||
Kinv = K.inv();
|
||||
|
||||
for (int i = 0; i < uv.cols; i++)
|
||||
{
|
||||
const float z = depths.at<float>(i);
|
||||
// skip points with zero depth
|
||||
if (cv::abs(z) < 0.001f)
|
||||
continue;
|
||||
|
||||
const cv::Matx31f p(uv.at<float>(0, i), uv.at<float>(1, i), 1.0);
|
||||
|
||||
// convert to normalized image-plane coordinates
|
||||
Matx31f xy = Kinv * p;
|
||||
float x = xy(0,0);
|
||||
float y = xy(1,0);
|
||||
|
||||
Matx<float, 2, 6> Jp;
|
||||
|
||||
// 2x6 Jacobian for this point
|
||||
Jp(0, 0) = -1 / z;
|
||||
Jp(0, 1) = 0.0;
|
||||
Jp(0, 2) = x / z;
|
||||
Jp(0, 3) = x * y;
|
||||
Jp(0, 4) = -(1 + x * x);
|
||||
Jp(0, 5) = y;
|
||||
Jp(1, 0) = 0.0;
|
||||
Jp(1, 1) = -1 / z;
|
||||
Jp(1, 2) = y / z;
|
||||
Jp(1, 3) = 1 + y * y;
|
||||
Jp(1, 4) = -x * y;
|
||||
Jp(1, 5) = -x;
|
||||
|
||||
Jp = Matx22f(K(0,0), K(0,1), K(1,0), K(1,1)) * Jp;
|
||||
|
||||
// push into Jacobian
|
||||
Mat(2, 6, CV_32F, Jp.val).copyTo(J(cv::Rect(0, 2 * i, 6, 2)));
|
||||
}
|
||||
}
|
||||
|
||||
cv::Vec6d computeTwist(const cv::Mat& uv, const cv::Mat& duv, const cv::Mat& depths,
|
||||
const cv::Mat& K)
|
||||
{
|
||||
CV_Assert(uv.cols * 2 == duv.rows);
|
||||
|
||||
cv::Mat J;
|
||||
computeInteractionMatrix(uv, depths, K, J);
|
||||
cv::Mat Jinv;
|
||||
cv::invert(J, Jinv, cv::DECOMP_SVD);
|
||||
cv::Mat twist = Jinv * duv;
|
||||
return twist;
|
||||
}
|
||||
|
||||
} // namespace tracking
|
||||
} // namespace detail
|
||||
} // namespace cv
|
||||
@@ -0,0 +1,372 @@
|
||||
/*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) 2015, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "opencv2/tracking/kalman_filters.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
inline namespace kalman_filters {
|
||||
|
||||
void UnscentedKalmanFilterParams::
|
||||
init( int dp, int mp, int cp, double processNoiseCovDiag, double measurementNoiseCovDiag,
|
||||
Ptr<UkfSystemModel> dynamicalSystem, int type )
|
||||
{
|
||||
CV_Assert( dp > 0 && mp > 0 );
|
||||
DP = dp;
|
||||
MP = mp;
|
||||
CP = std::max( cp, 0 );
|
||||
CV_Assert( type == CV_32F || type == CV_64F );
|
||||
dataType = type;
|
||||
|
||||
this->model = dynamicalSystem;
|
||||
|
||||
stateInit = Mat::zeros( DP, 1, type );
|
||||
errorCovInit = Mat::eye( DP, DP, type );
|
||||
|
||||
processNoiseCov = processNoiseCovDiag*Mat::eye( DP, DP, type );
|
||||
measurementNoiseCov = measurementNoiseCovDiag*Mat::eye( MP, MP, type );
|
||||
|
||||
alpha = 1e-3;
|
||||
k = 0.0;
|
||||
beta = 2.0;
|
||||
}
|
||||
|
||||
UnscentedKalmanFilterParams::
|
||||
UnscentedKalmanFilterParams( int dp, int mp, int cp, double processNoiseCovDiag, double measurementNoiseCovDiag,
|
||||
Ptr<UkfSystemModel> dynamicalSystem, int type )
|
||||
{
|
||||
init( dp, mp, cp, processNoiseCovDiag, measurementNoiseCovDiag, dynamicalSystem, type );
|
||||
}
|
||||
|
||||
class UnscentedKalmanFilterImpl: public UnscentedKalmanFilter
|
||||
{
|
||||
|
||||
int DP; // dimensionality of the state vector
|
||||
int MP; // dimensionality of the measurement vector
|
||||
int CP; // dimensionality of the control vector
|
||||
int dataType; // type of elements of vectors and matrices
|
||||
|
||||
Mat state; // estimate of the system state (x*), DP x 1
|
||||
Mat errorCov; // estimate of the state cross-covariance matrix (P), DP x DP
|
||||
|
||||
Mat processNoiseCov; // process noise cross-covariance matrix (Q), DP x DP
|
||||
Mat measurementNoiseCov; // measurement noise cross-covariance matrix (R), MP x MP
|
||||
|
||||
Ptr<UkfSystemModel> model; // object of the class containing functions for computing the next state and the measurement.
|
||||
|
||||
// Parameters of algorithm
|
||||
double alpha; // parameter, default is 1e-3
|
||||
double k; // parameter, default is 0
|
||||
double beta; // parameter, default is 2.0
|
||||
|
||||
double lambda; // internal parameter, lambda = alpha*alpha*( DP + k ) - DP;
|
||||
double tmpLambda; // internal parameter, tmpLambda = alpha*alpha*( DP + k );
|
||||
|
||||
// Auxillary members
|
||||
Mat measurementEstimate; // estimate of current measurement (y*), MP x 1
|
||||
|
||||
Mat sigmaPoints; // set of sigma points ( x_i, i = 1..2*DP+1 ), DP x 2*DP+1
|
||||
|
||||
Mat transitionSPFuncVals; // set of state function values at sigma points ( f_i, i = 1..2*DP+1 ), DP x 2*DP+1
|
||||
Mat measurementSPFuncVals; // set of measurement function values at sigma points ( h_i, i = 1..2*DP+1 ), MP x 2*DP+1
|
||||
|
||||
Mat transitionSPFuncValsCenter; // set of state function values at sigma points minus estimate of state ( fc_i, i = 1..2*DP+1 ), DP x 2*DP+1
|
||||
Mat measurementSPFuncValsCenter; // set of measurement function values at sigma points minus estimate of measurement ( hc_i, i = 1..2*DP+1 ), MP x 2*DP+1
|
||||
|
||||
Mat Wm; // vector of weights for estimate mean, 2*DP+1 x 1
|
||||
Mat Wc; // matrix of weights for estimate covariance, 2*DP+1 x 2*DP+1
|
||||
|
||||
Mat gain; // Kalman gain matrix (K), DP x MP
|
||||
Mat xyCov; // estimate of the covariance between x* and y* (Sxy), DP x MP
|
||||
Mat yyCov; // estimate of the y* cross-covariance matrix (Syy), MP x MP
|
||||
|
||||
Mat r; // zero vector of process noise for getting transitionSPFuncVals,
|
||||
Mat q; // zero vector of measurement noise for getting measurementSPFuncVals
|
||||
|
||||
Mat getSigmaPoints( const Mat& mean, const Mat& covMatrix, double coef );
|
||||
|
||||
public:
|
||||
|
||||
UnscentedKalmanFilterImpl( const UnscentedKalmanFilterParams& params );
|
||||
~UnscentedKalmanFilterImpl();
|
||||
|
||||
// perform prediction step
|
||||
// control - the optional control vector, CP x 1
|
||||
Mat predict( InputArray control = noArray() ) CV_OVERRIDE;
|
||||
|
||||
// perform correction step
|
||||
// measurement - current measurement vector, MP x 1
|
||||
Mat correct( InputArray measurement ) CV_OVERRIDE;
|
||||
|
||||
// Get system parameters
|
||||
Mat getProcessNoiseCov() const CV_OVERRIDE;
|
||||
Mat getMeasurementNoiseCov() const CV_OVERRIDE;
|
||||
Mat getErrorCov() const CV_OVERRIDE;
|
||||
|
||||
// Get the state estimate
|
||||
Mat getState() const CV_OVERRIDE;
|
||||
};
|
||||
|
||||
UnscentedKalmanFilterImpl::UnscentedKalmanFilterImpl(const UnscentedKalmanFilterParams& params)
|
||||
{
|
||||
alpha = params.alpha;
|
||||
beta = params.beta;
|
||||
k = params.k;
|
||||
|
||||
CV_Assert( params.DP > 0 && params.MP > 0 );
|
||||
CV_Assert( params.dataType == CV_32F || params.dataType == CV_64F );
|
||||
DP = params.DP;
|
||||
MP = params.MP;
|
||||
CP = std::max( params.CP, 0 );
|
||||
dataType = params.dataType;
|
||||
|
||||
model = params.model;
|
||||
|
||||
CV_Assert( params.stateInit.cols == 1 && params.stateInit.rows == DP );
|
||||
CV_Assert( params.errorCovInit.cols == DP && params.errorCovInit.rows == DP );
|
||||
state = params.stateInit.clone();
|
||||
errorCov = params.errorCovInit.clone();
|
||||
|
||||
CV_Assert( params.processNoiseCov.cols == DP && params.processNoiseCov.rows == DP );
|
||||
CV_Assert( params.measurementNoiseCov.cols == MP && params.measurementNoiseCov.rows == MP );
|
||||
processNoiseCov = params.processNoiseCov.clone();
|
||||
measurementNoiseCov = params.measurementNoiseCov.clone();
|
||||
|
||||
measurementEstimate = Mat::zeros( MP, 1, dataType);
|
||||
|
||||
q = Mat::zeros( DP, 1, dataType);
|
||||
r = Mat::zeros( MP, 1, dataType);
|
||||
|
||||
gain = Mat::zeros( DP, DP, dataType );
|
||||
|
||||
transitionSPFuncVals = Mat::zeros( DP, 2*DP+1, dataType );
|
||||
measurementSPFuncVals = Mat::zeros( MP, 2*DP+1, dataType );
|
||||
|
||||
transitionSPFuncValsCenter = Mat::zeros( DP, 2*DP+1, dataType );
|
||||
measurementSPFuncValsCenter = Mat::zeros( MP, 2*DP+1, dataType );
|
||||
|
||||
lambda = alpha*alpha*( DP + k ) - DP;
|
||||
tmpLambda = lambda + DP;
|
||||
|
||||
double tmp2Lambda = 0.5/tmpLambda;
|
||||
|
||||
Wm = tmp2Lambda * Mat::ones( 2*DP+1, 1, dataType );
|
||||
Wc = tmp2Lambda * Mat::eye( 2*DP+1, 2*DP+1, dataType );
|
||||
|
||||
if ( dataType == CV_64F )
|
||||
{
|
||||
Wm.at<double>(0,0) = lambda/tmpLambda;
|
||||
Wc.at<double>(0,0) = lambda/tmpLambda + 1.0 - alpha*alpha + beta;
|
||||
}
|
||||
else
|
||||
{
|
||||
Wm.at<float>(0,0) = (float)(lambda/tmpLambda);
|
||||
Wc.at<float>(0,0) = (float)(lambda/tmpLambda + 1.0 - alpha*alpha + beta);
|
||||
}
|
||||
}
|
||||
|
||||
UnscentedKalmanFilterImpl::~UnscentedKalmanFilterImpl()
|
||||
{
|
||||
state.release();
|
||||
errorCov.release();
|
||||
|
||||
processNoiseCov.release();
|
||||
measurementNoiseCov.release();
|
||||
|
||||
measurementEstimate.release();
|
||||
|
||||
sigmaPoints.release();
|
||||
|
||||
transitionSPFuncVals.release();
|
||||
measurementSPFuncVals.release();
|
||||
|
||||
transitionSPFuncValsCenter.release();
|
||||
measurementSPFuncValsCenter.release();
|
||||
|
||||
Wm.release();
|
||||
Wc.release();
|
||||
|
||||
gain.release();
|
||||
xyCov.release();
|
||||
yyCov.release();
|
||||
|
||||
r.release();
|
||||
q.release();
|
||||
}
|
||||
|
||||
Mat UnscentedKalmanFilterImpl::getSigmaPoints(const Mat &mean, const Mat &covMatrix, double coef)
|
||||
{
|
||||
// x_0 = mean
|
||||
// x_i = mean + coef * cholesky( covMatrix ), i = 1..n
|
||||
// x_(i+n) = mean - coef * cholesky( covMatrix ), i = 1..n
|
||||
|
||||
int n = mean.rows;
|
||||
Mat points = repeat(mean, 1, 2*n+1);
|
||||
|
||||
Mat covMatrixL = covMatrix.clone();
|
||||
|
||||
// covMatrixL = cholesky( covMatrix )
|
||||
if ( dataType == CV_64F )
|
||||
choleskyDecomposition<double>(
|
||||
covMatrix.ptr<double>(), covMatrix.step, covMatrix.rows,
|
||||
covMatrixL.ptr<double>(), covMatrixL.step );
|
||||
else if ( dataType == CV_32F )
|
||||
choleskyDecomposition<float>(
|
||||
covMatrix.ptr<float>(), covMatrix.step, covMatrix.rows,
|
||||
covMatrixL.ptr<float>(), covMatrixL.step );
|
||||
|
||||
covMatrixL = coef * covMatrixL;
|
||||
|
||||
Mat p_plus = points( Rect( 1, 0, n, n ) );
|
||||
Mat p_minus = points( Rect( n+1, 0, n, n ) );
|
||||
|
||||
add(p_plus, covMatrixL, p_plus);
|
||||
subtract(p_minus, covMatrixL, p_minus);
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
Mat UnscentedKalmanFilterImpl::predict(InputArray _control)
|
||||
{
|
||||
Mat control = _control.getMat();
|
||||
// get sigma points from x* and P
|
||||
sigmaPoints = getSigmaPoints( state, errorCov, sqrt( tmpLambda ) );
|
||||
|
||||
// compute f-function values at sigma points
|
||||
// f_i = f(x_i, control, 0), i = 0..2*DP
|
||||
Mat x, fx;
|
||||
for ( int i = 0; i<2*DP+1; i++)
|
||||
{
|
||||
x = sigmaPoints( Rect( i, 0, 1, DP) );
|
||||
fx = transitionSPFuncVals( Rect( i, 0, 1, DP) );
|
||||
model->stateConversionFunction( x, control, q, fx );
|
||||
}
|
||||
// compute the estimate of state as mean f-function value at sigma point
|
||||
// x* = SUM_{i=0}^{2*DP}( Wm[i]*f_i )
|
||||
state = transitionSPFuncVals * Wm;
|
||||
|
||||
// compute f-function values at sigma points minus estimate of state
|
||||
// fc_i = f_i - x*, i = 0..2*DP
|
||||
subtract(transitionSPFuncVals, repeat( state, 1, 2*DP+1 ), transitionSPFuncValsCenter);
|
||||
|
||||
// compute the estimate of the state cross-covariance matrix
|
||||
// P = SUM_{i=0}^{2*DP}( Wc[i]*fc_i*fc_i.t ) + Q
|
||||
errorCov = transitionSPFuncValsCenter * Wc * transitionSPFuncValsCenter.t() + processNoiseCov;
|
||||
|
||||
return state.clone();
|
||||
}
|
||||
|
||||
Mat UnscentedKalmanFilterImpl::correct(InputArray _measurement)
|
||||
{
|
||||
Mat measurement = _measurement.getMat();
|
||||
// get sigma points from x* and P
|
||||
sigmaPoints = getSigmaPoints( state, errorCov, sqrt( tmpLambda ) );
|
||||
|
||||
// compute h-function values at sigma points
|
||||
// h_i = h(x_i, 0), i = 0..2*DP
|
||||
Mat x, hx;
|
||||
for ( int i = 0; i<2*DP+1; i++)
|
||||
{
|
||||
x = sigmaPoints( Rect( i, 0, 1, DP) );
|
||||
hx = measurementSPFuncVals( Rect( i, 0, 1, MP) );
|
||||
model->measurementFunction( x, r, hx );
|
||||
}
|
||||
|
||||
// compute the estimate of measurement as mean h-function value at sigma point
|
||||
// y* = SUM_{i=0}^{2*DP}( Wm[i]*h_i )
|
||||
measurementEstimate = measurementSPFuncVals * Wm;
|
||||
|
||||
// compute h-function values at sigma points minus estimate of state
|
||||
// hc_i = h_i - y*, i = 0..2*DP
|
||||
subtract(measurementSPFuncVals, repeat( measurementEstimate, 1, 2*DP+1 ), measurementSPFuncValsCenter);
|
||||
|
||||
// compute the estimate of the y* cross-covariance matrix
|
||||
// Syy = SUM_{i=0}^{2*DP}( Wc[i]*hc_i*hc_i.t ) + R
|
||||
yyCov = measurementSPFuncValsCenter * Wc * measurementSPFuncValsCenter.t() + measurementNoiseCov;
|
||||
|
||||
// compute the estimate of the covariance between x* and y*
|
||||
// Sxy = SUM_{i=0}^{2*DP}( Wc[i]*fc_i*hc_i.t )
|
||||
xyCov = transitionSPFuncValsCenter * Wc * measurementSPFuncValsCenter.t();
|
||||
|
||||
// compute the Kalman gain matrix
|
||||
// K = Sxy * Syy^(-1)
|
||||
gain = xyCov * yyCov.inv(DECOMP_SVD);
|
||||
|
||||
// compute the corrected estimate of state
|
||||
// x* = x* + K*(y - y*), y - current measurement
|
||||
state = state + gain * ( measurement - measurementEstimate );
|
||||
|
||||
// compute the corrected estimate of the state cross-covariance matrix
|
||||
// P = P - K*Sxy.t
|
||||
errorCov = errorCov - gain * xyCov.t();
|
||||
|
||||
return state.clone();
|
||||
}
|
||||
|
||||
Mat UnscentedKalmanFilterImpl::getProcessNoiseCov() const
|
||||
{
|
||||
return processNoiseCov.clone();
|
||||
}
|
||||
|
||||
Mat UnscentedKalmanFilterImpl::getMeasurementNoiseCov() const
|
||||
{
|
||||
return measurementNoiseCov.clone();
|
||||
}
|
||||
|
||||
Mat UnscentedKalmanFilterImpl::getErrorCov() const
|
||||
{
|
||||
return errorCov.clone();
|
||||
}
|
||||
|
||||
Mat UnscentedKalmanFilterImpl::getState() const
|
||||
{
|
||||
return state.clone();
|
||||
}
|
||||
|
||||
Ptr<UnscentedKalmanFilter> createUnscentedKalmanFilter(const UnscentedKalmanFilterParams ¶ms)
|
||||
{
|
||||
Ptr<UnscentedKalmanFilter> kfu( new UnscentedKalmanFilterImpl(params) );
|
||||
return kfu;
|
||||
}
|
||||
|
||||
}}}} // namespace
|
||||
@@ -0,0 +1,438 @@
|
||||
/*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) 2015, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
#include "opencv2/tracking/kalman_filters.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
using namespace cv::detail;
|
||||
|
||||
// In this two tests Augmented Unscented Kalman Filter are applied to the dynamic system from example "The reentry problem" from
|
||||
// "A New Extension of the Kalman Filter to Nonlinear Systems" by Simon J. Julier and Jeffrey K. Uhlmann.
|
||||
class BallisticModel: public UkfSystemModel
|
||||
{
|
||||
static const double step_h;
|
||||
|
||||
Mat diff_eq(const Mat& x)
|
||||
{
|
||||
double x1 = x.at<double>(0, 0);
|
||||
double x2 = x.at<double>(1, 0);
|
||||
double x3 = x.at<double>(2, 0);
|
||||
double x4 = x.at<double>(3, 0);
|
||||
double x5 = x.at<double>(4, 0);
|
||||
|
||||
const double h0 = 9.3;
|
||||
const double beta0 = 0.59783;
|
||||
const double Gm = 3.9860044 * 1e5;
|
||||
const double r_e = 6374;
|
||||
|
||||
const double r = sqrt( x1*x1 + x2*x2 );
|
||||
const double v = sqrt( x3*x3 + x4*x4 );
|
||||
const double d = - beta0 * exp( ( r_e - r )/h0 ) * exp( x5 ) * v;
|
||||
const double g = - Gm / (r*r*r);
|
||||
|
||||
Mat fx = x.clone();
|
||||
|
||||
fx.at<double>(0, 0) = x3;
|
||||
fx.at<double>(1, 0) = x4;
|
||||
fx.at<double>(2, 0) = d * x3 + g * x1;
|
||||
fx.at<double>(3, 0) = d * x4 + g * x2;
|
||||
fx.at<double>(4, 0) = 0.0;
|
||||
|
||||
return fx;
|
||||
}
|
||||
public:
|
||||
void stateConversionFunction(const Mat& x_k, const Mat& u_k, const Mat& v_k, Mat& x_kplus1)
|
||||
{
|
||||
Mat v = sqrt(step_h) * v_k.clone();
|
||||
v.at<double>(0, 0) = 0.0;
|
||||
v.at<double>(1, 0) = 0.0;
|
||||
|
||||
Mat k1 = diff_eq( x_k ) + v;
|
||||
Mat tmp = x_k + step_h*0.5*k1;
|
||||
Mat k2 = diff_eq( tmp ) + v;
|
||||
tmp = x_k + step_h*0.5*k2;
|
||||
Mat k3 = diff_eq( tmp ) + v;
|
||||
tmp = x_k + step_h*k3;
|
||||
Mat k4 = diff_eq( tmp ) + v;
|
||||
|
||||
x_kplus1 = x_k + (1.0/6.0)*step_h*( k1 + 2.0*k2 + 2.0*k3 + k4 ) + u_k;
|
||||
}
|
||||
|
||||
void measurementFunction(const Mat& x_k, const Mat& n_k, Mat& z_k)
|
||||
{
|
||||
double x1 = x_k.at<double>(0, 0);
|
||||
double x2 = x_k.at<double>(1, 0);
|
||||
double x1_r = 6374.0;
|
||||
double x2_r = 0.0;
|
||||
|
||||
double R = sqrt( pow( x1 - x1_r, 2 ) + pow( x2 - x2_r, 2 ) );
|
||||
double Phi = atan( (x2 - x2_r)/(x1 - x1_r) );
|
||||
|
||||
R += n_k.at<double>(0, 0);
|
||||
Phi += n_k.at<double>(1, 0);
|
||||
|
||||
z_k.at<double>(0, 0) = R;
|
||||
z_k.at<double>(1, 0) = Phi;
|
||||
}
|
||||
};
|
||||
|
||||
const double BallisticModel::step_h = 0.05;
|
||||
|
||||
TEST(AUKF, br_landing_point)
|
||||
{
|
||||
const double abs_error = 0.1;
|
||||
|
||||
const int nIterations = 4000; // number of iterations before landing
|
||||
const double landing_coordinate = 2.5; // the expected landing coordinate
|
||||
|
||||
const double alpha = 1;
|
||||
const double beta = 2.0;
|
||||
const double kappa = -2.0;
|
||||
|
||||
int MP = 2;
|
||||
int DP = 5;
|
||||
int CP = 0;
|
||||
int type = CV_64F;
|
||||
|
||||
Mat processNoiseCov = Mat::zeros( DP, DP, type );
|
||||
processNoiseCov.at<double>(0, 0) = 1e-14;
|
||||
processNoiseCov.at<double>(1, 1) = 1e-14;
|
||||
processNoiseCov.at<double>(2, 2) = 2.4065 * 1e-5;
|
||||
processNoiseCov.at<double>(3, 3) = 2.4065 * 1e-5;
|
||||
processNoiseCov.at<double>(4, 4) = 1e-6;
|
||||
Mat processNoiseCovSqrt = Mat::zeros( DP, DP, type );
|
||||
sqrt( processNoiseCov, processNoiseCovSqrt );
|
||||
|
||||
Mat measurementNoiseCov = Mat::zeros( MP, MP, type );
|
||||
measurementNoiseCov.at<double>(0, 0) = 1e-3*1e-3;
|
||||
measurementNoiseCov.at<double>(1, 1) = 0.13*0.13;
|
||||
Mat measurementNoiseCovSqrt = Mat::zeros( MP, MP, type );
|
||||
sqrt( measurementNoiseCov, measurementNoiseCovSqrt );
|
||||
|
||||
RNG rng( 117 );
|
||||
|
||||
Mat state( DP, 1, type );
|
||||
state.at<double>(0, 0) = 6500.4;
|
||||
state.at<double>(1, 0) = 349.14;
|
||||
state.at<double>(2, 0) = -1.8093;
|
||||
state.at<double>(3, 0) = -6.7967;
|
||||
state.at<double>(4, 0) = 0.6932;
|
||||
|
||||
Mat initState = state.clone();
|
||||
initState.at<double>(4, 0) = 0.0;
|
||||
|
||||
Mat P = 1e-6 * Mat::eye( DP, DP, type );
|
||||
P.at<double>(4, 4) = 1.0;
|
||||
|
||||
Mat measurement( MP, 1, type );
|
||||
|
||||
Mat q( DP, 1, type );
|
||||
Mat r( MP, 1, type );
|
||||
|
||||
Ptr<BallisticModel> model( new BallisticModel() );
|
||||
AugmentedUnscentedKalmanFilterParams params( DP, MP, CP, 0, 0, model );
|
||||
|
||||
params.stateInit = initState.clone();
|
||||
params.errorCovInit = P.clone();
|
||||
params.measurementNoiseCov = measurementNoiseCov.clone();
|
||||
params.processNoiseCov = processNoiseCov.clone();
|
||||
|
||||
params.alpha = alpha;
|
||||
params.beta = beta;
|
||||
params.k = kappa;
|
||||
|
||||
Ptr<UnscentedKalmanFilter> augmentedUncsentedKalmanFilter = createAugmentedUnscentedKalmanFilter(params);
|
||||
|
||||
Mat correctStateUKF( DP, 1, type );
|
||||
Mat u = Mat::zeros( DP, 1, type );
|
||||
|
||||
for (int i = 0; i<nIterations; i++)
|
||||
{
|
||||
rng.fill( q, RNG::NORMAL, Scalar::all(0), Scalar::all(1) );
|
||||
q = processNoiseCovSqrt*q;
|
||||
|
||||
rng.fill( r, RNG::NORMAL, Scalar::all(0), Scalar::all(1) );
|
||||
r = measurementNoiseCovSqrt*r;
|
||||
|
||||
model->stateConversionFunction(state, u, q, state);
|
||||
model->measurementFunction(state, r, measurement);
|
||||
|
||||
augmentedUncsentedKalmanFilter->predict();
|
||||
correctStateUKF = augmentedUncsentedKalmanFilter->correct( measurement );
|
||||
}
|
||||
|
||||
double landing_y = correctStateUKF.at<double>(1, 0);
|
||||
ASSERT_NEAR(landing_coordinate, landing_y, abs_error);
|
||||
}
|
||||
|
||||
TEST(DISABLED_AUKF, DISABLED_br_mean_squared_error)
|
||||
{
|
||||
const double velocity_treshold = 0.004;
|
||||
const double state_treshold = 0.04;
|
||||
|
||||
const int nIterations = 4000; // number of iterations before landing
|
||||
|
||||
const double alpha = 1;
|
||||
const double beta = 2.0;
|
||||
const double kappa = -2.0;
|
||||
|
||||
int MP = 2;
|
||||
int DP = 5;
|
||||
int CP = 0;
|
||||
int type = CV_64F;
|
||||
|
||||
Mat processNoiseCov = Mat::zeros( DP, DP, type );
|
||||
processNoiseCov.at<double>(0, 0) = 1e-14;
|
||||
processNoiseCov.at<double>(1, 1) = 1e-14;
|
||||
processNoiseCov.at<double>(2, 2) = 2.4065 * 1e-5;
|
||||
processNoiseCov.at<double>(3, 3) = 2.4065 * 1e-5;
|
||||
processNoiseCov.at<double>(4, 4) = 1e-6;
|
||||
Mat processNoiseCovSqrt = Mat::zeros( DP, DP, type );
|
||||
sqrt( processNoiseCov, processNoiseCovSqrt );
|
||||
|
||||
Mat measurementNoiseCov = Mat::zeros( MP, MP, type );
|
||||
measurementNoiseCov.at<double>(0, 0) = 1e-3*1e-3;
|
||||
measurementNoiseCov.at<double>(1, 1) = 0.13*0.13;
|
||||
Mat measurementNoiseCovSqrt = Mat::zeros( MP, MP, type );
|
||||
sqrt( measurementNoiseCov, measurementNoiseCovSqrt );
|
||||
|
||||
RNG rng( 464 );
|
||||
|
||||
Mat state( DP, 1, type );
|
||||
state.at<double>(0, 0) = 6500.4;
|
||||
state.at<double>(1, 0) = 349.14;
|
||||
state.at<double>(2, 0) = -1.8093;
|
||||
state.at<double>(3, 0) = -6.7967;
|
||||
state.at<double>(4, 0) = 0.6932;
|
||||
|
||||
Mat initState = state.clone();
|
||||
Mat initStateKF = state.clone();
|
||||
initStateKF.at<double>(4, 0) = 0.0;
|
||||
|
||||
Mat P = 1e-6 * Mat::eye( DP, DP, type );
|
||||
P.at<double>(4, 4) = 1.0;
|
||||
|
||||
Mat measurement( MP, 1, type );
|
||||
|
||||
Mat q( DP, 1, type);
|
||||
Mat r( MP, 1, type);
|
||||
|
||||
Ptr<BallisticModel> model( new BallisticModel() );
|
||||
AugmentedUnscentedKalmanFilterParams params( DP, MP, CP, 0, 0, model );
|
||||
|
||||
params.stateInit = initStateKF.clone();
|
||||
params.errorCovInit = P.clone();
|
||||
params.measurementNoiseCov = measurementNoiseCov.clone();
|
||||
params.processNoiseCov = processNoiseCov.clone();
|
||||
|
||||
params.alpha = alpha;
|
||||
params.beta = beta;
|
||||
params.k = kappa;
|
||||
|
||||
Mat predictStateUKF( DP, 1, type );
|
||||
Mat correctStateUKF( DP, 1, type );
|
||||
|
||||
Mat errors = Mat::zeros( nIterations, 4, type );
|
||||
Mat u = Mat::zeros( DP, 1, type );
|
||||
|
||||
for (int j = 0; j<100; j++)
|
||||
{
|
||||
cv::Ptr<UnscentedKalmanFilter> augmentedUncsentedKalmanFilter = createAugmentedUnscentedKalmanFilter(params);
|
||||
state = initState.clone();
|
||||
|
||||
for (int i = 0; i<nIterations; i++)
|
||||
{
|
||||
rng.fill( q, RNG::NORMAL, Scalar::all(0), Scalar::all(1) );
|
||||
q = processNoiseCovSqrt*q;
|
||||
|
||||
rng.fill( r, RNG::NORMAL, Scalar::all(0), Scalar::all(1) );
|
||||
r = measurementNoiseCovSqrt*r;
|
||||
|
||||
model->stateConversionFunction(state, u, q, state);
|
||||
model->measurementFunction(state, r, measurement);
|
||||
|
||||
predictStateUKF = augmentedUncsentedKalmanFilter->predict();
|
||||
correctStateUKF = augmentedUncsentedKalmanFilter->correct( measurement );
|
||||
|
||||
Mat errorUKF = state - correctStateUKF;
|
||||
|
||||
for (int l = 0; l<4; l++)
|
||||
errors.at<double>(i, l) += pow( errorUKF.at<double>(l, 0), 2.0 );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
errors = errors/100.0;
|
||||
sqrt( errors, errors );
|
||||
|
||||
double max_x1 = cvtest::norm(errors.col(0), NORM_INF);
|
||||
double max_x2 = cvtest::norm(errors.col(1), NORM_INF);
|
||||
double max_x3 = cvtest::norm(errors.col(2), NORM_INF);
|
||||
double max_x4 = cvtest::norm(errors.col(3), NORM_INF);
|
||||
|
||||
ASSERT_GE( state_treshold, max_x1 );
|
||||
ASSERT_GE( state_treshold, max_x2 );
|
||||
ASSERT_GE( velocity_treshold, max_x3 );
|
||||
ASSERT_GE( velocity_treshold, max_x4 );
|
||||
|
||||
}
|
||||
|
||||
|
||||
// In this test Augmented Unscented Kalman Filter are applied to the univariate nonstationary growth model (UNGM).
|
||||
// This model was used in example from "Unscented Kalman filtering for additive noise case: Augmented vs. non-augmented"
|
||||
// by Yuanxin Wu and Dewen Hu.
|
||||
class UnivariateNonstationaryGrowthModel: public UkfSystemModel
|
||||
{
|
||||
|
||||
public:
|
||||
void stateConversionFunction(const Mat& x_k, const Mat& u_k, const Mat& v_k, Mat& x_kplus1)
|
||||
{
|
||||
double x = x_k.at<double>(0, 0);
|
||||
double n = u_k.at<double>(0, 0);
|
||||
double q = v_k.at<double>(0, 0);
|
||||
double u = u_k.at<double>(0, 0);
|
||||
|
||||
double x1 = 0.5*x + 25*( x/(x*x + 1) ) + 8*cos( 1.2*(n-1) ) + q + u;
|
||||
x_kplus1.at<double>(0, 0) = x1;
|
||||
}
|
||||
void measurementFunction(const Mat& x_k, const Mat& n_k, Mat& z_k)
|
||||
{
|
||||
double x = x_k.at<double>(0, 0);
|
||||
double r = n_k.at<double>(0, 0);
|
||||
|
||||
double y = x*x/20.0 + r;
|
||||
z_k.at<double>(0, 0) = y;
|
||||
}
|
||||
};
|
||||
|
||||
TEST(AUKF, DISABLED_ungm_mean_squared_error)
|
||||
{
|
||||
|
||||
const double alpha = 1.5;
|
||||
const double beta = 2.0;
|
||||
const double kappa = 0.0;
|
||||
|
||||
const double mse_treshold = 0.05;
|
||||
const int nIterations = 500; // number of observed iterations
|
||||
|
||||
int MP = 1;
|
||||
int DP = 1;
|
||||
int CP = 0;
|
||||
int type = CV_64F;
|
||||
|
||||
Ptr<UnivariateNonstationaryGrowthModel> model( new UnivariateNonstationaryGrowthModel() );
|
||||
AugmentedUnscentedKalmanFilterParams params( DP, MP, CP, 0, 0, model );
|
||||
|
||||
Mat processNoiseCov = Mat::zeros( DP, DP, type );
|
||||
processNoiseCov.at<double>(0, 0) = 1.0;
|
||||
Mat processNoiseCovSqrt = Mat::zeros( DP, DP, type );
|
||||
sqrt( processNoiseCov, processNoiseCovSqrt );
|
||||
|
||||
Mat measurementNoiseCov = Mat::zeros( MP, MP, type );
|
||||
measurementNoiseCov.at<double>(0, 0) = 1.0;
|
||||
Mat measurementNoiseCovSqrt = Mat::zeros( MP, MP, type );
|
||||
sqrt( measurementNoiseCov, measurementNoiseCovSqrt );
|
||||
|
||||
Mat P = Mat::eye( DP, DP, type );
|
||||
|
||||
Mat state( DP, 1, type );
|
||||
state.at<double>(0, 0) = 0.1;
|
||||
|
||||
Mat initState = state.clone();
|
||||
initState.at<double>(0, 0) = 0.0;
|
||||
|
||||
params.errorCovInit = P;
|
||||
params.measurementNoiseCov = measurementNoiseCov;
|
||||
params.processNoiseCov = processNoiseCov;
|
||||
params.stateInit = initState.clone();
|
||||
|
||||
params.alpha = alpha;
|
||||
params.beta = beta;
|
||||
params.k = kappa;
|
||||
|
||||
Mat correctStateAUKF( DP, 1, type );
|
||||
|
||||
Mat measurement( MP, 1, type );
|
||||
Mat exactMeasurement( MP, 1, type );
|
||||
|
||||
Mat q( DP, 1, type );
|
||||
Mat r( MP, 1, type );
|
||||
|
||||
Mat u( DP, 1, type );
|
||||
Mat zero = Mat::zeros( MP, 1, type );
|
||||
|
||||
RNG rng( 216 );
|
||||
|
||||
double average_error = 0.0;
|
||||
for (int j = 0; j<1000; j++)
|
||||
{
|
||||
cv::Ptr<UnscentedKalmanFilter> augmentedUncsentedKalmanFilter = createAugmentedUnscentedKalmanFilter( params );
|
||||
state = params.stateInit.clone();
|
||||
|
||||
double mse = 0.0;
|
||||
for (int i = 0; i<nIterations; i++)
|
||||
{
|
||||
rng.fill( q, RNG::NORMAL, Scalar::all(0), Scalar::all(1) );
|
||||
rng.fill( r, RNG::NORMAL, Scalar::all(0), Scalar::all(1) );
|
||||
q = processNoiseCovSqrt*q;
|
||||
r = measurementNoiseCovSqrt*r;
|
||||
|
||||
u.at<double>(0, 0) = (double)i;
|
||||
model->stateConversionFunction(state, u, q, state);
|
||||
|
||||
model->measurementFunction(state, zero, exactMeasurement);
|
||||
model->measurementFunction(state, r, measurement);
|
||||
|
||||
augmentedUncsentedKalmanFilter->predict( u );
|
||||
correctStateAUKF = augmentedUncsentedKalmanFilter->correct( measurement );
|
||||
|
||||
mse += pow( state.at<double>(0, 0) - correctStateAUKF.at<double>(0, 0), 2.0 );
|
||||
}
|
||||
mse /= nIterations;
|
||||
average_error += mse;
|
||||
}
|
||||
average_error /= 1000.0;
|
||||
|
||||
ASSERT_GE( mse_treshold, average_error );
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,21 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
#include "test_precomp.hpp"
|
||||
|
||||
static
|
||||
void initTrackingTests()
|
||||
{
|
||||
const char* extraTestDataPath =
|
||||
#ifdef WINRT
|
||||
NULL;
|
||||
#else
|
||||
getenv("OPENCV_DNN_TEST_DATA_PATH");
|
||||
#endif
|
||||
if (extraTestDataPath)
|
||||
cvtest::addDataSearchPath(extraTestDataPath);
|
||||
|
||||
cvtest::addDataSearchSubDirectory(""); // override "cv" prefix below to access without "../dnn" hacks
|
||||
}
|
||||
|
||||
CV_TEST_MAIN("cv", initTrackingTests())
|
||||
@@ -0,0 +1,10 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
#ifndef __OPENCV_TEST_PRECOMP_HPP__
|
||||
#define __OPENCV_TEST_PRECOMP_HPP__
|
||||
|
||||
#include "opencv2/ts.hpp"
|
||||
#include "opencv2/tracking.hpp"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,132 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
|
||||
#include <opencv2/tracking/tracking_legacy.hpp>
|
||||
//using namespace cv::tracking::legacy;
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
|
||||
TEST(MEDIAN_FLOW_Parameters, IO)
|
||||
{
|
||||
legacy::TrackerMedianFlow::Params parameters;
|
||||
|
||||
parameters.maxLevel = 10;
|
||||
parameters.maxMedianLengthOfDisplacementDifference = 11;
|
||||
parameters.pointsInGrid = 12;
|
||||
parameters.winSize = Size(6, 5);
|
||||
parameters.winSizeNCC = Size(41, 40);
|
||||
parameters.termCriteria.maxCount = 100;
|
||||
parameters.termCriteria.epsilon = 0.1;
|
||||
|
||||
FileStorage fsWriter("parameters.xml", FileStorage::WRITE + FileStorage::MEMORY);
|
||||
parameters.write(fsWriter);
|
||||
|
||||
String serializedParameters = fsWriter.releaseAndGetString();
|
||||
|
||||
FileStorage fsReader(serializedParameters, FileStorage::READ + FileStorage::MEMORY);
|
||||
|
||||
legacy::TrackerMedianFlow::Params readParameters;
|
||||
readParameters.read(fsReader.root());
|
||||
|
||||
ASSERT_EQ(parameters.maxLevel, readParameters.maxLevel);
|
||||
ASSERT_EQ(parameters.maxMedianLengthOfDisplacementDifference,
|
||||
readParameters.maxMedianLengthOfDisplacementDifference);
|
||||
ASSERT_EQ(parameters.pointsInGrid, readParameters.pointsInGrid);
|
||||
ASSERT_EQ(parameters.winSize, readParameters.winSize);
|
||||
ASSERT_EQ(parameters.winSizeNCC, readParameters.winSizeNCC);
|
||||
ASSERT_EQ(parameters.termCriteria.epsilon, readParameters.termCriteria.epsilon);
|
||||
ASSERT_EQ(parameters.termCriteria.maxCount, readParameters.termCriteria.maxCount);
|
||||
}
|
||||
|
||||
|
||||
TEST(MEDIAN_FLOW_Parameters, Default_Value_If_Absent)
|
||||
{
|
||||
legacy::TrackerMedianFlow::Params defaultParameters;
|
||||
|
||||
FileStorage fsReader(String("%YAML 1.0"), FileStorage::READ + FileStorage::MEMORY);
|
||||
|
||||
legacy::TrackerMedianFlow::Params readParameters;
|
||||
readParameters.read(fsReader.root());
|
||||
|
||||
ASSERT_EQ(defaultParameters.maxLevel, readParameters.maxLevel);
|
||||
ASSERT_EQ(defaultParameters.maxMedianLengthOfDisplacementDifference,
|
||||
readParameters.maxMedianLengthOfDisplacementDifference);
|
||||
ASSERT_EQ(defaultParameters.pointsInGrid, readParameters.pointsInGrid);
|
||||
ASSERT_EQ(defaultParameters.winSize, readParameters.winSize);
|
||||
ASSERT_EQ(defaultParameters.winSizeNCC, readParameters.winSizeNCC);
|
||||
ASSERT_EQ(defaultParameters.termCriteria.epsilon, readParameters.termCriteria.epsilon);
|
||||
ASSERT_EQ(defaultParameters.termCriteria.maxCount, readParameters.termCriteria.maxCount);
|
||||
}
|
||||
|
||||
TEST(KCF_Parameters, IO)
|
||||
{
|
||||
legacy::TrackerKCF::Params parameters;
|
||||
|
||||
parameters.sigma = 0.3f;
|
||||
parameters.lambda = 0.02f;
|
||||
parameters.interp_factor = 0.08f;
|
||||
parameters.output_sigma_factor = 1.0f/ 32.0f;
|
||||
parameters.resize=false;
|
||||
parameters.max_patch_size=90*90;
|
||||
parameters.split_coeff=false;
|
||||
parameters.wrap_kernel=true;
|
||||
parameters.desc_npca = TrackerKCF::CN;
|
||||
parameters.desc_pca = TrackerKCF::GRAY;
|
||||
parameters.compress_feature=false;
|
||||
parameters.compressed_size=3;
|
||||
parameters.pca_learning_rate=0.2f;
|
||||
|
||||
FileStorage fsWriter("parameters.xml", FileStorage::WRITE + FileStorage::MEMORY);
|
||||
parameters.write(fsWriter);
|
||||
|
||||
String serializedParameters = fsWriter.releaseAndGetString();
|
||||
|
||||
FileStorage fsReader(serializedParameters, FileStorage::READ + FileStorage::MEMORY);
|
||||
|
||||
legacy::TrackerKCF::Params readParameters;
|
||||
readParameters.read(fsReader.root());
|
||||
|
||||
ASSERT_DOUBLE_EQ(parameters.sigma, readParameters.sigma);
|
||||
ASSERT_DOUBLE_EQ(parameters.lambda, readParameters.lambda);
|
||||
ASSERT_DOUBLE_EQ(parameters.interp_factor, readParameters.interp_factor);
|
||||
ASSERT_DOUBLE_EQ(parameters.output_sigma_factor, readParameters.output_sigma_factor);
|
||||
ASSERT_EQ(parameters.resize, readParameters.resize);
|
||||
ASSERT_EQ(parameters.max_patch_size, readParameters.max_patch_size);
|
||||
ASSERT_EQ(parameters.split_coeff, readParameters.split_coeff);
|
||||
ASSERT_EQ(parameters.wrap_kernel, readParameters.wrap_kernel);
|
||||
ASSERT_EQ(parameters.desc_npca, readParameters.desc_npca);
|
||||
ASSERT_EQ(parameters.desc_pca, readParameters.desc_pca);
|
||||
ASSERT_EQ(parameters.compress_feature, readParameters.compress_feature);
|
||||
ASSERT_EQ(parameters.compressed_size, readParameters.compressed_size);
|
||||
ASSERT_DOUBLE_EQ(parameters.pca_learning_rate, readParameters.pca_learning_rate);
|
||||
}
|
||||
|
||||
TEST(KCF_Parameters, Default_Value_If_Absent)
|
||||
{
|
||||
legacy::TrackerKCF::Params defaultParameters;
|
||||
|
||||
FileStorage fsReader(String("%YAML 1.0"), FileStorage::READ + FileStorage::MEMORY);
|
||||
|
||||
legacy::TrackerKCF::Params readParameters;
|
||||
readParameters.read(fsReader.root());
|
||||
|
||||
ASSERT_DOUBLE_EQ(defaultParameters.sigma, readParameters.sigma);
|
||||
ASSERT_DOUBLE_EQ(defaultParameters.lambda, readParameters.lambda);
|
||||
ASSERT_DOUBLE_EQ(defaultParameters.interp_factor, readParameters.interp_factor);
|
||||
ASSERT_DOUBLE_EQ(defaultParameters.output_sigma_factor, readParameters.output_sigma_factor);
|
||||
ASSERT_EQ(defaultParameters.resize, readParameters.resize);
|
||||
ASSERT_EQ(defaultParameters.max_patch_size, readParameters.max_patch_size);
|
||||
ASSERT_EQ(defaultParameters.split_coeff, readParameters.split_coeff);
|
||||
ASSERT_EQ(defaultParameters.wrap_kernel, readParameters.wrap_kernel);
|
||||
ASSERT_EQ(defaultParameters.desc_npca, readParameters.desc_npca);
|
||||
ASSERT_EQ(defaultParameters.desc_pca, readParameters.desc_pca);
|
||||
ASSERT_EQ(defaultParameters.compress_feature, readParameters.compress_feature);
|
||||
ASSERT_EQ(defaultParameters.compressed_size, readParameters.compressed_size);
|
||||
ASSERT_DOUBLE_EQ(defaultParameters.pca_learning_rate, readParameters.pca_learning_rate);
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,236 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
|
||||
#define TEST_LEGACY
|
||||
#include <opencv2/tracking/tracking_legacy.hpp>
|
||||
|
||||
//#define DEBUG_TEST
|
||||
#ifdef DEBUG_TEST
|
||||
#include <opencv2/highgui.hpp>
|
||||
#endif
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
//using namespace cv::tracking;
|
||||
|
||||
#define TESTSET_NAMES testing::Values("david","dudek","faceocc2")
|
||||
|
||||
const string TRACKING_DIR = "tracking";
|
||||
const string FOLDER_IMG = "data";
|
||||
const string FOLDER_OMIT_INIT = "initOmit";
|
||||
|
||||
// Check used "cmake" version in case of errors
|
||||
// Check compiler command line options for <opencv>/modules include
|
||||
#include "video/test/test_trackers.impl.hpp"
|
||||
|
||||
|
||||
/****************************************************************************************\
|
||||
* Tests registrations *
|
||||
\****************************************************************************************/
|
||||
|
||||
//[TESTDATA]
|
||||
PARAM_TEST_CASE(DistanceAndOverlap, string)
|
||||
{
|
||||
string dataset;
|
||||
virtual void SetUp()
|
||||
{
|
||||
dataset = GET_PARAM(0);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(DistanceAndOverlap, MedianFlow)
|
||||
{
|
||||
TrackerTest<legacy::Tracker> test(legacy::TrackerMedianFlow::create(), dataset, 35, .5f, NoTransform, 1, 1);
|
||||
test.run();
|
||||
}
|
||||
|
||||
TEST_P(DistanceAndOverlap, Boosting)
|
||||
{
|
||||
TrackerTest<legacy::Tracker> test(legacy::TrackerBoosting::create(), dataset, 70, .7f, NoTransform);
|
||||
test.run();
|
||||
}
|
||||
|
||||
TEST_P(DistanceAndOverlap, KCF)
|
||||
{
|
||||
TrackerTest<Tracker, Rect> test(TrackerKCF::create(), dataset, 20, .35f, NoTransform, 5);
|
||||
test.run();
|
||||
}
|
||||
#ifdef TEST_LEGACY
|
||||
TEST_P(DistanceAndOverlap, KCF_legacy)
|
||||
{
|
||||
TrackerTest<legacy::Tracker> test(legacy::TrackerKCF::create(), dataset, 20, .35f, NoTransform, 5);
|
||||
test.run();
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_P(DistanceAndOverlap, TLD)
|
||||
{
|
||||
TrackerTest<legacy::Tracker> test(legacy::TrackerTLD::create(), dataset, 40, .45f, NoTransform);
|
||||
test.run();
|
||||
}
|
||||
|
||||
TEST_P(DistanceAndOverlap, MOSSE)
|
||||
{
|
||||
TrackerTest<legacy::Tracker> test(legacy::TrackerMOSSE::create(), dataset, 22, .7f, NoTransform);
|
||||
test.run();
|
||||
}
|
||||
|
||||
TEST_P(DistanceAndOverlap, CSRT)
|
||||
{
|
||||
TrackerTest<Tracker, Rect> test(TrackerCSRT::create(), dataset, 22, .7f, NoTransform);
|
||||
test.run();
|
||||
}
|
||||
#ifdef TEST_LEGACY
|
||||
TEST_P(DistanceAndOverlap, CSRT_legacy)
|
||||
{
|
||||
TrackerTest<legacy::Tracker> test(legacy::TrackerCSRT::create(), dataset, 22, .7f, NoTransform);
|
||||
test.run();
|
||||
}
|
||||
#endif
|
||||
|
||||
/***************************************************************************************/
|
||||
//Tests with shifted initial window
|
||||
TEST_P(DistanceAndOverlap, Shifted_Data_MedianFlow)
|
||||
{
|
||||
TrackerTest<legacy::Tracker> test(legacy::TrackerMedianFlow::create(), dataset, 80, .2f, CenterShiftLeft, 1, 1);
|
||||
test.run();
|
||||
}
|
||||
|
||||
TEST_P(DistanceAndOverlap, Shifted_Data_Boosting)
|
||||
{
|
||||
TrackerTest<legacy::Tracker> test(legacy::TrackerBoosting::create(), dataset, 80, .65f, CenterShiftLeft);
|
||||
test.run();
|
||||
}
|
||||
|
||||
TEST_P(DistanceAndOverlap, Shifted_Data_KCF)
|
||||
{
|
||||
TrackerTest<Tracker, Rect> test(TrackerKCF::create(), dataset, 20, .4f, CenterShiftLeft, 5);
|
||||
test.run();
|
||||
}
|
||||
#ifdef TEST_LEGACY
|
||||
TEST_P(DistanceAndOverlap, Shifted_Data_KCF_legacy)
|
||||
{
|
||||
TrackerTest<legacy::Tracker> test(legacy::TrackerKCF::create(), dataset, 20, .4f, CenterShiftLeft, 5);
|
||||
test.run();
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_P(DistanceAndOverlap, Shifted_Data_TLD)
|
||||
{
|
||||
TrackerTest<legacy::Tracker> test(legacy::TrackerTLD::create(), dataset, 30, .35f, CenterShiftLeft);
|
||||
test.run();
|
||||
}
|
||||
|
||||
TEST_P(DistanceAndOverlap, Shifted_Data_MOSSE)
|
||||
{
|
||||
TrackerTest<legacy::Tracker> test(legacy::TrackerMOSSE::create(), dataset, 13, .69f, CenterShiftLeft);
|
||||
test.run();
|
||||
}
|
||||
|
||||
TEST_P(DistanceAndOverlap, Shifted_Data_CSRT)
|
||||
{
|
||||
TrackerTest<Tracker, Rect> test(TrackerCSRT::create(), dataset, 13, .69f, CenterShiftLeft);
|
||||
test.run();
|
||||
}
|
||||
#ifdef TEST_LEGACY
|
||||
TEST_P(DistanceAndOverlap, Shifted_Data_CSRT_legacy)
|
||||
{
|
||||
TrackerTest<legacy::Tracker> test(legacy::TrackerCSRT::create(), dataset, 13, .69f, CenterShiftLeft);
|
||||
test.run();
|
||||
}
|
||||
#endif
|
||||
|
||||
/***************************************************************************************/
|
||||
//Tests with scaled initial window
|
||||
TEST_P(DistanceAndOverlap, Scaled_Data_MedianFlow)
|
||||
{
|
||||
TrackerTest<legacy::Tracker> test(legacy::TrackerMedianFlow::create(), dataset, 25, .5f, Scale_1_1, 1, 1);
|
||||
test.run();
|
||||
}
|
||||
|
||||
TEST_P(DistanceAndOverlap, Scaled_Data_Boosting)
|
||||
{
|
||||
TrackerTest<legacy::Tracker> test(legacy::TrackerBoosting::create(), dataset, 80, .7f, Scale_1_1);
|
||||
test.run();
|
||||
}
|
||||
|
||||
TEST_P(DistanceAndOverlap, Scaled_Data_KCF)
|
||||
{
|
||||
TrackerTest<Tracker, Rect> test(TrackerKCF::create(), dataset, 20, .4f, Scale_1_1, 5);
|
||||
test.run();
|
||||
}
|
||||
#ifdef TEST_LEGACY
|
||||
TEST_P(DistanceAndOverlap, Scaled_Data_KCF_legacy)
|
||||
{
|
||||
TrackerTest<legacy::Tracker> test(legacy::TrackerKCF::create(), dataset, 20, .4f, Scale_1_1, 5);
|
||||
test.run();
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_P(DistanceAndOverlap, Scaled_Data_TLD)
|
||||
{
|
||||
TrackerTest<legacy::Tracker> test(legacy::TrackerTLD::create(), dataset, 30, .45f, Scale_1_1);
|
||||
test.run();
|
||||
}
|
||||
|
||||
TEST_P(DistanceAndOverlap, Scaled_Data_MOSSE)
|
||||
{
|
||||
TrackerTest<legacy::Tracker> test(legacy::TrackerMOSSE::create(), dataset, 22, 0.69f, Scale_1_1, 1);
|
||||
test.run();
|
||||
}
|
||||
|
||||
TEST_P(DistanceAndOverlap, Scaled_Data_CSRT)
|
||||
{
|
||||
TrackerTest<Tracker, Rect> test(TrackerCSRT::create(), dataset, 22, 0.69f, Scale_1_1, 1);
|
||||
test.run();
|
||||
}
|
||||
#ifdef TEST_LEGACY
|
||||
TEST_P(DistanceAndOverlap, Scaled_Data_CSRT_legacy)
|
||||
{
|
||||
TrackerTest<Tracker, Rect> test(TrackerCSRT::create(), dataset, 22, 0.69f, Scale_1_1, 1);
|
||||
test.run();
|
||||
}
|
||||
#endif
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(Tracking, DistanceAndOverlap, TESTSET_NAMES);
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,111 @@
|
||||
#include "test_precomp.hpp"
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/tracking/twist.hpp"
|
||||
|
||||
namespace opencv_test
|
||||
{
|
||||
namespace
|
||||
{
|
||||
|
||||
using namespace cv::detail::tracking;
|
||||
|
||||
float const eps = 1e-4f;
|
||||
|
||||
class TwistTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
cv::Mat J, K;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
cv::Matx33f K = {1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0};
|
||||
this->K = cv::Mat(K);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TwistTest, TestInteractionMatrix)
|
||||
{
|
||||
// import machinevisiontoolbox as mv
|
||||
// cam = mv.CentralCamera()
|
||||
// print(cam.K)
|
||||
// print(cam.visjac_p([1, 1], 2.0))
|
||||
// [[1. 0. 0.]
|
||||
// [0. 1. 0.]
|
||||
// [0. 0. 1.]]
|
||||
// [[-0.5 0. 0.5 1. -2. 1. ]
|
||||
// [ 0. -0.5 0.5 2. -1. -1. ]]
|
||||
|
||||
cv::Mat uv = cv::Mat(2, 1, CV_32F, {1.0f, 1.0f});
|
||||
cv::Mat depth = cv::Mat(1, 1, CV_32F, {2.0f});
|
||||
|
||||
computeInteractionMatrix(uv, depth, K, J);
|
||||
ASSERT_EQ(J.cols, 6);
|
||||
ASSERT_EQ(J.rows, 2);
|
||||
float expected[2][6] = {{-0.5f, 0.0f, 0.5f, 1.0f, -2.0f, 1.0f},
|
||||
{0.0f, -0.5f, 0.5f, 2.0f, -1.0f, -1.0f}};
|
||||
for (int i = 0; i < 2; i++)
|
||||
for (int j = 0; j < 6; j++)
|
||||
ASSERT_NEAR(J.at<float>(i, j), expected[i][j], eps);
|
||||
}
|
||||
|
||||
TEST_F(TwistTest, TestComputeWithZeroPixelVelocities)
|
||||
{
|
||||
cv::Mat uv = cv::Mat(2, 2, CV_32F, {1.0f, 0.0f, 3.0f, 0.0f});
|
||||
cv::Mat depths = cv::Mat(1, 2, CV_32F, {1.1f, 1.0f});
|
||||
cv::Mat duv = cv::Mat(4, 1, CV_32F, {0.0f, 0.0f, 0.0f, 0.0f});
|
||||
|
||||
cv::Vec6d result = computeTwist(uv, duv, depths, K);
|
||||
for (int i = 0; i < 6; i++)
|
||||
ASSERT_NEAR(result[i], 0.0, eps);
|
||||
}
|
||||
|
||||
TEST_F(TwistTest, TestComputeWithNonZeroPixelVelocities)
|
||||
{
|
||||
// import machinevisiontoolbox as mv
|
||||
// cam = mv.CentralCamera()
|
||||
// pixels = np.array([[1, 2, 3],
|
||||
// [1, 2, 3]], dtype=float)
|
||||
// depths = np.array([1.0, 2.0, 3.0])
|
||||
// Jac = cam.visjac_p(pixels, depths)
|
||||
// duv = np.array([1, 2, 1, 3, 1, 4])
|
||||
// twist = np.linalg.lstsq(Jac, duv, rcond=None)[0]
|
||||
// print(twist)
|
||||
// print(Jac)
|
||||
// [ 0.5 0.5 1.875 0.041667 -0.041667 -0.5 ]
|
||||
// [[ -1. 0. 1. 1. -2. 1. ]
|
||||
// [ 0. -1. 1. 2. -1. -1. ]
|
||||
// [ -0.5 0. 1. 4. -5. 2. ]
|
||||
// [ 0. -0.5 1. 5. -4. -2. ]
|
||||
// [ -0.333333 0. 1. 9. -10. 3. ]
|
||||
// [ 0. -0.333333 1. 10. -9. -3. ]]
|
||||
|
||||
float uv_data[] = {1.0f, 2.0f, 3.0f, 1.0f, 2.0f, 3.0f};
|
||||
cv::Mat uv = cv::Mat(2, 3, CV_32F, uv_data);
|
||||
float depth_data[] = {1.0f, 2.0f, 3.0f};
|
||||
cv::Mat depth = cv::Mat(1, 3, CV_32F, depth_data);
|
||||
float duv_data[] = {1.0f, 2.0f, 1.0f, 3.0f, 1.0f, 4.0f};
|
||||
cv::Mat duv = cv::Mat(6, 1, CV_32F, duv_data);
|
||||
|
||||
computeInteractionMatrix(uv, depth, K, J);
|
||||
ASSERT_EQ(J.cols, 6);
|
||||
ASSERT_EQ(J.rows, 6);
|
||||
float expected_jac[6][6] = {{-1.0f, 0.0f, 1.0f, 1.0f, -2.0f, 1.0f},
|
||||
{0.0f, -1.0f, 1.0f, 2.0f, -1.0f, -1.0f},
|
||||
{-0.5f, 0.0f, 1.0f, 4.0f, -5.0f, 2.0f},
|
||||
{0.0f, -0.5f, 1.0f, 5.0f, -4.0f, -2.0f},
|
||||
{-0.333333f, 0.0f, 1.0f, 9.0f, -10.0f, 3.0f},
|
||||
{0.0f, -0.333333f, 1.0f, 10.0f, -9.0f, -3.0f}};
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
for (int j = 0; j < 6; j++)
|
||||
ASSERT_NEAR(J.at<float>(i, j), expected_jac[i][j], eps);
|
||||
|
||||
cv::Vec6d result = computeTwist(uv, duv, depth, K);
|
||||
float expected_twist[6] = {0.5f, 0.5f, 1.875f, 0.041667f, -0.041667f, -0.5f};
|
||||
for (int i = 0; i < 6; i++)
|
||||
ASSERT_NEAR(result[i], expected_twist[i], eps);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace opencv_test
|
||||
@@ -0,0 +1,436 @@
|
||||
/*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) 2015, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
#include "opencv2/tracking/kalman_filters.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
using namespace cv::detail;
|
||||
|
||||
// In this two tests Unscented Kalman Filter are applied to the dynamic system from example "The reentry problem" from
|
||||
// "A New Extension of the Kalman Filter to Nonlinear Systems" by Simon J. Julier and Jeffrey K. Uhlmann.
|
||||
class BallisticModel: public UkfSystemModel
|
||||
{
|
||||
static const double step;
|
||||
|
||||
Mat diff_eq(const Mat& x)
|
||||
{
|
||||
double x1 = x.at<double>(0, 0);
|
||||
double x2 = x.at<double>(1, 0);
|
||||
double x3 = x.at<double>(2, 0);
|
||||
double x4 = x.at<double>(3, 0);
|
||||
double x5 = x.at<double>(4, 0);
|
||||
|
||||
const double h0 = 9.3;
|
||||
const double beta0 = 0.59783;
|
||||
const double Gm = 3.9860044 * 1e5;
|
||||
const double r_e = 6374;
|
||||
|
||||
const double r = sqrt( x1*x1 + x2*x2 );
|
||||
const double v = sqrt( x3*x3 + x4*x4 );
|
||||
const double d = - beta0 * exp( ( r_e - r )/h0 ) * exp( x5 ) * v;
|
||||
const double g = - Gm / (r*r*r);
|
||||
|
||||
Mat fx = x.clone();
|
||||
|
||||
fx.at<double>(0, 0) = x3;
|
||||
fx.at<double>(1, 0) = x4;
|
||||
fx.at<double>(2, 0) = d * x3 + g * x1;
|
||||
fx.at<double>(3, 0) = d * x4 + g * x2;
|
||||
fx.at<double>(4, 0) = 0.0;
|
||||
|
||||
return fx;
|
||||
}
|
||||
public:
|
||||
void stateConversionFunction(const Mat& x_k, const Mat& u_k, const Mat& v_k, Mat& x_kplus1)
|
||||
{
|
||||
Mat v = sqrt(step) * v_k.clone();
|
||||
v.at<double>(0, 0) = 0.0;
|
||||
v.at<double>(1, 0) = 0.0;
|
||||
|
||||
Mat k1 = diff_eq( x_k ) + v;
|
||||
Mat tmp = x_k + step*0.5*k1;
|
||||
Mat k2 = diff_eq( tmp ) + v;
|
||||
tmp = x_k + step*0.5*k2;
|
||||
Mat k3 = diff_eq( tmp ) + v;
|
||||
tmp = x_k + step*k3;
|
||||
Mat k4 = diff_eq( tmp ) + v;
|
||||
|
||||
x_kplus1 = x_k + (1.0/6.0)*step*( k1 + 2.0*k2 + 2.0*k3 + k4 ) + u_k;
|
||||
}
|
||||
|
||||
void measurementFunction(const Mat& x_k, const Mat& n_k, Mat& z_k)
|
||||
{
|
||||
double x1 = x_k.at<double>(0, 0);
|
||||
double x2 = x_k.at<double>(1, 0);
|
||||
double x1_r = 6374.0;
|
||||
double x2_r = 0.0;
|
||||
|
||||
double R = sqrt( pow( x1 - x1_r, 2 ) + pow( x2 - x2_r, 2 ) );
|
||||
double Phi = atan( (x2 - x2_r)/(x1 - x1_r) );
|
||||
|
||||
R += n_k.at<double>(0, 0);
|
||||
Phi += n_k.at<double>(1, 0);
|
||||
|
||||
z_k.at<double>(0, 0) = R;
|
||||
z_k.at<double>(1, 0) = Phi;
|
||||
}
|
||||
};
|
||||
|
||||
const double BallisticModel::step = 0.05;
|
||||
|
||||
TEST(UKF, br_landing_point)
|
||||
{
|
||||
const double abs_error = 0.1;
|
||||
|
||||
const int nIterations = 4000; // number of iterations before landing
|
||||
const double landing_coordinate = 2.5; // the expected landing coordinate
|
||||
|
||||
const double alpha = 1;
|
||||
const double beta = 2.0;
|
||||
const double kappa = -2.0;
|
||||
|
||||
int MP = 2;
|
||||
int DP = 5;
|
||||
int CP = 0;
|
||||
int type = CV_64F;
|
||||
|
||||
Mat processNoiseCov = Mat::zeros( DP, DP, type );
|
||||
processNoiseCov.at<double>(0, 0) = 1e-14;
|
||||
processNoiseCov.at<double>(1, 1) = 1e-14;
|
||||
processNoiseCov.at<double>(2, 2) = 2.4065 * 1e-5;
|
||||
processNoiseCov.at<double>(3, 3) = 2.4065 * 1e-5;
|
||||
processNoiseCov.at<double>(4, 4) = 1e-6;
|
||||
Mat processNoiseCovSqrt = Mat::zeros( DP, DP, type );
|
||||
sqrt( processNoiseCov, processNoiseCovSqrt );
|
||||
|
||||
Mat measurementNoiseCov = Mat::zeros( MP, MP, type );
|
||||
measurementNoiseCov.at<double>(0, 0) = 1e-3*1e-3;
|
||||
measurementNoiseCov.at<double>(1, 1) = 0.13*0.13;
|
||||
Mat measurementNoiseCovSqrt = Mat::zeros( MP, MP, type );
|
||||
sqrt( measurementNoiseCov, measurementNoiseCovSqrt );
|
||||
|
||||
RNG rng( 117 );
|
||||
|
||||
Mat state( DP, 1, type );
|
||||
state.at<double>(0, 0) = 6500.4;
|
||||
state.at<double>(1, 0) = 349.14;
|
||||
state.at<double>(2, 0) = -1.8093;
|
||||
state.at<double>(3, 0) = -6.7967;
|
||||
state.at<double>(4, 0) = 0.6932;
|
||||
|
||||
Mat initState = state.clone();
|
||||
initState.at<double>(4, 0) = 0.0;
|
||||
|
||||
Mat P = 1e-6 * Mat::eye( DP, DP, type );
|
||||
P.at<double>(4, 4) = 1.0;
|
||||
|
||||
Mat measurement( MP, 1, type );
|
||||
|
||||
Mat q( DP, 1, type );
|
||||
Mat r( MP, 1, type );
|
||||
|
||||
Ptr<BallisticModel> model( new BallisticModel() );
|
||||
UnscentedKalmanFilterParams params( DP, MP, CP, 0, 0, model );
|
||||
|
||||
params.stateInit = initState.clone();
|
||||
params.errorCovInit = P.clone();
|
||||
params.measurementNoiseCov = measurementNoiseCov.clone();
|
||||
params.processNoiseCov = processNoiseCov.clone();
|
||||
|
||||
params.alpha = alpha;
|
||||
params.beta = beta;
|
||||
params.k = kappa;
|
||||
|
||||
Ptr<UnscentedKalmanFilter> uncsentedKalmanFilter = createUnscentedKalmanFilter(params);
|
||||
|
||||
Mat correctStateUKF( DP, 1, type );
|
||||
Mat u = Mat::zeros( DP, 1, type );
|
||||
|
||||
for (int i = 0; i<nIterations; i++)
|
||||
{
|
||||
rng.fill( q, RNG::NORMAL, Scalar::all(0), Scalar::all(1) );
|
||||
q = processNoiseCovSqrt*q;
|
||||
|
||||
rng.fill( r, RNG::NORMAL, Scalar::all(0), Scalar::all(1) );
|
||||
r = measurementNoiseCovSqrt*r;
|
||||
|
||||
model->stateConversionFunction(state, u, q, state);
|
||||
model->measurementFunction(state, r, measurement);
|
||||
|
||||
uncsentedKalmanFilter->predict();
|
||||
correctStateUKF = uncsentedKalmanFilter->correct( measurement );
|
||||
}
|
||||
|
||||
double landing_y = correctStateUKF.at<double>(1, 0);
|
||||
ASSERT_NEAR(landing_coordinate, landing_y, abs_error);
|
||||
}
|
||||
|
||||
TEST(UKF, DISABLED_br_mean_squared_error)
|
||||
{
|
||||
const double velocity_treshold = 0.09;
|
||||
const double state_treshold = 0.9;
|
||||
|
||||
const int nIterations = 4000; // number of iterations before landing
|
||||
|
||||
const double alpha = 1;
|
||||
const double beta = 2.0;
|
||||
const double kappa = -2.0;
|
||||
|
||||
int MP = 2;
|
||||
int DP = 5;
|
||||
int CP = 0;
|
||||
int type = CV_64F;
|
||||
|
||||
Mat processNoiseCov = Mat::zeros( DP, DP, type );
|
||||
processNoiseCov.at<double>(0, 0) = 1e-14;
|
||||
processNoiseCov.at<double>(1, 1) = 1e-14;
|
||||
processNoiseCov.at<double>(2, 2) = 2.4065 * 1e-5;
|
||||
processNoiseCov.at<double>(3, 3) = 2.4065 * 1e-5;
|
||||
processNoiseCov.at<double>(4, 4) = 1e-6;
|
||||
Mat processNoiseCovSqrt = Mat::zeros( DP, DP, type );
|
||||
sqrt( processNoiseCov, processNoiseCovSqrt );
|
||||
|
||||
Mat measurementNoiseCov = Mat::zeros( MP, MP, type );
|
||||
measurementNoiseCov.at<double>(0, 0) = 1e-3*1e-3;
|
||||
measurementNoiseCov.at<double>(1, 1) = 0.13*0.13;
|
||||
Mat measurementNoiseCovSqrt = Mat::zeros( MP, MP, type );
|
||||
sqrt( measurementNoiseCov, measurementNoiseCovSqrt );
|
||||
|
||||
RNG rng( 464 );
|
||||
|
||||
Mat state( DP, 1, type );
|
||||
state.at<double>(0, 0) = 6500.4;
|
||||
state.at<double>(1, 0) = 349.14;
|
||||
state.at<double>(2, 0) = -1.8093;
|
||||
state.at<double>(3, 0) = -6.7967;
|
||||
state.at<double>(4, 0) = 0.6932;
|
||||
|
||||
Mat initState = state.clone();
|
||||
Mat initStateKF = state.clone();
|
||||
initStateKF.at<double>(4, 0) = 0.0;
|
||||
|
||||
Mat P = 1e-6 * Mat::eye( DP, DP, type );
|
||||
P.at<double>(4, 4) = 1.0;
|
||||
|
||||
Mat measurement( MP, 1, type );
|
||||
|
||||
Mat q( DP, 1, type);
|
||||
Mat r( MP, 1, type);
|
||||
|
||||
Ptr<BallisticModel> model( new BallisticModel() );
|
||||
UnscentedKalmanFilterParams params( DP, MP, CP, 0, 0, model );
|
||||
|
||||
params.stateInit = initStateKF.clone();
|
||||
params.errorCovInit = P.clone();
|
||||
params.measurementNoiseCov = measurementNoiseCov.clone();
|
||||
params.processNoiseCov = processNoiseCov.clone();
|
||||
|
||||
params.alpha = alpha;
|
||||
params.beta = beta;
|
||||
params.k = kappa;
|
||||
|
||||
Mat predictStateUKF( DP, 1, type );
|
||||
Mat correctStateUKF( DP, 1, type );
|
||||
|
||||
Mat errors = Mat::zeros( nIterations, 4, type );
|
||||
Mat u = Mat::zeros( DP, 1, type );
|
||||
|
||||
for (int j = 0; j<100; j++)
|
||||
{
|
||||
Ptr<UnscentedKalmanFilter> uncsentedKalmanFilter = createUnscentedKalmanFilter(params);
|
||||
state = initState.clone();
|
||||
|
||||
for (int i = 0; i<nIterations; i++)
|
||||
{
|
||||
rng.fill( q, RNG::NORMAL, Scalar::all(0), Scalar::all(1) );
|
||||
q = processNoiseCovSqrt*q;
|
||||
|
||||
rng.fill( r, RNG::NORMAL, Scalar::all(0), Scalar::all(1) );
|
||||
r = measurementNoiseCovSqrt*r;
|
||||
|
||||
model->stateConversionFunction(state, u, q, state);
|
||||
model->measurementFunction(state, r, measurement);
|
||||
|
||||
predictStateUKF = uncsentedKalmanFilter->predict();
|
||||
correctStateUKF = uncsentedKalmanFilter->correct( measurement );
|
||||
|
||||
Mat errorUKF = state - correctStateUKF;
|
||||
|
||||
for (int l = 0; l<4; l++)
|
||||
errors.at<double>(i, l) += pow( errorUKF.at<double>(l, 0), 2.0 );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
errors = errors/100.0;
|
||||
sqrt( errors, errors );
|
||||
|
||||
double max_x1 = cvtest::norm(errors.col(0), NORM_INF);
|
||||
double max_x2 = cvtest::norm(errors.col(1), NORM_INF);
|
||||
double max_x3 = cvtest::norm(errors.col(2), NORM_INF);
|
||||
double max_x4 = cvtest::norm(errors.col(3), NORM_INF);
|
||||
|
||||
ASSERT_GE( state_treshold, max_x1 );
|
||||
ASSERT_GE( state_treshold, max_x2 );
|
||||
ASSERT_GE( velocity_treshold, max_x3 );
|
||||
ASSERT_GE( velocity_treshold, max_x4 );
|
||||
}
|
||||
|
||||
|
||||
//In this test Unscented Kalman Filter are applied to the univariate nonstationary growth model (UNGM).
|
||||
//This model was used in example from "Unscented Kalman filtering for additive noise case: Augmented vs. non-augmented"
|
||||
//by Yuanxin Wu and Dewen Hu.
|
||||
class UnivariateNonstationaryGrowthModel: public UkfSystemModel
|
||||
{
|
||||
|
||||
public:
|
||||
void stateConversionFunction(const Mat& x_k, const Mat& u_k, const Mat& v_k, Mat& x_kplus1)
|
||||
{
|
||||
double x = x_k.at<double>(0, 0);
|
||||
double n = u_k.at<double>(0, 0);
|
||||
double q = v_k.at<double>(0, 0);
|
||||
double u = u_k.at<double>(0, 0);
|
||||
|
||||
double x1 = 0.5*x + 25*( x/(x*x + 1) ) + 8*cos( 1.2*(n-1) ) + q + u;
|
||||
x_kplus1.at<double>(0, 0) = x1;
|
||||
}
|
||||
void measurementFunction(const Mat& x_k, const Mat& n_k, Mat& z_k)
|
||||
{
|
||||
double x = x_k.at<double>(0, 0);
|
||||
double r = n_k.at<double>(0, 0);
|
||||
|
||||
double y = x*x/20.0 + r;
|
||||
z_k.at<double>(0, 0) = y;
|
||||
}
|
||||
};
|
||||
|
||||
TEST(UKF, DISABLED_ungm_mean_squared_error)
|
||||
{
|
||||
const double alpha = 1.5;
|
||||
const double beta = 2.0;
|
||||
const double kappa = 0.0;
|
||||
|
||||
const double mse_treshold = 0.5;
|
||||
const int nIterations = 500; // number of observed iterations
|
||||
|
||||
int MP = 1;
|
||||
int DP = 1;
|
||||
int CP = 0;
|
||||
int type = CV_64F;
|
||||
|
||||
Ptr<UnivariateNonstationaryGrowthModel> model( new UnivariateNonstationaryGrowthModel() );
|
||||
UnscentedKalmanFilterParams params( DP, MP, CP, 0, 0, model );
|
||||
|
||||
Mat processNoiseCov = Mat::zeros( DP, DP, type );
|
||||
processNoiseCov.at<double>(0, 0) = 1.0;
|
||||
Mat processNoiseCovSqrt = Mat::zeros( DP, DP, type );
|
||||
sqrt( processNoiseCov, processNoiseCovSqrt );
|
||||
|
||||
Mat measurementNoiseCov = Mat::zeros( MP, MP, type );
|
||||
measurementNoiseCov.at<double>(0, 0) = 1.0;
|
||||
Mat measurementNoiseCovSqrt = Mat::zeros( MP, MP, type );
|
||||
sqrt( measurementNoiseCov, measurementNoiseCovSqrt );
|
||||
|
||||
Mat P = Mat::eye( DP, DP, type );
|
||||
|
||||
Mat state( DP, 1, type );
|
||||
state.at<double>(0, 0) = 0.1;
|
||||
|
||||
Mat initState = state.clone();
|
||||
initState.at<double>(0, 0) = 0.0;
|
||||
|
||||
params.errorCovInit = P;
|
||||
params.measurementNoiseCov = measurementNoiseCov;
|
||||
params.processNoiseCov = processNoiseCov;
|
||||
params.stateInit = initState.clone();
|
||||
|
||||
params.alpha = alpha;
|
||||
params.beta = beta;
|
||||
params.k = kappa;
|
||||
|
||||
Mat correctStateAUKF( DP, 1, type );
|
||||
|
||||
Mat measurement( MP, 1, type );
|
||||
Mat exactMeasurement( MP, 1, type );
|
||||
|
||||
Mat q( DP, 1, type );
|
||||
Mat r( MP, 1, type );
|
||||
|
||||
Mat u( DP, 1, type );
|
||||
Mat zero = Mat::zeros( MP, 1, type );
|
||||
|
||||
RNG rng( 216 );
|
||||
|
||||
double average_error = 0.0;
|
||||
for (int j = 0; j<1000; j++)
|
||||
{
|
||||
cv::Ptr<UnscentedKalmanFilter> uncsentedKalmanFilter = createUnscentedKalmanFilter( params );
|
||||
state.at<double>(0, 0) = 0.1;
|
||||
|
||||
double mse = 0.0;
|
||||
for (int i = 0; i<nIterations; i++)
|
||||
{
|
||||
rng.fill( q, RNG::NORMAL, Scalar::all(0), Scalar::all(1) );
|
||||
rng.fill( r, RNG::NORMAL, Scalar::all(0), Scalar::all(1) );
|
||||
q = processNoiseCovSqrt*q;
|
||||
r = measurementNoiseCovSqrt*r;
|
||||
|
||||
u.at<double>(0, 0) = (double)i;
|
||||
model->stateConversionFunction(state, u, q, state);
|
||||
|
||||
model->measurementFunction(state, zero, exactMeasurement);
|
||||
model->measurementFunction(state, r, measurement);
|
||||
|
||||
uncsentedKalmanFilter->predict( u );
|
||||
correctStateAUKF = uncsentedKalmanFilter->correct( measurement );
|
||||
|
||||
mse += pow( state.at<double>(0, 0) - correctStateAUKF.at<double>(0, 0), 2.0 );
|
||||
}
|
||||
mse /= nIterations;
|
||||
average_error += mse;
|
||||
}
|
||||
average_error /= 1000.0;
|
||||
|
||||
ASSERT_GE( mse_treshold, average_error );
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,73 @@
|
||||
Customizing the CN Tracker {#tutorial_customizing_cn_tracker}
|
||||
======================
|
||||
|
||||
Goal
|
||||
----
|
||||
|
||||
In this tutorial you will learn how to
|
||||
|
||||
- Set custom parameters for CN tracker.
|
||||
- Use your own feature-extractor function for the CN tracker.
|
||||
|
||||
This document contains tutorial for the @ref cv::TrackerKCF.
|
||||
|
||||
Source Code
|
||||
-----------
|
||||
|
||||
@includelineno tracking/samples/tutorial_customizing_cn_tracker.cpp
|
||||
|
||||
Explanation
|
||||
-----------
|
||||
|
||||
This part explains how to set custom parameters and use your own feature-extractor function for the CN tracker.
|
||||
If you need a more detailed information to use @ref cv::Tracker, please refer to @ref tutorial_introduction_to_tracker.
|
||||
|
||||
-# **Set Custom Parameters**
|
||||
|
||||
@snippet tracking/samples/tutorial_customizing_cn_tracker.cpp param
|
||||
|
||||
To set custom paramters, an object should be created. Each tracker algorithm has their own parameter format.
|
||||
So, in this case we should use parameter from @ref cv::TrackerKCF since we are interested in modifying the parameter of this tracker algorithm.
|
||||
|
||||
There are several parameters that can be configured as explained in @ref cv::TrackerKCF::Params.
|
||||
For this tutorial, we focussed on the feature extractor functions.
|
||||
|
||||
Several feature types can be used in @ref cv::TrackerKCF.
|
||||
In this case, the grayscale value (1 dimension) and color-names features (10 dimension),
|
||||
will be merged as 11 dimension feature and then compressed into 2 dimension as specified in the code.
|
||||
|
||||
If you want to use another type of pre-defined feature-extractor function, you can check in @ref cv::TrackerKCF::MODE.
|
||||
We will leave the non-compressed feature as 0 since we want to use a customized function.
|
||||
|
||||
-# **Using a custom function**
|
||||
|
||||
You can define your own feature-extractor function for the CN tracker.
|
||||
However, you need to take care about several things:
|
||||
- The extracted feature should have the same size as the size of the given bounding box (width and height).
|
||||
For the number of channels you can check the limitation in @ref cv::Mat.
|
||||
- You can only use features that can be compared using Euclidean distance.
|
||||
Features like local binary pattern (LBP) may not be suitable since it should be compared using Hamming distance.
|
||||
|
||||
Since the size of the extracted feature should be in the same size with the given bounding box,
|
||||
we need to take care whenever the given bounding box is partially out of range.
|
||||
In this case, we can copy part of image contained in the bounding box as shown in the snippet below.
|
||||
|
||||
@snippet tracking/samples/tutorial_customizing_cn_tracker.cpp insideimage
|
||||
|
||||
Whenever the copied image is smaller than the given bounding box,
|
||||
padding should be given to the sides where the bounding box is partially out of frame.
|
||||
|
||||
@snippet tracking/samples/tutorial_customizing_cn_tracker.cpp padding
|
||||
|
||||
-# **Defining the feature**
|
||||
|
||||
In this tutorial, the extracted feature is response of the Sobel filter in x and y direction.
|
||||
Those Sobel filter responses are concatenated, resulting a feature with 2 channels.
|
||||
|
||||
@snippet tracking/samples/tutorial_customizing_cn_tracker.cpp sobel
|
||||
|
||||
-# **Post processing**
|
||||
|
||||
Make sure to normalize the feature with range -0.5 to 0.5
|
||||
|
||||
@snippet tracking/samples/tutorial_customizing_cn_tracker.cpp postprocess
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user