@ -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 fi rst_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 ( in t 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 ( ( " \t median of length of difference of displacements = %f \n " , median_displacements ) ) ;
if ( median_displacements > params . maxMedianLengthOfDisplacementDifference ) {
dprintf ( ( " \t median 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 ( oldPoi nts . 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 ( in t 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_locatio n ) ;
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 ( in t 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 ( in t 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 ( in t 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 ( in t 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 */