mirror of
https://github.com/opencv/opencv_contrib.git
synced 2025-10-18 17:24:28 +08:00
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
This commit is contained in:

committed by
Vadim Pisarevsky

parent
7e5ca520e3
commit
f2c324a280
@@ -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);
|
||||
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;
|
||||
TermCriteria termcrit;
|
||||
TrackerMedianFlow::Params params;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
T getMedian( const std::vector<T>& values );
|
||||
|
||||
template<typename T>
|
||||
T getMedianAndDoPartition( std::vector<T>& values );
|
||||
|
||||
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(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(){}
|
||||
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(){}
|
||||
};
|
||||
|
||||
/*
|
||||
* Parameters
|
||||
*/
|
||||
TrackerMedianFlow::Params::Params(){
|
||||
pointsInGrid=10;
|
||||
}
|
||||
|
||||
void TrackerMedianFlow::Params::read( const cv::FileNode& fn ){
|
||||
pointsInGrid=fn["pointsInGrid"];
|
||||
}
|
||||
|
||||
void TrackerMedianFlow::Params::write( cv::FileStorage& fs ) const{
|
||||
fs << "pointsInGrid" << pointsInGrid;
|
||||
}
|
||||
|
||||
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 ¶meters){
|
||||
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());
|
||||
|
||||
uchar depth = type & CV_MAT_DEPTH_MASK;
|
||||
uchar chans = (uchar)(1 + (type >> CV_CN_SHIFT));
|
||||
size_t first_bad_idx = 0;
|
||||
while(first_bad_idx < status.size())
|
||||
{
|
||||
if(status[first_bad_idx] != goodValue)
|
||||
break;
|
||||
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;
|
||||
}
|
||||
if (first_bad_idx >= status.size())
|
||||
return first_bad_idx;
|
||||
|
||||
r += "C";
|
||||
r += (chans+'0');
|
||||
for(size_t i = first_bad_idx + 1; i < status.size(); i++)
|
||||
{
|
||||
if (status[i] != goodValue)
|
||||
continue;
|
||||
|
||||
return r;
|
||||
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 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);
|
||||
|
||||
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);
|
||||
dprintf(("\t num_good_points_after_optical_flow = %d\n",num_good_points_after_optical_flow));
|
||||
|
||||
// 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--;
|
||||
}
|
||||
}
|
||||
dprintf(("\t%d after LK backward\n",(int)pointsToTrackOld.size()));
|
||||
|
||||
if(pointsToTrackOld.size()==0 || di.size()==0){
|
||||
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<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;
|
||||
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(int i = 0; i < (int)oldPoints.size(); i++) {
|
||||
status[i] = status[i] && (NCC[i]>median);
|
||||
}
|
||||
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 ¶meters){
|
||||
return Ptr<TrackerMedianFlowImpl>(new TrackerMedianFlowImpl(parameters));
|
||||
}
|
||||
|
||||
} /* namespace cv */
|
||||
|
60
modules/tracking/test/test_trackerParametersIO.cpp
Normal file
60
modules/tracking/test/test_trackerParametersIO.cpp
Normal 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.
|
||||
|
||||
#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);
|
||||
}
|
Reference in New Issue
Block a user