vendor: OpenCV 5.0.0 snapshot at 755e50675d97db9b7d449d8bd6b09888646f6c6e

This commit is contained in:
Gitea Mirror Bot
2026-08-22 00:11:13 +08:00
commit 12022378a3
3872 changed files with 2513409 additions and 0 deletions
+28
View File
@@ -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()
+4
View File
@@ -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 &parameters = 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 &parameters){
return Ptr<TrackerKCFImpl>(new TrackerKCFImpl(parameters));
}
TrackerKCFImpl::TrackerKCFImpl( const TrackerKCF::Params &parameters ) :
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 */
+118
View File
@@ -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 &parameters = 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 &parameters = 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 &params );
/** @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 &params );
} // 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 &params() const = 0;
///
/// \brief Pipeline parameters setter.
/// \param[in] params Parameters of pipeline.
///
virtual void setParams(const TrackerParams &params) = 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 &params = 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 &parameters){
...
}
@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 &parameters = 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 &parameters = 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 &parameters = 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 &parameters = 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 &parameters = 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 &parameters = 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 &parameters);
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 &parameters);
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 &parameters);
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 &parameters);
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 &parameters);
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 &parameters);
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
+5
View File
@@ -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();
}
}
+8
View File
@@ -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()
+21
View File
@@ -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())
+14
View File
@@ -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
+102
View File
@@ -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
+357
View File
@@ -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 &gtBox, 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;
}
+144
View File
@@ -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;
}
+82
View File
@@ -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
+154
View File
@@ -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;
}
}
+47
View File
@@ -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
+166
View File
@@ -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;
}
+39
View File
@@ -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;
}
}
+222
View File
@@ -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 &params)
{
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
+147
View File
@@ -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;
}
}
}
+58
View File
@@ -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
+176
View File
@@ -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
+60
View File
@@ -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 &parameters)
: 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 &parameters)
{
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 &parameters)
: 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 &parameters)
{
return makePtr<legacy::tracking::impl::TrackerKCFImpl>(parameters);
}
Ptr<legacy::TrackerKCF> legacy::TrackerKCF::create()
{
return create(legacy::TrackerKCF::Params());
}
}
+252
View File
@@ -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
+649
View File
@@ -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
+59
View File
@@ -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
+111
View File
@@ -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
+736
View File
@@ -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
+133
View File
@@ -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;
}
}
+77
View File
@@ -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;
}
}
+118
View File
@@ -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
+189
View File
@@ -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;
}
}
}}}
+606
View File
@@ -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
+121
View File
@@ -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
+344
View File
@@ -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
+94
View File
@@ -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
+331
View File
@@ -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 &parameters)
{
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 &parameters) :
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
+172
View File
@@ -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 &parameters = 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
+196
View File
@@ -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
+53
View File
@@ -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
+9
View File
@@ -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"
+326
View File
@@ -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 &parameters = 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 &parameters){
return Ptr<TrackerBoostingImpl>(new TrackerBoostingImpl(parameters));
}
Ptr<TrackerBoosting> TrackerBoosting::create(){
return Ptr<TrackerBoostingImpl>(new TrackerBoostingImpl());
}
TrackerBoostingImpl::TrackerBoostingImpl( const TrackerBoostingImpl::Params &parameters ) :
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
+655
View File
@@ -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 &parameters = 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 &region);
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 &parameters) :
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 &region)
{
// 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 &parameters)
{
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
+563
View File
@@ -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
+52
View File
@@ -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
+317
View File
@@ -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 &parameters ) :
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
+144
View File
@@ -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
+938
View File
@@ -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 &parameters);
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 &parameters ) :
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 &parameters)
{
return makePtr<TrackerKCFImpl>(parameters);
}
}} // namespace
#include "legacy/trackerKCF.legacy.hpp"
+128
View File
@@ -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 &parameters)
: 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 &parameters)
{
return makePtr<legacy::tracking::impl::TrackerMILImpl>(parameters);
}
Ptr<legacy::TrackerMIL> legacy::TrackerMIL::create()
{
return create(legacy::TrackerMIL::Params());
}
} // namespace
+457
View File
@@ -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 &parameters)
{
return makePtr<impl::TrackerMedianFlowImpl>(parameters);
}
Ptr<TrackerMedianFlow> TrackerMedianFlow::create()
{
return create(TrackerMedianFlow::Params());
}
}}} // namespace
+144
View File
@@ -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 &parameters ) :
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 &parameters ) :
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 &parameters):
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
+72
View File
@@ -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
+46
View File
@@ -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
+77
View File
@@ -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
+372
View File
@@ -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 &params)
{
Ptr<UnscentedKalmanFilter> kfu( new UnscentedKalmanFilterImpl(params) );
return kfu;
}
}}}} // namespace
+438
View File
@@ -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
+21
View File
@@ -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())
+10
View File
@@ -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
+236
View File
@@ -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
+111
View File
@@ -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
+436
View File
@@ -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