Median flow tracker update (#952)

* Fix several issues in TrackerMedianFlow implementation

Particularly, add possibility to tune optical flow parameters for a median
flow tracker.

* Improve code of TrackerMedianFlow

Replace a lot of calls of std::vector::erase by one call of
std::remove_if.

* Delete unused code, use norm from OpenCV

* medianFlow:turn getMedian method into function, small code cleanup

* TrackerMedianFlow:fixes in parameters I/O, add test for them

* TrackerMedianFlow:replace double with float in temp buffers

* Fix indentation

* TrackerMedianFlow:add absent parameter case handling in read()

* TrackerMedianFlow:use ROI instead of copy when getting a patch

* TrackerMedianFlow:don't calc image pyramids 2 times

* MedianFlowTracker: use cvIsNan()

* MedianFlow: refactor vector filtration code

* MedianFlow: change if statements layout in filterPointsInVectors
pull/991/head
Vladislav Sovrasov 8 years ago committed by Vadim Pisarevsky
parent 7e5ca520e3
commit f2c324a280
  1. 13
      modules/tracking/include/opencv2/tracking/tracker.hpp
  2. 443
      modules/tracking/src/trackerMedianFlow.cpp
  3. 60
      modules/tracking/test/test_trackerParametersIO.cpp

@ -1156,9 +1156,16 @@ class CV_EXPORTS TrackerMedianFlow : public Tracker
public:
struct CV_EXPORTS Params
{
Params();
int pointsInGrid; //!<square root of number of keypoints used; increase it to trade
//!<accurateness for speed; default value is sensible and recommended
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;
};

@ -45,16 +45,15 @@
#include <algorithm>
#include <limits.h>
namespace cv
namespace
{
using namespace cv;
#undef ALEX_DEBUG
#ifdef ALEX_DEBUG
#define dfprintf(x) fprintf x
#undef MEDIAN_FLOW_TRACKER_DEBUG_LOGS
#ifdef MEDIAN_FLOW_TRACKER_DEBUG_LOGS
#define dprintf(x) printf x
#else
#define dfprintf(x)
#define dprintf(x)
#define dprintf(x) do{} while(false)
#endif
/*
@ -70,78 +69,79 @@ namespace cv
* FIXME:
* when patch is cut from image to compute NCC, there can be problem with size
* optimize (allocation<-->reallocation)
* optimize (remove vector.erase() calls)
* bring "out" all the parameters to TrackerMedianFlow::Param
*/
class TrackerMedianFlowImpl : public TrackerMedianFlow{
public:
TrackerMedianFlowImpl(TrackerMedianFlow::Params paramsIn):termcrit(TermCriteria::COUNT|TermCriteria::EPS,20,0.3){params=paramsIn;isInit=false;}
void read( const FileNode& fn );
void write( FileStorage& fs ) const;
private:
bool initImpl( const Mat& image, const Rect2d& boundingBox );
bool updateImpl( const Mat& image, Rect2d& boundingBox );
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);
//FIXME: this can be optimized: current method uses sort->select approach, there are O(n) selection algo for median; besides
//it makes copy all the time
template<typename T>
T getMedian( std::vector<T>& values,int size=-1);
float dist(Point2f p1,Point2f p2);
std::string type2str(int type);
void computeStatistics(std::vector<float>& data,int size=-1);
void check_FB(const Mat& oldImage,const Mat& newImage,
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);
inline double l2distance(Point2f p1,Point2f p2);
TrackerMedianFlow::Params params;
TermCriteria termcrit;
public:
TrackerMedianFlowImpl(TrackerMedianFlow::Params paramsIn) {params=paramsIn;isInit=false;}
void read( const FileNode& fn );
void write( FileStorage& fs ) const;
private:
bool initImpl( const Mat& image, const Rect2d& boundingBox );
bool updateImpl( const Mat& image, Rect2d& boundingBox );
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);
void computeStatistics(std::vector<float>& data,int size=-1);
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;
};
class TrackerMedianFlowModel : public TrackerModel{
public:
TrackerMedianFlowModel(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*/ ){}
void modelUpdateImpl(){}
};
template<typename T>
T getMedian( const std::vector<T>& values );
/*
* Parameters
*/
TrackerMedianFlow::Params::Params(){
pointsInGrid=10;
}
template<typename T>
T getMedianAndDoPartition( std::vector<T>& values );
void TrackerMedianFlow::Params::read( const cv::FileNode& fn ){
pointsInGrid=fn["pointsInGrid"];
}
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.));
void TrackerMedianFlow::Params::write( cv::FileStorage& fs ) const{
fs << "pointsInGrid" << pointsInGrid;
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(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*/ ){}
void modelUpdateImpl(){}
};
void TrackerMedianFlowImpl::read( const cv::FileNode& fn )
{
params.read( fn );
params.read( fn );
}
void TrackerMedianFlowImpl::write( cv::FileStorage& fs ) const
{
params.write( fs );
}
Ptr<TrackerMedianFlow> TrackerMedianFlow::createTracker(const TrackerMedianFlow::Params &parameters){
return Ptr<TrackerMedianFlowImpl>(new TrackerMedianFlowImpl(parameters));
params.write( fs );
}
bool TrackerMedianFlowImpl::initImpl( const Mat& image, const Rect2d& boundingBox ){
@ -164,28 +164,38 @@ bool TrackerMedianFlowImpl::updateImpl( const Mat& image, Rect2d& boundingBox ){
return true;
}
std::string TrackerMedianFlowImpl::type2str(int type) {
std::string r;
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++;
}
uchar depth = type & CV_MAT_DEPTH_MASK;
uchar chans = (uchar)(1 + (type >> CV_CN_SHIFT));
if (first_bad_idx >= status.size())
return first_bad_idx;
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;
}
for(size_t i = first_bad_idx + 1; i < status.size(); i++)
{
if (status[i] != goodValue)
continue;
r += "C";
r += (chans+'0');
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());
return r;
return first_bad_idx;
}
bool TrackerMedianFlowImpl::medianFlowImpl(Mat oldImage,Mat newImage,Rect2d& oldBox){
std::vector<Point2f> pointsToTrackOld,pointsToTrackNew;
@ -203,51 +213,76 @@ bool TrackerMedianFlowImpl::medianFlowImpl(Mat oldImage,Mat newImage,Rect2d& old
//"open ended" grid
for(int i=0;i<params.pointsInGrid;i++){
for(int j=0;j<params.pointsInGrid;j++){
pointsToTrackOld.push_back(
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)));
(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());
calcOpticalFlowPyrLK(oldImage_gray, newImage_gray,pointsToTrackOld,pointsToTrackNew,status,errors,Size(3,3),5,termcrit,0);
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()));
std::vector<Point2f> di;
for(int i=0;i<(int)pointsToTrackOld.size();i++){
if(status[i]==1){
di.push_back(pointsToTrackNew[i]-pointsToTrackOld[i]);
}
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;
}
std::vector<bool> filter_status;
check_FB(oldImage_gray,newImage_gray,pointsToTrackOld,pointsToTrackNew,filter_status);
check_NCC(oldImage_gray,newImage_gray,pointsToTrackOld,pointsToTrackNew,filter_status);
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
for(int i=0;i<(int)pointsToTrackOld.size();i++){
if(!filter_status[i]){
pointsToTrackOld.erase(pointsToTrackOld.begin()+i);
pointsToTrackNew.erase(pointsToTrackNew.begin()+i);
filter_status.erase(filter_status.begin()+i);
i--;
}
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()));
if(pointsToTrackOld.size()==0 || di.size()==0){
return false;
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<double> displacements;
for(int i=0;i<(int)di.size();i++){
std::vector<float> displacements;
for(size_t i=0;i<di.size();i++){
di[i]-=mDisplacement;
displacements.push_back(sqrt(di[i].ddot(di[i])));
displacements.push_back((float)sqrt(di[i].ddot(di[i])));
}
if(getMedian(displacements,(int)displacements.size())>10){
float median_displacements = 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;
}
@ -255,77 +290,52 @@ bool TrackerMedianFlowImpl::medianFlowImpl(Mat oldImage,Mat newImage,Rect2d& old
}
Rect2d TrackerMedianFlowImpl::vote(const std::vector<Point2f>& oldPoints,const std::vector<Point2f>& newPoints,const Rect2d& oldRect,Point2f& mD){
static int iteration=0;//FIXME -- we don't want this static var in final release
Rect2d newRect;
Point2d newCenter(oldRect.x+oldRect.width/2.0,oldRect.y+oldRect.height/2.0);
int n=(int)oldPoints.size();
std::vector<double> buf(std::max(n*(n-1)/2,3),0.0);
const size_t n=oldPoints.size();
if(oldPoints.size()==1){
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;
}
double xshift=0,yshift=0;
for(int i=0;i<n;i++){ buf[i]=newPoints[i].x-oldPoints[i].x; }
xshift=getMedian(buf,n);
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=getMedianAndDoPartition(buf_for_location);
newCenter.x+=xshift;
for(int i=0;i<n;i++){ buf[i]=newPoints[i].y-oldPoints[i].y; }
yshift=getMedian(buf,n);
for(size_t i=0;i<n;i++){ buf_for_location[i]=newPoints[i].y-oldPoints[i].y; }
yshift=getMedianAndDoPartition(buf_for_location);
newCenter.y+=yshift;
mD=Point2f((float)xshift,(float)yshift);
if(oldPoints.size()==1){
newRect.x=newCenter.x-oldRect.width/2.0;
newRect.y=newCenter.y-oldRect.height/2.0;
newRect.width=oldRect.width;
newRect.height=oldRect.height;
return newRect;
}
double nd,od;
for(int i=0,ctr=0;i<n;i++){
for(int j=0;j<i;j++){
nd=l2distance(newPoints[i],newPoints[j]);
od=l2distance(oldPoints[i],oldPoints[j]);
buf[ctr]=(od==0.0)?0.0:(nd/od);
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=getMedian(buf,n*(n-1)/2);
dprintf(("iter %d %f %f %f\n",iteration,xshift,yshift,scale));
double scale=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;
/*if(newRect.x<=0){
exit(0);
}*/
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));
iteration++;
return newRect;
}
template<typename T>
T TrackerMedianFlowImpl::getMedian(std::vector<T>& values,int size){
if(size==-1){
size=(int)values.size();
}
std::vector<T> copy(values.begin(),values.begin()+size);
std::sort(copy.begin(),copy.end());
if(size%2==0){
return (copy[size/2-1]+copy[size/2])/((T)2.0);
}else{
return copy[(size-1)/2];
}
}
void TrackerMedianFlowImpl::computeStatistics(std::vector<float>& data,int size){
int binnum=10;
if(size==-1){
@ -340,56 +350,139 @@ void TrackerMedianFlowImpl::computeStatistics(std::vector<float>& data,int size)
dprintf(("[%4f,%4f] -- %4d\n",mini+(maxi-mini)/binnum*i,mini+(maxi-mini)/binnum*(i+1),bins[i]));
}
}
double TrackerMedianFlowImpl::l2distance(Point2f p1,Point2f p2){
double dx=p1.x-p2.x, dy=p1.y-p2.y;
return sqrt(dx*dx+dy*dy);
}
void TrackerMedianFlowImpl::check_FB(const Mat& oldImage,const Mat& newImage,
const std::vector<Point2f>& oldPoints,const std::vector<Point2f>& newPoints,std::vector<bool>& status){
if(status.size()==0){
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<double> FBerror(oldPoints.size());
std::vector<float> FBerror(oldPoints.size());
std::vector<Point2f> pointsToTrackReprojection;
calcOpticalFlowPyrLK(newImage, oldImage,newPoints,pointsToTrackReprojection,LKstatus,errors,Size(3,3),5,termcrit,0);
calcOpticalFlowPyrLK(newImagePyr, oldImagePyr,newPoints,pointsToTrackReprojection,LKstatus,errors,
params.winSize, params.maxLevel, params.termCriteria, 0);
for(int i=0;i<(int)oldPoints.size();i++){
FBerror[i]=l2distance(oldPoints[i],pointsToTrackReprojection[i]);
for(size_t i=0;i<oldPoints.size();i++){
FBerror[i]=(float)norm(oldPoints[i]-pointsToTrackReprojection[i]);
}
double FBerrorMedian=getMedian(FBerror);
float FBerrorMedian=getMedian(FBerror);
dprintf(("point median=%f\n",FBerrorMedian));
dprintf(("FBerrorMedian=%f\n",FBerrorMedian));
for(int i=0;i<(int)oldPoints.size();i++){
status[i]=(FBerror[i]<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){
const std::vector<Point2f>& oldPoints,const std::vector<Point2f>& newPoints,std::vector<bool>& status){
std::vector<float> NCC(oldPoints.size(),0.0);
Size patch(30,30);
Mat p1,p2;
for (int i = 0; i < (int)oldPoints.size(); i++) {
getRectSubPix( oldImage, patch, oldPoints[i],p1);
getRectSubPix( newImage, patch, newPoints[i],p2);
for (size_t i = 0; i < oldPoints.size(); i++) {
p1 = getPatch(oldImage, params.winSizeNCC, oldPoints[i]);
p2 = getPatch(newImage, params.winSizeNCC, newPoints[i]);
const int N=900;
const int patch_area=params.winSizeNCC.area();
double s1=sum(p1)(0),s2=sum(p2)(0);
double n1=norm(p1),n2=norm(p2);
double prod=p1.dot(p2);
double sq1=sqrt(n1*n1-s1*s1/N),sq2=sqrt(n2*n2-s2*s2/N);
double ares=(sq2==0)?sq1/abs(sq1):(prod-s1*s2/N)/sq1/sq2;
NCC[i] = (float)ares;
}
float median = getMedian(NCC);
for(int i = 0; i < (int)oldPoints.size(); i++) {
status[i] = status[i] && (NCC[i]>median);
}
double sq1=sqrt(n1*n1-s1*s1/patch_area),sq2=sqrt(n2*n2-s2*s2/patch_area);
double ares=(sq2==0)?sq1/abs(sq1):(prod-s1*s2/patch_area)/sq1/sq2;
NCC[i] = (float)ares;
}
float median = getMedian(NCC);
for(size_t i = 0; i < oldPoints.size(); i++) {
status[i] = status[i] && (NCC[i] >= median);
}
}
template<typename T>
T getMedian(const std::vector<T>& values)
{
std::vector<T> copy(values);
return getMedianAndDoPartition(copy);
}
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];
}
}
} /* anonymous namespace */
namespace cv
{
/*
* 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::createTracker(const TrackerMedianFlow::Params &parameters){
return Ptr<TrackerMedianFlowImpl>(new TrackerMedianFlowImpl(parameters));
}
} /* namespace cv */

@ -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.
#include "test_precomp.hpp"
#include "opencv2/tracking.hpp"
using namespace cv;
TEST(MEDIAN_FLOW_Parameters_IO, MEDIAN_FLOW)
{
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);
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_IO_Default_Value_If_Absent, MEDIAN_FLOW)
{
TrackerMedianFlow::Params defaultParameters;
FileStorage fsReader(String("%YAML 1.0"), FileStorage::READ + FileStorage::MEMORY);
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);
}
Loading…
Cancel
Save