本文主要讲KCF在OpenCV中的代码,准备结合着paper一起看。
OpenCV3在Algorithm中新增了Tracker的类,见tracker.hpp中的:class CV_EXPORTS_W Tracker : public virtual Algorithm{...}
Tracker类包括几个目前比较新的Tracker:
这几个Tracker都在tracker.hpp中进行了声明,比如:
class CV_EXPORTS TrackerKCF : public Tracker{...}
这里的CV_EXPORTS在cvdef.h中的定义如下:
#if (defined WIN32 || defined _WIN32 || defined WINCE || defined __CYGWIN__) && defined CVAPI_EXPORTS
# define CV_EXPORTS __declspec(dllexport)
#elif defined __GNUC__ && __GNUC__ >= 4
# define CV_EXPORTS __attribute__ ((visibility ("default")))
#else
# define CV_EXPORTS
#endif
就是从dll文件中导出数据、函数、类或类成员函数,换句话说,就是TrackerKCF的“定义”不在某个.cpp文件中,而在dll文件中:opencv_tracking310d.dll和opencv_tracking310.dll,而这两个dll,就是由trackerKCF.cpp在OpenCV.sln中Build的。
------------------------------------------------------------------------------------------创建KCF Tracker-----------------------------------------------------------------------------
Tracker.cpp中:
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);
return Ptr<Tracker>();
}
#define BOILERPLATE_CODE(name,classname)\
if(trackerType==name){\
return classname::createTracker();\
}
转到trackerKCF.cpp:
Ptr<TrackerKCF> TrackerKCF::createTracker(const TrackerKCF::Params &meters){
return Ptr<TrackerKCFImpl>(new TrackerKCFImpl(parameters));
TrackerKCFImpl是TrackerKCF的一个子类。
构造函数:
TrackerKCFImpl( const TrackerKCF::Params ¶meters = TrackerKCF::Params() );
创建时的参数在tracker.hpp中声明:
class CV_EXPORTS TrackerKCF : public 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
*/
enum MODE { //这个枚举类型用于初始化desc_npca和desc_pca
GRAY = (1u << 0),
CN = (1u << 1),
CUSTOM = (1u << 2)
};
struct CV_EXPORTS Params
{
/**
* \brief Constructor
*/
Params();
/**
* \brief Read parameters from file, currently unused
*/
void read(const FileNode& /*fn*/);
/**
* \brief Read parameters from file, currently unused
*/
void write(FileStorage& /*fs*/) const;
double sigma; //!< gaussian kernel bandwidth
double lambda; //!< regularization
double interp_factor; //!< linear interpolation factor for adaptation
double output_sigma_factor; //!< spatial bandwidth (proportional to target)
double pca_learning_rate; //!< compression learning rate
bool resize; //!< activate the resize feature to improve the processing speed
bool split_coeff; //!< split the training coefficients into two matrices
bool wrap_kernel; //!< wrap around the kernel values
bool compress_feature; //!< activate the pca method to compress the features
int max_patch_size; //!< threshold for the ROI size
int compressed_size; //!< feature size after compression
unsigned int desc_pca; //!< compressed descriptors of TrackerKCF::MODE
unsigned int desc_npca; //!< non-compressed descriptors of TrackerKCF::MODE
};
virtual void setFeatureExtractor(void(*)(const Mat, const Rect, Mat&), bool pca_func = false);
/** @brief Constructor
@param parameters KCF parameters TrackerKCF::Params
*/
BOILERPLATE_CODE("KCF", TrackerKCF);
};
在trackerKCF.cpp中,默认参数为:
TrackerKCF::Params::Params(){
sigma=0.2;
lambda=0.01;
interp_factor=0.075;
output_sigma_factor=1.0/16.0;
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.15;
}
------------------------------------------------------------------------------------------初始化KCF Tracker-----------------------------------------------------------------------------
tracker.cpp中:
bool Tracker::init( const Mat& image, const Rect2d& boundingBox )
{
if( isInit )
{
return false;
}
if( image.empty() )
return false;
sampler = Ptr<TrackerSampler>( new TrackerSampler() );
featureSet = Ptr<TrackerFeatureSet>( new TrackerFeatureSet() );
model = Ptr<TrackerModel>();
bool initTracker = initImpl( image, boundingBox );
//check if the model component is initialized
if( model == 0 )
{
CV_Error( -1, "The model is not initialized" );
return false;
}
if( initTracker )
{
isInit = true;
}
return initTracker;
}
initImpl是tracker.hpp中在class CV_EXPORTS_W Tracker : public virtual Algorithm中声明的protected、virtual(意味着可以被子类覆盖):
protected:
virtual bool initImpl( const Mat& image, const Rect2d& boundingBox ) = 0;
virtual bool updateImpl( const Mat& image, Rect2d& boundingBox ) = 0;
bool isInit;
Ptr<TrackerFeatureSet> featureSet;
Ptr<TrackerSampler> sampler;
Ptr<TrackerModel> model;
在trackerKCF.cpp中有:
bool TrackerKCFImpl::initImpl( const Mat& /*image*/, const Rect2d& boundingBox ){
frame=0;
roi = boundingBox;
//calclulate output sigma
output_sigma=sqrt(roi.width*roi.height)*params.output_sigma_factor;
output_sigma=-0.5/(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_64F);
// 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_64F);
for(unsigned i=0;i<roi.height;i++){
for(unsigned j=0;j<roi.width;j++){
y.at<double>(i,j)=(i-roi.height/2+1)*(i-roi.height/2+1)+(j-roi.width/2+1)*(j-roi.width/2+1);
}
}
y*=(double)output_sigma;
cv::exp(y,y);
// perform fourier transfor to the gaussian response
fft2(y,yf);
model=Ptr<TrackerKCFModel>(new TrackerKCFModel(params));
// 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
);
// TODO: return true only if roi inside the image
return true;
}
------------------------------------------------------------------------------------------更新KCF Tracker-----------------------------------------------------------------------------
更新部分的代码结构和初始化一样,暂不表。
明天继续看文章。