vendor: OpenCV 5.0.0 snapshot at 755e50675d97db9b7d449d8bd6b09888646f6c6e
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
set(the_description "Optical Flow Algorithms")
|
||||
ocv_define_module(optflow opencv_core opencv_imgproc opencv_features opencv_geometry opencv_video opencv_ximgproc opencv_imgcodecs opencv_flann WRAP python)
|
||||
@@ -0,0 +1,4 @@
|
||||
Optical Flow Algorithms
|
||||
=======================
|
||||
|
||||
Algorithms for running and evaluating deepflow, simpleflow, sparsetodenseflow, robust local optical flow and motion templates (silhouette flow).
|
||||
@@ -0,0 +1,129 @@
|
||||
@article{Bradski00,
|
||||
title={Motion segmentation and pose recognition with motion history gradients},
|
||||
author={Bradski, Gary R and Davis, James W},
|
||||
journal={Machine Vision and Applications},
|
||||
volume={13},
|
||||
number={3},
|
||||
pages={174--184},
|
||||
year={2002},
|
||||
publisher={Springer}
|
||||
}
|
||||
|
||||
@inproceedings{Davis97,
|
||||
title={The representation and recognition of human movement using temporal templates},
|
||||
author={Davis, James W and Bobick, Aaron F},
|
||||
booktitle={Computer Vision and Pattern Recognition, 1997. Proceedings., 1997 IEEE Computer Society Conference on},
|
||||
pages={928--934},
|
||||
year={1997},
|
||||
organization={IEEE}
|
||||
}
|
||||
|
||||
@inproceedings{Tao2012,
|
||||
title={SimpleFlow: A Non-iterative, Sublinear Optical Flow Algorithm},
|
||||
author={Tao, Michael and Bai, Jiamin and Kohli, Pushmeet and Paris, Sylvain},
|
||||
booktitle={Computer Graphics Forum},
|
||||
volume={31},
|
||||
number={2pt1},
|
||||
pages={345--353},
|
||||
year={2012},
|
||||
organization={Wiley Online Library}
|
||||
}
|
||||
|
||||
@inproceedings{Weinzaepfel2013,
|
||||
title={DeepFlow: Large displacement optical flow with deep matching},
|
||||
author={Weinzaepfel, Philippe and Revaud, Jerome and Harchaoui, Zaid and Schmid, Cordelia},
|
||||
booktitle={Computer Vision (ICCV), 2013 IEEE International Conference on},
|
||||
pages={1385--1392},
|
||||
year={2013},
|
||||
organization={IEEE}
|
||||
}
|
||||
|
||||
@inproceedings{Brox2004,
|
||||
title={High accuracy optical flow estimation based on a theory for warping},
|
||||
author={Brox, Thomas and Bruhn, Andr{\'e}s and Papenberg, Nils and Weickert, Joachim},
|
||||
booktitle={European Conference on Computer Vision (ECCV)},
|
||||
pages={25--36},
|
||||
year={2004}
|
||||
}
|
||||
|
||||
@inproceedings{Wulff:CVPR:2015,
|
||||
title = {Efficient Sparse-to-Dense Optical Flow Estimation using a Learned Basis and Layers},
|
||||
author = {Wulff, Jonas and Black, Michael J.},
|
||||
booktitle = { IEEE Conf. on Computer Vision and Pattern Recognition (CVPR) 2015},
|
||||
month = {June},
|
||||
year = {2015}
|
||||
}
|
||||
|
||||
@inproceedings{Wang_2016_CVPR,
|
||||
author = {Wang, Shenlong and Ryan Fanello, Sean and Rhemann, Christoph and Izadi, Shahram and Kohli, Pushmeet},
|
||||
title = {The Global Patch Collider},
|
||||
booktitle = {The IEEE Conference on Computer Vision and Pattern Recognition (CVPR)},
|
||||
month = {June},
|
||||
year = {2016}
|
||||
}
|
||||
|
||||
@inproceedings{Geistert2016,
|
||||
author = {Jonas Geistert and Tobias Senst and Thomas Sikora},
|
||||
title = {Robust Local Optical Flow: Dense Motion Vector Field Interpolation},
|
||||
booktitle = {Picture Coding Symposium},
|
||||
year = {2016},
|
||||
pages = {1--5},
|
||||
}
|
||||
|
||||
@inproceedings{Senst2016,
|
||||
author = {Tobias Senst and Jonas Geistert and Thomas Sikora},
|
||||
title = {Robust local optical flow: Long-range motions and varying illuminations},
|
||||
booktitle = {IEEE International Conference on Image Processing},
|
||||
year = {2016},
|
||||
pages = {4478--4482},
|
||||
}
|
||||
|
||||
@inproceedings{Senst2014,
|
||||
author = {Tobias Senst and Thilo Borgmann and Ivo Keller and Thomas Sikora},
|
||||
title = {Cross based Robust Local Optical Flow},
|
||||
booktitle = {21th IEEE International Conference on Image Processing},
|
||||
year = {2014},
|
||||
pages = {1967--1971},
|
||||
}
|
||||
|
||||
@inproceedings{Senst2013,
|
||||
author = {Tobias Senst and Jonas Geistert and Ivo Keller and Thomas Sikora},
|
||||
title = {Robust Local Optical Flow Estimation using Bilinear Equations for Sparse Motion Estimation},
|
||||
booktitle = {20th IEEE International Conference on Image Processing},
|
||||
year = {2013},
|
||||
pages = {2499--2503},
|
||||
}
|
||||
|
||||
@article{Senst2012,
|
||||
author = {Tobias Senst and Volker Eiselein and Thomas Sikora},
|
||||
title = {Robust Local Optical Flow for Feature Tracking},
|
||||
journal = {IEEE Transactions on Circuits and Systems for Video Technology},
|
||||
year = {2012},
|
||||
pages = {1377--1387},
|
||||
volume = {22},
|
||||
number = {9},
|
||||
}
|
||||
|
||||
@phdthesis{Senst2019,
|
||||
title={Estimation and analysis of motion in video data},
|
||||
author={Senst, Tobias},
|
||||
year={2019},
|
||||
school={Technical Univerity Berlin},
|
||||
doi = {10.14279/depositonce-9085},
|
||||
url = {https://doi.org/10.14279/depositonce-9085}
|
||||
}
|
||||
|
||||
@article{Tibshirani2008,
|
||||
title={Fast computation of the median by successive binning},
|
||||
author={Tibshirani, Ryan J},
|
||||
journal={arXiv preprint arXiv:0806.3301},
|
||||
year={2008}
|
||||
}
|
||||
|
||||
@inproceedings{Hu2017,
|
||||
title={Robust interpolation of correspondences for large displacement optical flow},
|
||||
author={Hu, Yinlin and Li, Yunsong and Song, Rui},
|
||||
booktitle={IEEE Conference on Computer Vision and Pattern Recognition},
|
||||
pages={481--489},
|
||||
year={2017}
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
/*
|
||||
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
|
||||
(3-clause BSD License)
|
||||
|
||||
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:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions 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.
|
||||
|
||||
* Neither the names of the copyright holders nor the names of the contributors
|
||||
may 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 copyright holders 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.
|
||||
*/
|
||||
|
||||
#ifndef __OPENCV_OPTFLOW_HPP__
|
||||
#define __OPENCV_OPTFLOW_HPP__
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/video.hpp"
|
||||
|
||||
/**
|
||||
@defgroup optflow Optical Flow Algorithms
|
||||
|
||||
Dense optical flow algorithms compute motion for each point:
|
||||
|
||||
- cv::optflow::calcOpticalFlowSF
|
||||
- cv::optflow::createOptFlow_DeepFlow
|
||||
|
||||
Motion templates is alternative technique for detecting motion and computing its direction.
|
||||
See samples/motempl.py.
|
||||
|
||||
- cv::motempl::updateMotionHistory
|
||||
- cv::motempl::calcMotionGradient
|
||||
- cv::motempl::calcGlobalOrientation
|
||||
- cv::motempl::segmentMotion
|
||||
|
||||
Functions reading and writing .flo files in "Middlebury" format, see: <http://vision.middlebury.edu/flow/code/flow-code/README.txt>
|
||||
|
||||
- cv::optflow::readOpticalFlow
|
||||
- cv::optflow::writeOpticalFlow
|
||||
|
||||
*/
|
||||
|
||||
#include "opencv2/optflow/pcaflow.hpp"
|
||||
#include "opencv2/optflow/sparse_matching_gpc.hpp"
|
||||
#include "opencv2/optflow/rlofflow.hpp"
|
||||
namespace cv
|
||||
{
|
||||
namespace optflow
|
||||
{
|
||||
|
||||
//! @addtogroup optflow
|
||||
//! @{
|
||||
|
||||
/** @overload */
|
||||
CV_EXPORTS_W void calcOpticalFlowSF( InputArray from, InputArray to, OutputArray flow,
|
||||
int layers, int averaging_block_size, int max_flow);
|
||||
|
||||
/** @brief Calculate an optical flow using "SimpleFlow" algorithm.
|
||||
|
||||
@param from First 8-bit 3-channel image.
|
||||
@param to Second 8-bit 3-channel image of the same size as prev
|
||||
@param flow computed flow image that has the same size as prev and type CV_32FC2
|
||||
@param layers Number of layers
|
||||
@param averaging_block_size Size of block through which we sum up when calculate cost function
|
||||
for pixel
|
||||
@param max_flow maximal flow that we search at each level
|
||||
@param sigma_dist vector smooth spatial sigma parameter
|
||||
@param sigma_color vector smooth color sigma parameter
|
||||
@param postprocess_window window size for postprocess cross bilateral filter
|
||||
@param sigma_dist_fix spatial sigma for postprocess cross bilateralf filter
|
||||
@param sigma_color_fix color sigma for postprocess cross bilateral filter
|
||||
@param occ_thr threshold for detecting occlusions
|
||||
@param upscale_averaging_radius window size for bilateral upscale operation
|
||||
@param upscale_sigma_dist spatial sigma for bilateral upscale operation
|
||||
@param upscale_sigma_color color sigma for bilateral upscale operation
|
||||
@param speed_up_thr threshold to detect point with irregular flow - where flow should be
|
||||
recalculated after upscale
|
||||
|
||||
See @cite Tao2012 . And site of project - <http://graphics.berkeley.edu/papers/Tao-SAN-2012-05/>.
|
||||
|
||||
@note
|
||||
- An example using the simpleFlow algorithm can be found at samples/simpleflow_demo.cpp
|
||||
*/
|
||||
CV_EXPORTS_W void calcOpticalFlowSF( InputArray from, InputArray to, OutputArray flow, int layers,
|
||||
int averaging_block_size, int max_flow,
|
||||
double sigma_dist, double sigma_color, int postprocess_window,
|
||||
double sigma_dist_fix, double sigma_color_fix, double occ_thr,
|
||||
int upscale_averaging_radius, double upscale_sigma_dist,
|
||||
double upscale_sigma_color, double speed_up_thr );
|
||||
|
||||
/** @brief Fast dense optical flow based on PyrLK sparse matches interpolation.
|
||||
|
||||
@param from first 8-bit 3-channel or 1-channel image.
|
||||
@param to second 8-bit 3-channel or 1-channel image of the same size as from
|
||||
@param flow computed flow image that has the same size as from and CV_32FC2 type
|
||||
@param grid_step stride used in sparse match computation. Lower values usually
|
||||
result in higher quality but slow down the algorithm.
|
||||
@param k number of nearest-neighbor matches considered, when fitting a locally affine
|
||||
model. Lower values can make the algorithm noticeably faster at the cost of
|
||||
some quality degradation.
|
||||
@param sigma parameter defining how fast the weights decrease in the locally-weighted affine
|
||||
fitting. Higher values can help preserve fine details, lower values can help to get rid
|
||||
of the noise in the output flow.
|
||||
@param use_post_proc defines whether the ximgproc::fastGlobalSmootherFilter() is used
|
||||
for post-processing after interpolation
|
||||
@param fgs_lambda see the respective parameter of the ximgproc::fastGlobalSmootherFilter()
|
||||
@param fgs_sigma see the respective parameter of the ximgproc::fastGlobalSmootherFilter()
|
||||
*/
|
||||
CV_EXPORTS_W void calcOpticalFlowSparseToDense ( InputArray from, InputArray to, OutputArray flow,
|
||||
int grid_step = 8, int k = 128, float sigma = 0.05f,
|
||||
bool use_post_proc = true, float fgs_lambda = 500.0f,
|
||||
float fgs_sigma = 1.5f );
|
||||
|
||||
/** @brief DeepFlow optical flow algorithm implementation.
|
||||
|
||||
The class implements the DeepFlow optical flow algorithm described in @cite Weinzaepfel2013 . See
|
||||
also <http://lear.inrialpes.fr/src/deepmatching/> .
|
||||
Parameters - class fields - that may be modified after creating a class instance:
|
||||
- member float alpha
|
||||
Smoothness assumption weight
|
||||
- member float delta
|
||||
Color constancy assumption weight
|
||||
- member float gamma
|
||||
Gradient constancy weight
|
||||
- member float sigma
|
||||
Gaussian smoothing parameter
|
||||
- member int minSize
|
||||
Minimal dimension of an image in the pyramid (next, smaller images in the pyramid are generated
|
||||
until one of the dimensions reaches this size)
|
||||
- member float downscaleFactor
|
||||
Scaling factor in the image pyramid (must be \< 1)
|
||||
- member int fixedPointIterations
|
||||
How many iterations on each level of the pyramid
|
||||
- member int sorIterations
|
||||
Iterations of Succesive Over-Relaxation (solver)
|
||||
- member float omega
|
||||
Relaxation factor in SOR
|
||||
*/
|
||||
CV_EXPORTS_W Ptr<DenseOpticalFlow> createOptFlow_DeepFlow();
|
||||
|
||||
//! Additional interface to the SimpleFlow algorithm - calcOpticalFlowSF()
|
||||
CV_EXPORTS_W Ptr<DenseOpticalFlow> createOptFlow_SimpleFlow();
|
||||
|
||||
//! Additional interface to the Farneback's algorithm - calcOpticalFlowFarneback()
|
||||
CV_EXPORTS_W Ptr<DenseOpticalFlow> createOptFlow_Farneback();
|
||||
|
||||
//! Additional interface to the SparseToDenseFlow algorithm - calcOpticalFlowSparseToDense()
|
||||
CV_EXPORTS_W Ptr<DenseOpticalFlow> createOptFlow_SparseToDense();
|
||||
|
||||
/** @brief "Dual TV L1" Optical Flow Algorithm.
|
||||
|
||||
The class implements the "Dual TV L1" optical flow algorithm described in @cite Zach2007 and
|
||||
@cite Javier2012 .
|
||||
Here are important members of the class that control the algorithm, which you can set after
|
||||
constructing the class instance:
|
||||
|
||||
- member double tau
|
||||
Time step of the numerical scheme.
|
||||
|
||||
- member double lambda
|
||||
Weight parameter for the data term, attachment parameter. This is the most relevant
|
||||
parameter, which determines the smoothness of the output. The smaller this parameter is,
|
||||
the smoother the solutions we obtain. It depends on the range of motions of the images, so
|
||||
its value should be adapted to each image sequence.
|
||||
|
||||
- member double theta
|
||||
Weight parameter for (u - v)\^2, tightness parameter. It serves as a link between the
|
||||
attachment and the regularization terms. In theory, it should have a small value in order
|
||||
to maintain both parts in correspondence. The method is stable for a large range of values
|
||||
of this parameter.
|
||||
|
||||
- member int nscales
|
||||
Number of scales used to create the pyramid of images.
|
||||
|
||||
- member int warps
|
||||
Number of warpings per scale. Represents the number of times that I1(x+u0) and grad(
|
||||
I1(x+u0) ) are computed per scale. This is a parameter that assures the stability of the
|
||||
method. It also affects the running time, so it is a compromise between speed and
|
||||
accuracy.
|
||||
|
||||
- member double epsilon
|
||||
Stopping criterion threshold used in the numerical scheme, which is a trade-off between
|
||||
precision and running time. A small value will yield more accurate solutions at the
|
||||
expense of a slower convergence.
|
||||
|
||||
- member int iterations
|
||||
Stopping criterion iterations number used in the numerical scheme.
|
||||
|
||||
C. Zach, T. Pock and H. Bischof, "A Duality Based Approach for Realtime TV-L1 Optical Flow".
|
||||
Javier Sanchez, Enric Meinhardt-Llopis and Gabriele Facciolo. "TV-L1 Optical Flow Estimation".
|
||||
*/
|
||||
class CV_EXPORTS_W DualTVL1OpticalFlow : public DenseOpticalFlow
|
||||
{
|
||||
public:
|
||||
//! @brief Time step of the numerical scheme
|
||||
/** @see setTau */
|
||||
CV_WRAP virtual double getTau() const = 0;
|
||||
/** @copybrief getTau @see getTau */
|
||||
CV_WRAP virtual void setTau(double val) = 0;
|
||||
//! @brief Weight parameter for the data term, attachment parameter
|
||||
/** @see setLambda */
|
||||
CV_WRAP virtual double getLambda() const = 0;
|
||||
/** @copybrief getLambda @see getLambda */
|
||||
CV_WRAP virtual void setLambda(double val) = 0;
|
||||
//! @brief Weight parameter for (u - v)^2, tightness parameter
|
||||
/** @see setTheta */
|
||||
CV_WRAP virtual double getTheta() const = 0;
|
||||
/** @copybrief getTheta @see getTheta */
|
||||
CV_WRAP virtual void setTheta(double val) = 0;
|
||||
//! @brief coefficient for additional illumination variation term
|
||||
/** @see setGamma */
|
||||
CV_WRAP virtual double getGamma() const = 0;
|
||||
/** @copybrief getGamma @see getGamma */
|
||||
CV_WRAP virtual void setGamma(double val) = 0;
|
||||
//! @brief Number of scales used to create the pyramid of images
|
||||
/** @see setScalesNumber */
|
||||
CV_WRAP virtual int getScalesNumber() const = 0;
|
||||
/** @copybrief getScalesNumber @see getScalesNumber */
|
||||
CV_WRAP virtual void setScalesNumber(int val) = 0;
|
||||
//! @brief Number of warpings per scale
|
||||
/** @see setWarpingsNumber */
|
||||
CV_WRAP virtual int getWarpingsNumber() const = 0;
|
||||
/** @copybrief getWarpingsNumber @see getWarpingsNumber */
|
||||
CV_WRAP virtual void setWarpingsNumber(int val) = 0;
|
||||
//! @brief Stopping criterion threshold used in the numerical scheme, which is a trade-off between precision and running time
|
||||
/** @see setEpsilon */
|
||||
CV_WRAP virtual double getEpsilon() const = 0;
|
||||
/** @copybrief getEpsilon @see getEpsilon */
|
||||
CV_WRAP virtual void setEpsilon(double val) = 0;
|
||||
//! @brief Inner iterations (between outlier filtering) used in the numerical scheme
|
||||
/** @see setInnerIterations */
|
||||
CV_WRAP virtual int getInnerIterations() const = 0;
|
||||
/** @copybrief getInnerIterations @see getInnerIterations */
|
||||
CV_WRAP virtual void setInnerIterations(int val) = 0;
|
||||
//! @brief Outer iterations (number of inner loops) used in the numerical scheme
|
||||
/** @see setOuterIterations */
|
||||
CV_WRAP virtual int getOuterIterations() const = 0;
|
||||
/** @copybrief getOuterIterations @see getOuterIterations */
|
||||
CV_WRAP virtual void setOuterIterations(int val) = 0;
|
||||
//! @brief Use initial flow
|
||||
/** @see setUseInitialFlow */
|
||||
CV_WRAP virtual bool getUseInitialFlow() const = 0;
|
||||
/** @copybrief getUseInitialFlow @see getUseInitialFlow */
|
||||
CV_WRAP virtual void setUseInitialFlow(bool val) = 0;
|
||||
//! @brief Step between scales (<1)
|
||||
/** @see setScaleStep */
|
||||
CV_WRAP virtual double getScaleStep() const = 0;
|
||||
/** @copybrief getScaleStep @see getScaleStep */
|
||||
CV_WRAP virtual void setScaleStep(double val) = 0;
|
||||
//! @brief Median filter kernel size (1 = no filter) (3 or 5)
|
||||
/** @see setMedianFiltering */
|
||||
CV_WRAP virtual int getMedianFiltering() const = 0;
|
||||
/** @copybrief getMedianFiltering @see getMedianFiltering */
|
||||
CV_WRAP virtual void setMedianFiltering(int val) = 0;
|
||||
|
||||
/** @brief Creates instance of cv::DualTVL1OpticalFlow*/
|
||||
CV_WRAP static Ptr<DualTVL1OpticalFlow> create(
|
||||
double tau = 0.25,
|
||||
double lambda = 0.15,
|
||||
double theta = 0.3,
|
||||
int nscales = 5,
|
||||
int warps = 5,
|
||||
double epsilon = 0.01,
|
||||
int innnerIterations = 30,
|
||||
int outerIterations = 10,
|
||||
double scaleStep = 0.8,
|
||||
double gamma = 0.0,
|
||||
int medianFiltering = 5,
|
||||
bool useInitialFlow = false);
|
||||
};
|
||||
|
||||
/** @brief Creates instance of cv::DenseOpticalFlow
|
||||
*/
|
||||
CV_EXPORTS_W Ptr<DualTVL1OpticalFlow> createOptFlow_DualTVL1();
|
||||
|
||||
//! @}
|
||||
|
||||
} //optflow
|
||||
}
|
||||
|
||||
#include "opencv2/optflow/motempl.hpp"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
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
|
||||
(3-clause BSD License)
|
||||
|
||||
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:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions 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.
|
||||
|
||||
* Neither the names of the copyright holders nor the names of the contributors
|
||||
may 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 copyright holders 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.
|
||||
*/
|
||||
|
||||
#ifndef __OPENCV_OPTFLOW_MOTEMPL_HPP__
|
||||
#define __OPENCV_OPTFLOW_MOTEMPL_HPP__
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace motempl
|
||||
{
|
||||
|
||||
//! @addtogroup optflow
|
||||
//! @{
|
||||
|
||||
/** @brief Updates the motion history image by a moving silhouette.
|
||||
|
||||
@param silhouette Silhouette mask that has non-zero pixels where the motion occurs.
|
||||
@param mhi Motion history image that is updated by the function (single-channel, 32-bit
|
||||
floating-point).
|
||||
@param timestamp Current time in milliseconds or other units.
|
||||
@param duration Maximal duration of the motion track in the same units as timestamp .
|
||||
|
||||
The function updates the motion history image as follows:
|
||||
|
||||
\f[\texttt{mhi} (x,y)= \forkthree{\texttt{timestamp}}{if \(\texttt{silhouette}(x,y) \ne 0\)}{0}{if \(\texttt{silhouette}(x,y) = 0\) and \(\texttt{mhi} < (\texttt{timestamp} - \texttt{duration})\)}{\texttt{mhi}(x,y)}{otherwise}\f]
|
||||
|
||||
That is, MHI pixels where the motion occurs are set to the current timestamp , while the pixels
|
||||
where the motion happened last time a long time ago are cleared.
|
||||
|
||||
The function, together with calcMotionGradient and calcGlobalOrientation , implements a motion
|
||||
templates technique described in @cite Davis97 and @cite Bradski00 .
|
||||
*/
|
||||
CV_EXPORTS_W void updateMotionHistory( InputArray silhouette, InputOutputArray mhi,
|
||||
double timestamp, double duration );
|
||||
|
||||
/** @brief Calculates a gradient orientation of a motion history image.
|
||||
|
||||
@param mhi Motion history single-channel floating-point image.
|
||||
@param mask Output mask image that has the type CV_8UC1 and the same size as mhi . Its non-zero
|
||||
elements mark pixels where the motion gradient data is correct.
|
||||
@param orientation Output motion gradient orientation image that has the same type and the same
|
||||
size as mhi . Each pixel of the image is a motion orientation, from 0 to 360 degrees.
|
||||
@param delta1 Minimal (or maximal) allowed difference between mhi values within a pixel
|
||||
neighborhood.
|
||||
@param delta2 Maximal (or minimal) allowed difference between mhi values within a pixel
|
||||
neighborhood. That is, the function finds the minimum ( \f$m(x,y)\f$ ) and maximum ( \f$M(x,y)\f$ ) mhi
|
||||
values over \f$3 \times 3\f$ neighborhood of each pixel and marks the motion orientation at \f$(x, y)\f$
|
||||
as valid only if
|
||||
\f[\min ( \texttt{delta1} , \texttt{delta2} ) \le M(x,y)-m(x,y) \le \max ( \texttt{delta1} , \texttt{delta2} ).\f]
|
||||
@param apertureSize Aperture size of the Sobel operator.
|
||||
|
||||
The function calculates a gradient orientation at each pixel \f$(x, y)\f$ as:
|
||||
|
||||
\f[\texttt{orientation} (x,y)= \arctan{\frac{d\texttt{mhi}/dy}{d\texttt{mhi}/dx}}\f]
|
||||
|
||||
In fact, fastAtan2 and phase are used so that the computed angle is measured in degrees and covers
|
||||
the full range 0..360. Also, the mask is filled to indicate pixels where the computed angle is
|
||||
valid.
|
||||
|
||||
@note
|
||||
- (Python) An example on how to perform a motion template technique can be found at
|
||||
opencv_source_code/samples/python2/motempl.py
|
||||
*/
|
||||
CV_EXPORTS_W void calcMotionGradient( InputArray mhi, OutputArray mask, OutputArray orientation,
|
||||
double delta1, double delta2, int apertureSize = 3 );
|
||||
|
||||
/** @brief Calculates a global motion orientation in a selected region.
|
||||
|
||||
@param orientation Motion gradient orientation image calculated by the function calcMotionGradient
|
||||
@param mask Mask image. It may be a conjunction of a valid gradient mask, also calculated by
|
||||
calcMotionGradient , and the mask of a region whose direction needs to be calculated.
|
||||
@param mhi Motion history image calculated by updateMotionHistory .
|
||||
@param timestamp Timestamp passed to updateMotionHistory .
|
||||
@param duration Maximum duration of a motion track in milliseconds, passed to updateMotionHistory
|
||||
|
||||
The function calculates an average motion direction in the selected region and returns the angle
|
||||
between 0 degrees and 360 degrees. The average direction is computed from the weighted orientation
|
||||
histogram, where a recent motion has a larger weight and the motion occurred in the past has a
|
||||
smaller weight, as recorded in mhi .
|
||||
*/
|
||||
CV_EXPORTS_W double calcGlobalOrientation( InputArray orientation, InputArray mask, InputArray mhi,
|
||||
double timestamp, double duration );
|
||||
|
||||
/** @brief Splits a motion history image into a few parts corresponding to separate independent motions (for
|
||||
example, left hand, right hand).
|
||||
|
||||
@param mhi Motion history image.
|
||||
@param segmask Image where the found mask should be stored, single-channel, 32-bit floating-point.
|
||||
@param boundingRects Vector containing ROIs of motion connected components.
|
||||
@param timestamp Current time in milliseconds or other units.
|
||||
@param segThresh Segmentation threshold that is recommended to be equal to the interval between
|
||||
motion history "steps" or greater.
|
||||
|
||||
The function finds all of the motion segments and marks them in segmask with individual values
|
||||
(1,2,...). It also computes a vector with ROIs of motion connected components. After that the motion
|
||||
direction for every component can be calculated with calcGlobalOrientation using the extracted mask
|
||||
of the particular component.
|
||||
*/
|
||||
CV_EXPORTS_W void segmentMotion( InputArray mhi, OutputArray segmask,
|
||||
CV_OUT std::vector<Rect>& boundingRects,
|
||||
double timestamp, double segThresh );
|
||||
|
||||
|
||||
//! @}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
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
|
||||
(3-clause BSD License)
|
||||
|
||||
Copyright (C) 2016, 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:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions 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.
|
||||
|
||||
* Neither the names of the copyright holders nor the names of the contributors
|
||||
may 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 copyright holders 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file pcaflow.hpp
|
||||
* @author Vladislav Samsonov <vvladxx@gmail.com>
|
||||
* @brief Implementation of the PCAFlow algorithm from the following paper:
|
||||
* http://files.is.tue.mpg.de/black/papers/cvpr2015_pcaflow.pdf
|
||||
*
|
||||
* @cite Wulff:CVPR:2015
|
||||
*
|
||||
* There are some key differences which distinguish this algorithm from the original PCAFlow (see paper):
|
||||
* - Discrete Cosine Transform basis is used instead of basis extracted with PCA.
|
||||
* Reasoning: DCT basis has comparable performance and it doesn't require additional storage space.
|
||||
* Also, this decision helps to avoid overloading the algorithm with a lot of external input.
|
||||
* - Usage of built-in OpenCV feature tracking instead of libviso.
|
||||
*/
|
||||
|
||||
#ifndef __OPENCV_OPTFLOW_PCAFLOW_HPP__
|
||||
#define __OPENCV_OPTFLOW_PCAFLOW_HPP__
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/video.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace optflow
|
||||
{
|
||||
|
||||
//! @addtogroup optflow
|
||||
//! @{
|
||||
|
||||
/** @brief
|
||||
* This class can be used for imposing a learned prior on the resulting optical flow.
|
||||
* Solution will be regularized according to this prior.
|
||||
* You need to generate appropriate prior file with "learn_prior.py" script beforehand.
|
||||
*/
|
||||
class CV_EXPORTS_W PCAPrior
|
||||
{
|
||||
private:
|
||||
Mat L1;
|
||||
Mat L2;
|
||||
Mat c1;
|
||||
Mat c2;
|
||||
|
||||
public:
|
||||
PCAPrior( const char *pathToPrior );
|
||||
|
||||
int getPadding() const { return L1.size().height; }
|
||||
|
||||
int getBasisSize() const { return L1.size().width; }
|
||||
|
||||
void fillConstraints( float *A1, float *A2, float *b1, float *b2 ) const;
|
||||
};
|
||||
|
||||
/** @brief PCAFlow algorithm.
|
||||
*/
|
||||
class CV_EXPORTS_W OpticalFlowPCAFlow : public DenseOpticalFlow
|
||||
{
|
||||
protected:
|
||||
const Ptr<const PCAPrior> prior;
|
||||
const Size basisSize;
|
||||
const float sparseRate; // (0 .. 0.1)
|
||||
const float retainedCornersFraction; // [0 .. 1]
|
||||
const float occlusionsThreshold;
|
||||
const float dampingFactor;
|
||||
const float claheClip;
|
||||
bool useOpenCL;
|
||||
|
||||
public:
|
||||
/** @brief Creates an instance of PCAFlow algorithm.
|
||||
* @param _prior Learned prior or no prior (default). @see cv::optflow::PCAPrior
|
||||
* @param _basisSize Number of basis vectors.
|
||||
* @param _sparseRate Controls density of sparse matches.
|
||||
* @param _retainedCornersFraction Retained corners fraction.
|
||||
* @param _occlusionsThreshold Occlusion threshold.
|
||||
* @param _dampingFactor Regularization term for solving least-squares. It is not related to the prior regularization.
|
||||
* @param _claheClip Clip parameter for CLAHE.
|
||||
*/
|
||||
OpticalFlowPCAFlow( Ptr<const PCAPrior> _prior = Ptr<const PCAPrior>(), const Size _basisSize = Size( 18, 14 ),
|
||||
float _sparseRate = 0.024, float _retainedCornersFraction = 0.2,
|
||||
float _occlusionsThreshold = 0.0003, float _dampingFactor = 0.00002, float _claheClip = 14 );
|
||||
|
||||
void calc( InputArray I0, InputArray I1, InputOutputArray flow ) CV_OVERRIDE;
|
||||
void collectGarbage() CV_OVERRIDE;
|
||||
|
||||
private:
|
||||
void findSparseFeatures( UMat &from, UMat &to, std::vector<Point2f> &features,
|
||||
std::vector<Point2f> &predictedFeatures ) const;
|
||||
|
||||
void removeOcclusions( UMat &from, UMat &to, std::vector<Point2f> &features,
|
||||
std::vector<Point2f> &predictedFeatures ) const;
|
||||
|
||||
void getSystem( OutputArray AOut, OutputArray b1Out, OutputArray b2Out, const std::vector<Point2f> &features,
|
||||
const std::vector<Point2f> &predictedFeatures, const Size size );
|
||||
|
||||
void getSystem( OutputArray A1Out, OutputArray A2Out, OutputArray b1Out, OutputArray b2Out,
|
||||
const std::vector<Point2f> &features, const std::vector<Point2f> &predictedFeatures,
|
||||
const Size size );
|
||||
|
||||
OpticalFlowPCAFlow& operator=( const OpticalFlowPCAFlow& ); // make it non-assignable
|
||||
};
|
||||
|
||||
/** @brief Creates an instance of PCAFlow
|
||||
*/
|
||||
CV_EXPORTS_W Ptr<DenseOpticalFlow> createOptFlow_PCAFlow();
|
||||
|
||||
//! @}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,553 @@
|
||||
// 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_OPTFLOW_RLOFFLOW_HPP__
|
||||
#define __OPENCV_OPTFLOW_RLOFFLOW_HPP__
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/video.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace optflow
|
||||
{
|
||||
//! @addtogroup optflow
|
||||
//! @{
|
||||
|
||||
enum SupportRegionType {
|
||||
SR_FIXED = 0, /**< Apply a constant support region */
|
||||
SR_CROSS = 1 /**< Apply a adaptive support region obtained by cross-based segmentation
|
||||
* as described in @cite Senst2014
|
||||
*/
|
||||
};
|
||||
enum SolverType {
|
||||
ST_STANDART = 0, /**< Apply standard iterative refinement */
|
||||
ST_BILINEAR = 1 /**< Apply optimized iterative refinement based bilinear equation solutions
|
||||
* as described in @cite Senst2013
|
||||
*/
|
||||
};
|
||||
|
||||
enum InterpolationType
|
||||
{
|
||||
INTERP_GEO = 0, /**< Fast geodesic interpolation, see @cite Geistert2016 */
|
||||
INTERP_EPIC = 1, /**< Edge-preserving interpolation using ximgproc::EdgeAwareInterpolator, see @cite Revaud2015,Geistert2016. */
|
||||
INTERP_RIC = 2, /**< SLIC based robust interpolation using ximgproc::RICInterpolator, see @cite Hu2017. */
|
||||
};
|
||||
|
||||
/** @brief This is used store and set up the parameters of the robust local optical flow (RLOF) algoritm.
|
||||
*
|
||||
* The RLOF is a fast local optical flow approach described in @cite Senst2012 @cite Senst2013 @cite Senst2014
|
||||
* and @cite Senst2016 similar to the pyramidal iterative Lucas-Kanade method as
|
||||
* proposed by @cite Bouguet00. More details and experiments can be found in the following thesis @cite Senst2019.
|
||||
* The implementation is derived from optflow::calcOpticalFlowPyrLK().
|
||||
* This RLOF implementation can be seen as an improved pyramidal iterative Lucas-Kanade and includes
|
||||
* a set of improving modules. The main improvements in respect to the pyramidal iterative Lucas-Kanade
|
||||
* are:
|
||||
* - A more robust redecending M-estimator framework (see @cite Senst2012) to improve the accuracy at
|
||||
* motion boundaries and appearing and disappearing pixels.
|
||||
* - an adaptive support region strategies to improve the accuracy at motion boundaries to reduce the
|
||||
* corona effect, i.e oversmoothing of the PLK at motion/object boundaries. The cross-based segementation
|
||||
* strategy (SR_CROSS) proposed in @cite Senst2014 uses a simple segmenation approach to obtain the optimal
|
||||
* shape of the support region.
|
||||
* - To deal with illumination changes (outdoor sequences and shadow) the intensity constancy assumption
|
||||
* based optical flow equation has been adopt with the Gennert and Negahdaripour illumination model
|
||||
* (see @cite Senst2016). This model can be switched on/off with the useIlluminationModel variable.
|
||||
* - By using a global motion prior initialization (see @cite Senst2016) of the iterative refinement
|
||||
* the accuracy could be significantly improved for large displacements. This initialization can be
|
||||
* switched on and of with useGlobalMotionPrior variable.
|
||||
*
|
||||
* The RLOF can be computed with the SparseOpticalFlow class or function interface to track a set of features
|
||||
* or with the DenseOpticalFlow class or function interface to compute dense optical flow.
|
||||
*
|
||||
* @see optflow::DenseRLOFOpticalFlow, optflow::calcOpticalFlowDenseRLOF(), optflow::SparseRLOFOpticalFlow, optflow::calcOpticalFlowSparseRLOF()
|
||||
*/
|
||||
class CV_EXPORTS_W RLOFOpticalFlowParameter{
|
||||
public:
|
||||
RLOFOpticalFlowParameter()
|
||||
:solverType(ST_BILINEAR)
|
||||
,supportRegionType(SR_CROSS)
|
||||
,normSigma0(std::numeric_limits<float>::max())
|
||||
,normSigma1(std::numeric_limits<float>::max())
|
||||
,smallWinSize(9)
|
||||
,largeWinSize(21)
|
||||
,crossSegmentationThreshold(25)
|
||||
,maxLevel(4)
|
||||
,useInitialFlow(false)
|
||||
,useIlluminationModel(true)
|
||||
,useGlobalMotionPrior(true)
|
||||
,maxIteration(30)
|
||||
,minEigenValue(0.0001f)
|
||||
,globalMotionRansacThreshold(10)
|
||||
{}
|
||||
|
||||
SolverType solverType;
|
||||
/**< Variable specifies the iterative refinement strategy. Please consider citing @cite Senst2013 when
|
||||
* using ST_BILINEAR.
|
||||
*/
|
||||
|
||||
SupportRegionType supportRegionType;
|
||||
/**< Variable specifies the support region shape extraction or shrinking strategy.
|
||||
*/
|
||||
|
||||
float normSigma0;
|
||||
/**< &sigma parameter of the shrinked Hampel norm introduced in @cite Senst2012. If
|
||||
* &sigma = std::numeric_limist<float>::max() the least-square estimator will be used
|
||||
* instead of the M-estimator. Althoug M-estimator is more robust against outlier in the support
|
||||
* region the least-square can be fast in computation.
|
||||
*/
|
||||
float normSigma1;
|
||||
/**< &sigma parameter of the shrinked Hampel norm introduced in @cite Senst2012. If
|
||||
* &sigma = std::numeric_limist<float>::max() the least-square estimator will be used
|
||||
* instead of the M-estimator. Althoug M-estimator is more robust against outlier in the support
|
||||
* region the least-square can be fast in computation.
|
||||
*/
|
||||
int smallWinSize;
|
||||
/**< Minimal window size of the support region. This parameter is only used if supportRegionType is SR_CROSS.
|
||||
*/
|
||||
int largeWinSize;
|
||||
/**< Maximal window size of the support region. If supportRegionType is SR_FIXED this gives the exact support
|
||||
* region size. The speed of the RLOF is related to the applied win sizes. The smaller the window size the lower is the runtime,
|
||||
* but the more sensitive to noise is the method.
|
||||
*/
|
||||
int crossSegmentationThreshold;
|
||||
/**< Color similarity threshold used by cross-based segmentation following @cite Senst2014 .
|
||||
* (Only used if supportRegionType is SR_CROSS). With the cross-bassed segmentation
|
||||
* motion boundaries can be computed more accurately.
|
||||
*/
|
||||
int maxLevel;
|
||||
/**< Maximal number of pyramid level used. The large this value is the more likely it is
|
||||
* to obtain accurate solutions for long-range motions. The runtime is linear related to
|
||||
* this parameter.
|
||||
*/
|
||||
bool useInitialFlow;
|
||||
/**< Use next point list as initial values. A good intialization can imporve the algortihm
|
||||
* accuracy and reduce the runtime by a faster convergence of the iteration refinement.
|
||||
*/
|
||||
bool useIlluminationModel;
|
||||
/**< Use the Gennert and Negahdaripour illumination model instead of the intensity brigthness
|
||||
* constraint. (proposed in @cite Senst2016 ) This model is defined as follow:
|
||||
* \f[ I(\mathbf{x},t) + m \cdot I(\mathbf{x},t) + c = I(\mathbf{x},t+1) \f]
|
||||
* and contains with m and c a multiplicative and additive term which makes the estimate
|
||||
* more robust against illumination changes. The computational complexity is increased by
|
||||
* enabling the illumination model.
|
||||
*/
|
||||
bool useGlobalMotionPrior;
|
||||
/**< Use global motion prior initialisation has been introduced in @cite Senst2016 . It
|
||||
* allows to be more accurate for long-range motion. The computational complexity is
|
||||
* slightly increased by enabling the global motion prior initialisation.
|
||||
*/
|
||||
int maxIteration;
|
||||
/**< Number of maximal iterations used for the iterative refinement. Lower values can
|
||||
* reduce the runtime but also the accuracy.
|
||||
*/
|
||||
float minEigenValue;
|
||||
/**< Threshold for the minimal eigenvalue of the gradient matrix defines when to abort the
|
||||
* iterative refinement.
|
||||
*/
|
||||
float globalMotionRansacThreshold;
|
||||
/**< To apply the global motion prior motion vectors will be computed on a regulary sampled which
|
||||
* are the basis for Homography estimation using RANSAC. The reprojection threshold is based on
|
||||
* n-th percentil (given by this value [0 ... 100]) of the motion vectors magnitude.
|
||||
* See @cite Senst2016 for more details.
|
||||
*/
|
||||
|
||||
//! @brief Enable M-estimator or disable and use least-square estimator.
|
||||
/** Enables M-estimator by setting sigma parameters to (3.2, 7.0). Disabling M-estimator can reduce
|
||||
* runtime, while enabling can improve the accuracy.
|
||||
* @param val If true M-estimator is used. If false least-square estimator is used.
|
||||
* @see setNormSigma0, setNormSigma1
|
||||
*/
|
||||
CV_WRAP void setUseMEstimator(bool val);
|
||||
|
||||
CV_WRAP void setSolverType(SolverType val);
|
||||
CV_WRAP SolverType getSolverType() const;
|
||||
|
||||
CV_WRAP void setSupportRegionType(SupportRegionType val);
|
||||
CV_WRAP SupportRegionType getSupportRegionType() const;
|
||||
|
||||
CV_WRAP void setNormSigma0(float val);
|
||||
CV_WRAP float getNormSigma0() const;
|
||||
|
||||
CV_WRAP void setNormSigma1(float val);
|
||||
CV_WRAP float getNormSigma1() const;
|
||||
|
||||
CV_WRAP void setSmallWinSize(int val);
|
||||
CV_WRAP int getSmallWinSize() const;
|
||||
|
||||
CV_WRAP void setLargeWinSize(int val);
|
||||
CV_WRAP int getLargeWinSize() const;
|
||||
|
||||
CV_WRAP void setCrossSegmentationThreshold(int val);
|
||||
CV_WRAP int getCrossSegmentationThreshold() const;
|
||||
|
||||
CV_WRAP void setMaxLevel(int val);
|
||||
CV_WRAP int getMaxLevel() const;
|
||||
|
||||
CV_WRAP void setUseInitialFlow(bool val);
|
||||
CV_WRAP bool getUseInitialFlow() const;
|
||||
|
||||
CV_WRAP void setUseIlluminationModel(bool val);
|
||||
CV_WRAP bool getUseIlluminationModel() const;
|
||||
|
||||
CV_WRAP void setUseGlobalMotionPrior(bool val);
|
||||
CV_WRAP bool getUseGlobalMotionPrior() const;
|
||||
|
||||
CV_WRAP void setMaxIteration(int val);
|
||||
CV_WRAP int getMaxIteration() const;
|
||||
|
||||
CV_WRAP void setMinEigenValue(float val);
|
||||
CV_WRAP float getMinEigenValue() const;
|
||||
|
||||
CV_WRAP void setGlobalMotionRansacThreshold(float val);
|
||||
CV_WRAP float getGlobalMotionRansacThreshold() const;
|
||||
|
||||
//! @brief Creates instance of optflow::RLOFOpticalFlowParameter
|
||||
CV_WRAP static Ptr<RLOFOpticalFlowParameter> create();
|
||||
};
|
||||
|
||||
/** @brief Fast dense optical flow computation based on robust local optical flow (RLOF) algorithms and sparse-to-dense interpolation
|
||||
* scheme.
|
||||
*
|
||||
* The RLOF is a fast local optical flow approach described in @cite Senst2012 @cite Senst2013 @cite Senst2014
|
||||
* and @cite Senst2016 similar to the pyramidal iterative Lucas-Kanade method as
|
||||
* proposed by @cite Bouguet00. More details and experiments can be found in the following thesis @cite Senst2019.
|
||||
* The implementation is derived from optflow::calcOpticalFlowPyrLK().
|
||||
*
|
||||
* The sparse-to-dense interpolation scheme allows for fast computation of dense optical flow using RLOF (see @cite Geistert2016).
|
||||
* For this scheme the following steps are applied:
|
||||
* -# motion vector seeded at a regular sampled grid are computed. The sparsity of this grid can be configured with setGridStep
|
||||
* -# (optinally) errornous motion vectors are filter based on the forward backward confidence. The threshold can be configured
|
||||
* with setForwardBackward. The filter is only applied if the threshold >0 but than the runtime is doubled due to the estimation
|
||||
* of the backward flow.
|
||||
* -# Vector field interpolation is applied to the motion vector set to obtain a dense vector field.
|
||||
*
|
||||
* For the RLOF configuration see optflow::RLOFOpticalFlowParameter for further details.
|
||||
* Parameters have been described in @cite Senst2012 @cite Senst2013 @cite Senst2014 and @cite Senst2016.
|
||||
*
|
||||
* @note If the grid size is set to (1,1) and the forward backward threshold <= 0 than pixelwise dense optical flow field is
|
||||
* computed by RLOF without using interpolation.
|
||||
*
|
||||
* @note Note that in output, if no correspondences are found between \a I0 and \a I1, the \a flow is set to 0.
|
||||
* @see optflow::calcOpticalFlowDenseRLOF(), optflow::RLOFOpticalFlowParameter
|
||||
*/
|
||||
class CV_EXPORTS_W DenseRLOFOpticalFlow : public DenseOpticalFlow
|
||||
{
|
||||
public:
|
||||
//! @brief Configuration of the RLOF alogrithm.
|
||||
/**
|
||||
@see optflow::RLOFOpticalFlowParameter, getRLOFOpticalFlowParameter
|
||||
*/
|
||||
CV_WRAP virtual void setRLOFOpticalFlowParameter(Ptr<RLOFOpticalFlowParameter> val) = 0;
|
||||
/** @copybrief setRLOFOpticalFlowParameter
|
||||
@see optflow::RLOFOpticalFlowParameter, setRLOFOpticalFlowParameter
|
||||
*/
|
||||
CV_WRAP virtual Ptr<RLOFOpticalFlowParameter> getRLOFOpticalFlowParameter() const = 0;
|
||||
//! @brief Threshold for the forward backward confidence check
|
||||
/**For each grid point \f$ \mathbf{x} \f$ a motion vector \f$ d_{I0,I1}(\mathbf{x}) \f$ is computed.
|
||||
* If the forward backward error \f[ EP_{FB} = || d_{I0,I1} + d_{I1,I0} || \f]
|
||||
* is larger than threshold given by this function then the motion vector will not be used by the following
|
||||
* vector field interpolation. \f$ d_{I1,I0} \f$ denotes the backward flow. Note, the forward backward test
|
||||
* will only be applied if the threshold > 0. This may results into a doubled runtime for the motion estimation.
|
||||
* @see getForwardBackward, setGridStep
|
||||
*/
|
||||
CV_WRAP virtual void setForwardBackward(float val) = 0;
|
||||
/** @copybrief setForwardBackward
|
||||
@see setForwardBackward
|
||||
*/
|
||||
CV_WRAP virtual float getForwardBackward() const = 0;
|
||||
//! @brief Size of the grid to spawn the motion vectors.
|
||||
/** For each grid point a motion vector is computed. Some motion vectors will be removed due to the forwatd backward
|
||||
* threshold (if set >0). The rest will be the base of the vector field interpolation.
|
||||
* @see getForwardBackward, setGridStep
|
||||
*/
|
||||
CV_WRAP virtual Size getGridStep() const = 0;
|
||||
/** @copybrief getGridStep
|
||||
* @see getGridStep
|
||||
*/
|
||||
CV_WRAP virtual void setGridStep(Size val) = 0;
|
||||
|
||||
//! @brief Interpolation used to compute the dense optical flow.
|
||||
/** Two interpolation algorithms are supported
|
||||
* - **INTERP_GEO** applies the fast geodesic interpolation, see @cite Geistert2016.
|
||||
* - **INTERP_EPIC_RESIDUAL** applies the edge-preserving interpolation, see @cite Revaud2015,Geistert2016.
|
||||
* @see ximgproc::EdgeAwareInterpolator, getInterpolation
|
||||
*/
|
||||
CV_WRAP virtual void setInterpolation(InterpolationType val) = 0;
|
||||
/** @copybrief setInterpolation
|
||||
* @see ximgproc::EdgeAwareInterpolator, setInterpolation
|
||||
*/
|
||||
CV_WRAP virtual InterpolationType getInterpolation() const = 0;
|
||||
//! @brief see ximgproc::EdgeAwareInterpolator() K value.
|
||||
/** K is a number of nearest-neighbor matches considered, when fitting a locally affine
|
||||
* model. Usually it should be around 128. However, lower values would make the interpolation noticeably faster.
|
||||
* @see ximgproc::EdgeAwareInterpolator, setEPICK
|
||||
*/
|
||||
CV_WRAP virtual int getEPICK() const = 0;
|
||||
/** @copybrief getEPICK
|
||||
* @see ximgproc::EdgeAwareInterpolator, getEPICK
|
||||
*/
|
||||
CV_WRAP virtual void setEPICK(int val) = 0;
|
||||
//! @brief see ximgproc::EdgeAwareInterpolator() sigma value.
|
||||
/** Sigma is a parameter defining how fast the weights decrease in the locally-weighted affine
|
||||
* fitting. Higher values can help preserve fine details, lower values can help to get rid of noise in the
|
||||
* output flow.
|
||||
* @see ximgproc::EdgeAwareInterpolator, setEPICSigma
|
||||
*/
|
||||
CV_WRAP virtual float getEPICSigma() const = 0;
|
||||
/** @copybrief getEPICSigma
|
||||
* @see ximgproc::EdgeAwareInterpolator, getEPICSigma
|
||||
*/
|
||||
CV_WRAP virtual void setEPICSigma(float val) = 0;
|
||||
//! @brief see ximgproc::EdgeAwareInterpolator() lambda value.
|
||||
/** Lambda is a parameter defining the weight of the edge-aware term in geodesic distance,
|
||||
* should be in the range of 0 to 1000.
|
||||
* @see ximgproc::EdgeAwareInterpolator, setEPICSigma
|
||||
*/
|
||||
CV_WRAP virtual float getEPICLambda() const = 0;
|
||||
/** @copybrief getEPICLambda
|
||||
* @see ximgproc::EdgeAwareInterpolator, getEPICLambda
|
||||
*/
|
||||
CV_WRAP virtual void setEPICLambda(float val) = 0;
|
||||
//! @brief see ximgproc::EdgeAwareInterpolator().
|
||||
/** Sets the respective fastGlobalSmootherFilter() parameter.
|
||||
* @see ximgproc::EdgeAwareInterpolator, setFgsLambda
|
||||
*/
|
||||
CV_WRAP virtual float getFgsLambda() const = 0;
|
||||
/** @copybrief getFgsLambda
|
||||
* @see ximgproc::EdgeAwareInterpolator, ximgproc::fastGlobalSmootherFilter, getFgsLambda
|
||||
*/
|
||||
CV_WRAP virtual void setFgsLambda(float val) = 0;
|
||||
//! @brief see ximgproc::EdgeAwareInterpolator().
|
||||
/** Sets the respective fastGlobalSmootherFilter() parameter.
|
||||
* @see ximgproc::EdgeAwareInterpolator, ximgproc::fastGlobalSmootherFilter, setFgsSigma
|
||||
*/
|
||||
CV_WRAP virtual float getFgsSigma() const = 0;
|
||||
/** @copybrief getFgsSigma
|
||||
* @see ximgproc::EdgeAwareInterpolator, ximgproc::fastGlobalSmootherFilter, getFgsSigma
|
||||
*/
|
||||
CV_WRAP virtual void setFgsSigma(float val) = 0;
|
||||
//! @brief enables ximgproc::fastGlobalSmootherFilter
|
||||
/**
|
||||
* @see getUsePostProc
|
||||
*/
|
||||
CV_WRAP virtual void setUsePostProc(bool val) = 0;
|
||||
/** @copybrief setUsePostProc
|
||||
* @see ximgproc::fastGlobalSmootherFilter, setUsePostProc
|
||||
*/
|
||||
CV_WRAP virtual bool getUsePostProc() const = 0;
|
||||
//! @brief enables VariationalRefinement
|
||||
/**
|
||||
* @see getUseVariationalRefinement
|
||||
*/
|
||||
CV_WRAP virtual void setUseVariationalRefinement(bool val) = 0;
|
||||
/** @copybrief setUseVariationalRefinement
|
||||
* @see ximgproc::fastGlobalSmootherFilter, setUsePostProc
|
||||
*/
|
||||
CV_WRAP virtual bool getUseVariationalRefinement() const = 0;
|
||||
//! @brief Parameter to tune the approximate size of the superpixel used for oversegmentation.
|
||||
/**
|
||||
* @see cv::ximgproc::createSuperpixelSLIC, cv::ximgproc::RICInterpolator
|
||||
*/
|
||||
CV_WRAP virtual void setRICSPSize(int val) = 0;
|
||||
/** @copybrief setRICSPSize
|
||||
* @see setRICSPSize
|
||||
*/
|
||||
CV_WRAP virtual int getRICSPSize() const = 0;
|
||||
/** @brief Parameter to choose superpixel algorithm variant to use:
|
||||
* - cv::ximgproc::SLICType SLIC segments image using a desired region_size (value: 100)
|
||||
* - cv::ximgproc::SLICType SLICO will optimize using adaptive compactness factor (value: 101)
|
||||
* - cv::ximgproc::SLICType MSLIC will optimize using manifold methods resulting in more content-sensitive superpixels (value: 102).
|
||||
* @see cv::ximgproc::createSuperpixelSLIC, cv::ximgproc::RICInterpolator
|
||||
*/
|
||||
CV_WRAP virtual void setRICSLICType(int val) = 0;
|
||||
/** @copybrief setRICSLICType
|
||||
* @see setRICSLICType
|
||||
*/
|
||||
CV_WRAP virtual int getRICSLICType() const = 0;
|
||||
//! @brief Creates instance of optflow::DenseRLOFOpticalFlow
|
||||
/**
|
||||
* @param rlofParam see optflow::RLOFOpticalFlowParameter
|
||||
* @param forwardBackwardThreshold see setForwardBackward
|
||||
* @param gridStep see setGridStep
|
||||
* @param interp_type see setInterpolation
|
||||
* @param epicK see setEPICK
|
||||
* @param epicSigma see setEPICSigma
|
||||
* @param epicLambda see setEPICLambda
|
||||
* @param ricSPSize see setRICSPSize
|
||||
* @param ricSLICType see setRICSLICType
|
||||
* @param use_post_proc see setUsePostProc
|
||||
* @param fgsLambda see setFgsLambda
|
||||
* @param fgsSigma see setFgsSigma
|
||||
* @param use_variational_refinement see setUseVariationalRefinement
|
||||
*/
|
||||
CV_WRAP static Ptr<DenseRLOFOpticalFlow> create(
|
||||
Ptr<RLOFOpticalFlowParameter> rlofParam = Ptr<RLOFOpticalFlowParameter>(),
|
||||
float forwardBackwardThreshold = 1.f,
|
||||
Size gridStep = Size(6, 6),
|
||||
InterpolationType interp_type = InterpolationType::INTERP_EPIC,
|
||||
int epicK = 128,
|
||||
float epicSigma = 0.05f,
|
||||
float epicLambda = 999.0f,
|
||||
int ricSPSize = 15,
|
||||
int ricSLICType = 100,
|
||||
bool use_post_proc = true,
|
||||
float fgsLambda = 500.0f,
|
||||
float fgsSigma = 1.5f,
|
||||
bool use_variational_refinement = false);
|
||||
};
|
||||
|
||||
/** @brief Class used for calculation sparse optical flow and feature tracking with robust local optical flow (RLOF) algorithms.
|
||||
*
|
||||
* The RLOF is a fast local optical flow approach described in @cite Senst2012 @cite Senst2013 @cite Senst2014
|
||||
* and @cite Senst2016 similar to the pyramidal iterative Lucas-Kanade method as
|
||||
* proposed by @cite Bouguet00. More details and experiments can be found in the following thesis @cite Senst2019.
|
||||
* The implementation is derived from optflow::calcOpticalFlowPyrLK().
|
||||
*
|
||||
* For the RLOF configuration see optflow::RLOFOpticalFlowParameter for further details.
|
||||
* Parameters have been described in @cite Senst2012, @cite Senst2013, @cite Senst2014 and @cite Senst2016.
|
||||
*
|
||||
* @note SIMD parallelization is only available when compiling with SSE4.1.
|
||||
* @see optflow::calcOpticalFlowSparseRLOF(), optflow::RLOFOpticalFlowParameter
|
||||
*/
|
||||
class CV_EXPORTS_W SparseRLOFOpticalFlow : public SparseOpticalFlow
|
||||
{
|
||||
public:
|
||||
/** @copydoc DenseRLOFOpticalFlow::setRLOFOpticalFlowParameter
|
||||
*/
|
||||
CV_WRAP virtual void setRLOFOpticalFlowParameter(Ptr<RLOFOpticalFlowParameter> val) = 0;
|
||||
/** @copybrief setRLOFOpticalFlowParameter
|
||||
* @see setRLOFOpticalFlowParameter
|
||||
*/
|
||||
CV_WRAP virtual Ptr<RLOFOpticalFlowParameter> getRLOFOpticalFlowParameter() const = 0;
|
||||
//! @brief Threshold for the forward backward confidence check
|
||||
/** For each feature point a motion vector \f$ d_{I0,I1}(\mathbf{x}) \f$ is computed.
|
||||
* If the forward backward error \f[ EP_{FB} = || d_{I0,I1} + d_{I1,I0} || \f]
|
||||
* is larger than threshold given by this function then the status will not be used by the following
|
||||
* vector field interpolation. \f$ d_{I1,I0} \f$ denotes the backward flow. Note, the forward backward test
|
||||
* will only be applied if the threshold > 0. This may results into a doubled runtime for the motion estimation.
|
||||
* @see setForwardBackward
|
||||
*/
|
||||
CV_WRAP virtual void setForwardBackward(float val) = 0;
|
||||
/** @copybrief setForwardBackward
|
||||
* @see setForwardBackward
|
||||
*/
|
||||
CV_WRAP virtual float getForwardBackward() const = 0;
|
||||
|
||||
//! @brief Creates instance of SparseRLOFOpticalFlow
|
||||
/**
|
||||
* @param rlofParam see setRLOFOpticalFlowParameter
|
||||
* @param forwardBackwardThreshold see setForwardBackward
|
||||
*/
|
||||
CV_WRAP static Ptr<SparseRLOFOpticalFlow> create(
|
||||
Ptr<RLOFOpticalFlowParameter> rlofParam = Ptr<RLOFOpticalFlowParameter>(),
|
||||
float forwardBackwardThreshold = 1.f);
|
||||
|
||||
};
|
||||
|
||||
/** @brief Fast dense optical flow computation based on robust local optical flow (RLOF) algorithms and sparse-to-dense interpolation scheme.
|
||||
*
|
||||
* The RLOF is a fast local optical flow approach described in @cite Senst2012 @cite Senst2013 @cite Senst2014
|
||||
* and @cite Senst2016 similar to the pyramidal iterative Lucas-Kanade method as
|
||||
* proposed by @cite Bouguet00. More details and experiments can be found in the following thesis @cite Senst2019.
|
||||
* The implementation is derived from optflow::calcOpticalFlowPyrLK().
|
||||
*
|
||||
* The sparse-to-dense interpolation scheme allows for fast computation of dense optical flow using RLOF (see @cite Geistert2016).
|
||||
* For this scheme the following steps are applied:
|
||||
* -# motion vector seeded at a regular sampled grid are computed. The sparsity of this grid can be configured with setGridStep
|
||||
* -# (optinally) errornous motion vectors are filter based on the forward backward confidence. The threshold can be configured
|
||||
* with setForwardBackward. The filter is only applied if the threshold >0 but than the runtime is doubled due to the estimation
|
||||
* of the backward flow.
|
||||
* -# Vector field interpolation is applied to the motion vector set to obtain a dense vector field.
|
||||
*
|
||||
* @param I0 first 8-bit input image. If The cross-based RLOF is used (by selecting optflow::RLOFOpticalFlowParameter::supportRegionType
|
||||
* = SupportRegionType::SR_CROSS) image has to be a 8-bit 3 channel image.
|
||||
* @param I1 second 8-bit input image. If The cross-based RLOF is used (by selecting optflow::RLOFOpticalFlowParameter::supportRegionType
|
||||
* = SupportRegionType::SR_CROSS) image has to be a 8-bit 3 channel image.
|
||||
* @param flow computed flow image that has the same size as I0 and type CV_32FC2.
|
||||
* @param rlofParam see optflow::RLOFOpticalFlowParameter
|
||||
* @param forwardBackwardThreshold Threshold for the forward backward confidence check.
|
||||
* For each grid point \f$ \mathbf{x} \f$ a motion vector \f$ d_{I0,I1}(\mathbf{x}) \f$ is computed.
|
||||
* If the forward backward error \f[ EP_{FB} = || d_{I0,I1} + d_{I1,I0} || \f]
|
||||
* is larger than threshold given by this function then the motion vector will not be used by the following
|
||||
* vector field interpolation. \f$ d_{I1,I0} \f$ denotes the backward flow. Note, the forward backward test
|
||||
* will only be applied if the threshold > 0. This may results into a doubled runtime for the motion estimation.
|
||||
* @param gridStep Size of the grid to spawn the motion vectors. For each grid point a motion vector is computed.
|
||||
* Some motion vectors will be removed due to the forwatd backward threshold (if set >0). The rest will be the
|
||||
* base of the vector field interpolation.
|
||||
* @param interp_type interpolation method used to compute the dense optical flow. Two interpolation algorithms are
|
||||
* supported:
|
||||
* - **INTERP_GEO** applies the fast geodesic interpolation, see @cite Geistert2016.
|
||||
* - **INTERP_EPIC_RESIDUAL** applies the edge-preserving interpolation, see @cite Revaud2015,Geistert2016.
|
||||
* @param epicK see ximgproc::EdgeAwareInterpolator sets the respective parameter.
|
||||
* @param epicSigma see ximgproc::EdgeAwareInterpolator sets the respective parameter.
|
||||
* @param epicLambda see ximgproc::EdgeAwareInterpolator sets the respective parameter.
|
||||
* @param ricSPSize see ximgproc::RICInterpolator sets the respective parameter.
|
||||
* @param ricSLICType see ximgproc::RICInterpolator sets the respective parameter.
|
||||
* @param use_post_proc enables ximgproc::fastGlobalSmootherFilter() parameter.
|
||||
* @param fgsLambda sets the respective ximgproc::fastGlobalSmootherFilter() parameter.
|
||||
* @param fgsSigma sets the respective ximgproc::fastGlobalSmootherFilter() parameter.
|
||||
* @param use_variational_refinement enables VariationalRefinement
|
||||
*
|
||||
* Parameters have been described in @cite Senst2012, @cite Senst2013, @cite Senst2014, @cite Senst2016.
|
||||
* For the RLOF configuration see optflow::RLOFOpticalFlowParameter for further details.
|
||||
* @note If the grid size is set to (1,1) and the forward backward threshold <= 0 that the dense optical flow field is purely
|
||||
* computed with the RLOF.
|
||||
*
|
||||
* @note SIMD parallelization is only available when compiling with SSE4.1.
|
||||
* @note Note that in output, if no correspondences are found between \a I0 and \a I1, the \a flow is set to 0.
|
||||
*
|
||||
* @sa optflow::DenseRLOFOpticalFlow, optflow::RLOFOpticalFlowParameter
|
||||
*/
|
||||
CV_EXPORTS_W void calcOpticalFlowDenseRLOF(InputArray I0, InputArray I1, InputOutputArray flow,
|
||||
Ptr<RLOFOpticalFlowParameter> rlofParam = Ptr<RLOFOpticalFlowParameter>(),
|
||||
float forwardBackwardThreshold = 0, Size gridStep = Size(6, 6),
|
||||
InterpolationType interp_type = InterpolationType::INTERP_EPIC,
|
||||
int epicK = 128, float epicSigma = 0.05f, float epicLambda = 100.f,
|
||||
int ricSPSize = 15, int ricSLICType = 100,
|
||||
bool use_post_proc = true, float fgsLambda = 500.0f, float fgsSigma = 1.5f,
|
||||
bool use_variational_refinement = false);
|
||||
|
||||
/** @brief Calculates fast optical flow for a sparse feature set using the robust local optical flow (RLOF) similar
|
||||
* to optflow::calcOpticalFlowPyrLK().
|
||||
*
|
||||
* The RLOF is a fast local optical flow approach described in @cite Senst2012 @cite Senst2013 @cite Senst2014
|
||||
* and @cite Senst2016 similar to the pyramidal iterative Lucas-Kanade method as
|
||||
* proposed by @cite Bouguet00. More details and experiments can be found in the following thesis @cite Senst2019.
|
||||
* The implementation is derived from optflow::calcOpticalFlowPyrLK().
|
||||
*
|
||||
* @param prevImg first 8-bit input image. If The cross-based RLOF is used (by selecting optflow::RLOFOpticalFlowParameter::supportRegionType
|
||||
* = SupportRegionType::SR_CROSS) image has to be a 8-bit 3 channel image.
|
||||
* @param nextImg second 8-bit input image. If The cross-based RLOF is used (by selecting optflow::RLOFOpticalFlowParameter::supportRegionType
|
||||
* = SupportRegionType::SR_CROSS) image has to be a 8-bit 3 channel image.
|
||||
* @param prevPts vector of 2D points for which the flow needs to be found; point coordinates must be single-precision
|
||||
* floating-point numbers.
|
||||
* @param nextPts output vector of 2D points (with single-precision floating-point coordinates) containing the calculated
|
||||
* new positions of input features in the second image; when optflow::RLOFOpticalFlowParameter::useInitialFlow variable is true the vector must
|
||||
* have the same size as in the input and contain the initialization point correspondences.
|
||||
* @param status output status vector (of unsigned chars); each element of the vector is set to 1 if the flow for the
|
||||
* corresponding features has passed the forward backward check.
|
||||
* @param err output vector of errors; each element of the vector is set to the forward backward error for the corresponding feature.
|
||||
* @param rlofParam see optflow::RLOFOpticalFlowParameter
|
||||
* @param forwardBackwardThreshold Threshold for the forward backward confidence check. If forewardBackwardThreshold <=0 the forward
|
||||
*
|
||||
* @note SIMD parallelization is only available when compiling with SSE4.1.
|
||||
*
|
||||
* Parameters have been described in @cite Senst2012, @cite Senst2013, @cite Senst2014 and @cite Senst2016.
|
||||
* For the RLOF configuration see optflow::RLOFOpticalFlowParameter for further details.
|
||||
*/
|
||||
CV_EXPORTS_W void calcOpticalFlowSparseRLOF(InputArray prevImg, InputArray nextImg,
|
||||
InputArray prevPts, InputOutputArray nextPts,
|
||||
OutputArray status, OutputArray err,
|
||||
Ptr<RLOFOpticalFlowParameter> rlofParam = Ptr<RLOFOpticalFlowParameter>(),
|
||||
float forwardBackwardThreshold = 0);
|
||||
|
||||
//! Additional interface to the Dense RLOF algorithm - optflow::calcOpticalFlowDenseRLOF()
|
||||
CV_EXPORTS_W Ptr<DenseOpticalFlow> createOptFlow_DenseRLOF();
|
||||
|
||||
//! Additional interface to the Sparse RLOF algorithm - optflow::calcOpticalFlowSparseRLOF()
|
||||
CV_EXPORTS_W Ptr<SparseOpticalFlow> createOptFlow_SparseRLOF();
|
||||
//! @}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
#endif
|
||||
@@ -0,0 +1,372 @@
|
||||
/*
|
||||
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
|
||||
(3-clause BSD License)
|
||||
|
||||
Copyright (C) 2016, 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:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions 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.
|
||||
|
||||
* Neither the names of the copyright holders nor the names of the contributors
|
||||
may 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 copyright holders 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file sparse_matching_gpc.hpp
|
||||
* @author Vladislav Samsonov <vvladxx@gmail.com>
|
||||
* @brief Implementation of the Global Patch Collider.
|
||||
*
|
||||
* Implementation of the Global Patch Collider algorithm from the following paper:
|
||||
* http://research.microsoft.com/en-us/um/people/pkohli/papers/wfrik_cvpr2016.pdf
|
||||
*
|
||||
* @cite Wang_2016_CVPR
|
||||
*/
|
||||
|
||||
#ifndef __OPENCV_OPTFLOW_SPARSE_MATCHING_GPC_HPP__
|
||||
#define __OPENCV_OPTFLOW_SPARSE_MATCHING_GPC_HPP__
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace optflow
|
||||
{
|
||||
|
||||
//! @addtogroup optflow
|
||||
//! @{
|
||||
|
||||
struct CV_EXPORTS_W GPCPatchDescriptor
|
||||
{
|
||||
static const unsigned nFeatures = 18; //!< number of features in a patch descriptor
|
||||
Vec< double, nFeatures > feature;
|
||||
|
||||
double dot( const Vec< double, nFeatures > &coef ) const;
|
||||
|
||||
void markAsSeparated() { feature[0] = std::numeric_limits< double >::quiet_NaN(); }
|
||||
|
||||
bool isSeparated() const { return cvIsNaN( feature[0] ) != 0; }
|
||||
};
|
||||
|
||||
struct CV_EXPORTS_W GPCPatchSample
|
||||
{
|
||||
GPCPatchDescriptor ref;
|
||||
GPCPatchDescriptor pos;
|
||||
GPCPatchDescriptor neg;
|
||||
|
||||
void getDirections( bool &refdir, bool &posdir, bool &negdir, const Vec< double, GPCPatchDescriptor::nFeatures > &coef, double rhs ) const;
|
||||
};
|
||||
|
||||
typedef std::vector< GPCPatchSample > GPCSamplesVector;
|
||||
|
||||
/** @brief Descriptor types for the Global Patch Collider.
|
||||
*/
|
||||
enum GPCDescType
|
||||
{
|
||||
GPC_DESCRIPTOR_DCT = 0, //!< Better quality but slow
|
||||
GPC_DESCRIPTOR_WHT //!< Worse quality but much faster
|
||||
};
|
||||
|
||||
/** @brief Class encapsulating training samples.
|
||||
*/
|
||||
class CV_EXPORTS_W GPCTrainingSamples
|
||||
{
|
||||
private:
|
||||
GPCSamplesVector samples;
|
||||
int descriptorType;
|
||||
|
||||
public:
|
||||
/** @brief This function can be used to extract samples from a pair of images and a ground truth flow.
|
||||
* Sizes of all the provided vectors must be equal.
|
||||
*/
|
||||
static Ptr< GPCTrainingSamples > create( const std::vector< String > &imagesFrom, const std::vector< String > &imagesTo,
|
||||
const std::vector< String > >, int descriptorType );
|
||||
|
||||
static Ptr< GPCTrainingSamples > create( InputArrayOfArrays imagesFrom, InputArrayOfArrays imagesTo, InputArrayOfArrays gt,
|
||||
int descriptorType );
|
||||
|
||||
size_t size() const { return samples.size(); }
|
||||
|
||||
int type() const { return descriptorType; }
|
||||
|
||||
operator GPCSamplesVector &() { return samples; }
|
||||
};
|
||||
|
||||
/** @brief Class encapsulating training parameters.
|
||||
*/
|
||||
struct GPCTrainingParams
|
||||
{
|
||||
unsigned maxTreeDepth; //!< Maximum tree depth to stop partitioning.
|
||||
int minNumberOfSamples; //!< Minimum number of samples in the node to stop partitioning.
|
||||
int descriptorType; //!< Type of descriptors to use.
|
||||
bool printProgress; //!< Print progress to stdout.
|
||||
|
||||
GPCTrainingParams( unsigned _maxTreeDepth = 20, int _minNumberOfSamples = 3, GPCDescType _descriptorType = GPC_DESCRIPTOR_DCT,
|
||||
bool _printProgress = true )
|
||||
: maxTreeDepth( _maxTreeDepth ), minNumberOfSamples( _minNumberOfSamples ), descriptorType( _descriptorType ),
|
||||
printProgress( _printProgress )
|
||||
{
|
||||
CV_Assert( check() );
|
||||
}
|
||||
|
||||
bool check() const { return maxTreeDepth > 1 && minNumberOfSamples > 1; }
|
||||
};
|
||||
|
||||
/** @brief Class encapsulating matching parameters.
|
||||
*/
|
||||
struct GPCMatchingParams
|
||||
{
|
||||
bool useOpenCL; //!< Whether to use OpenCL to speed up the matching.
|
||||
|
||||
GPCMatchingParams( bool _useOpenCL = false ) : useOpenCL( _useOpenCL ) {}
|
||||
|
||||
GPCMatchingParams( const GPCMatchingParams ¶ms ) : useOpenCL( params.useOpenCL ) {}
|
||||
};
|
||||
|
||||
/** @brief Class for individual tree.
|
||||
*/
|
||||
class CV_EXPORTS_W GPCTree : public Algorithm
|
||||
{
|
||||
public:
|
||||
struct Node
|
||||
{
|
||||
Vec< double, GPCPatchDescriptor::nFeatures > coef; //!< Hyperplane coefficients
|
||||
double rhs; //!< Bias term of the hyperplane
|
||||
unsigned left;
|
||||
unsigned right;
|
||||
|
||||
bool operator==( const Node &n ) const { return coef == n.coef && rhs == n.rhs && left == n.left && right == n.right; }
|
||||
};
|
||||
|
||||
private:
|
||||
typedef GPCSamplesVector::iterator SIter;
|
||||
|
||||
std::vector< Node > nodes;
|
||||
GPCTrainingParams params;
|
||||
|
||||
bool trainNode( size_t nodeId, SIter begin, SIter end, unsigned depth );
|
||||
|
||||
public:
|
||||
void train( GPCTrainingSamples &samples, const GPCTrainingParams params = GPCTrainingParams() );
|
||||
|
||||
void write( FileStorage &fs ) const CV_OVERRIDE;
|
||||
|
||||
void read( const FileNode &fn ) CV_OVERRIDE;
|
||||
|
||||
unsigned findLeafForPatch( const GPCPatchDescriptor &descr ) const;
|
||||
|
||||
static Ptr< GPCTree > create() { return makePtr< GPCTree >(); }
|
||||
|
||||
bool operator==( const GPCTree &t ) const { return nodes == t.nodes; }
|
||||
|
||||
int getDescriptorType() const { return params.descriptorType; }
|
||||
};
|
||||
|
||||
template < int T > class GPCForest : public Algorithm
|
||||
{
|
||||
private:
|
||||
struct Trail
|
||||
{
|
||||
unsigned leaf[T]; //!< Inside which leaf of the tree 0..T the patch fell?
|
||||
Point2i coord; //!< Patch coordinates.
|
||||
|
||||
bool operator==( const Trail &trail ) const { return memcmp( leaf, trail.leaf, sizeof( leaf ) ) == 0; }
|
||||
|
||||
bool operator<( const Trail &trail ) const
|
||||
{
|
||||
for ( int i = 0; i < T - 1; ++i )
|
||||
if ( leaf[i] != trail.leaf[i] )
|
||||
return leaf[i] < trail.leaf[i];
|
||||
return leaf[T - 1] < trail.leaf[T - 1];
|
||||
}
|
||||
};
|
||||
|
||||
class ParallelTrailsFilling : public ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
const GPCForest *forest;
|
||||
const std::vector< GPCPatchDescriptor > *descr;
|
||||
std::vector< Trail > *trails;
|
||||
|
||||
ParallelTrailsFilling &operator=( const ParallelTrailsFilling & );
|
||||
|
||||
public:
|
||||
ParallelTrailsFilling( const GPCForest *_forest, const std::vector< GPCPatchDescriptor > *_descr, std::vector< Trail > *_trails )
|
||||
: forest( _forest ), descr( _descr ), trails( _trails ){};
|
||||
|
||||
void operator()( const Range &range ) const CV_OVERRIDE
|
||||
{
|
||||
for ( int t = range.start; t < range.end; ++t )
|
||||
for ( size_t i = 0; i < descr->size(); ++i )
|
||||
trails->at( i ).leaf[t] = forest->tree[t].findLeafForPatch( descr->at( i ) );
|
||||
}
|
||||
};
|
||||
|
||||
GPCTree tree[T];
|
||||
|
||||
public:
|
||||
/** @brief Train the forest using one sample set for every tree.
|
||||
* Please, consider using the next method instead of this one for better quality.
|
||||
*/
|
||||
void train( GPCTrainingSamples &samples, const GPCTrainingParams params = GPCTrainingParams() )
|
||||
{
|
||||
for ( int i = 0; i < T; ++i )
|
||||
tree[i].train( samples, params );
|
||||
}
|
||||
|
||||
/** @brief Train the forest using individual samples for each tree.
|
||||
* It is generally better to use this instead of the first method.
|
||||
*/
|
||||
void train( const std::vector< String > &imagesFrom, const std::vector< String > &imagesTo, const std::vector< String > >,
|
||||
const GPCTrainingParams params = GPCTrainingParams() )
|
||||
{
|
||||
for ( int i = 0; i < T; ++i )
|
||||
{
|
||||
Ptr< GPCTrainingSamples > samples =
|
||||
GPCTrainingSamples::create( imagesFrom, imagesTo, gt, params.descriptorType ); // Create training set for the tree
|
||||
tree[i].train( *samples, params );
|
||||
}
|
||||
}
|
||||
|
||||
void train( InputArrayOfArrays imagesFrom, InputArrayOfArrays imagesTo, InputArrayOfArrays gt,
|
||||
const GPCTrainingParams params = GPCTrainingParams() )
|
||||
{
|
||||
for ( int i = 0; i < T; ++i )
|
||||
{
|
||||
Ptr< GPCTrainingSamples > samples =
|
||||
GPCTrainingSamples::create( imagesFrom, imagesTo, gt, params.descriptorType ); // Create training set for the tree
|
||||
tree[i].train( *samples, params );
|
||||
}
|
||||
}
|
||||
|
||||
void write( FileStorage &fs ) const CV_OVERRIDE
|
||||
{
|
||||
fs << "ntrees" << T << "trees"
|
||||
<< "[";
|
||||
for ( int i = 0; i < T; ++i )
|
||||
{
|
||||
fs << "{";
|
||||
tree[i].write( fs );
|
||||
fs << "}";
|
||||
}
|
||||
fs << "]";
|
||||
}
|
||||
|
||||
void read( const FileNode &fn ) CV_OVERRIDE
|
||||
{
|
||||
CV_Assert( T <= (int)fn["ntrees"] );
|
||||
FileNodeIterator it = fn["trees"].begin();
|
||||
for ( int i = 0; i < T; ++i, ++it )
|
||||
tree[i].read( *it );
|
||||
}
|
||||
|
||||
/** @brief Find correspondences between two images.
|
||||
* @param[in] imgFrom First image in a sequence.
|
||||
* @param[in] imgTo Second image in a sequence.
|
||||
* @param[out] corr Output vector with pairs of corresponding points.
|
||||
* @param[in] params Additional matching parameters for fine-tuning.
|
||||
*/
|
||||
void findCorrespondences( InputArray imgFrom, InputArray imgTo, std::vector< std::pair< Point2i, Point2i > > &corr,
|
||||
const GPCMatchingParams params = GPCMatchingParams() ) const;
|
||||
|
||||
static Ptr< GPCForest > create() { return makePtr< GPCForest >(); }
|
||||
};
|
||||
|
||||
class CV_EXPORTS_W GPCDetails
|
||||
{
|
||||
public:
|
||||
static void dropOutliers( std::vector< std::pair< Point2i, Point2i > > &corr );
|
||||
|
||||
static void getAllDescriptorsForImage( const Mat *imgCh, std::vector< GPCPatchDescriptor > &descr, const GPCMatchingParams &mp,
|
||||
int type );
|
||||
|
||||
static void getCoordinatesFromIndex( size_t index, Size sz, int &x, int &y );
|
||||
};
|
||||
|
||||
template < int T >
|
||||
void GPCForest< T >::findCorrespondences( InputArray imgFrom, InputArray imgTo, std::vector< std::pair< Point2i, Point2i > > &corr,
|
||||
const GPCMatchingParams params ) const
|
||||
{
|
||||
CV_Assert( imgFrom.channels() == 3 );
|
||||
CV_Assert( imgTo.channels() == 3 );
|
||||
|
||||
Mat from, to;
|
||||
imgFrom.getMat().convertTo( from, CV_32FC3 );
|
||||
imgTo.getMat().convertTo( to, CV_32FC3 );
|
||||
cvtColor( from, from, COLOR_BGR2YCrCb );
|
||||
cvtColor( to, to, COLOR_BGR2YCrCb );
|
||||
|
||||
Mat fromCh[3], toCh[3];
|
||||
split( from, fromCh );
|
||||
split( to, toCh );
|
||||
|
||||
std::vector< GPCPatchDescriptor > descr;
|
||||
GPCDetails::getAllDescriptorsForImage( fromCh, descr, params, tree[0].getDescriptorType() );
|
||||
std::vector< Trail > trailsFrom( descr.size() ), trailsTo( descr.size() );
|
||||
|
||||
for ( size_t i = 0; i < descr.size(); ++i )
|
||||
GPCDetails::getCoordinatesFromIndex( i, from.size(), trailsFrom[i].coord.x, trailsFrom[i].coord.y );
|
||||
parallel_for_( Range( 0, T ), ParallelTrailsFilling( this, &descr, &trailsFrom ) );
|
||||
|
||||
descr.clear();
|
||||
GPCDetails::getAllDescriptorsForImage( toCh, descr, params, tree[0].getDescriptorType() );
|
||||
|
||||
for ( size_t i = 0; i < descr.size(); ++i )
|
||||
GPCDetails::getCoordinatesFromIndex( i, to.size(), trailsTo[i].coord.x, trailsTo[i].coord.y );
|
||||
parallel_for_( Range( 0, T ), ParallelTrailsFilling( this, &descr, &trailsTo ) );
|
||||
|
||||
std::sort( trailsFrom.begin(), trailsFrom.end() );
|
||||
std::sort( trailsTo.begin(), trailsTo.end() );
|
||||
|
||||
for ( size_t i = 0; i < trailsFrom.size(); ++i )
|
||||
{
|
||||
bool uniq = true;
|
||||
while ( i + 1 < trailsFrom.size() && trailsFrom[i] == trailsFrom[i + 1] )
|
||||
++i, uniq = false;
|
||||
if ( uniq )
|
||||
{
|
||||
typename std::vector< Trail >::const_iterator lb = std::lower_bound( trailsTo.begin(), trailsTo.end(), trailsFrom[i] );
|
||||
if ( lb != trailsTo.end() && *lb == trailsFrom[i] && ( ( lb + 1 ) == trailsTo.end() || !( *lb == *( lb + 1 ) ) ) )
|
||||
corr.push_back( std::make_pair( trailsFrom[i].coord, lb->coord ) );
|
||||
}
|
||||
}
|
||||
|
||||
GPCDetails::dropOutliers( corr );
|
||||
}
|
||||
|
||||
//! @}
|
||||
|
||||
} // namespace optflow
|
||||
|
||||
CV_EXPORTS void write( FileStorage &fs, const String &name, const optflow::GPCTree::Node &node );
|
||||
|
||||
CV_EXPORTS void read( const FileNode &fn, optflow::GPCTree::Node &node, optflow::GPCTree::Node );
|
||||
} // namespace cv
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,230 @@
|
||||
/*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) 2010-2012, Multicoreware, Inc., all rights reserved.
|
||||
// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// @Authors
|
||||
// Fangfang Bai, fangfang@multicorewareinc.com
|
||||
// Jin Ma, jin@multicorewareinc.com
|
||||
//
|
||||
// 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 "../perf_precomp.hpp"
|
||||
#include "opencv2/ts/ocl_perf.hpp"
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
|
||||
namespace opencv_test {
|
||||
namespace ocl {
|
||||
|
||||
#undef VISUALIZE_FLOW
|
||||
//#define VISUALIZE_FLOW 1
|
||||
|
||||
#ifdef VISUALIZE_FLOW
|
||||
// [TODO] move it to opencv_video/tracking.hpp
|
||||
// Standard visualization of the optical flow, according to Scharstein/Middlebury (ICCV 2007).
|
||||
// flow — input flow of CV_32FC2 type (field of motion vectors)
|
||||
// dst — output visual optical flow representation of CV_8UC3 type
|
||||
// maxFlow — max flow magnitude or (if <=0, it's computed automatically; use with care,
|
||||
// because it 'boosts' flow when there is almost no one).
|
||||
// pixels where the motion vector magnitude is equal or exceeds maxFlow will be
|
||||
// painted with maximum saturation.
|
||||
// eps — maxFlow threshold for 'no motion/still scene' cases
|
||||
// computedMaxFlow — the optional output value to hold the computed maxFlow, to let user
|
||||
// gradually calibrate maxFlow parameter.
|
||||
static void tsVisualizeFlow(InputArray flowarr, OutputArray dstarr,
|
||||
float maxFlow0 = -1.f, float eps = 1e-3f,
|
||||
float* computedMaxFlow=nullptr)
|
||||
{
|
||||
Mat flow = flowarr.getMat();
|
||||
CV_Assert(flow.type() == CV_32FC2);
|
||||
|
||||
// --- color ring (55 colors, RGB) ---
|
||||
// sectors: RY=15, YG=6, GC=4, CB=11, BM=13, MR=6
|
||||
constexpr int RY=15, YG=6, GC=4, CB=11, BM=13, MR=6;
|
||||
constexpr int NCOLS = RY+YG+GC+CB+BM+MR; // 55
|
||||
|
||||
std::array<Vec3b, NCOLS+1> cwheel_; // RGB
|
||||
int k = 0;
|
||||
for (int i=0;i<RY;i++,k++) cwheel_[k]=Vec3b(0, uint8_t(255*i/RY), 255);
|
||||
for (int i=0;i<YG;i++,k++) cwheel_[k]=Vec3b(0, 255, uint8_t(255-255*i/YG));
|
||||
for (int i=0;i<GC;i++,k++) cwheel_[k]=Vec3b(uint8_t(255*i/GC), 255, 0);
|
||||
for (int i=0;i<CB;i++,k++) cwheel_[k]=Vec3b(255, uint8_t(255-255*i/CB), 0);
|
||||
for (int i=0;i<BM;i++,k++) cwheel_[k]=Vec3b(255, 0, uint8_t(255*i/BM));
|
||||
for (int i=0;i<MR;i++,k++) cwheel_[k]=Vec3b(uint8_t(255-255*i/MR), 0, 255);
|
||||
cwheel_[NCOLS] = cwheel_[0];
|
||||
|
||||
std::vector<float> maxvals(flow.rows);
|
||||
|
||||
// --- compute max flow automatically ---
|
||||
if (maxFlow0 <= 0.0f) {
|
||||
parallel_for_(cv::Range(0, flow.rows), [&](const cv::Range& range) {
|
||||
for (int y = range.start; y < range.end; y++) {
|
||||
float maxval = 0.f;
|
||||
const cv::Vec2f* row = flow.ptr<cv::Vec2f>(y);
|
||||
for (int x = 0; x < flow.cols; x++) {
|
||||
float dx = row[x][0], dy = row[x][1];
|
||||
float mag = std::hypot(dx, dy);
|
||||
maxval = std::max(maxval, mag);
|
||||
}
|
||||
maxvals[y] = maxval;
|
||||
}
|
||||
});
|
||||
maxFlow0 = 0.f;
|
||||
for (int y = 0; y < flow.rows; y++)
|
||||
maxFlow0 = std::max(maxFlow0, maxvals[y]);
|
||||
}
|
||||
|
||||
maxFlow0 = maxFlow0 > eps ? maxFlow0 : 1.f;
|
||||
dstarr.create(flow.size(), CV_8UC3);
|
||||
Mat dst = dstarr.getMat();
|
||||
|
||||
// paint the optical flow map
|
||||
parallel_for_(cv::Range(0, flow.rows), [&](const cv::Range& range) {
|
||||
const Vec3b* cwheel = cwheel_.data();
|
||||
float maxval = 0.f, maxflow = maxFlow0;
|
||||
for (int y = range.start; y < range.end; y++) {
|
||||
const cv::Vec2f* src = flow.ptr<cv::Vec2f>(y);
|
||||
cv::Vec3b* out = dst.ptr<cv::Vec3b>(y);
|
||||
|
||||
for (int x = 0; x < flow.cols; x++) {
|
||||
float dx = src[x][0];
|
||||
float dy = src[x][1];
|
||||
float mag = std::hypot(dx, dy);
|
||||
maxval = std::max(maxval, mag);
|
||||
mag = std::min(mag / maxflow, 1.f);
|
||||
|
||||
// compute the color from angle
|
||||
float a = std::atan2(-dy, -dx) / static_cast<float>(M_PI);
|
||||
float f = (a + 1.0f) * 0.5f * (NCOLS - 1);
|
||||
int idx = (int)f;
|
||||
f -= idx;
|
||||
|
||||
// 'white' means no motion,
|
||||
// the stronger the motion the more saturated the corresponding pixel is.
|
||||
Vec3b clr;
|
||||
for (int c = 0; c < 3; c++) {
|
||||
float chval = cwheel[idx][c] * (1.f - f) + cwheel[idx + 1][c] * f;
|
||||
chval = 255.f - mag * (255.f - chval);
|
||||
clr[c] = saturate_cast<uint8_t>(chval);
|
||||
}
|
||||
out[x] = clr;
|
||||
}
|
||||
maxvals[y] = maxval;
|
||||
}
|
||||
});
|
||||
|
||||
if (computedMaxFlow) {
|
||||
maxFlow0 = 0.f;
|
||||
for (int y = 0; y < flow.rows; y++)
|
||||
maxFlow0 = std::max(maxFlow0, maxvals[y]);
|
||||
*computedMaxFlow = maxFlow0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
///////////// OpticalFlow Dual TVL1 ////////////////////////
|
||||
typedef tuple< tuple<int, double>, bool> OpticalFlowDualTVL1Params;
|
||||
typedef TestBaseWithParam<OpticalFlowDualTVL1Params> OpticalFlowDualTVL1Fixture;
|
||||
|
||||
OCL_PERF_TEST_P(OpticalFlowDualTVL1Fixture, OpticalFlowDualTVL1,
|
||||
::testing::Combine(
|
||||
::testing::Values(make_tuple<int, double>(-1, 0.3),
|
||||
make_tuple<int, double>(3, 0.5)),
|
||||
::testing::Bool()
|
||||
)
|
||||
)
|
||||
{
|
||||
Mat frame0 = imread(getDataPath("cv/optflow/RubberWhale1.png"), IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame0.empty()) << "can't load RubberWhale1.png";
|
||||
|
||||
Mat frame1 = imread(getDataPath("cv/optflow/RubberWhale2.png"), IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame1.empty()) << "can't load RubberWhale2.png";
|
||||
|
||||
const Size srcSize = frame0.size();
|
||||
|
||||
const OpticalFlowDualTVL1Params params = GetParam();
|
||||
const tuple<int, double> filteringScale = get<0>(params);
|
||||
const int medianFiltering = get<0>(filteringScale);
|
||||
const double scaleStep = get<1>(filteringScale);
|
||||
const bool useInitFlow = get<1>(params);
|
||||
//double eps = 0.9;
|
||||
|
||||
UMat uFrame0; frame0.copyTo(uFrame0);
|
||||
UMat uFrame1; frame1.copyTo(uFrame1);
|
||||
UMat uFlow(srcSize, CV_32FC2);
|
||||
declare.in(uFrame0, uFrame1, WARMUP_READ).out(uFlow, WARMUP_READ);
|
||||
|
||||
//create algorithm
|
||||
Ptr<DualTVL1OpticalFlow> alg = createOptFlow_DualTVL1();
|
||||
|
||||
//set parameters
|
||||
alg->setScaleStep(scaleStep);
|
||||
alg->setMedianFiltering(medianFiltering);
|
||||
|
||||
if (useInitFlow)
|
||||
{
|
||||
//calculate initial flow as result of optical flow
|
||||
alg->calc(uFrame0, uFrame1, uFlow);
|
||||
}
|
||||
|
||||
//set flag to use initial flow
|
||||
alg->setUseInitialFlow(useInitFlow);
|
||||
OCL_TEST_CYCLE()
|
||||
alg->calc(uFrame0, uFrame1, uFlow);
|
||||
|
||||
#ifdef VISUALIZE_FLOW
|
||||
imshow("frame0", uFrame0);
|
||||
UMat framediff;
|
||||
absdiff(uFrame0, uFrame1, framediff);
|
||||
imshow("framediff", framediff);
|
||||
|
||||
Mat flow8u;
|
||||
tsVisualizeFlow(uFlow, flow8u);
|
||||
imshow("uFlow", flow8u);
|
||||
waitKey();
|
||||
#endif
|
||||
|
||||
//SANITY_CHECK(uFlow, eps, ERROR_RELATIVE);
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace opencv_test::ocl
|
||||
|
||||
#endif // HAVE_OPENCL
|
||||
@@ -0,0 +1,32 @@
|
||||
// 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"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
typedef tuple<Size> DFParams;
|
||||
typedef TestBaseWithParam<DFParams> DenseOpticalFlow_DeepFlow;
|
||||
|
||||
PERF_TEST_P(DenseOpticalFlow_DeepFlow, perf, Values(szVGA, sz720p))
|
||||
{
|
||||
DFParams params = GetParam();
|
||||
Size sz = get<0>(params);
|
||||
|
||||
Mat frame1(sz, CV_8U);
|
||||
Mat frame2(sz, CV_8U);
|
||||
Mat flow;
|
||||
|
||||
randu(frame1, 0, 255);
|
||||
randu(frame2, 0, 255);
|
||||
|
||||
TEST_CYCLE_N(1)
|
||||
{
|
||||
Ptr<DenseOpticalFlow> algo = createOptFlow_DeepFlow();
|
||||
algo->calc(frame1, frame2, flow);
|
||||
}
|
||||
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,6 @@
|
||||
// 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"
|
||||
|
||||
CV_PERF_TEST_MAIN(optflow)
|
||||
@@ -0,0 +1,15 @@
|
||||
// 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_PERF_PRECOMP_HPP__
|
||||
#define __OPENCV_PERF_PRECOMP_HPP__
|
||||
|
||||
#include "opencv2/ts.hpp"
|
||||
#include "opencv2/optflow.hpp"
|
||||
|
||||
namespace opencv_test {
|
||||
using namespace perf;
|
||||
using namespace cv::optflow;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,74 @@
|
||||
#include "perf_precomp.hpp"
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
typedef tuple<std::string, std::string, bool> ST_SR_IM_Sparse_t;
|
||||
typedef TestBaseWithParam<ST_SR_IM_Sparse_t> ST_SR_IM_Sparse;
|
||||
PERF_TEST_P(ST_SR_IM_Sparse, OpticalFlow_SparseRLOF,
|
||||
testing::Combine(
|
||||
testing::Values<std::string>("ST_BILINEAR", "ST_STANDART"),
|
||||
testing::Values<std::string>("SR_CROSS", "SR_FIXED"),
|
||||
testing::Values(true, false))
|
||||
)
|
||||
{
|
||||
Mat frame1 = imread(getDataPath("cv/optflow/RubberWhale1.png"));
|
||||
Mat frame2 = imread(getDataPath("cv/optflow/RubberWhale2.png"));
|
||||
ASSERT_FALSE(frame1.empty());
|
||||
ASSERT_FALSE(frame2.empty());
|
||||
vector<Point2f> prevPts, currPts;
|
||||
for (int r = 0; r < frame1.rows; r += 10)
|
||||
{
|
||||
for (int c = 0; c < frame1.cols; c += 10)
|
||||
{
|
||||
prevPts.push_back(Point2f(static_cast<float>(c), static_cast<float>(r)));
|
||||
}
|
||||
}
|
||||
vector<uchar> status(prevPts.size());
|
||||
vector<float> err(prevPts.size());
|
||||
|
||||
Ptr<RLOFOpticalFlowParameter> param = Ptr<RLOFOpticalFlowParameter>(new RLOFOpticalFlowParameter);
|
||||
if (get<0>(GetParam()) == "ST_BILINEAR")
|
||||
param->solverType = ST_BILINEAR;
|
||||
if (get<0>(GetParam()) == "ST_STANDART")
|
||||
param->solverType = ST_STANDART;
|
||||
if (get<1>(GetParam()) == "SR_CROSS")
|
||||
param->supportRegionType = SR_CROSS;
|
||||
if (get<1>(GetParam()) == "SR_FIXED")
|
||||
param->supportRegionType = SR_FIXED;
|
||||
param->useIlluminationModel = get<2>(GetParam());
|
||||
|
||||
PERF_SAMPLE_BEGIN()
|
||||
calcOpticalFlowSparseRLOF(frame1, frame2, prevPts, currPts, status, err, param, 1.f);
|
||||
PERF_SAMPLE_END()
|
||||
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
typedef tuple<std::string, int> INTERP_GRID_Dense_t;
|
||||
typedef TestBaseWithParam<INTERP_GRID_Dense_t> INTERP_GRID_Dense;
|
||||
PERF_TEST_P(INTERP_GRID_Dense, OpticalFlow_DenseRLOF,
|
||||
testing::Combine(
|
||||
testing::Values<std::string>("INTERP_EPIC", "INTERP_GEO", "INTERP_RIC"),
|
||||
testing::Values<int>(4,10))
|
||||
)
|
||||
{
|
||||
Mat flow;
|
||||
Mat frame1 = imread(getDataPath("cv/optflow/RubberWhale1.png"));
|
||||
Mat frame2 = imread(getDataPath("cv/optflow/RubberWhale1.png"));
|
||||
ASSERT_FALSE(frame1.empty());
|
||||
ASSERT_FALSE(frame2.empty());
|
||||
Ptr<RLOFOpticalFlowParameter> param = Ptr<RLOFOpticalFlowParameter>(new RLOFOpticalFlowParameter);;
|
||||
Ptr< DenseRLOFOpticalFlow> algo = DenseRLOFOpticalFlow::create();
|
||||
InterpolationType interp_type = INTERP_EPIC;
|
||||
if (get<0>(GetParam()) == "INTERP_EPIC")
|
||||
interp_type = INTERP_EPIC;
|
||||
if (get<0>(GetParam()) == "INTERP_GEO")
|
||||
interp_type = INTERP_GEO;
|
||||
if (get<0>(GetParam()) == "INTERP_RIC")
|
||||
interp_type = INTERP_RIC;
|
||||
PERF_SAMPLE_BEGIN()
|
||||
calcOpticalFlowDenseRLOF(frame1, frame2,flow, param, 1.0f, Size(get<1>(GetParam()), get<1>(GetParam())), interp_type);
|
||||
PERF_SAMPLE_END()
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
using namespace perf;
|
||||
|
||||
typedef TestBaseWithParam< std::pair<string, string> > ImagePair;
|
||||
|
||||
std::pair<string, string> impair(const char* im1, const char* im2)
|
||||
{
|
||||
return std::make_pair(string(im1), string(im2));
|
||||
}
|
||||
|
||||
PERF_TEST_P(ImagePair, OpticalFlowDual_TVL1, testing::Values(impair("cv/optflow/RubberWhale1.png", "cv/optflow/RubberWhale2.png")))
|
||||
{
|
||||
declare.time(260);
|
||||
|
||||
Mat frame1 = imread(getDataPath(GetParam().first), IMREAD_GRAYSCALE);
|
||||
Mat frame2 = imread(getDataPath(GetParam().second), IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame1.empty());
|
||||
ASSERT_FALSE(frame2.empty());
|
||||
|
||||
Mat flow;
|
||||
|
||||
Ptr<DenseOpticalFlow> tvl1 = createOptFlow_DualTVL1();
|
||||
|
||||
TEST_CYCLE() tvl1->calc(frame1, frame2, flow);
|
||||
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,174 @@
|
||||
#include "opencv2/core/ocl.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/optflow.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
|
||||
/* This tool finds correspondences between two images using Global Patch Collider
|
||||
* and calculates error using provided ground truth flow.
|
||||
*
|
||||
* It will look for the file named "forest.yml.gz" with a learned forest.
|
||||
* You can obtain the "forest.yml.gz" either by manually training it using another tool with *_train suffix
|
||||
* or by downloading one of the files trained on some publicly available dataset from here:
|
||||
*
|
||||
* https://drive.google.com/open?id=0B7Hb8cfuzrIIZDFscXVYd0NBNFU
|
||||
*/
|
||||
|
||||
using namespace cv;
|
||||
|
||||
const String keys = "{help h ? | | print this message}"
|
||||
"{@image1 |<none> | image1}"
|
||||
"{@image2 |<none> | image2}"
|
||||
"{@groundtruth |<none> | path to the .flo file}"
|
||||
"{@output | | output to a file instead of displaying, output image path}"
|
||||
"{g gpu | | use OpenCL}"
|
||||
"{f forest |forest.yml.gz| path to the forest.yml.gz}";
|
||||
|
||||
const int nTrees = 5;
|
||||
|
||||
static double normL2( const Point2f &v ) { return sqrt( v.x * v.x + v.y * v.y ); }
|
||||
|
||||
static Vec3d getFlowColor( const Point2f &f, const bool logScale = true, const double scaleDown = 5 )
|
||||
{
|
||||
if ( f.x == 0 && f.y == 0 )
|
||||
return Vec3d( 0, 0, 1 );
|
||||
|
||||
double radius = normL2( f );
|
||||
if ( logScale )
|
||||
radius = log( radius + 1 );
|
||||
radius /= scaleDown;
|
||||
radius = std::min( 1.0, radius );
|
||||
|
||||
double angle = ( atan2( -f.y, -f.x ) + CV_PI ) * 180 / CV_PI;
|
||||
return Vec3d( angle, radius, 1 );
|
||||
}
|
||||
|
||||
static void displayFlow( InputArray _flow, OutputArray _img )
|
||||
{
|
||||
const Size sz = _flow.size();
|
||||
Mat flow = _flow.getMat();
|
||||
_img.create( sz, CV_32FC3 );
|
||||
Mat img = _img.getMat();
|
||||
|
||||
for ( int i = 0; i < sz.height; ++i )
|
||||
for ( int j = 0; j < sz.width; ++j )
|
||||
img.at< Vec3f >( i, j ) = getFlowColor( flow.at< Point2f >( i, j ) );
|
||||
|
||||
cvtColor( img, img, COLOR_HSV2BGR );
|
||||
}
|
||||
|
||||
static bool fileProbe( const char *name ) { return std::ifstream( name ).good(); }
|
||||
|
||||
int main( int argc, const char **argv )
|
||||
{
|
||||
CommandLineParser parser( argc, argv, keys );
|
||||
parser.about( "Global Patch Collider evaluation tool" );
|
||||
|
||||
if ( parser.has( "help" ) )
|
||||
{
|
||||
parser.printMessage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
String fromPath = parser.get< String >( 0 );
|
||||
String toPath = parser.get< String >( 1 );
|
||||
String gtPath = parser.get< String >( 2 );
|
||||
String outPath = parser.get< String >( 3 );
|
||||
const bool useOpenCL = parser.has( "gpu" );
|
||||
String forestDumpPath = parser.get< String >( "forest" );
|
||||
|
||||
if ( !parser.check() )
|
||||
{
|
||||
parser.printErrors();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ( !fileProbe( forestDumpPath.c_str() ) )
|
||||
{
|
||||
std::cerr << "Can't open the file with a trained model: `" << forestDumpPath
|
||||
<< "`.\nYou can obtain this file either by manually training the model using another tool with *_train suffix or by "
|
||||
"downloading one of the files trained on some publicly available dataset from "
|
||||
"here:\nhttps://drive.google.com/open?id=0B7Hb8cfuzrIIZDFscXVYd0NBNFU"
|
||||
<< std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
ocl::setUseOpenCL( useOpenCL );
|
||||
|
||||
Ptr< optflow::GPCForest< nTrees > > forest = Algorithm::load< optflow::GPCForest< nTrees > >( forestDumpPath );
|
||||
|
||||
Mat from = imread( fromPath );
|
||||
Mat to = imread( toPath );
|
||||
Mat gt = readOpticalFlow( gtPath );
|
||||
std::vector< std::pair< Point2i, Point2i > > corr;
|
||||
|
||||
TickMeter meter;
|
||||
meter.start();
|
||||
|
||||
forest->findCorrespondences( from, to, corr, optflow::GPCMatchingParams( useOpenCL ) );
|
||||
|
||||
meter.stop();
|
||||
|
||||
std::cout << "Found " << corr.size() << " matches." << std::endl;
|
||||
std::cout << "Time: " << meter.getTimeSec() << " sec." << std::endl;
|
||||
double error = 0;
|
||||
int totalCorrectFlowVectors = 0;
|
||||
Mat dispErr = Mat::zeros( from.size(), CV_32FC3 );
|
||||
dispErr = Scalar( 0, 0, 1 );
|
||||
Mat disp = Mat::zeros( from.size(), CV_32FC3 );
|
||||
disp = Scalar( 0, 0, 1 );
|
||||
|
||||
for ( size_t i = 0; i < corr.size(); ++i )
|
||||
{
|
||||
const Point2f a = corr[i].first;
|
||||
const Point2f b = corr[i].second;
|
||||
const Point2f gtDisplacement = gt.at< Point2f >( corr[i].first.y, corr[i].first.x );
|
||||
|
||||
// Check that flow vector is correct
|
||||
if (!cvIsNaN(gtDisplacement.x) && !cvIsNaN(gtDisplacement.y) && gtDisplacement.x < 1e9 && gtDisplacement.y < 1e9)
|
||||
{
|
||||
const Point2f c = a + gtDisplacement;
|
||||
error += normL2( b - c );
|
||||
circle( dispErr, a, 3, getFlowColor( b - c, false, 32 ), -1 );
|
||||
++totalCorrectFlowVectors;
|
||||
}
|
||||
|
||||
circle( disp, a, 3, getFlowColor( b - a ), -1 );
|
||||
}
|
||||
|
||||
if (totalCorrectFlowVectors)
|
||||
error /= totalCorrectFlowVectors;
|
||||
|
||||
std::cout << "Average endpoint error: " << error << " px." << std::endl;
|
||||
|
||||
cvtColor( disp, disp, COLOR_HSV2BGR );
|
||||
cvtColor( dispErr, dispErr, COLOR_HSV2BGR );
|
||||
|
||||
Mat dispGroundTruth;
|
||||
displayFlow( gt, dispGroundTruth );
|
||||
|
||||
if ( outPath.length() )
|
||||
{
|
||||
putText( disp, "Sparse matching: Global Patch Collider", Point2i( 24, 40 ), FONT_HERSHEY_DUPLEX, 1, Vec3b( 1, 0, 0 ), 2, LINE_AA );
|
||||
char buf[256];
|
||||
sprintf( buf, "Average EPE: %.2f", error );
|
||||
putText( disp, buf, Point2i( 24, 80 ), FONT_HERSHEY_DUPLEX, 1, Vec3b( 1, 0, 0 ), 2, LINE_AA );
|
||||
sprintf( buf, "Number of matches: %u", (unsigned)corr.size() );
|
||||
putText( disp, buf, Point2i( 24, 120 ), FONT_HERSHEY_DUPLEX, 1, Vec3b( 1, 0, 0 ), 2, LINE_AA );
|
||||
disp *= 255;
|
||||
imwrite( outPath, disp );
|
||||
return 0;
|
||||
}
|
||||
|
||||
namedWindow( "Correspondences", WINDOW_AUTOSIZE );
|
||||
imshow( "Correspondences", disp );
|
||||
namedWindow( "Error", WINDOW_AUTOSIZE );
|
||||
imshow( "Error", dispErr );
|
||||
namedWindow( "Ground truth", WINDOW_AUTOSIZE );
|
||||
imshow( "Ground truth", dispGroundTruth );
|
||||
waitKey( 0 );
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
#include "opencv2/optflow.hpp"
|
||||
#include <iostream>
|
||||
|
||||
/* This tool trains the forest for the Global Patch Collider and stores output to the "forest.yml.gz".
|
||||
*/
|
||||
|
||||
using namespace cv;
|
||||
|
||||
const String keys = "{help h ? | | print this message}"
|
||||
"{max-tree-depth | | Maximum tree depth to stop partitioning}"
|
||||
"{min-samples | | Minimum number of samples in the node to stop partitioning}"
|
||||
"{descriptor-type|0 | Descriptor type. Set to 0 for quality, 1 for speed.}"
|
||||
"{print-progress | | Set to 0 to enable quiet mode, set to 1 to print progress}"
|
||||
"{f forest |forest.yml.gz| Path where to store resulting forest. It is recommended to use .yml.gz extension.}";
|
||||
|
||||
const int nTrees = 5;
|
||||
|
||||
static void fillInputImagesFromCommandLine( std::vector< String > &img1, std::vector< String > &img2, std::vector< String > >, int argc,
|
||||
const char **argv )
|
||||
{
|
||||
for ( int i = 1, j = 0; i < argc; ++i )
|
||||
{
|
||||
if ( argv[i][0] == '-' )
|
||||
continue;
|
||||
if ( j % 3 == 0 )
|
||||
img1.push_back( argv[i] );
|
||||
if ( j % 3 == 1 )
|
||||
img2.push_back( argv[i] );
|
||||
if ( j % 3 == 2 )
|
||||
gt.push_back( argv[i] );
|
||||
++j;
|
||||
}
|
||||
}
|
||||
|
||||
int main( int argc, const char **argv )
|
||||
{
|
||||
CommandLineParser parser( argc, argv, keys );
|
||||
parser.about( "Global Patch Collider training tool" );
|
||||
|
||||
std::vector< String > img1, img2, gt;
|
||||
optflow::GPCTrainingParams params;
|
||||
|
||||
if ( parser.has( "max-tree-depth" ) )
|
||||
params.maxTreeDepth = parser.get< unsigned >( "max-tree-depth" );
|
||||
if ( parser.has( "min-samples" ) )
|
||||
params.minNumberOfSamples = parser.get< unsigned >( "min-samples" );
|
||||
if ( parser.has( "descriptor-type" ) )
|
||||
params.descriptorType = parser.get< int >( "descriptor-type" );
|
||||
if ( parser.has( "print-progress" ) )
|
||||
params.printProgress = parser.get< unsigned >( "print-progress" ) != 0;
|
||||
|
||||
fillInputImagesFromCommandLine( img1, img2, gt, argc, argv );
|
||||
|
||||
if ( parser.has( "help" ) || img1.size() != img2.size() || img1.size() != gt.size() || img1.size() == 0 )
|
||||
{
|
||||
std::cerr << "\nUsage: " << argv[0] << " [params] ImageFrom1 ImageTo1 GroundTruth1 ... ImageFromN ImageToN GroundTruthN\n" << std::endl;
|
||||
parser.printMessage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
Ptr< optflow::GPCForest< nTrees > > forest = optflow::GPCForest< nTrees >::create();
|
||||
forest->train( img1, img2, gt, params );
|
||||
forest->save( parser.get< String >( "forest" ) );
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import argparse
|
||||
import glob
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
|
||||
def execute(cmd):
|
||||
popen = subprocess.Popen(cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
for stdout_line in iter(popen.stdout.readline, ''):
|
||||
print(stdout_line.rstrip())
|
||||
for stderr_line in iter(popen.stderr.readline, ''):
|
||||
print(stderr_line.rstrip())
|
||||
popen.stdout.close()
|
||||
popen.stderr.close()
|
||||
return_code = popen.wait()
|
||||
if return_code != 0:
|
||||
raise subprocess.CalledProcessError(return_code, cmd)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Train Global Patch Collider using Middlebury dataset')
|
||||
parser.add_argument(
|
||||
'--bin_path',
|
||||
help='Path to the training executable (example_optflow_gpc_train)',
|
||||
required=True)
|
||||
parser.add_argument('--dataset_path',
|
||||
help='Path to the directory with frames',
|
||||
required=True)
|
||||
parser.add_argument('--gt_path',
|
||||
help='Path to the directory with ground truth flow',
|
||||
required=True)
|
||||
parser.add_argument('--descriptor_type',
|
||||
help='Descriptor type',
|
||||
type=int,
|
||||
default=0)
|
||||
args = parser.parse_args()
|
||||
seq = glob.glob(os.path.join(args.dataset_path, '*'))
|
||||
seq.sort()
|
||||
input_files = []
|
||||
for s in seq:
|
||||
if os.path.isdir(s):
|
||||
seq_name = os.path.basename(s)
|
||||
frames = glob.glob(os.path.join(s, 'frame*.png'))
|
||||
frames.sort()
|
||||
assert (len(frames) == 2)
|
||||
assert (os.path.basename(frames[0]) == 'frame10.png')
|
||||
assert (os.path.basename(frames[1]) == 'frame11.png')
|
||||
gt_flow = os.path.join(args.gt_path, seq_name, 'flow10.flo')
|
||||
if os.path.isfile(gt_flow):
|
||||
input_files += [frames[0], frames[1], gt_flow]
|
||||
execute([args.bin_path, '--descriptor-type=%d' % args.descriptor_type] + input_files)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,60 @@
|
||||
import argparse
|
||||
import glob
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
FRAME_DIST = 2
|
||||
|
||||
assert (FRAME_DIST >= 1)
|
||||
|
||||
|
||||
def execute(cmd):
|
||||
popen = subprocess.Popen(cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
for stdout_line in iter(popen.stdout.readline, ''):
|
||||
print(stdout_line.rstrip())
|
||||
for stderr_line in iter(popen.stderr.readline, ''):
|
||||
print(stderr_line.rstrip())
|
||||
popen.stdout.close()
|
||||
popen.stderr.close()
|
||||
return_code = popen.wait()
|
||||
if return_code != 0:
|
||||
raise subprocess.CalledProcessError(return_code, cmd)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Train Global Patch Collider using MPI Sintel dataset')
|
||||
parser.add_argument(
|
||||
'--bin_path',
|
||||
help='Path to the training executable (example_optflow_gpc_train)',
|
||||
required=True)
|
||||
parser.add_argument('--dataset_path',
|
||||
help='Path to the directory with frames',
|
||||
required=True)
|
||||
parser.add_argument('--gt_path',
|
||||
help='Path to the directory with ground truth flow',
|
||||
required=True)
|
||||
parser.add_argument('--descriptor_type',
|
||||
help='Descriptor type',
|
||||
type=int,
|
||||
default=0)
|
||||
args = parser.parse_args()
|
||||
seq = glob.glob(os.path.join(args.dataset_path, '*'))
|
||||
seq.sort()
|
||||
input_files = []
|
||||
for s in seq:
|
||||
seq_name = os.path.basename(s)
|
||||
frames = glob.glob(os.path.join(s, 'frame*.png'))
|
||||
frames.sort()
|
||||
for i in range(0, len(frames) - 1, FRAME_DIST):
|
||||
gt_flow = os.path.join(args.gt_path, seq_name,
|
||||
os.path.basename(frames[i])[0:-4] + '.flo')
|
||||
assert (os.path.isfile(gt_flow))
|
||||
input_files += [frames[i], frames[i + 1], gt_flow]
|
||||
execute([args.bin_path, '--descriptor-type=%d' % args.descriptor_type] + input_files)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,168 @@
|
||||
#include "opencv2/optflow.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/videoio.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
using namespace cv::motempl;
|
||||
|
||||
static void help(void)
|
||||
{
|
||||
printf(
|
||||
"\nThis program demonstrated the use of motion templates -- basically using the gradients\n"
|
||||
"of thresholded layers of decaying frame differencing. New movements are stamped on top with floating system\n"
|
||||
"time code and motions too old are thresholded away. This is the 'motion history file'. The program reads from the camera of your choice or from\n"
|
||||
"a file. Gradients of motion history are used to detect direction of motion etc\n"
|
||||
"Usage :\n"
|
||||
"./motempl [camera number 0-n or file name, default is camera 0]\n"
|
||||
);
|
||||
}
|
||||
// various tracking parameters (in seconds)
|
||||
const double MHI_DURATION = 5;
|
||||
const double MAX_TIME_DELTA = 0.5;
|
||||
const double MIN_TIME_DELTA = 0.05;
|
||||
// number of cyclic frame buffer used for motion detection
|
||||
// (should, probably, depend on FPS)
|
||||
|
||||
// ring image buffer
|
||||
vector<Mat> buf;
|
||||
int last = 0;
|
||||
|
||||
// temporary images
|
||||
Mat mhi, orient, mask, segmask, zplane;
|
||||
vector<Rect> regions;
|
||||
|
||||
// parameters:
|
||||
// img - input video frame
|
||||
// dst - resultant motion picture
|
||||
// args - optional parameters
|
||||
static void update_mhi(const Mat& img, Mat& dst, int diff_threshold)
|
||||
{
|
||||
double timestamp = (double)clock() / CLOCKS_PER_SEC; // get current time in seconds
|
||||
Size size = img.size();
|
||||
int i, idx1 = last;
|
||||
Rect comp_rect;
|
||||
double count;
|
||||
double angle;
|
||||
Point center;
|
||||
double magnitude;
|
||||
Scalar color;
|
||||
|
||||
// allocate images at the beginning or
|
||||
// reallocate them if the frame size is changed
|
||||
if (mhi.size() != size)
|
||||
{
|
||||
mhi = Mat::zeros(size, CV_32F);
|
||||
zplane = Mat::zeros(size, CV_8U);
|
||||
|
||||
buf[0] = Mat::zeros(size, CV_8U);
|
||||
buf[1] = Mat::zeros(size, CV_8U);
|
||||
}
|
||||
|
||||
cvtColor(img, buf[last], COLOR_BGR2GRAY); // convert frame to grayscale
|
||||
|
||||
int idx2 = (last + 1) % 2; // index of (last - (N-1))th frame
|
||||
last = idx2;
|
||||
|
||||
Mat silh = buf[idx2];
|
||||
absdiff(buf[idx1], buf[idx2], silh); // get difference between frames
|
||||
|
||||
threshold(silh, silh, diff_threshold, 1, THRESH_BINARY); // and threshold it
|
||||
updateMotionHistory(silh, mhi, timestamp, MHI_DURATION); // update MHI
|
||||
|
||||
// convert MHI to blue 8u image
|
||||
mhi.convertTo(mask, CV_8U, 255. / MHI_DURATION, (MHI_DURATION - timestamp)*255. / MHI_DURATION);
|
||||
|
||||
Mat planes[] = { mask, zplane, zplane };
|
||||
merge(planes, 3, dst);
|
||||
|
||||
// calculate motion gradient orientation and valid orientation mask
|
||||
calcMotionGradient(mhi, mask, orient, MAX_TIME_DELTA, MIN_TIME_DELTA, 3);
|
||||
|
||||
// segment motion: get sequence of motion components
|
||||
// segmask is marked motion components map. It is not used further
|
||||
regions.clear();
|
||||
segmentMotion(mhi, segmask, regions, timestamp, MAX_TIME_DELTA);
|
||||
|
||||
// iterate through the motion components,
|
||||
// One more iteration (i == -1) corresponds to the whole image (global motion)
|
||||
for (i = -1; i < (int)regions.size(); i++) {
|
||||
|
||||
if (i < 0) { // case of the whole image
|
||||
comp_rect = Rect(0, 0, size.width, size.height);
|
||||
color = Scalar(255, 255, 255);
|
||||
magnitude = 100;
|
||||
}
|
||||
else { // i-th motion component
|
||||
comp_rect = regions[i];
|
||||
if (comp_rect.width + comp_rect.height < 100) // reject very small components
|
||||
continue;
|
||||
color = Scalar(0, 0, 255);
|
||||
magnitude = 30;
|
||||
}
|
||||
|
||||
// select component ROI
|
||||
Mat silh_roi = silh(comp_rect);
|
||||
Mat mhi_roi = mhi(comp_rect);
|
||||
Mat orient_roi = orient(comp_rect);
|
||||
Mat mask_roi = mask(comp_rect);
|
||||
|
||||
// calculate orientation
|
||||
angle = calcGlobalOrientation(orient_roi, mask_roi, mhi_roi, timestamp, MHI_DURATION);
|
||||
angle = 360.0 - angle; // adjust for images with top-left origin
|
||||
|
||||
count = norm(silh_roi, NORM_L1);; // calculate number of points within silhouette ROI
|
||||
|
||||
// check for the case of little motion
|
||||
if (count < comp_rect.width*comp_rect.height * 0.05)
|
||||
continue;
|
||||
|
||||
// draw a clock with arrow indicating the direction
|
||||
center = Point((comp_rect.x + comp_rect.width / 2),
|
||||
(comp_rect.y + comp_rect.height / 2));
|
||||
|
||||
circle(img, center, cvRound(magnitude*1.2), color, 3, 16, 0);
|
||||
line(img, center, Point(cvRound(center.x + magnitude*cos(angle*CV_PI / 180)),
|
||||
cvRound(center.y - magnitude*sin(angle*CV_PI / 180))), color, 3, 16, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
VideoCapture cap;
|
||||
|
||||
help();
|
||||
|
||||
if (argc == 1 || (argc == 2 && strlen(argv[1]) == 1 && isdigit(argv[1][0])))
|
||||
cap.open(argc == 2 ? argv[1][0] - '0' : 0);
|
||||
else if (argc == 2)
|
||||
cap.open(argv[1]);
|
||||
|
||||
if (!cap.isOpened())
|
||||
{
|
||||
printf("Could not initialize video capture\n");
|
||||
return 0;
|
||||
}
|
||||
buf.resize(2);
|
||||
Mat image, motion;
|
||||
for (;;)
|
||||
{
|
||||
cap >> image;
|
||||
if (image.empty())
|
||||
break;
|
||||
|
||||
update_mhi(image, motion, 30);
|
||||
imshow("Image", image);
|
||||
imshow("Motion", motion);
|
||||
|
||||
if (waitKey(10) >= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Executable
+97
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env python
|
||||
import numpy as np
|
||||
import cv2 as cv
|
||||
|
||||
MHI_DURATION = 0.5
|
||||
DEFAULT_THRESHOLD = 32
|
||||
MAX_TIME_DELTA = 0.25
|
||||
MIN_TIME_DELTA = 0.05
|
||||
|
||||
# (empty) trackbar callback
|
||||
def nothing(dummy):
|
||||
pass
|
||||
|
||||
def draw_motion_comp(vis, rect, angle, color):
|
||||
x, y, w, h = rect
|
||||
cv.rectangle(vis, (x, y), (x+w, y+h), (0, 255, 0))
|
||||
r = min(w//2, h//2)
|
||||
cx, cy = x+w//2, y+h//2
|
||||
angle = angle*np.pi/180
|
||||
cv.circle(vis, (cx, cy), r, color, 3)
|
||||
cv.line(vis, (cx, cy), (int(cx+np.cos(angle)*r), int(cy+np.sin(angle)*r)), color, 3)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
try:
|
||||
video_src = sys.argv[1]
|
||||
except:
|
||||
video_src = 0
|
||||
|
||||
cv.namedWindow('motempl')
|
||||
visuals = ['input', 'frame_diff', 'motion_hist', 'grad_orient']
|
||||
cv.createTrackbar('visual', 'motempl', 2, len(visuals)-1, nothing)
|
||||
cv.createTrackbar('threshold', 'motempl', DEFAULT_THRESHOLD, 255, nothing)
|
||||
|
||||
cam = cv.VideoCapture(video_src)
|
||||
if not cam.isOpened():
|
||||
print("could not open video_src " + str(video_src) + " !\n")
|
||||
sys.exit(1)
|
||||
ret, frame = cam.read()
|
||||
if ret == False:
|
||||
print("could not read from " + str(video_src) + " !\n")
|
||||
sys.exit(1)
|
||||
h, w = frame.shape[:2]
|
||||
prev_frame = frame.copy()
|
||||
motion_history = np.zeros((h, w), np.float32)
|
||||
hsv = np.zeros((h, w, 3), np.uint8)
|
||||
hsv[:,:,1] = 255
|
||||
while True:
|
||||
ret, frame = cam.read()
|
||||
if ret == False:
|
||||
break
|
||||
frame_diff = cv.absdiff(frame, prev_frame)
|
||||
gray_diff = cv.cvtColor(frame_diff, cv.COLOR_BGR2GRAY)
|
||||
thrs = cv.getTrackbarPos('threshold', 'motempl')
|
||||
ret, motion_mask = cv.threshold(gray_diff, thrs, 1, cv.THRESH_BINARY)
|
||||
timestamp = cv.getTickCount() / cv.getTickFrequency()
|
||||
cv.motempl.updateMotionHistory(motion_mask, motion_history, timestamp, MHI_DURATION)
|
||||
mg_mask, mg_orient = cv.motempl.calcMotionGradient( motion_history, MAX_TIME_DELTA, MIN_TIME_DELTA, apertureSize=5 )
|
||||
seg_mask, seg_bounds = cv.motempl.segmentMotion(motion_history, timestamp, MAX_TIME_DELTA)
|
||||
|
||||
visual_name = visuals[cv.getTrackbarPos('visual', 'motempl')]
|
||||
if visual_name == 'input':
|
||||
vis = frame.copy()
|
||||
elif visual_name == 'frame_diff':
|
||||
vis = frame_diff.copy()
|
||||
elif visual_name == 'motion_hist':
|
||||
vis = np.uint8(np.clip((motion_history-(timestamp-MHI_DURATION)) / MHI_DURATION, 0, 1)*255)
|
||||
vis = cv.cvtColor(vis, cv.COLOR_GRAY2BGR)
|
||||
elif visual_name == 'grad_orient':
|
||||
hsv[:,:,0] = mg_orient/2
|
||||
hsv[:,:,2] = mg_mask*255
|
||||
vis = cv.cvtColor(hsv, cv.COLOR_HSV2BGR)
|
||||
|
||||
for i, rect in enumerate([(0, 0, w, h)] + list(seg_bounds)):
|
||||
x, y, rw, rh = rect
|
||||
area = rw*rh
|
||||
if area < 64**2:
|
||||
continue
|
||||
silh_roi = motion_mask [y:y+rh,x:x+rw]
|
||||
orient_roi = mg_orient [y:y+rh,x:x+rw]
|
||||
mask_roi = mg_mask [y:y+rh,x:x+rw]
|
||||
mhi_roi = motion_history[y:y+rh,x:x+rw]
|
||||
if cv.norm(silh_roi, cv.NORM_L1) < area*0.05:
|
||||
continue
|
||||
angle = cv.motempl.calcGlobalOrientation(orient_roi, mask_roi, mhi_roi, timestamp, MHI_DURATION)
|
||||
color = ((255, 0, 0), (0, 0, 255))[i == 0]
|
||||
draw_motion_comp(vis, rect, angle, color)
|
||||
|
||||
cv.putText(vis, visual_name, (20, 20), cv.FONT_HERSHEY_PLAIN, 1.0, (200,0,0))
|
||||
cv.imshow('motempl', vis)
|
||||
|
||||
prev_frame = frame.copy()
|
||||
if 0xFF & cv.waitKey(5) == 27:
|
||||
break
|
||||
# cleanup the camera and close any open windows
|
||||
cam.release()
|
||||
cv.destroyAllWindows()
|
||||
@@ -0,0 +1,268 @@
|
||||
#!/usr/bin/env python
|
||||
from __future__ import print_function
|
||||
import os, sys, shutil
|
||||
import argparse
|
||||
import json, re
|
||||
from subprocess import check_output
|
||||
import datetime
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
|
||||
def load_json(path):
|
||||
f = open(path, "r")
|
||||
data = json.load(f)
|
||||
return data
|
||||
|
||||
|
||||
def save_json(obj, path):
|
||||
tmp_file = path + ".bak"
|
||||
f = open(tmp_file, "w")
|
||||
json.dump(obj, f, indent=2)
|
||||
f.flush()
|
||||
os.fsync(f.fileno())
|
||||
f.close()
|
||||
try:
|
||||
os.rename(tmp_file, path)
|
||||
except:
|
||||
os.remove(path)
|
||||
os.rename(tmp_file, path)
|
||||
|
||||
|
||||
def parse_evaluation_result(input_str, i):
|
||||
res = {}
|
||||
res['frame_number'] = i + 1
|
||||
res['error'] = {}
|
||||
regex = "([A-Za-z. \\[\\].0-9]+):[ ]*([0-9]*\.[0-9]+|[0-9]+)"
|
||||
for elem in re.findall(regex,input_str):
|
||||
if "Time" in elem[0]:
|
||||
res['time'] = float(elem[1])
|
||||
elif "Average" in elem[0]:
|
||||
res['error']['average'] = float(elem[1])
|
||||
elif "deviation" in elem[0]:
|
||||
res['error']['std'] = float(elem[1])
|
||||
else:
|
||||
res['error'][elem[0]] = float(elem[1])
|
||||
return res
|
||||
|
||||
|
||||
def evaluate_sequence(sequence, algorithm, dataset, executable, img_files, gt_files,
|
||||
state, state_path):
|
||||
if "eval_results" not in state[dataset][algorithm][-1].keys():
|
||||
state[dataset][algorithm][-1]["eval_results"] = {}
|
||||
elif sequence in state[dataset][algorithm][-1]["eval_results"].keys():
|
||||
return
|
||||
|
||||
res = []
|
||||
for i in range(len(img_files) - 1):
|
||||
sys.stdout.write("Algorithm: %-20s Sequence: %-10s Done: [%3d/%3d]\r" %
|
||||
(algorithm, sequence, i, len(img_files) - 1)),
|
||||
sys.stdout.flush()
|
||||
|
||||
res_string = check_output([executable, img_files[i], img_files[i + 1],
|
||||
algorithm, gt_files[i]])
|
||||
res.append(parse_evaluation_result(res_string, i))
|
||||
state[dataset][algorithm][-1]["eval_results"][sequence] = res
|
||||
save_json(state, state_path)
|
||||
|
||||
#############################DATSET DEFINITIONS################################
|
||||
|
||||
def evaluate_mpi_sintel(source_dir, algorithm, evaluation_executable, state, state_path):
|
||||
evaluation_result = {}
|
||||
img_dir = os.path.join(source_dir, 'mpi_sintel', 'training', 'final')
|
||||
gt_dir = os.path.join(source_dir, 'mpi_sintel', 'training', 'flow')
|
||||
sequences = [f for f in os.listdir(img_dir)
|
||||
if os.path.isdir(os.path.join(img_dir, f))]
|
||||
for seq in sequences:
|
||||
img_files = sorted([os.path.join(img_dir, seq, f)
|
||||
for f in os.listdir(os.path.join(img_dir, seq))
|
||||
if f.endswith(".png")])
|
||||
gt_files = sorted([os.path.join(gt_dir, seq, f)
|
||||
for f in os.listdir(os.path.join(gt_dir, seq))
|
||||
if f.endswith(".flo")])
|
||||
evaluation_result[seq] = evaluate_sequence(seq, algorithm, 'mpi_sintel',
|
||||
evaluation_executable, img_files, gt_files, state, state_path)
|
||||
return evaluation_result
|
||||
|
||||
|
||||
def evaluate_middlebury(source_dir, algorithm, evaluation_executable, state, state_path):
|
||||
evaluation_result = {}
|
||||
img_dir = os.path.join(source_dir, 'middlebury', 'other-data')
|
||||
gt_dir = os.path.join(source_dir, 'middlebury', 'other-gt-flow')
|
||||
sequences = [f for f in os.listdir(gt_dir)
|
||||
if os.path.isdir(os.path.join(gt_dir, f))]
|
||||
for seq in sequences:
|
||||
img_files = sorted([os.path.join(img_dir, seq, f)
|
||||
for f in os.listdir(os.path.join(img_dir, seq))
|
||||
if f.endswith(".png")])
|
||||
gt_files = sorted([os.path.join(gt_dir, seq, f)
|
||||
for f in os.listdir(os.path.join(gt_dir, seq))
|
||||
if f.endswith(".flo")])
|
||||
evaluation_result[seq] = evaluate_sequence(seq, algorithm, 'middlebury',
|
||||
evaluation_executable, img_files, gt_files, state, state_path)
|
||||
return evaluation_result
|
||||
|
||||
|
||||
dataset_eval_functions = {
|
||||
"mpi_sintel": evaluate_mpi_sintel,
|
||||
"middlebury": evaluate_middlebury
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
|
||||
def create_dir(dir):
|
||||
if not os.path.exists(dir):
|
||||
os.makedirs(dir)
|
||||
|
||||
|
||||
def parse_sequence(input_str):
|
||||
if len(input_str) == 0:
|
||||
return []
|
||||
else:
|
||||
return [o.strip() for o in input_str.split(",") if o]
|
||||
|
||||
|
||||
def build_chart(dst_folder, state, dataset):
|
||||
fig = plt.figure(figsize=(16, 10))
|
||||
markers = ["o", "s", "h", "^", "D"]
|
||||
marker_idx = 0
|
||||
colors = ["b", "g", "r"]
|
||||
color_idx = 0
|
||||
for algo in state[dataset].keys():
|
||||
for eval_instance in state[dataset][algo]:
|
||||
name = algo + "--" + eval_instance["timestamp"]
|
||||
average_time = 0.0
|
||||
average_error = 0.0
|
||||
num_elem = 0
|
||||
for seq in eval_instance["eval_results"].keys():
|
||||
for frame in eval_instance["eval_results"][seq]:
|
||||
average_time += frame["time"]
|
||||
average_error += frame["error"]["average"]
|
||||
num_elem += 1
|
||||
average_time /= num_elem
|
||||
average_error /= num_elem
|
||||
|
||||
marker_style = colors[color_idx] + markers[marker_idx]
|
||||
color_idx += 1
|
||||
if color_idx >= len(colors):
|
||||
color_idx = 0
|
||||
marker_idx += 1
|
||||
if marker_idx >= len(markers):
|
||||
marker_idx = 0
|
||||
plt.gca().plot([average_time], [average_error],
|
||||
marker_style,
|
||||
markersize=14,
|
||||
label=name)
|
||||
|
||||
plt.gca().set_ylabel('Average Endpoint Error (EPE)', fontsize=20)
|
||||
plt.gca().set_xlabel('Average Runtime (seconds per frame)', fontsize=20)
|
||||
plt.gca().set_xscale("log")
|
||||
plt.gca().set_title('Evaluation on ' + dataset, fontsize=20)
|
||||
|
||||
plt.gca().legend()
|
||||
fig.savefig(os.path.join(dst_folder, "evaluation_results_" + dataset + ".png"),
|
||||
bbox_inches='tight')
|
||||
plt.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Optical flow benchmarking script',
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
parser.add_argument(
|
||||
"bin_path",
|
||||
default="./optflow-example-optical_flow_evaluation",
|
||||
help="Path to the optical flow evaluation executable")
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
"--algorithms",
|
||||
metavar="ALGORITHMS",
|
||||
default="",
|
||||
help=("Comma-separated list of optical-flow algorithms to evaluate "
|
||||
"(example: -a farneback,tvl1,deepflow). Note that previously "
|
||||
"evaluated algorithms are also included in the output charts"))
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--datasets",
|
||||
metavar="DATASETS",
|
||||
default="mpi_sintel",
|
||||
help=("Comma-separated list of datasets for evaluation (currently only "
|
||||
"'mpi_sintel' and 'middlebury' are supported)"))
|
||||
parser.add_argument(
|
||||
"-f",
|
||||
"--dataset_folder",
|
||||
metavar="DATASET_FOLDER",
|
||||
default="./OF_datasets",
|
||||
help=("Path to a folder containing datasets. To enable evaluation on "
|
||||
"MPI Sintel dataset, please download it using the following links: "
|
||||
"http://files.is.tue.mpg.de/sintel/MPI-Sintel-training_images.zip and "
|
||||
"http://files.is.tue.mpg.de/sintel/MPI-Sintel-training_extras.zip and "
|
||||
"unzip these archives into the 'mpi_sintel' folder. To enable evaluation "
|
||||
"on the Middlebury dataset use the following links: "
|
||||
"http://vision.middlebury.edu/flow/data/comp/zip/other-color-twoframes.zip, "
|
||||
"http://vision.middlebury.edu/flow/data/comp/zip/other-gt-flow.zip. "
|
||||
"These should be unzipped into 'middlebury' folder"))
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
"--out",
|
||||
metavar="OUT_DIR",
|
||||
default="./OF_evaluation_results",
|
||||
help="Output directory where to store benchmark results")
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--state",
|
||||
metavar="STATE_JSON",
|
||||
default="./OF_evaluation_state.json",
|
||||
help=("Path to a json file that stores the current evaluation state and "
|
||||
"previous evaluation results"))
|
||||
args, other_args = parser.parse_known_args()
|
||||
|
||||
if not os.path.isfile(args.bin_path):
|
||||
print("Error: " + args.bin_path + " does not exist")
|
||||
sys.exit(1)
|
||||
|
||||
if not os.path.exists(args.dataset_folder):
|
||||
print("Error: " + args.dataset_folder + (" does not exist. Please, correctly "
|
||||
"specify the -f parameter"))
|
||||
sys.exit(1)
|
||||
|
||||
state = {}
|
||||
if os.path.isfile(args.state):
|
||||
state = load_json(args.state)
|
||||
|
||||
algorithm_list = parse_sequence(args.algorithms)
|
||||
dataset_list = parse_sequence(args.datasets)
|
||||
for dataset in dataset_list:
|
||||
if dataset not in dataset_eval_functions.keys():
|
||||
print("Error: unsupported dataset " + dataset)
|
||||
sys.exit(1)
|
||||
if dataset not in os.listdir(args.dataset_folder):
|
||||
print("Error: " + os.path.join(args.dataset_folder, dataset) + (" does not exist. "
|
||||
"Please, download the dataset and follow the naming conventions "
|
||||
"(use -h for more information)"))
|
||||
sys.exit(1)
|
||||
|
||||
for dataset in dataset_list:
|
||||
if dataset not in state.keys():
|
||||
state[dataset] = {}
|
||||
for algorithm in algorithm_list:
|
||||
if algorithm in state[dataset].keys():
|
||||
last_eval_instance = state[dataset][algorithm][-1]
|
||||
if "finished" not in last_eval_instance.keys():
|
||||
print(("Continuing an unfinished evaluation of " +
|
||||
algorithm + " started at " + last_eval_instance["timestamp"]))
|
||||
else:
|
||||
state[dataset][algorithm].append({"timestamp":
|
||||
datetime.datetime.now().strftime("%Y-%m-%d--%H-%M")})
|
||||
else:
|
||||
state[dataset][algorithm] = [{"timestamp":
|
||||
datetime.datetime.now().strftime("%Y-%m-%d--%H-%M")}]
|
||||
save_json(state, args.state)
|
||||
dataset_eval_functions[dataset](args.dataset_folder, algorithm, args.bin_path,
|
||||
state, args.state)
|
||||
state[dataset][algorithm][-1]["finished"] = True
|
||||
save_json(state, args.state)
|
||||
save_json(state, args.state)
|
||||
|
||||
create_dir(args.out)
|
||||
for dataset in dataset_list:
|
||||
build_chart(args.out, state, dataset)
|
||||
@@ -0,0 +1,411 @@
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/video.hpp"
|
||||
#include "opencv2/optflow.hpp"
|
||||
#include "opencv2/core/ocl.hpp"
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
using namespace optflow;
|
||||
|
||||
const String keys = "{help h usage ? | | print this message }"
|
||||
"{@image1 | | image1 }"
|
||||
"{@image2 | | image2 }"
|
||||
"{@algorithm | | [farneback, simpleflow, tvl1, deepflow, sparsetodenseflow, RLOF_EPIC, RLOF_RIC, pcaflow, DISflow_ultrafast, DISflow_fast, DISflow_medium] }"
|
||||
"{@groundtruth | | path to the .flo file (optional), Middlebury format }"
|
||||
"{m measure |endpoint| error measure - [endpoint or angular] }"
|
||||
"{r region |all | region to compute stats about [all, discontinuities, untextured] }"
|
||||
"{d display | | display additional info images (pauses program execution) }"
|
||||
"{g gpu | | use OpenCL}"
|
||||
"{prior | | path to a prior file for PCAFlow}";
|
||||
|
||||
inline bool isFlowCorrect( const Point2f u )
|
||||
{
|
||||
return !cvIsNaN(u.x) && !cvIsNaN(u.y) && (fabs(u.x) < 1e9) && (fabs(u.y) < 1e9);
|
||||
}
|
||||
inline bool isFlowCorrect( const Point3f u )
|
||||
{
|
||||
return !cvIsNaN(u.x) && !cvIsNaN(u.y) && !cvIsNaN(u.z) && (fabs(u.x) < 1e9) && (fabs(u.y) < 1e9)
|
||||
&& (fabs(u.z) < 1e9);
|
||||
}
|
||||
static Mat endpointError( const Mat_<Point2f>& flow1, const Mat_<Point2f>& flow2 )
|
||||
{
|
||||
Mat result(flow1.size(), CV_32FC1);
|
||||
for ( int i = 0; i < flow1.rows; ++i )
|
||||
{
|
||||
for ( int j = 0; j < flow1.cols; ++j )
|
||||
{
|
||||
const Point2f u1 = flow1(i, j);
|
||||
const Point2f u2 = flow2(i, j);
|
||||
|
||||
if ( isFlowCorrect(u1) && isFlowCorrect(u2) )
|
||||
{
|
||||
const Point2f diff = u1 - u2;
|
||||
result.at<float>(i, j) = sqrt((float)diff.ddot(diff)); //distance
|
||||
} else
|
||||
result.at<float>(i, j) = std::numeric_limits<float>::quiet_NaN();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
static Mat angularError( const Mat_<Point2f>& flow1, const Mat_<Point2f>& flow2 )
|
||||
{
|
||||
Mat result(flow1.size(), CV_32FC1);
|
||||
|
||||
for ( int i = 0; i < flow1.rows; ++i )
|
||||
{
|
||||
for ( int j = 0; j < flow1.cols; ++j )
|
||||
{
|
||||
const Point2f u1_2d = flow1(i, j);
|
||||
const Point2f u2_2d = flow2(i, j);
|
||||
const Point3f u1(u1_2d.x, u1_2d.y, 1);
|
||||
const Point3f u2(u2_2d.x, u2_2d.y, 1);
|
||||
|
||||
if ( isFlowCorrect(u1) && isFlowCorrect(u2) )
|
||||
result.at<float>(i, j) = acos((float)(u1.ddot(u2) / norm(u1) * norm(u2)));
|
||||
else
|
||||
result.at<float>(i, j) = std::numeric_limits<float>::quiet_NaN();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// what fraction of pixels have errors higher than given threshold?
|
||||
static float stat_RX( Mat errors, float threshold, Mat mask )
|
||||
{
|
||||
CV_Assert(errors.size() == mask.size());
|
||||
CV_Assert(mask.depth() == CV_8U);
|
||||
|
||||
int count = 0, all = 0;
|
||||
for ( int i = 0; i < errors.rows; ++i )
|
||||
{
|
||||
for ( int j = 0; j < errors.cols; ++j )
|
||||
{
|
||||
if ( mask.at<char>(i, j) != 0 )
|
||||
{
|
||||
++all;
|
||||
if ( errors.at<float>(i, j) > threshold )
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (float)count / all;
|
||||
}
|
||||
static float stat_AX( Mat hist, int cutoff_count, float max_value )
|
||||
{
|
||||
int counter = 0;
|
||||
int bin = 0;
|
||||
int bin_count = hist.rows;
|
||||
while ( bin < bin_count && counter < cutoff_count )
|
||||
{
|
||||
counter += (int) hist.at<float>(bin, 0);
|
||||
++bin;
|
||||
}
|
||||
return (float) bin / bin_count * max_value;
|
||||
}
|
||||
static void calculateStats( Mat errors, Mat mask = Mat(), bool display_images = false )
|
||||
{
|
||||
float R_thresholds[] = { 0.5f, 1.f, 2.f, 5.f, 10.f };
|
||||
float A_thresholds[] = { 0.5f, 0.75f, 0.95f };
|
||||
if ( mask.empty() )
|
||||
mask = Mat::ones(errors.size(), CV_8U);
|
||||
CV_Assert(errors.size() == mask.size());
|
||||
CV_Assert(mask.depth() == CV_8U);
|
||||
|
||||
//displaying the mask
|
||||
if(display_images)
|
||||
{
|
||||
namedWindow( "Region mask", WINDOW_AUTOSIZE );
|
||||
imshow( "Region mask", mask );
|
||||
}
|
||||
|
||||
//mean and std computation
|
||||
Scalar s_mean, s_std;
|
||||
float mean, std;
|
||||
meanStdDev(errors, s_mean, s_std, mask);
|
||||
mean = (float)s_mean[0];
|
||||
std = (float)s_std[0];
|
||||
printf("Average: %.2f\nStandard deviation: %.2f\n", mean, std);
|
||||
|
||||
//RX stats - displayed in percent
|
||||
float R;
|
||||
int R_thresholds_count = sizeof(R_thresholds) / sizeof(float);
|
||||
for ( int i = 0; i < R_thresholds_count; ++i )
|
||||
{
|
||||
R = stat_RX(errors, R_thresholds[i], mask);
|
||||
printf("R%.1f: %.2f%%\n", R_thresholds[i], R * 100);
|
||||
}
|
||||
|
||||
//AX stats
|
||||
double max_value;
|
||||
minMaxLoc(errors, NULL, &max_value, NULL, NULL, mask);
|
||||
|
||||
Mat hist;
|
||||
const int n_images = 1;
|
||||
const int channels[] = { 0 };
|
||||
const int n_dimensions = 1;
|
||||
const int hist_bins[] = { 1024 };
|
||||
const float iranges[] = { 0, (float) max_value };
|
||||
const float* ranges[] = { iranges };
|
||||
const bool uniform = true;
|
||||
const bool accumulate = false;
|
||||
calcHist(&errors, n_images, channels, mask, hist, n_dimensions, hist_bins, ranges, uniform,
|
||||
accumulate);
|
||||
int all_pixels = countNonZero(mask);
|
||||
int cutoff_count;
|
||||
float A;
|
||||
int A_thresholds_count = sizeof(A_thresholds) / sizeof(float);
|
||||
for ( int i = 0; i < A_thresholds_count; ++i )
|
||||
{
|
||||
cutoff_count = (int) (floor(A_thresholds[i] * all_pixels + 0.5f));
|
||||
A = stat_AX(hist, cutoff_count, (float) max_value);
|
||||
printf("A%.2f: %.2f\n", A_thresholds[i], A);
|
||||
}
|
||||
}
|
||||
|
||||
static Mat flowToDisplay(const Mat flow)
|
||||
{
|
||||
Mat flow_split[2];
|
||||
Mat magnitude, angle;
|
||||
Mat hsv_split[3], hsv, rgb;
|
||||
split(flow, flow_split);
|
||||
cartToPolar(flow_split[0], flow_split[1], magnitude, angle, true);
|
||||
normalize(magnitude, magnitude, 0, 1, NORM_MINMAX);
|
||||
hsv_split[0] = angle; // already in degrees - no normalization needed
|
||||
hsv_split[1] = Mat::ones(angle.size(), angle.type());
|
||||
hsv_split[2] = magnitude;
|
||||
merge(hsv_split, 3, hsv);
|
||||
cvtColor(hsv, rgb, COLOR_HSV2BGR);
|
||||
return rgb;
|
||||
}
|
||||
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
CommandLineParser parser(argc, argv, keys);
|
||||
parser.about("OpenCV optical flow evaluation app");
|
||||
if ( parser.has("help") || argc < 4 )
|
||||
{
|
||||
parser.printMessage();
|
||||
printf("EXAMPLES:\n");
|
||||
printf("./example_optflow_optical_flow_evaluation im1.png im2.png farneback -d \n");
|
||||
printf("\t - compute flow field between im1 and im2 with farneback's method and display it");
|
||||
printf("./example_optflow_optical_flow_evaluation im1.png im2.png simpleflow groundtruth.flo \n");
|
||||
printf("\t - compute error statistics given the groundtruth; all pixels, endpoint error measure");
|
||||
printf("./example_optflow_optical_flow_evaluation im1.png im2.png farneback groundtruth.flo -m=angular -r=untextured \n");
|
||||
printf("\t - as before, but with changed error measure and stats computed only about \"untextured\" areas");
|
||||
printf("\n\n Flow file format description: http://vision.middlebury.edu/flow/code/flow-code/README.txt\n\n");
|
||||
return 0;
|
||||
}
|
||||
String i1_path = parser.get<String>(0);
|
||||
String i2_path = parser.get<String>(1);
|
||||
String method = parser.get<String>(2);
|
||||
String groundtruth_path = parser.get<String>(3);
|
||||
String error_measure = parser.get<String>("measure");
|
||||
String region = parser.get<String>("region");
|
||||
bool display_images = parser.has("display");
|
||||
const bool useGpu = parser.has("gpu");
|
||||
|
||||
if ( !parser.check() )
|
||||
{
|
||||
parser.printErrors();
|
||||
return 0;
|
||||
}
|
||||
|
||||
cv::ocl::setUseOpenCL(useGpu);
|
||||
printf("OpenCL Enabled: %u\n", useGpu && cv::ocl::haveOpenCL());
|
||||
|
||||
Mat i1, i2;
|
||||
Mat_<Point2f> flow, ground_truth;
|
||||
Mat computed_errors;
|
||||
i1 = imread(i1_path, 1);
|
||||
i2 = imread(i2_path, 1);
|
||||
|
||||
if ( !i1.data || !i2.data )
|
||||
{
|
||||
printf("No image data \n");
|
||||
return -1;
|
||||
}
|
||||
if ( i1.size() != i2.size() || i1.channels() != i2.channels() )
|
||||
{
|
||||
printf("Dimension mismatch between input images\n");
|
||||
return -1;
|
||||
}
|
||||
// 8-bit images expected by all algorithms
|
||||
if ( i1.depth() != CV_8U )
|
||||
i1.convertTo(i1, CV_8U);
|
||||
if ( i2.depth() != CV_8U )
|
||||
i2.convertTo(i2, CV_8U);
|
||||
|
||||
if ( (method == "farneback" || method == "tvl1" || method == "deepflow" || method == "DISflow_ultrafast" || method == "DISflow_fast" || method == "DISflow_medium") && i1.channels() == 3 )
|
||||
{ // 1-channel images are expected
|
||||
cvtColor(i1, i1, COLOR_BGR2GRAY);
|
||||
cvtColor(i2, i2, COLOR_BGR2GRAY);
|
||||
} else if ( method == "simpleflow" && i1.channels() == 1 )
|
||||
{ // 3-channel images expected
|
||||
cvtColor(i1, i1, COLOR_GRAY2BGR);
|
||||
cvtColor(i2, i2, COLOR_GRAY2BGR);
|
||||
}
|
||||
|
||||
flow = Mat(i1.size[0], i1.size[1], CV_32FC2);
|
||||
Ptr<DenseOpticalFlow> algorithm;
|
||||
|
||||
if ( method == "farneback" )
|
||||
algorithm = createOptFlow_Farneback();
|
||||
else if ( method == "simpleflow" )
|
||||
algorithm = createOptFlow_SimpleFlow();
|
||||
else if ( method == "tvl1" )
|
||||
algorithm = createOptFlow_DualTVL1();
|
||||
else if ( method == "deepflow" )
|
||||
algorithm = createOptFlow_DeepFlow();
|
||||
else if ( method == "sparsetodenseflow" )
|
||||
algorithm = createOptFlow_SparseToDense();
|
||||
else if (method == "RLOF_EPIC")
|
||||
{
|
||||
algorithm = createOptFlow_DenseRLOF();
|
||||
Ptr<DenseRLOFOpticalFlow> rlof = algorithm.dynamicCast< DenseRLOFOpticalFlow>();
|
||||
rlof->setInterpolation(INTERP_EPIC);
|
||||
rlof->setForwardBackward(1.f);
|
||||
}
|
||||
else if (method == "RLOF_RIC")
|
||||
{
|
||||
algorithm = createOptFlow_DenseRLOF();
|
||||
Ptr<DenseRLOFOpticalFlow> rlof = algorithm.dynamicCast< DenseRLOFOpticalFlow>();;
|
||||
rlof->setInterpolation(INTERP_RIC);
|
||||
rlof->setForwardBackward(1.f);
|
||||
}
|
||||
else if ( method == "pcaflow" ) {
|
||||
if ( parser.has("prior") ) {
|
||||
String prior = parser.get<String>("prior");
|
||||
printf("Using prior file: %s\n", prior.c_str());
|
||||
algorithm = makePtr<OpticalFlowPCAFlow>(makePtr<PCAPrior>(prior.c_str()));
|
||||
}
|
||||
else
|
||||
algorithm = createOptFlow_PCAFlow();
|
||||
}
|
||||
else if ( method == "DISflow_ultrafast" )
|
||||
algorithm = DISOpticalFlow::create(DISOpticalFlow::PRESET_ULTRAFAST);
|
||||
else if (method == "DISflow_fast")
|
||||
algorithm = DISOpticalFlow::create(DISOpticalFlow::PRESET_FAST);
|
||||
else if (method == "DISflow_medium")
|
||||
algorithm = DISOpticalFlow::create(DISOpticalFlow::PRESET_MEDIUM);
|
||||
else
|
||||
{
|
||||
printf("Wrong method!\n");
|
||||
parser.printMessage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
double startTick, time;
|
||||
startTick = (double) getTickCount(); // measure time
|
||||
|
||||
if (useGpu)
|
||||
algorithm->calc(i1, i2, flow.getUMat(ACCESS_RW));
|
||||
else
|
||||
algorithm->calc(i1, i2, flow);
|
||||
|
||||
time = ((double) getTickCount() - startTick) / getTickFrequency();
|
||||
printf("\nTime [s]: %.3f\n", time);
|
||||
if(display_images)
|
||||
{
|
||||
Mat flow_image = flowToDisplay(flow);
|
||||
namedWindow( "Computed flow", WINDOW_AUTOSIZE );
|
||||
imshow( "Computed flow", flow_image );
|
||||
}
|
||||
|
||||
if ( !groundtruth_path.empty() )
|
||||
{ // compare to ground truth
|
||||
ground_truth = readOpticalFlow(groundtruth_path);
|
||||
if ( flow.size() != ground_truth.size() || flow.channels() != 2
|
||||
|| ground_truth.channels() != 2 )
|
||||
{
|
||||
printf("Dimension mismatch between the computed flow and the provided ground truth\n");
|
||||
return -1;
|
||||
}
|
||||
if ( error_measure == "endpoint" )
|
||||
computed_errors = endpointError(flow, ground_truth);
|
||||
else if ( error_measure == "angular" )
|
||||
computed_errors = angularError(flow, ground_truth);
|
||||
else
|
||||
{
|
||||
printf("Invalid error measure! Available options: endpoint, angular\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Mat mask;
|
||||
if( region == "all" )
|
||||
mask = Mat::ones(ground_truth.size(), CV_8U) * 255;
|
||||
else if ( region == "discontinuities" )
|
||||
{
|
||||
Mat truth_merged, grad_x, grad_y, gradient;
|
||||
vector<Mat> truth_split;
|
||||
split(ground_truth, truth_split);
|
||||
truth_merged = truth_split[0] + truth_split[1];
|
||||
|
||||
Sobel( truth_merged, grad_x, CV_16S, 1, 0, -1, 1, 0, BORDER_REPLICATE );
|
||||
grad_x = abs(grad_x);
|
||||
Sobel( truth_merged, grad_y, CV_16S, 0, 1, 1, 1, 0, BORDER_REPLICATE );
|
||||
grad_y = abs(grad_y);
|
||||
addWeighted(grad_x, 0.5, grad_y, 0.5, 0, gradient); //approximation!
|
||||
|
||||
Scalar s_mean;
|
||||
s_mean = mean(gradient);
|
||||
double threshold = s_mean[0]; // threshold value arbitrary
|
||||
mask = gradient > threshold;
|
||||
dilate(mask, mask, Mat::ones(9, 9, CV_8U));
|
||||
}
|
||||
else if ( region == "untextured" )
|
||||
{
|
||||
Mat i1_grayscale, grad_x, grad_y, gradient;
|
||||
if( i1.channels() == 3 )
|
||||
cvtColor(i1, i1_grayscale, COLOR_BGR2GRAY);
|
||||
else
|
||||
i1_grayscale = i1;
|
||||
Sobel( i1_grayscale, grad_x, CV_16S, 1, 0, 7 );
|
||||
grad_x = abs(grad_x);
|
||||
Sobel( i1_grayscale, grad_y, CV_16S, 0, 1, 7 );
|
||||
grad_y = abs(grad_y);
|
||||
addWeighted(grad_x, 0.5, grad_y, 0.5, 0, gradient); //approximation!
|
||||
GaussianBlur(gradient, gradient, Size(5,5), 1, 1);
|
||||
|
||||
Scalar s_mean;
|
||||
s_mean = mean(gradient);
|
||||
// arbitrary threshold value used - could be determined statistically from the image?
|
||||
double threshold = 1000;
|
||||
mask = gradient < threshold;
|
||||
dilate(mask, mask, Mat::ones(3, 3, CV_8U));
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
printf("Invalid region selected! Available options: all, discontinuities, untextured");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//masking out NaNs and incorrect GT values
|
||||
Mat truth_split[2];
|
||||
split(ground_truth, truth_split);
|
||||
Mat abs_mask = Mat((abs(truth_split[0]) < 1e9) & (abs(truth_split[1]) < 1e9));
|
||||
Mat nan_mask = Mat((truth_split[0]==truth_split[0]) & (truth_split[1] == truth_split[1]));
|
||||
bitwise_and(abs_mask, nan_mask, nan_mask);
|
||||
|
||||
bitwise_and(nan_mask, mask, mask); //including the selected region
|
||||
|
||||
if(display_images) // display difference between computed and GT flow
|
||||
{
|
||||
Mat difference = ground_truth - flow;
|
||||
Mat masked_difference;
|
||||
difference.copyTo(masked_difference, mask);
|
||||
Mat flow_image = flowToDisplay(masked_difference);
|
||||
namedWindow( "Error map", WINDOW_AUTOSIZE );
|
||||
imshow( "Error map", flow_image );
|
||||
}
|
||||
|
||||
printf("Using %s error measure\n", error_measure.c_str());
|
||||
calculateStats(computed_errors, mask, display_images);
|
||||
|
||||
}
|
||||
if(display_images) // wait for the user to see all the images
|
||||
waitKey(0);
|
||||
return 0;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
#include "opencv2/core/ocl.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/optflow.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace cv;
|
||||
using optflow::OpticalFlowPCAFlow;
|
||||
using optflow::PCAPrior;
|
||||
|
||||
const String keys = "{help h ? | | print this message}"
|
||||
"{@image1 |<none>| image1}"
|
||||
"{@image2 |<none>| image2}"
|
||||
"{@groundtruth |<none>| path to the .flo file}"
|
||||
"{@prior |<none>| path to a prior file for PCAFlow}"
|
||||
"{@output |<none>| output image path}"
|
||||
"{g gpu | | use OpenCL}";
|
||||
|
||||
static double normL2( const Point2f &v ) { return sqrt( v.x * v.x + v.y * v.y ); }
|
||||
|
||||
static bool fileProbe( const char *name ) { return std::ifstream( name ).good(); }
|
||||
|
||||
static Vec3d getFlowColor( const Point2f &f, const bool logScale = true, const double scaleDown = 5 )
|
||||
{
|
||||
if ( f.x == 0 && f.y == 0 )
|
||||
return Vec3d( 0, 0, 1 );
|
||||
|
||||
double radius = normL2( f );
|
||||
if ( logScale )
|
||||
radius = log( radius + 1 );
|
||||
radius /= scaleDown;
|
||||
radius = std::min( 1.0, radius );
|
||||
|
||||
double angle = ( atan2( -f.y, -f.x ) + CV_PI ) * 180 / CV_PI;
|
||||
return Vec3d( angle, radius, 1 );
|
||||
}
|
||||
|
||||
static void displayFlow( InputArray _flow, OutputArray _img )
|
||||
{
|
||||
const Size sz = _flow.size();
|
||||
Mat flow = _flow.getMat();
|
||||
_img.create( sz, CV_32FC3 );
|
||||
Mat img = _img.getMat();
|
||||
|
||||
for ( int i = 0; i < sz.height; ++i )
|
||||
for ( int j = 0; j < sz.width; ++j )
|
||||
img.at< Vec3f >( i, j ) = getFlowColor( flow.at< Point2f >( i, j ) );
|
||||
|
||||
cvtColor( img, img, COLOR_HSV2BGR );
|
||||
}
|
||||
|
||||
static bool isFlowCorrect( const Point2f &u )
|
||||
{
|
||||
return !cvIsNaN( u.x ) && !cvIsNaN( u.y ) && ( fabs( u.x ) < 1e9 ) && ( fabs( u.y ) < 1e9 );
|
||||
}
|
||||
|
||||
static double calcEPE( const Mat &f1, const Mat &f2 )
|
||||
{
|
||||
double sum = 0;
|
||||
Size sz = f1.size();
|
||||
size_t cnt = 0;
|
||||
for ( int i = 0; i < sz.height; ++i )
|
||||
for ( int j = 0; j < sz.width; ++j )
|
||||
if ( isFlowCorrect( f1.at< Point2f >( i, j ) ) && isFlowCorrect( f2.at< Point2f >( i, j ) ) )
|
||||
{
|
||||
sum += normL2( f1.at< Point2f >( i, j ) - f2.at< Point2f >( i, j ) );
|
||||
++cnt;
|
||||
}
|
||||
return sum / cnt;
|
||||
}
|
||||
|
||||
static void displayResult( Mat &i1, Mat &i2, Mat >, Ptr< DenseOpticalFlow > &algo, OutputArray _img, const char *descr,
|
||||
const bool useGpu = false )
|
||||
{
|
||||
Mat flow( i1.size[0], i1.size[1], CV_32FC2 );
|
||||
TickMeter meter;
|
||||
meter.start();
|
||||
|
||||
if ( useGpu )
|
||||
algo->calc( i1, i2, flow.getUMat( ACCESS_RW ) );
|
||||
else
|
||||
algo->calc( i1, i2, flow );
|
||||
|
||||
meter.stop();
|
||||
displayFlow( flow, _img );
|
||||
Mat img = _img.getMat();
|
||||
putText( img, descr, Point2i( 24, 40 ), FONT_HERSHEY_DUPLEX, 1, Vec3b( 1, 0, 0 ), 2, LINE_AA );
|
||||
char buf[256];
|
||||
sprintf( buf, "Average EPE: %.2f", calcEPE( flow, gt ) );
|
||||
putText( img, buf, Point2i( 24, 80 ), FONT_HERSHEY_DUPLEX, 1, Vec3b( 1, 0, 0 ), 2, LINE_AA );
|
||||
sprintf( buf, "Time: %.2fs", meter.getTimeSec() );
|
||||
putText( img, buf, Point2i( 24, 120 ), FONT_HERSHEY_DUPLEX, 1, Vec3b( 1, 0, 0 ), 2, LINE_AA );
|
||||
}
|
||||
|
||||
static void displayGT( InputArray _flow, OutputArray _img, const char *descr )
|
||||
{
|
||||
displayFlow( _flow, _img );
|
||||
Mat img = _img.getMat();
|
||||
putText( img, descr, Point2i( 24, 40 ), FONT_HERSHEY_DUPLEX, 1, Vec3b( 1, 0, 0 ), 2, LINE_AA );
|
||||
}
|
||||
|
||||
int main( int argc, const char **argv )
|
||||
{
|
||||
CommandLineParser parser( argc, argv, keys );
|
||||
parser.about( "PCAFlow demonstration" );
|
||||
|
||||
if ( parser.has( "help" ) )
|
||||
{
|
||||
parser.printMessage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
String img1 = parser.get< String >( 0 );
|
||||
String img2 = parser.get< String >( 1 );
|
||||
String groundtruth = parser.get< String >( 2 );
|
||||
String prior = parser.get< String >( 3 );
|
||||
String outimg = parser.get< String >( 4 );
|
||||
const bool useGpu = parser.has( "gpu" );
|
||||
|
||||
if ( !parser.check() )
|
||||
{
|
||||
parser.printErrors();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ( !fileProbe( prior.c_str() ) )
|
||||
{
|
||||
std::cerr << "Can't open the file with prior! Check the provided path: " << prior << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
cv::ocl::setUseOpenCL( useGpu );
|
||||
|
||||
Mat i1 = imread( img1 );
|
||||
Mat i2 = imread( img2 );
|
||||
Mat gt = readOpticalFlow( groundtruth );
|
||||
|
||||
Mat i1g, i2g;
|
||||
cvtColor( i1, i1g, COLOR_BGR2GRAY );
|
||||
cvtColor( i2, i2g, COLOR_BGR2GRAY );
|
||||
|
||||
Mat pcaflowDisp, pcaflowpriDisp, farnebackDisp, gtDisp;
|
||||
|
||||
{
|
||||
Ptr< DenseOpticalFlow > pcaflow = makePtr< OpticalFlowPCAFlow >( makePtr< PCAPrior >( prior.c_str() ) );
|
||||
displayResult( i1, i2, gt, pcaflow, pcaflowpriDisp, "PCAFlow with prior", useGpu );
|
||||
}
|
||||
|
||||
{
|
||||
Ptr< DenseOpticalFlow > pcaflow = makePtr< OpticalFlowPCAFlow >();
|
||||
displayResult( i1, i2, gt, pcaflow, pcaflowDisp, "PCAFlow without prior", useGpu );
|
||||
}
|
||||
|
||||
{
|
||||
Ptr< DenseOpticalFlow > farneback = optflow::createOptFlow_Farneback();
|
||||
displayResult( i1g, i2g, gt, farneback, farnebackDisp, "Farneback", useGpu );
|
||||
}
|
||||
|
||||
displayGT( gt, gtDisp, "Ground truth" );
|
||||
|
||||
Mat disp1, disp2;
|
||||
vconcat( pcaflowpriDisp, farnebackDisp, disp1 );
|
||||
vconcat( pcaflowDisp, gtDisp, disp2 );
|
||||
hconcat( disp1, disp2, disp1 );
|
||||
disp1 *= 255;
|
||||
|
||||
imwrite( outimg, disp1 );
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
#include "opencv2/optflow.hpp"
|
||||
#include <opencv2/core/utility.hpp>
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace cv::optflow;
|
||||
using namespace std;
|
||||
|
||||
#define APP_NAME "simpleflow_demo : "
|
||||
|
||||
static void help()
|
||||
{
|
||||
// print a welcome message, and the OpenCV version
|
||||
printf("This is a demo of SimpleFlow optical flow algorithm,\n"
|
||||
"Using OpenCV version %s\n\n", CV_VERSION);
|
||||
|
||||
printf("Usage: simpleflow_demo frame1 frame2 output_flow"
|
||||
"\nApplication will write estimated flow "
|
||||
"\nbetween 'frame1' and 'frame2' in binary format"
|
||||
"\ninto file 'output_flow'"
|
||||
"\nThen one can use code from http://vision.middlebury.edu/flow/data/"
|
||||
"\nto convert flow in binary file to image\n");
|
||||
}
|
||||
|
||||
// binary file format for flow data specified here:
|
||||
// http://vision.middlebury.edu/flow/data/
|
||||
static void writeOpticalFlowToFile(const Mat& flow, FILE* file) {
|
||||
int cols = flow.cols;
|
||||
int rows = flow.rows;
|
||||
|
||||
fprintf(file, "PIEH");
|
||||
|
||||
if (fwrite(&cols, sizeof(int), 1, file) != 1 ||
|
||||
fwrite(&rows, sizeof(int), 1, file) != 1) {
|
||||
printf(APP_NAME "writeOpticalFlowToFile : problem writing header\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (int i= 0; i < rows; ++i) {
|
||||
for (int j = 0; j < cols; ++j) {
|
||||
Vec2f flow_at_point = flow.at<Vec2f>(i, j);
|
||||
|
||||
if (fwrite(&(flow_at_point[0]), sizeof(float), 1, file) != 1 ||
|
||||
fwrite(&(flow_at_point[1]), sizeof(float), 1, file) != 1) {
|
||||
printf(APP_NAME "writeOpticalFlowToFile : problem writing data\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void run(int argc, char** argv) {
|
||||
if (argc < 3) {
|
||||
printf(APP_NAME "Wrong number of command line arguments for mode `run`: %d (expected %d)\n",
|
||||
argc, 3);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Mat frame1 = imread(argv[0]);
|
||||
Mat frame2 = imread(argv[1]);
|
||||
|
||||
if (frame1.empty()) {
|
||||
printf(APP_NAME "Image #1 : %s cannot be read\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (frame2.empty()) {
|
||||
printf(APP_NAME "Image #2 : %s cannot be read\n", argv[1]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (frame1.rows != frame2.rows && frame1.cols != frame2.cols) {
|
||||
printf(APP_NAME "Images should be of equal sizes\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (frame1.type() != 16 || frame2.type() != 16) {
|
||||
printf(APP_NAME "Images should be of equal type CV_8UC3\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf(APP_NAME "Read two images of size [rows = %d, cols = %d]\n",
|
||||
frame1.rows, frame1.cols);
|
||||
|
||||
Mat flow;
|
||||
|
||||
float start = (float)getTickCount();
|
||||
calcOpticalFlowSF(frame1, frame2,
|
||||
flow,
|
||||
3, 2, 4, 4.1, 25.5, 18, 55.0, 25.5, 0.35, 18, 55.0, 25.5, 10);
|
||||
printf(APP_NAME "calcOpticalFlowSF : %lf sec\n", (getTickCount() - start) / getTickFrequency());
|
||||
|
||||
FILE* file = fopen(argv[2], "wb");
|
||||
if (file == NULL) {
|
||||
printf(APP_NAME "Unable to open file '%s' for writing\n", argv[2]);
|
||||
exit(1);
|
||||
}
|
||||
printf(APP_NAME "Writing to file\n");
|
||||
writeOpticalFlowToFile(flow, file);
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
static bool readOpticalFlowFromFile(FILE* file, Mat& flow) {
|
||||
char header[5];
|
||||
if (fread(header, 1, 4, file) < 4 && (string)header != "PIEH") {
|
||||
return false;
|
||||
}
|
||||
|
||||
int cols, rows;
|
||||
if (fread(&cols, sizeof(int), 1, file) != 1||
|
||||
fread(&rows, sizeof(int), 1, file) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
flow = Mat::zeros(rows, cols, CV_32FC2);
|
||||
|
||||
for (int i = 0; i < rows; ++i) {
|
||||
for (int j = 0; j < cols; ++j) {
|
||||
Vec2f flow_at_point;
|
||||
if (fread(&(flow_at_point[0]), sizeof(float), 1, file) != 1 ||
|
||||
fread(&(flow_at_point[1]), sizeof(float), 1, file) != 1) {
|
||||
return false;
|
||||
}
|
||||
flow.at<Vec2f>(i, j) = flow_at_point;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool isFlowCorrect(float u) {
|
||||
return !cvIsNaN(u) && (fabs(u) < 1e9);
|
||||
}
|
||||
|
||||
static float calc_rmse(Mat flow1, Mat flow2) {
|
||||
float sum = 0;
|
||||
int counter = 0;
|
||||
const int rows = flow1.rows;
|
||||
const int cols = flow1.cols;
|
||||
|
||||
for (int y = 0; y < rows; ++y) {
|
||||
for (int x = 0; x < cols; ++x) {
|
||||
Vec2f flow1_at_point = flow1.at<Vec2f>(y, x);
|
||||
Vec2f flow2_at_point = flow2.at<Vec2f>(y, x);
|
||||
|
||||
float u1 = flow1_at_point[0];
|
||||
float v1 = flow1_at_point[1];
|
||||
float u2 = flow2_at_point[0];
|
||||
float v2 = flow2_at_point[1];
|
||||
|
||||
if (isFlowCorrect(u1) && isFlowCorrect(u2) && isFlowCorrect(v1) && isFlowCorrect(v2)) {
|
||||
sum += (u1-u2)*(u1-u2) + (v1-v2)*(v1-v2);
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (float)sqrt(sum / (1e-9 + counter));
|
||||
}
|
||||
|
||||
static void eval(int argc, char** argv) {
|
||||
if (argc < 2) {
|
||||
printf(APP_NAME "Wrong number of command line arguments for mode `eval` : %d (expected %d)\n",
|
||||
argc, 2);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Mat flow1, flow2;
|
||||
|
||||
FILE* flow_file_1 = fopen(argv[0], "rb");
|
||||
if (flow_file_1 == NULL) {
|
||||
printf(APP_NAME "Cannot open file with first flow : %s\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
if (!readOpticalFlowFromFile(flow_file_1, flow1)) {
|
||||
printf(APP_NAME "Cannot read flow data from file %s\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
fclose(flow_file_1);
|
||||
|
||||
FILE* flow_file_2 = fopen(argv[1], "rb");
|
||||
if (flow_file_2 == NULL) {
|
||||
printf(APP_NAME "Cannot open file with first flow : %s\n", argv[1]);
|
||||
exit(1);
|
||||
}
|
||||
if (!readOpticalFlowFromFile(flow_file_2, flow2)) {
|
||||
printf(APP_NAME "Cannot read flow data from file %s\n", argv[1]);
|
||||
exit(1);
|
||||
}
|
||||
fclose(flow_file_2);
|
||||
|
||||
float rmse = calc_rmse(flow1, flow2);
|
||||
printf("%lf\n", rmse);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc < 2) {
|
||||
printf(APP_NAME "Mode is not specified\n");
|
||||
help();
|
||||
exit(1);
|
||||
}
|
||||
string mode = (string)argv[1];
|
||||
int new_argc = argc - 2;
|
||||
char** new_argv = &argv[2];
|
||||
|
||||
if ("run" == mode) {
|
||||
run(new_argc, new_argv);
|
||||
} else if ("eval" == mode) {
|
||||
eval(new_argc, new_argv);
|
||||
} else if ("help" == mode)
|
||||
help();
|
||||
else {
|
||||
printf(APP_NAME "Unknown mode : %s\n", argv[1]);
|
||||
help();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <opencv2/core/utility.hpp>
|
||||
#include "opencv2/video.hpp"
|
||||
#include "opencv2/optflow.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
using namespace optflow;
|
||||
|
||||
inline bool isFlowCorrect(Point2f u)
|
||||
{
|
||||
return !cvIsNaN(u.x) && !cvIsNaN(u.y) && fabs(u.x) < 1e9 && fabs(u.y) < 1e9;
|
||||
}
|
||||
|
||||
static Vec3b computeColor(float fx, float fy)
|
||||
{
|
||||
static bool first = true;
|
||||
|
||||
// relative lengths of color transitions:
|
||||
// these are chosen based on perceptual similarity
|
||||
// (e.g. one can distinguish more shades between red and yellow
|
||||
// than between yellow and green)
|
||||
const int RY = 15;
|
||||
const int YG = 6;
|
||||
const int GC = 4;
|
||||
const int CB = 11;
|
||||
const int BM = 13;
|
||||
const int MR = 6;
|
||||
const int NCOLS = RY + YG + GC + CB + BM + MR;
|
||||
static Vec3i colorWheel[NCOLS];
|
||||
|
||||
if (first)
|
||||
{
|
||||
int k = 0;
|
||||
|
||||
for (int i = 0; i < RY; ++i, ++k)
|
||||
colorWheel[k] = Vec3i(255, 255 * i / RY, 0);
|
||||
|
||||
for (int i = 0; i < YG; ++i, ++k)
|
||||
colorWheel[k] = Vec3i(255 - 255 * i / YG, 255, 0);
|
||||
|
||||
for (int i = 0; i < GC; ++i, ++k)
|
||||
colorWheel[k] = Vec3i(0, 255, 255 * i / GC);
|
||||
|
||||
for (int i = 0; i < CB; ++i, ++k)
|
||||
colorWheel[k] = Vec3i(0, 255 - 255 * i / CB, 255);
|
||||
|
||||
for (int i = 0; i < BM; ++i, ++k)
|
||||
colorWheel[k] = Vec3i(255 * i / BM, 0, 255);
|
||||
|
||||
for (int i = 0; i < MR; ++i, ++k)
|
||||
colorWheel[k] = Vec3i(255, 0, 255 - 255 * i / MR);
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
const float rad = sqrt(fx * fx + fy * fy);
|
||||
const float a = atan2(-fy, -fx) / (float)CV_PI;
|
||||
|
||||
const float fk = (a + 1.0f) / 2.0f * (NCOLS - 1);
|
||||
const int k0 = static_cast<int>(fk);
|
||||
const int k1 = (k0 + 1) % NCOLS;
|
||||
const float f = fk - k0;
|
||||
|
||||
Vec3b pix;
|
||||
|
||||
for (int b = 0; b < 3; b++)
|
||||
{
|
||||
const float col0 = colorWheel[k0][b] / 255.f;
|
||||
const float col1 = colorWheel[k1][b] / 255.f;
|
||||
|
||||
float col = (1 - f) * col0 + f * col1;
|
||||
|
||||
if (rad <= 1)
|
||||
col = 1 - rad * (1 - col); // increase saturation with radius
|
||||
else
|
||||
col *= .75; // out of range
|
||||
|
||||
pix[2 - b] = static_cast<uchar>(255.f * col);
|
||||
}
|
||||
|
||||
return pix;
|
||||
}
|
||||
|
||||
static void drawOpticalFlow(const Mat_<Point2f>& flow, Mat& dst, float maxmotion = -1)
|
||||
{
|
||||
dst.create(flow.size(), CV_8UC3);
|
||||
dst.setTo(Scalar::all(0));
|
||||
|
||||
// determine motion range:
|
||||
float maxrad = maxmotion;
|
||||
|
||||
if (maxmotion <= 0)
|
||||
{
|
||||
maxrad = 1;
|
||||
for (int y = 0; y < flow.rows; ++y)
|
||||
{
|
||||
for (int x = 0; x < flow.cols; ++x)
|
||||
{
|
||||
Point2f u = flow(y, x);
|
||||
|
||||
if (!isFlowCorrect(u))
|
||||
continue;
|
||||
|
||||
maxrad = max(maxrad, sqrt(u.x * u.x + u.y * u.y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int y = 0; y < flow.rows; ++y)
|
||||
{
|
||||
for (int x = 0; x < flow.cols; ++x)
|
||||
{
|
||||
Point2f u = flow(y, x);
|
||||
|
||||
if (isFlowCorrect(u))
|
||||
dst.at<Vec3b>(y, x) = computeColor(u.x / maxrad, u.y / maxrad);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// binary file format for flow data specified here:
|
||||
// http://vision.middlebury.edu/flow/data/
|
||||
static void writeOpticalFlowToFile(const Mat_<Point2f>& flow, const string& fileName)
|
||||
{
|
||||
static const char FLO_TAG_STRING[] = "PIEH";
|
||||
|
||||
ofstream file(fileName.c_str(), ios_base::binary);
|
||||
|
||||
file << FLO_TAG_STRING;
|
||||
|
||||
file.write((const char*) &flow.cols, sizeof(int));
|
||||
file.write((const char*) &flow.rows, sizeof(int));
|
||||
|
||||
for (int i = 0; i < flow.rows; ++i)
|
||||
{
|
||||
for (int j = 0; j < flow.cols; ++j)
|
||||
{
|
||||
const Point2f u = flow(i, j);
|
||||
|
||||
file.write((const char*) &u.x, sizeof(float));
|
||||
file.write((const char*) &u.y, sizeof(float));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
cv::CommandLineParser parser(argc, argv, "{help h || show help message}"
|
||||
"{ @frame0 | | frame 0}{ @frame1 | | frame 1}{ @output | | output flow}");
|
||||
if (parser.has("help"))
|
||||
{
|
||||
parser.printMessage();
|
||||
return 0;
|
||||
}
|
||||
string frame0_name = parser.get<string>("@frame0");
|
||||
string frame1_name = parser.get<string>("@frame1");
|
||||
string file = parser.get<string>("@output");
|
||||
if (frame0_name.empty() || frame1_name.empty() || file.empty())
|
||||
{
|
||||
cerr << "Usage : " << argv[0] << " [<frame0>] [<frame1>] [<output_flow>]" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
Mat frame0 = imread(frame0_name, IMREAD_GRAYSCALE);
|
||||
Mat frame1 = imread(frame1_name, IMREAD_GRAYSCALE);
|
||||
|
||||
if (frame0.empty())
|
||||
{
|
||||
cerr << "Can't open image [" << parser.get<string>("frame0") << "]" << endl;
|
||||
return -1;
|
||||
}
|
||||
if (frame1.empty())
|
||||
{
|
||||
cerr << "Can't open image [" << parser.get<string>("frame1") << "]" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (frame1.size() != frame0.size())
|
||||
{
|
||||
cerr << "Images should be of equal sizes" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
Mat_<Point2f> flow;
|
||||
Ptr<DualTVL1OpticalFlow> tvl1 = DualTVL1OpticalFlow::create();
|
||||
|
||||
const double start = (double)getTickCount();
|
||||
tvl1->calc(frame0, frame1, flow);
|
||||
const double timeSec = (getTickCount() - start) / getTickFrequency();
|
||||
cout << "calcOpticalFlowDual_TVL1 : " << timeSec << " sec" << endl;
|
||||
|
||||
Mat out;
|
||||
drawOpticalFlow(flow, out);
|
||||
if (!file.empty())
|
||||
writeOpticalFlowToFile(flow, file);
|
||||
|
||||
imshow("Flow", out);
|
||||
waitKey();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
/*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) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., 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 optflow
|
||||
{
|
||||
|
||||
class OpticalFlowDeepFlow: public DenseOpticalFlow
|
||||
{
|
||||
public:
|
||||
OpticalFlowDeepFlow();
|
||||
|
||||
void calc( InputArray I0, InputArray I1, InputOutputArray flow ) CV_OVERRIDE;
|
||||
void collectGarbage() CV_OVERRIDE;
|
||||
|
||||
protected:
|
||||
float sigma; // Gaussian smoothing parameter
|
||||
int minSize; // minimal dimension of an image in the pyramid
|
||||
float downscaleFactor; // scaling factor in the pyramid
|
||||
int fixedPointIterations; // during each level of the pyramid
|
||||
int sorIterations; // iterations of SOR
|
||||
float alpha; // smoothness assumption weight
|
||||
float delta; // color constancy weight
|
||||
float gamma; // gradient constancy weight
|
||||
float omega; // relaxation factor in SOR
|
||||
|
||||
int maxLayers; // max amount of layers in the pyramid
|
||||
int interpolationType;
|
||||
|
||||
private:
|
||||
std::vector<Mat> buildPyramid( const Mat& src );
|
||||
|
||||
};
|
||||
|
||||
OpticalFlowDeepFlow::OpticalFlowDeepFlow()
|
||||
{
|
||||
// parameters
|
||||
sigma = 0.6f;
|
||||
minSize = 25;
|
||||
downscaleFactor = 0.95f;
|
||||
fixedPointIterations = 5;
|
||||
sorIterations = 25;
|
||||
alpha = 1.0f;
|
||||
delta = 0.5f;
|
||||
gamma = 5.0f;
|
||||
omega = 1.6f;
|
||||
|
||||
//consts
|
||||
interpolationType = INTER_LINEAR;
|
||||
maxLayers = 200;
|
||||
}
|
||||
|
||||
std::vector<Mat> OpticalFlowDeepFlow::buildPyramid( const Mat& src )
|
||||
{
|
||||
std::vector<Mat> pyramid;
|
||||
pyramid.push_back(src);
|
||||
Mat prev = pyramid[0];
|
||||
for( int i = 0; i < this->maxLayers; ++i)
|
||||
{
|
||||
Mat next; //TODO: filtering at each level?
|
||||
Size nextSize((int) (prev.cols * downscaleFactor + 0.5f),
|
||||
(int) (prev.rows * downscaleFactor + 0.5f));
|
||||
if( nextSize.height <= minSize || nextSize.width <= minSize)
|
||||
break;
|
||||
resize(prev, next,
|
||||
nextSize, 0, 0,
|
||||
interpolationType);
|
||||
pyramid.push_back(next);
|
||||
prev = next;
|
||||
}
|
||||
return pyramid;
|
||||
}
|
||||
|
||||
void OpticalFlowDeepFlow::calc( InputArray _I0, InputArray _I1, InputOutputArray _flow )
|
||||
{
|
||||
Mat I0temp = _I0.getMat();
|
||||
Mat I1temp = _I1.getMat();
|
||||
|
||||
CV_Assert(I0temp.size() == I1temp.size());
|
||||
CV_Assert(I0temp.type() == I1temp.type());
|
||||
CV_Assert(I0temp.channels() == 1);
|
||||
// TODO: currently only grayscale - data term could be computed in color version as well...
|
||||
|
||||
Mat I0, I1;
|
||||
|
||||
I0temp.convertTo(I0, CV_32F);
|
||||
I1temp.convertTo(I1, CV_32F);
|
||||
|
||||
_flow.create(I0.size(), CV_32FC2);
|
||||
Mat W = _flow.getMat(); // if any data present - will be discarded
|
||||
|
||||
// pre-smooth images
|
||||
int kernelLen = ((int)floor(3 * sigma) * 2) + 1;
|
||||
Size kernelSize(kernelLen, kernelLen);
|
||||
GaussianBlur(I0, I0, kernelSize, sigma);
|
||||
GaussianBlur(I1, I1, kernelSize, sigma);
|
||||
// build down-sized pyramids
|
||||
std::vector<Mat> pyramid_I0 = buildPyramid(I0);
|
||||
std::vector<Mat> pyramid_I1 = buildPyramid(I1);
|
||||
int levelCount = (int) pyramid_I0.size();
|
||||
|
||||
// initialize the first version of flow estimate to zeros
|
||||
Size smallestSize = pyramid_I0[levelCount - 1].size();
|
||||
W = Mat::zeros(smallestSize, CV_32FC2);
|
||||
|
||||
for ( int level = levelCount - 1; level >= 0; --level )
|
||||
{ //iterate through all levels, beginning with the most coarse
|
||||
Ptr<VariationalRefinement> var = VariationalRefinement::create();
|
||||
|
||||
var->setAlpha(4 * alpha);
|
||||
var->setDelta(delta / 3);
|
||||
var->setGamma(gamma / 3);
|
||||
var->setFixedPointIterations(fixedPointIterations);
|
||||
var->setSorIterations(sorIterations);
|
||||
var->setOmega(omega);
|
||||
|
||||
var->calc(pyramid_I0[level], pyramid_I1[level], W);
|
||||
if ( level > 0 ) //not the last level
|
||||
{
|
||||
Mat temp;
|
||||
Size newSize = pyramid_I0[level - 1].size();
|
||||
resize(W, temp, newSize, 0, 0, interpolationType); //resize calculated flow
|
||||
W = temp * (1.0f / downscaleFactor); //scale values
|
||||
}
|
||||
}
|
||||
W.copyTo(_flow);
|
||||
}
|
||||
|
||||
void OpticalFlowDeepFlow::collectGarbage() {}
|
||||
|
||||
Ptr<DenseOpticalFlow> createOptFlow_DeepFlow() { return makePtr<OpticalFlowDeepFlow>(); }
|
||||
|
||||
}//optflow
|
||||
}//cv
|
||||
@@ -0,0 +1,217 @@
|
||||
/*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) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., 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/core.hpp"
|
||||
#include "opencv2/video.hpp"
|
||||
#include "opencv2/optflow.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace optflow
|
||||
{
|
||||
class OpticalFlowSimpleFlow : public DenseOpticalFlow
|
||||
{
|
||||
public:
|
||||
OpticalFlowSimpleFlow();
|
||||
void calc(InputArray I0, InputArray I1, InputOutputArray flow) CV_OVERRIDE;
|
||||
void collectGarbage() CV_OVERRIDE;
|
||||
|
||||
protected:
|
||||
int layers;
|
||||
int averaging_radius;
|
||||
int max_flow;
|
||||
double sigma_dist;
|
||||
double sigma_color;
|
||||
int postprocess_window;
|
||||
double sigma_dist_fix;
|
||||
double sigma_color_fix;
|
||||
double occ_thr;
|
||||
int upscale_averaging_radius;
|
||||
double upscale_sigma_dist;
|
||||
double upscale_sigma_color;
|
||||
double speed_up_thr;
|
||||
};
|
||||
|
||||
OpticalFlowSimpleFlow::OpticalFlowSimpleFlow()
|
||||
{
|
||||
// values from the example app
|
||||
layers = 3;
|
||||
averaging_radius = 2;
|
||||
max_flow = 4;
|
||||
// values from the default function parameters
|
||||
sigma_dist = 4.1;
|
||||
sigma_color = 25.5;
|
||||
postprocess_window = 18;
|
||||
sigma_dist_fix = 55.0;
|
||||
sigma_color_fix = 25.5;
|
||||
occ_thr = 0.35;
|
||||
upscale_averaging_radius = 18;
|
||||
upscale_sigma_dist = 55.0;
|
||||
upscale_sigma_color = 25.5;
|
||||
speed_up_thr = 10;
|
||||
}
|
||||
|
||||
void OpticalFlowSimpleFlow::calc(InputArray I0, InputArray I1, InputOutputArray flow)
|
||||
{
|
||||
optflow::calcOpticalFlowSF(I0, I1, flow, layers, averaging_radius, max_flow, sigma_dist, sigma_color,
|
||||
postprocess_window, sigma_dist_fix, sigma_color_fix, occ_thr,
|
||||
upscale_averaging_radius, upscale_sigma_dist, upscale_sigma_color, speed_up_thr);
|
||||
}
|
||||
void OpticalFlowSimpleFlow::collectGarbage()
|
||||
{
|
||||
|
||||
}
|
||||
//CV_INIT_ALGORITHM(OpticalFlowSimpleFlow, "DenseOpticalFlow.SimpleFlow"),
|
||||
// obj.info()->addParam(obj, "layers", obj.layers);
|
||||
// obj.info()->addParam(obj, "averaging_radius", obj.averaging_radius);
|
||||
// obj.info()->addParam(obj, "max_flow", obj.max_flow);
|
||||
// obj.info()->addParam(obj, "sigma_dist", obj.sigma_dist);
|
||||
// obj.info()->addParam(obj, "sigma_color", obj.sigma_color);
|
||||
// obj.info()->addParam(obj, "postprocess_window", obj.postprocess_window);
|
||||
// obj.info()->addParam(obj, "sigma_dist_fix", obj.sigma_dist_fix);
|
||||
// obj.info()->addParam(obj, "sigma_color_fix", obj.sigma_color_fix);
|
||||
// obj.info()->addParam(obj, "occ_thr", obj.occ_thr);
|
||||
// obj.info()->addParam(obj, "upscale_averaging_radius", obj.upscale_averaging_radius);
|
||||
// obj.info()->addParam(obj, "upscale_sigma_dist", obj.upscale_sigma_dist);
|
||||
// obj.info()->addParam(obj, "upscale_sigma_color", obj.upscale_sigma_color);
|
||||
// obj.info()->addParam(obj, "speed_up_thr", obj.speed_up_thr))
|
||||
|
||||
Ptr<DenseOpticalFlow> createOptFlow_SimpleFlow()
|
||||
{
|
||||
return makePtr<OpticalFlowSimpleFlow>();
|
||||
}
|
||||
|
||||
class OpticalFlowFarneback : public DenseOpticalFlow
|
||||
{
|
||||
public:
|
||||
OpticalFlowFarneback();
|
||||
void calc(InputArray I0, InputArray I1, InputOutputArray flow) CV_OVERRIDE;
|
||||
void collectGarbage() CV_OVERRIDE;
|
||||
protected:
|
||||
int numLevels;
|
||||
double pyrScale;
|
||||
bool fastPyramids;
|
||||
int winSize;
|
||||
int numIters;
|
||||
int polyN;
|
||||
double polySigma;
|
||||
int flags;
|
||||
};
|
||||
|
||||
OpticalFlowFarneback::OpticalFlowFarneback()
|
||||
{
|
||||
// values copied from the FarnebackOpticalFlow class
|
||||
numLevels = 5;
|
||||
pyrScale = 0.5;
|
||||
fastPyramids = false;
|
||||
winSize = 13;
|
||||
numIters = 10;
|
||||
polyN = 5;
|
||||
polySigma = 1.1;
|
||||
flags = 0;
|
||||
}
|
||||
|
||||
void OpticalFlowFarneback::calc(InputArray I0, InputArray I1, InputOutputArray flow)
|
||||
{
|
||||
calcOpticalFlowFarneback(I0, I1, flow, pyrScale, numLevels, winSize, numIters, polyN, polySigma, flags);
|
||||
}
|
||||
void OpticalFlowFarneback::collectGarbage()
|
||||
{
|
||||
}
|
||||
|
||||
//CV_INIT_ALGORITHM(OpticalFlowFarneback, "DenseOpticalFlow.Farneback",
|
||||
// obj.info()->addParam(obj, "numLevels", obj.numLevels);
|
||||
// obj.info()->addParam(obj, "pyrScale", obj.pyrScale);
|
||||
// obj.info()->addParam(obj, "fastPyramids", obj.fastPyramids);
|
||||
// obj.info()->addParam(obj, "winSize", obj.winSize);
|
||||
// obj.info()->addParam(obj, "numIters", obj.numIters);
|
||||
// obj.info()->addParam(obj, "polyN", obj.polyN);
|
||||
// obj.info()->addParam(obj, "polySigma", obj.polySigma);
|
||||
// obj.info()->addParam(obj, "flags", obj.flags))
|
||||
|
||||
|
||||
|
||||
|
||||
Ptr<DenseOpticalFlow> createOptFlow_Farneback()
|
||||
{
|
||||
return makePtr<OpticalFlowFarneback>();
|
||||
}
|
||||
|
||||
class OpticalFlowSparseToDense : public DenseOpticalFlow
|
||||
{
|
||||
public:
|
||||
OpticalFlowSparseToDense(int _grid_step, int _k, float _sigma, bool _use_post_proc, float _fgs_lambda, float _fgs_sigma);
|
||||
void calc(InputArray I0, InputArray I1, InputOutputArray flow) CV_OVERRIDE;
|
||||
void collectGarbage() CV_OVERRIDE;
|
||||
protected:
|
||||
int grid_step;
|
||||
int k;
|
||||
float sigma;
|
||||
bool use_post_proc;
|
||||
float fgs_lambda;
|
||||
float fgs_sigma;
|
||||
};
|
||||
|
||||
OpticalFlowSparseToDense::OpticalFlowSparseToDense(int _grid_step, int _k, float _sigma, bool _use_post_proc, float _fgs_lambda, float _fgs_sigma)
|
||||
{
|
||||
grid_step = _grid_step;
|
||||
k = _k;
|
||||
sigma = _sigma;
|
||||
use_post_proc = _use_post_proc;
|
||||
fgs_lambda = _fgs_lambda;
|
||||
fgs_sigma = _fgs_sigma;
|
||||
}
|
||||
|
||||
void OpticalFlowSparseToDense::calc(InputArray I0, InputArray I1, InputOutputArray flow)
|
||||
{
|
||||
calcOpticalFlowSparseToDense(I0,I1,flow,grid_step,k,sigma,use_post_proc,fgs_lambda,fgs_sigma);
|
||||
}
|
||||
|
||||
void OpticalFlowSparseToDense::collectGarbage() {}
|
||||
|
||||
Ptr<DenseOpticalFlow> createOptFlow_SparseToDense()
|
||||
{
|
||||
return makePtr<OpticalFlowSparseToDense>(8,128,0.05f,true,500.0f,1.5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import sys
|
||||
import numpy as np
|
||||
import cv2 as cv
|
||||
import struct
|
||||
import argparse
|
||||
from math import sqrt
|
||||
|
||||
argparser = argparse.ArgumentParser(
|
||||
description='''Use this script to generate prior for using with PCAFlow.
|
||||
Basis size here must match corresponding parameter in the PCAFlow.
|
||||
Gamma should be selected experimentally.''')
|
||||
|
||||
argparser.add_argument('-f',
|
||||
'--files',
|
||||
nargs='+',
|
||||
help='List of optical flow .flo files for learning. You can pass a directory here and it will be scanned recursively for .flo files.',
|
||||
required=True)
|
||||
argparser.add_argument('-o',
|
||||
'--output',
|
||||
help='Output file for prior',
|
||||
required=True)
|
||||
argparser.add_argument('--width',
|
||||
type=int,
|
||||
help='Size of the basis first dimension',
|
||||
required=True,
|
||||
default=18)
|
||||
argparser.add_argument('--height',
|
||||
type=int,
|
||||
help='Size of the basis second dimension',
|
||||
required=True,
|
||||
default=14)
|
||||
argparser.add_argument(
|
||||
'-g',
|
||||
'--gamma',
|
||||
type=float,
|
||||
help='Amount of regularization. The greater this parameter, the bigger will be an impact of the regularization.',
|
||||
required=True)
|
||||
args = argparser.parse_args()
|
||||
|
||||
basis_size = (args.height, args.width)
|
||||
gamma = args.gamma
|
||||
|
||||
|
||||
def find_flo(pp):
|
||||
f = []
|
||||
for p in pp:
|
||||
if os.path.isfile(p):
|
||||
f.append(p)
|
||||
else:
|
||||
for root, subdirs, files in os.walk(p):
|
||||
f += map(lambda x: os.path.join(root, x),
|
||||
filter(lambda x: x.split('.')[-1] == 'flo', files))
|
||||
return list(set(f))
|
||||
|
||||
|
||||
def load_flo(flo):
|
||||
with open(flo, 'rb') as f:
|
||||
magic = np.fromfile(f, np.float32, count=1)[0]
|
||||
if 202021.25 != magic:
|
||||
print('Magic number incorrect. Invalid .flo file')
|
||||
else:
|
||||
w = np.fromfile(f, np.int32, count=1)[0]
|
||||
h = np.fromfile(f, np.int32, count=1)[0]
|
||||
print('Reading %dx%d flo file %s' % (w, h, flo))
|
||||
data = np.fromfile(f, np.float32, count=2 * w * h)
|
||||
# Reshape data into 3D array (columns, rows, bands)
|
||||
flow = np.reshape(data, (h, w, 2))
|
||||
return flow[:, :, 0], flow[:, :, 1]
|
||||
|
||||
|
||||
def get_w(m):
|
||||
s = m.shape
|
||||
w = cv.dct(m)
|
||||
w *= 2.0 / sqrt(s[0] * s[1])
|
||||
#w[0,0] *= 0.5
|
||||
w[:, 0] *= sqrt(0.5)
|
||||
w[0, :] *= sqrt(0.5)
|
||||
w = w[0:basis_size[0], 0:basis_size[1]].transpose().flatten()
|
||||
return w
|
||||
|
||||
|
||||
w1 = []
|
||||
w2 = []
|
||||
|
||||
for flo in find_flo(args.files):
|
||||
x, y = load_flo(flo)
|
||||
w1.append(get_w(x))
|
||||
w2.append(get_w(y))
|
||||
|
||||
w1mean = sum(w1) / len(w1)
|
||||
w2mean = sum(w2) / len(w2)
|
||||
|
||||
for i in xrange(len(w1)):
|
||||
w1[i] -= w1mean
|
||||
for i in xrange(len(w2)):
|
||||
w2[i] -= w2mean
|
||||
|
||||
Q1 = sum([w1[i].reshape(-1, 1).dot(w1[i].reshape(1, -1))
|
||||
for i in xrange(len(w1))]) / len(w1)
|
||||
Q2 = sum([w2[i].reshape(-1, 1).dot(w2[i].reshape(1, -1))
|
||||
for i in xrange(len(w2))]) / len(w2)
|
||||
Q1 = np.matrix(Q1)
|
||||
Q2 = np.matrix(Q2)
|
||||
|
||||
if len(w1) > 1:
|
||||
while True:
|
||||
try:
|
||||
L1 = np.linalg.cholesky(Q1)
|
||||
break
|
||||
except np.linalg.LinAlgError:
|
||||
mev = min(np.linalg.eig(Q1)[0]).real
|
||||
assert (mev < 0)
|
||||
print('Q1', mev)
|
||||
if -mev < 1e-6:
|
||||
mev = -1e-6
|
||||
Q1 += (-mev * 1.000001) * np.identity(Q1.shape[0])
|
||||
|
||||
while True:
|
||||
try:
|
||||
L2 = np.linalg.cholesky(Q2)
|
||||
break
|
||||
except np.linalg.LinAlgError:
|
||||
mev = min(np.linalg.eig(Q2)[0]).real
|
||||
assert (mev < 0)
|
||||
print('Q2', mev)
|
||||
if -mev < 1e-6:
|
||||
mev = -1e-6
|
||||
Q2 += (-mev * 1.000001) * np.identity(Q2.shape[0])
|
||||
else:
|
||||
L1 = np.identity(Q1.shape[0])
|
||||
L2 = np.identity(Q2.shape[0])
|
||||
|
||||
L1 = np.linalg.inv(L1) * gamma
|
||||
L2 = np.linalg.inv(L2) * gamma
|
||||
|
||||
assert (L1.shape == L2.shape)
|
||||
assert (L1.shape[0] == L1.shape[1])
|
||||
|
||||
f = open(args.output, 'wb')
|
||||
|
||||
f.write(struct.pack('I', L1.shape[0]))
|
||||
f.write(struct.pack('I', L1.shape[1]))
|
||||
|
||||
for i in xrange(L1.shape[0]):
|
||||
for j in xrange(L1.shape[1]):
|
||||
f.write(struct.pack('f', L1[i, j]))
|
||||
|
||||
for i in xrange(L2.shape[0]):
|
||||
for j in xrange(L2.shape[1]):
|
||||
f.write(struct.pack('f', L2[i, j]))
|
||||
|
||||
b1 = L1.dot(w1mean.reshape(-1, 1))
|
||||
b2 = L2.dot(w2mean.reshape(-1, 1))
|
||||
|
||||
assert (L1.shape[0] == b1.shape[0])
|
||||
|
||||
for i in xrange(b1.shape[0]):
|
||||
f.write(struct.pack('f', b1[i, 0]))
|
||||
|
||||
for i in xrange(b2.shape[0]):
|
||||
f.write(struct.pack('f', b2[i, 0]))
|
||||
|
||||
f.close()
|
||||
@@ -0,0 +1,420 @@
|
||||
/*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.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, 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 Intel Corporation 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/core/utility.hpp"
|
||||
#include "opencv2/core/hal/hal.hpp"
|
||||
#include "opencv2/core/private.hpp"
|
||||
#include "opencl_kernels_optflow.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace motempl {
|
||||
|
||||
using std::vector;
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
|
||||
static bool ocl_updateMotionHistory( InputArray _silhouette, InputOutputArray _mhi,
|
||||
float timestamp, float delbound )
|
||||
{
|
||||
ocl::Kernel k("updateMotionHistory", ocl::optflow::updatemotionhistory_oclsrc);
|
||||
if (k.empty())
|
||||
return false;
|
||||
|
||||
UMat silh = _silhouette.getUMat(), mhi = _mhi.getUMat();
|
||||
|
||||
k.args(ocl::KernelArg::ReadOnlyNoSize(silh), ocl::KernelArg::ReadWrite(mhi),
|
||||
timestamp, delbound);
|
||||
|
||||
size_t globalsize[2] = { (size_t)silh.cols, (size_t)silh.rows };
|
||||
return k.run(2, globalsize, NULL, false);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void updateMotionHistory( InputArray _silhouette, InputOutputArray _mhi,
|
||||
double timestamp, double duration )
|
||||
{
|
||||
CV_Assert( _silhouette.type() == CV_8UC1 && _mhi.type() == CV_32FC1 );
|
||||
CV_Assert( _silhouette.sameSize(_mhi) );
|
||||
|
||||
float ts = (float)timestamp;
|
||||
float delbound = (float)(timestamp - duration);
|
||||
|
||||
CV_OCL_RUN(_mhi.isUMat() && _mhi.dims() <= 2,
|
||||
ocl_updateMotionHistory(_silhouette, _mhi, ts, delbound))
|
||||
|
||||
Mat silh = _silhouette.getMat(), mhi = _mhi.getMat();
|
||||
Size size = silh.size();
|
||||
#if defined(HAVE_IPP)
|
||||
int silhstep = (int)silh.step, mhistep = (int)mhi.step;
|
||||
#endif
|
||||
|
||||
if( silh.isContinuous() && mhi.isContinuous() )
|
||||
{
|
||||
size.width *= size.height;
|
||||
size.height = 1;
|
||||
#if defined(HAVE_IPP)
|
||||
silhstep = (int)silh.total();
|
||||
mhistep = (int)mhi.total() * sizeof(Ipp32f);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(HAVE_IPP)
|
||||
IppStatus status = ippiUpdateMotionHistory_8u32f_C1IR((const Ipp8u *)silh.data, silhstep, (Ipp32f *)mhi.data, mhistep,
|
||||
ippiSize(size.width, size.height), (Ipp32f)timestamp, (Ipp32f)duration);
|
||||
if (status >= 0)
|
||||
return;
|
||||
#endif
|
||||
|
||||
#if CV_SSE2
|
||||
volatile bool useSIMD = checkHardwareSupport(CV_CPU_SSE2);
|
||||
#endif
|
||||
|
||||
for(int y = 0; y < size.height; y++ )
|
||||
{
|
||||
const uchar* silhData = silh.ptr<uchar>(y);
|
||||
float* mhiData = mhi.ptr<float>(y);
|
||||
int x = 0;
|
||||
|
||||
#if CV_SSE2
|
||||
if( useSIMD )
|
||||
{
|
||||
__m128 ts4 = _mm_set1_ps(ts), db4 = _mm_set1_ps(delbound);
|
||||
for( ; x <= size.width - 8; x += 8 )
|
||||
{
|
||||
__m128i z = _mm_setzero_si128();
|
||||
__m128i s = _mm_unpacklo_epi8(_mm_loadl_epi64((const __m128i*)(silhData + x)), z);
|
||||
__m128 s0 = _mm_cvtepi32_ps(_mm_unpacklo_epi16(s, z)), s1 = _mm_cvtepi32_ps(_mm_unpackhi_epi16(s, z));
|
||||
__m128 v0 = _mm_loadu_ps(mhiData + x), v1 = _mm_loadu_ps(mhiData + x + 4);
|
||||
__m128 fz = _mm_setzero_ps();
|
||||
|
||||
v0 = _mm_and_ps(v0, _mm_cmpge_ps(v0, db4));
|
||||
v1 = _mm_and_ps(v1, _mm_cmpge_ps(v1, db4));
|
||||
|
||||
__m128 m0 = _mm_and_ps(_mm_xor_ps(v0, ts4), _mm_cmpneq_ps(s0, fz));
|
||||
__m128 m1 = _mm_and_ps(_mm_xor_ps(v1, ts4), _mm_cmpneq_ps(s1, fz));
|
||||
|
||||
v0 = _mm_xor_ps(v0, m0);
|
||||
v1 = _mm_xor_ps(v1, m1);
|
||||
|
||||
_mm_storeu_ps(mhiData + x, v0);
|
||||
_mm_storeu_ps(mhiData + x + 4, v1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
for( ; x < size.width; x++ )
|
||||
{
|
||||
float val = mhiData[x];
|
||||
val = silhData[x] ? ts : val < delbound ? 0 : val;
|
||||
mhiData[x] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void calcMotionGradient( InputArray _mhi, OutputArray _mask,
|
||||
OutputArray _orientation,
|
||||
double delta1, double delta2,
|
||||
int aperture_size )
|
||||
{
|
||||
Mat mhi = _mhi.getMat();
|
||||
Size size = mhi.size();
|
||||
|
||||
_mask.create(size, CV_8U);
|
||||
_orientation.create(size, CV_32F);
|
||||
|
||||
Mat mask = _mask.getMat();
|
||||
Mat orient = _orientation.getMat();
|
||||
|
||||
if( aperture_size < 3 || aperture_size > 7 || (aperture_size & 1) == 0 )
|
||||
CV_Error( Error::StsOutOfRange, "aperture_size must be 3, 5 or 7" );
|
||||
|
||||
if( delta1 <= 0 || delta2 <= 0 )
|
||||
CV_Error( Error::StsOutOfRange, "both delta's must be positive" );
|
||||
|
||||
if( mhi.type() != CV_32FC1 )
|
||||
CV_Error( Error::StsUnsupportedFormat,
|
||||
"MHI must be single-channel floating-point images" );
|
||||
|
||||
if( orient.data == mhi.data )
|
||||
{
|
||||
_orientation.release();
|
||||
_orientation.create(size, CV_32F);
|
||||
orient = _orientation.getMat();
|
||||
}
|
||||
|
||||
if( delta1 > delta2 )
|
||||
std::swap(delta1, delta2);
|
||||
|
||||
float gradient_epsilon = 1e-4f * aperture_size * aperture_size;
|
||||
float min_delta = (float)delta1;
|
||||
float max_delta = (float)delta2;
|
||||
|
||||
Mat dX_min, dY_max;
|
||||
|
||||
// calc Dx and Dy
|
||||
Sobel( mhi, dX_min, CV_32F, 1, 0, aperture_size, 1, 0, BORDER_REPLICATE );
|
||||
Sobel( mhi, dY_max, CV_32F, 0, 1, aperture_size, 1, 0, BORDER_REPLICATE );
|
||||
|
||||
int x, y;
|
||||
|
||||
if( mhi.isContinuous() && orient.isContinuous() && mask.isContinuous() )
|
||||
{
|
||||
size.width *= size.height;
|
||||
size.height = 1;
|
||||
}
|
||||
|
||||
// calc gradient
|
||||
for( y = 0; y < size.height; y++ )
|
||||
{
|
||||
const float* dX_min_row = dX_min.ptr<float>(y);
|
||||
const float* dY_max_row = dY_max.ptr<float>(y);
|
||||
float* orient_row = orient.ptr<float>(y);
|
||||
uchar* mask_row = mask.ptr<uchar>(y);
|
||||
|
||||
cv::hal::fastAtan2(dY_max_row, dX_min_row, orient_row, size.width, true);
|
||||
|
||||
// make orientation zero where the gradient is very small
|
||||
for( x = 0; x < size.width; x++ )
|
||||
{
|
||||
float dY = dY_max_row[x];
|
||||
float dX = dX_min_row[x];
|
||||
|
||||
if( std::abs(dX) < gradient_epsilon && std::abs(dY) < gradient_epsilon )
|
||||
{
|
||||
mask_row[x] = (uchar)0;
|
||||
orient_row[x] = 0.f;
|
||||
}
|
||||
else
|
||||
mask_row[x] = (uchar)1;
|
||||
}
|
||||
}
|
||||
|
||||
erode( mhi, dX_min, noArray(), Point(-1,-1), (aperture_size-1)/2, BORDER_REPLICATE );
|
||||
dilate( mhi, dY_max, noArray(), Point(-1,-1), (aperture_size-1)/2, BORDER_REPLICATE );
|
||||
|
||||
// mask off pixels which have little motion difference in their neighborhood
|
||||
for( y = 0; y < size.height; y++ )
|
||||
{
|
||||
const float* dX_min_row = dX_min.ptr<float>(y);
|
||||
const float* dY_max_row = dY_max.ptr<float>(y);
|
||||
float* orient_row = orient.ptr<float>(y);
|
||||
uchar* mask_row = mask.ptr<uchar>(y);
|
||||
|
||||
for( x = 0; x < size.width; x++ )
|
||||
{
|
||||
float d0 = dY_max_row[x] - dX_min_row[x];
|
||||
|
||||
if( mask_row[x] == 0 || d0 < min_delta || max_delta < d0 )
|
||||
{
|
||||
mask_row[x] = (uchar)0;
|
||||
orient_row[x] = 0.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double calcGlobalOrientation( InputArray _orientation, InputArray _mask,
|
||||
InputArray _mhi, double /*timestamp*/,
|
||||
double duration )
|
||||
{
|
||||
Mat orient = _orientation.getMat(), mask = _mask.getMat(), mhi = _mhi.getMat();
|
||||
Size size = mhi.size();
|
||||
|
||||
CV_Assert( mask.type() == CV_8U && orient.type() == CV_32F && mhi.type() == CV_32F );
|
||||
CV_Assert( mask.size() == size && orient.size() == size );
|
||||
CV_Assert( duration > 0 );
|
||||
|
||||
int histSize = 12;
|
||||
float _ranges[] = { 0.f, 360.f };
|
||||
const float* ranges = _ranges;
|
||||
Mat hist;
|
||||
|
||||
calcHist(&orient, 1, 0, mask, hist, 1, &histSize, &ranges);
|
||||
|
||||
// find the maximum index (the dominant orientation)
|
||||
Point baseOrientPt;
|
||||
minMaxLoc(hist, 0, 0, 0, &baseOrientPt);
|
||||
float fbaseOrient = (baseOrientPt.x + baseOrientPt.y)*360.f/histSize;
|
||||
|
||||
// override timestamp with the maximum value in MHI
|
||||
double timestamp = 0;
|
||||
minMaxLoc( mhi, 0, ×tamp, 0, 0, mask );
|
||||
|
||||
// find the shift relative to the dominant orientation as weighted sum of relative angles
|
||||
float a = (float)(254. / 255. / duration);
|
||||
float b = (float)(1. - timestamp * a);
|
||||
float delbound = (float)(timestamp - duration);
|
||||
|
||||
if( mhi.isContinuous() && mask.isContinuous() && orient.isContinuous() )
|
||||
{
|
||||
size.width *= size.height;
|
||||
size.height = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
a = 254/(255*dt)
|
||||
b = 1 - t*a = 1 - 254*t/(255*dur) =
|
||||
(255*dt - 254*t)/(255*dt) =
|
||||
(dt - (t - dt)*254)/(255*dt);
|
||||
--------------------------------------------------------
|
||||
ax + b = 254*x/(255*dt) + (dt - (t - dt)*254)/(255*dt) =
|
||||
(254*x + dt - (t - dt)*254)/(255*dt) =
|
||||
((x - (t - dt))*254 + dt)/(255*dt) =
|
||||
(((x - (t - dt))/dt)*254 + 1)/255 = (((x - low_time)/dt)*254 + 1)/255
|
||||
*/
|
||||
float shiftOrient = 0, shiftWeight = 0;
|
||||
for( int y = 0; y < size.height; y++ )
|
||||
{
|
||||
const float* mhiptr = mhi.ptr<float>(y);
|
||||
const float* oriptr = orient.ptr<float>(y);
|
||||
const uchar* maskptr = mask.ptr<uchar>(y);
|
||||
|
||||
for( int x = 0; x < size.width; x++ )
|
||||
{
|
||||
if( maskptr[x] != 0 && mhiptr[x] > delbound )
|
||||
{
|
||||
/*
|
||||
orient in 0..360, base_orient in 0..360
|
||||
-> (rel_angle = orient - base_orient) in -360..360.
|
||||
rel_angle is translated to -180..180
|
||||
*/
|
||||
float weight = mhiptr[x] * a + b;
|
||||
float relAngle = oriptr[x] - fbaseOrient;
|
||||
|
||||
relAngle += (relAngle < -180 ? 360 : 0);
|
||||
relAngle += (relAngle > 180 ? -360 : 0);
|
||||
|
||||
if( fabs(relAngle) < 45 )
|
||||
{
|
||||
shiftOrient += weight * relAngle;
|
||||
shiftWeight += weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add the dominant orientation and the relative shift
|
||||
if( shiftWeight == 0 )
|
||||
shiftWeight = 0.01f;
|
||||
|
||||
fbaseOrient += shiftOrient / shiftWeight;
|
||||
fbaseOrient -= (fbaseOrient < 360 ? 0 : 360);
|
||||
fbaseOrient += (fbaseOrient >= 0 ? 0 : 360);
|
||||
|
||||
return fbaseOrient;
|
||||
}
|
||||
|
||||
|
||||
void segmentMotion(InputArray _mhi, OutputArray _segmask,
|
||||
vector<Rect>& boundingRects,
|
||||
double timestamp, double segThresh)
|
||||
{
|
||||
Mat mhi = _mhi.getMat();
|
||||
|
||||
_segmask.create(mhi.size(), CV_32F);
|
||||
Mat segmask = _segmask.getMat();
|
||||
segmask = Scalar::all(0);
|
||||
|
||||
CV_Assert( mhi.type() == CV_32F );
|
||||
CV_Assert( segThresh >= 0 );
|
||||
|
||||
Mat mask = Mat::zeros( mhi.rows + 2, mhi.cols + 2, CV_8UC1 );
|
||||
|
||||
int x, y;
|
||||
|
||||
// protect zero mhi pixels from floodfill.
|
||||
for( y = 0; y < mhi.rows; y++ )
|
||||
{
|
||||
const float* mhiptr = mhi.ptr<float>(y);
|
||||
uchar* maskptr = mask.ptr<uchar>(y+1) + 1;
|
||||
|
||||
for( x = 0; x < mhi.cols; x++ )
|
||||
{
|
||||
if( mhiptr[x] == 0 )
|
||||
maskptr[x] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
float ts = (float)timestamp;
|
||||
float comp_idx = 1.f;
|
||||
|
||||
for( y = 0; y < mhi.rows; y++ )
|
||||
{
|
||||
float* mhiptr = mhi.ptr<float>(y);
|
||||
uchar* maskptr = mask.ptr<uchar>(y+1) + 1;
|
||||
|
||||
for( x = 0; x < mhi.cols; x++ )
|
||||
{
|
||||
if( mhiptr[x] == ts && maskptr[x] == 0 )
|
||||
{
|
||||
Rect cc;
|
||||
floodFill( mhi, mask, Point(x,y), Scalar::all(0),
|
||||
&cc, Scalar::all(segThresh), Scalar::all(segThresh),
|
||||
FLOODFILL_MASK_ONLY + 2*256 + 4 );
|
||||
|
||||
for( int y1 = 0; y1 < cc.height; y1++ )
|
||||
{
|
||||
float* segmaskptr = segmask.ptr<float>(cc.y + y1) + cc.x;
|
||||
uchar* maskptr1 = mask.ptr<uchar>(cc.y + y1 + 1) + cc.x + 1;
|
||||
|
||||
for( int x1 = 0; x1 < cc.width; x1++ )
|
||||
{
|
||||
if( maskptr1[x1] > 1 )
|
||||
{
|
||||
maskptr1[x1] = 1;
|
||||
segmaskptr[x1] = comp_idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
comp_idx += 1.f;
|
||||
boundingRects.push_back(cc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* End of file. */
|
||||
@@ -0,0 +1,378 @@
|
||||
/*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) 2010-2012, Multicoreware, Inc., all rights reserved.
|
||||
// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// @Authors
|
||||
// Jin Ma jin@multicorewareinc.com
|
||||
//
|
||||
// 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*/
|
||||
|
||||
__kernel void centeredGradientKernel(__global const float* src_ptr, int src_col, int src_row, int src_step,
|
||||
__global float* dx, __global float* dy, int d_step)
|
||||
{
|
||||
int x = get_global_id(0);
|
||||
int y = get_global_id(1);
|
||||
|
||||
if((x < src_col)&&(y < src_row))
|
||||
{
|
||||
int src_x1 = (x + 1) < (src_col -1)? (x + 1) : (src_col - 1);
|
||||
int src_x2 = (x - 1) > 0 ? (x -1) : 0;
|
||||
dx[y * d_step+ x] = 0.5f * (src_ptr[y * src_step + src_x1] - src_ptr[y * src_step+ src_x2]);
|
||||
|
||||
int src_y1 = (y+1) < (src_row - 1) ? (y + 1) : (src_row - 1);
|
||||
int src_y2 = (y - 1) > 0 ? (y - 1) : 0;
|
||||
dy[y * d_step+ x] = 0.5f * (src_ptr[src_y1 * src_step + x] - src_ptr[src_y2 * src_step+ x]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inline float bicubicCoeff(float x_)
|
||||
{
|
||||
|
||||
float x = fabs(x_);
|
||||
if (x <= 1.0f)
|
||||
return x * x * (1.5f * x - 2.5f) + 1.0f;
|
||||
else if (x < 2.0f)
|
||||
return x * (x * (-0.5f * x + 2.5f) - 4.0f) + 2.0f;
|
||||
else
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
__kernel void warpBackwardKernel(__global const float* I0, int I0_step, int I0_col, int I0_row,
|
||||
image2d_t tex_I1, image2d_t tex_I1x, image2d_t tex_I1y,
|
||||
__global const float* u1, int u1_step,
|
||||
__global const float* u2,
|
||||
__global float* I1w,
|
||||
__global float* I1wx, /*int I1wx_step,*/
|
||||
__global float* I1wy, /*int I1wy_step,*/
|
||||
__global float* grad, /*int grad_step,*/
|
||||
__global float* rho,
|
||||
int I1w_step,
|
||||
int u2_step,
|
||||
int u1_offset_x,
|
||||
int u1_offset_y,
|
||||
int u2_offset_x,
|
||||
int u2_offset_y)
|
||||
{
|
||||
int x = get_global_id(0);
|
||||
int y = get_global_id(1);
|
||||
|
||||
if(x < I0_col&&y < I0_row)
|
||||
{
|
||||
//float u1Val = u1(y, x);
|
||||
float u1Val = u1[(y + u1_offset_y) * u1_step + x + u1_offset_x];
|
||||
//float u2Val = u2(y, x);
|
||||
float u2Val = u2[(y + u2_offset_y) * u2_step + x + u2_offset_x];
|
||||
|
||||
float wx = x + u1Val;
|
||||
float wy = y + u2Val;
|
||||
|
||||
int xmin = ceil(wx - 2.0f);
|
||||
int xmax = floor(wx + 2.0f);
|
||||
|
||||
int ymin = ceil(wy - 2.0f);
|
||||
int ymax = floor(wy + 2.0f);
|
||||
|
||||
float sum = 0.0f;
|
||||
float sumx = 0.0f;
|
||||
float sumy = 0.0f;
|
||||
float wsum = 0.0f;
|
||||
sampler_t sampleri = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_NEAREST;
|
||||
for (int cy = ymin; cy <= ymax; ++cy)
|
||||
{
|
||||
for (int cx = xmin; cx <= xmax; ++cx)
|
||||
{
|
||||
float w = bicubicCoeff(wx - cx) * bicubicCoeff(wy - cy);
|
||||
//sum += w * tex2D(tex_I1 , cx, cy);
|
||||
int2 cood = (int2)(cx, cy);
|
||||
sum += w * read_imagef(tex_I1, sampleri, cood).x;
|
||||
//sumx += w * tex2D(tex_I1x, cx, cy);
|
||||
sumx += w * read_imagef(tex_I1x, sampleri, cood).x;
|
||||
//sumy += w * tex2D(tex_I1y, cx, cy);
|
||||
sumy += w * read_imagef(tex_I1y, sampleri, cood).x;
|
||||
wsum += w;
|
||||
}
|
||||
}
|
||||
float coeff = 1.0f / wsum;
|
||||
float I1wVal = sum * coeff;
|
||||
float I1wxVal = sumx * coeff;
|
||||
float I1wyVal = sumy * coeff;
|
||||
I1w[y * I1w_step + x] = I1wVal;
|
||||
I1wx[y * I1w_step + x] = I1wxVal;
|
||||
I1wy[y * I1w_step + x] = I1wyVal;
|
||||
float Ix2 = I1wxVal * I1wxVal;
|
||||
float Iy2 = I1wyVal * I1wyVal;
|
||||
|
||||
// store the |Grad(I1)|^2
|
||||
grad[y * I1w_step + x] = Ix2 + Iy2;
|
||||
|
||||
// compute the constant part of the rho function
|
||||
float I0Val = I0[y * I0_step + x];
|
||||
rho[y * I1w_step + x] = I1wVal - I1wxVal * u1Val - I1wyVal * u2Val - I0Val;
|
||||
}
|
||||
}
|
||||
|
||||
inline float readImage(__global const float *image, int x, int y, int rows, int cols, int elemCntPerRow)
|
||||
{
|
||||
int i0 = clamp(x, 0, cols - 1);
|
||||
int j0 = clamp(y, 0, rows - 1);
|
||||
|
||||
return image[j0 * elemCntPerRow + i0];
|
||||
}
|
||||
|
||||
__kernel void warpBackwardKernelNoImage2d(__global const float* I0, int I0_step, int I0_col, int I0_row,
|
||||
__global const float* tex_I1, __global const float* tex_I1x, __global const float* tex_I1y,
|
||||
__global const float* u1, int u1_step,
|
||||
__global const float* u2,
|
||||
__global float* I1w,
|
||||
__global float* I1wx, /*int I1wx_step,*/
|
||||
__global float* I1wy, /*int I1wy_step,*/
|
||||
__global float* grad, /*int grad_step,*/
|
||||
__global float* rho,
|
||||
int I1w_step,
|
||||
int u2_step,
|
||||
int I1_step,
|
||||
int I1x_step)
|
||||
{
|
||||
int x = get_global_id(0);
|
||||
int y = get_global_id(1);
|
||||
|
||||
if(x < I0_col&&y < I0_row)
|
||||
{
|
||||
//float u1Val = u1(y, x);
|
||||
float u1Val = u1[y * u1_step + x];
|
||||
//float u2Val = u2(y, x);
|
||||
float u2Val = u2[y * u2_step + x];
|
||||
|
||||
float wx = x + u1Val;
|
||||
float wy = y + u2Val;
|
||||
|
||||
int xmin = ceil(wx - 2.0f);
|
||||
int xmax = floor(wx + 2.0f);
|
||||
|
||||
int ymin = ceil(wy - 2.0f);
|
||||
int ymax = floor(wy + 2.0f);
|
||||
|
||||
float sum = 0.0f;
|
||||
float sumx = 0.0f;
|
||||
float sumy = 0.0f;
|
||||
float wsum = 0.0f;
|
||||
|
||||
for (int cy = ymin; cy <= ymax; ++cy)
|
||||
{
|
||||
for (int cx = xmin; cx <= xmax; ++cx)
|
||||
{
|
||||
float w = bicubicCoeff(wx - cx) * bicubicCoeff(wy - cy);
|
||||
|
||||
int2 cood = (int2)(cx, cy);
|
||||
sum += w * readImage(tex_I1, cood.x, cood.y, I0_col, I0_row, I1_step);
|
||||
sumx += w * readImage(tex_I1x, cood.x, cood.y, I0_col, I0_row, I1x_step);
|
||||
sumy += w * readImage(tex_I1y, cood.x, cood.y, I0_col, I0_row, I1x_step);
|
||||
wsum += w;
|
||||
}
|
||||
}
|
||||
|
||||
float coeff = 1.0f / wsum;
|
||||
|
||||
float I1wVal = sum * coeff;
|
||||
float I1wxVal = sumx * coeff;
|
||||
float I1wyVal = sumy * coeff;
|
||||
|
||||
I1w[y * I1w_step + x] = I1wVal;
|
||||
I1wx[y * I1w_step + x] = I1wxVal;
|
||||
I1wy[y * I1w_step + x] = I1wyVal;
|
||||
|
||||
float Ix2 = I1wxVal * I1wxVal;
|
||||
float Iy2 = I1wyVal * I1wyVal;
|
||||
|
||||
// store the |Grad(I1)|^2
|
||||
grad[y * I1w_step + x] = Ix2 + Iy2;
|
||||
|
||||
// compute the constant part of the rho function
|
||||
float I0Val = I0[y * I0_step + x];
|
||||
rho[y * I1w_step + x] = I1wVal - I1wxVal * u1Val - I1wyVal * u2Val - I0Val;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
__kernel void estimateDualVariablesKernel(__global const float* u1, int u1_col, int u1_row, int u1_step,
|
||||
__global const float* u2,
|
||||
__global float* p11, int p11_step,
|
||||
__global float* p12,
|
||||
__global float* p21,
|
||||
__global float* p22,
|
||||
float taut,
|
||||
int u2_step,
|
||||
int u1_offset_x,
|
||||
int u1_offset_y,
|
||||
int u2_offset_x,
|
||||
int u2_offset_y)
|
||||
{
|
||||
int x = get_global_id(0);
|
||||
int y = get_global_id(1);
|
||||
|
||||
if(x < u1_col && y < u1_row)
|
||||
{
|
||||
int src_x1 = (x + 1) < (u1_col - 1) ? (x + 1) : (u1_col - 1);
|
||||
float u1x = u1[(y + u1_offset_y) * u1_step + src_x1 + u1_offset_x] - u1[(y + u1_offset_y) * u1_step + x + u1_offset_x];
|
||||
|
||||
int src_y1 = (y + 1) < (u1_row - 1) ? (y + 1) : (u1_row - 1);
|
||||
float u1y = u1[(src_y1 + u1_offset_y) * u1_step + x + u1_offset_x] - u1[(y + u1_offset_y) * u1_step + x + u1_offset_x];
|
||||
|
||||
int src_x2 = (x + 1) < (u1_col - 1) ? (x + 1) : (u1_col - 1);
|
||||
float u2x = u2[(y + u2_offset_y) * u2_step + src_x2 + u2_offset_x] - u2[(y + u2_offset_y) * u2_step + x + u2_offset_x];
|
||||
|
||||
int src_y2 = (y + 1) < (u1_row - 1) ? (y + 1) : (u1_row - 1);
|
||||
float u2y = u2[(src_y2 + u2_offset_y) * u2_step + x + u2_offset_x] - u2[(y + u2_offset_y) * u2_step + x + u2_offset_x];
|
||||
|
||||
float g1 = hypot(u1x, u1y);
|
||||
float g2 = hypot(u2x, u2y);
|
||||
|
||||
float ng1 = 1.0f + taut * g1;
|
||||
float ng2 = 1.0f + taut * g2;
|
||||
|
||||
p11[y * p11_step + x] = (p11[y * p11_step + x] + taut * u1x) / ng1;
|
||||
p12[y * p11_step + x] = (p12[y * p11_step + x] + taut * u1y) / ng1;
|
||||
p21[y * p11_step + x] = (p21[y * p11_step + x] + taut * u2x) / ng2;
|
||||
p22[y * p11_step + x] = (p22[y * p11_step + x] + taut * u2y) / ng2;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inline float divergence(__global const float* v1, __global const float* v2, int y, int x, int v1_step, int v2_step)
|
||||
{
|
||||
|
||||
if (x > 0 && y > 0)
|
||||
{
|
||||
float v1x = v1[y * v1_step + x] - v1[y * v1_step + x - 1];
|
||||
float v2y = v2[y * v2_step + x] - v2[(y - 1) * v2_step + x];
|
||||
return v1x + v2y;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (y > 0)
|
||||
return v1[y * v1_step + 0] + v2[y * v2_step + 0] - v2[(y - 1) * v2_step + 0];
|
||||
else
|
||||
{
|
||||
if (x > 0)
|
||||
return v1[0 * v1_step + x] - v1[0 * v1_step + x - 1] + v2[0 * v2_step + x];
|
||||
else
|
||||
return v1[0 * v1_step + 0] + v2[0 * v2_step + 0];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
__kernel void estimateUKernel(__global const float* I1wx, int I1wx_col, int I1wx_row, int I1wx_step,
|
||||
__global const float* I1wy, /*int I1wy_step,*/
|
||||
__global const float* grad, /*int grad_step,*/
|
||||
__global const float* rho_c, /*int rho_c_step,*/
|
||||
__global const float* p11, /*int p11_step,*/
|
||||
__global const float* p12, /*int p12_step,*/
|
||||
__global const float* p21, /*int p21_step,*/
|
||||
__global const float* p22, /*int p22_step,*/
|
||||
__global float* u1, int u1_step,
|
||||
__global float* u2,
|
||||
__global float* error, float l_t, float theta, int u2_step,
|
||||
int u1_offset_x,
|
||||
int u1_offset_y,
|
||||
int u2_offset_x,
|
||||
int u2_offset_y,
|
||||
char calc_error)
|
||||
{
|
||||
int x = get_global_id(0);
|
||||
int y = get_global_id(1);
|
||||
|
||||
if(x < I1wx_col && y < I1wx_row)
|
||||
{
|
||||
float I1wxVal = I1wx[y * I1wx_step + x];
|
||||
float I1wyVal = I1wy[y * I1wx_step + x];
|
||||
float gradVal = grad[y * I1wx_step + x];
|
||||
float u1OldVal = u1[(y + u1_offset_y) * u1_step + x + u1_offset_x];
|
||||
float u2OldVal = u2[(y + u2_offset_y) * u2_step + x + u2_offset_x];
|
||||
|
||||
float rho = rho_c[y * I1wx_step + x] + (I1wxVal * u1OldVal + I1wyVal * u2OldVal);
|
||||
|
||||
// estimate the values of the variable (v1, v2) (thresholding operator TH)
|
||||
|
||||
float d1 = 0.0f;
|
||||
float d2 = 0.0f;
|
||||
|
||||
if (rho < -l_t * gradVal)
|
||||
{
|
||||
d1 = l_t * I1wxVal;
|
||||
d2 = l_t * I1wyVal;
|
||||
}
|
||||
else if (rho > l_t * gradVal)
|
||||
{
|
||||
d1 = -l_t * I1wxVal;
|
||||
d2 = -l_t * I1wyVal;
|
||||
}
|
||||
else if (gradVal > 1.192092896e-07f)
|
||||
{
|
||||
float fi = -rho / gradVal;
|
||||
d1 = fi * I1wxVal;
|
||||
d2 = fi * I1wyVal;
|
||||
}
|
||||
|
||||
float v1 = u1OldVal + d1;
|
||||
float v2 = u2OldVal + d2;
|
||||
|
||||
// compute the divergence of the dual variable (p1, p2)
|
||||
|
||||
float div_p1 = divergence(p11, p12, y, x, I1wx_step, I1wx_step);
|
||||
float div_p2 = divergence(p21, p22, y, x, I1wx_step, I1wx_step);
|
||||
|
||||
// estimate the values of the optical flow (u1, u2)
|
||||
|
||||
float u1NewVal = v1 + theta * div_p1;
|
||||
float u2NewVal = v2 + theta * div_p2;
|
||||
|
||||
u1[(y + u1_offset_y) * u1_step + x + u1_offset_x] = u1NewVal;
|
||||
u2[(y + u2_offset_y) * u2_step + x + u2_offset_x] = u2NewVal;
|
||||
|
||||
if(calc_error)
|
||||
{
|
||||
float n1 = (u1OldVal - u1NewVal) * (u1OldVal - u1NewVal);
|
||||
float n2 = (u2OldVal - u2NewVal) * (u2OldVal - u2NewVal);
|
||||
error[y * I1wx_step + x] = n1 + n2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// 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, Itseez, Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
|
||||
// @Authors
|
||||
// Vladislav Samsonov, vvladxx@gmail.com
|
||||
|
||||
__kernel void getPatchDescriptor(
|
||||
__global const uchar* imgCh0, int ic0step, int ic0off,
|
||||
__global const uchar* imgCh1, int ic1step, int ic1off,
|
||||
__global const uchar* imgCh2, int ic2step, int ic2off,
|
||||
__global uchar* out, int outstep, int outoff,
|
||||
const int gh, const int gw, const int PR )
|
||||
{
|
||||
const int i = get_global_id(0);
|
||||
const int j = get_global_id(1);
|
||||
|
||||
if (i >= gh || j >= gw)
|
||||
return;
|
||||
|
||||
__global double* desc = (__global double*)(out + (outstep * (i * gw + j) + outoff));
|
||||
const int patchRadius = PR * 2;
|
||||
float patch[PATCH_RADIUS_DOUBLED][PATCH_RADIUS_DOUBLED];
|
||||
|
||||
for (int i0 = 0; i0 < patchRadius; ++i0) {
|
||||
__global const float* ch0Row = (__global const float*)(imgCh0 + (ic0step * (i + i0) + ic0off + j * sizeof(float)));
|
||||
for (int j0 = 0; j0 < patchRadius; ++j0)
|
||||
patch[i0][j0] = ch0Row[j0];
|
||||
}
|
||||
|
||||
#pragma unroll
|
||||
for (int n0 = 0; n0 < 4; ++n0) {
|
||||
#pragma unroll
|
||||
for (int n1 = 0; n1 < 4; ++n1) {
|
||||
double sum = 0;
|
||||
for (int i0 = 0; i0 < patchRadius; ++i0)
|
||||
for (int j0 = 0; j0 < patchRadius; ++j0)
|
||||
sum += patch[i0][j0] * cos(CV_PI * (i0 + 0.5) * n0 / patchRadius) * cos(CV_PI * (j0 + 0.5) * n1 / patchRadius);
|
||||
desc[n0 * 4 + n1] = sum / PR;
|
||||
}
|
||||
}
|
||||
|
||||
for (int k = 0; k < 4; ++k) {
|
||||
desc[k] *= SQRT2_INV;
|
||||
desc[k * 4] *= SQRT2_INV;
|
||||
}
|
||||
|
||||
double sum = 0;
|
||||
|
||||
for (int i0 = 0; i0 < patchRadius; ++i0) {
|
||||
__global const float* ch1Row = (__global const float*)(imgCh1 + (ic1step * (i + i0) + ic1off + j * sizeof(float)));
|
||||
for (int j0 = 0; j0 < patchRadius; ++j0)
|
||||
sum += ch1Row[j0];
|
||||
}
|
||||
|
||||
desc[16] = sum / patchRadius;
|
||||
sum = 0;
|
||||
|
||||
for (int i0 = 0; i0 < patchRadius; ++i0) {
|
||||
__global const float* ch2Row = (__global const float*)(imgCh2 + (ic2step * (i + i0) + ic2off + j * sizeof(float)));
|
||||
for (int j0 = 0; j0 < patchRadius; ++j0)
|
||||
sum += ch2Row[j0];
|
||||
}
|
||||
|
||||
desc[17] = sum / patchRadius;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// 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 updateMotionHistory(__global const uchar * silh, int silh_step, int silh_offset,
|
||||
__global uchar * mhiptr, int mhi_step, int mhi_offset, int mhi_rows, int mhi_cols,
|
||||
float timestamp, float delbound)
|
||||
{
|
||||
int x = get_global_id(0);
|
||||
int y = get_global_id(1);
|
||||
|
||||
if (x < mhi_cols && y < mhi_rows)
|
||||
{
|
||||
int silh_index = mad24(y, silh_step, silh_offset + x);
|
||||
int mhi_index = mad24(y, mhi_step, mhi_offset + x * (int)sizeof(float));
|
||||
|
||||
silh += silh_index;
|
||||
__global float * mhi = (__global float *)(mhiptr + mhi_index);
|
||||
|
||||
float val = mhi[0];
|
||||
val = silh[0] ? timestamp : val < delbound ? 0 : val;
|
||||
mhi[0] = val;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,528 @@
|
||||
/*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) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., 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/ximgproc/edge_filter.hpp"
|
||||
|
||||
/* Disable "from double to float" and "from size_t to int" warnings.
|
||||
* Fixing these would make the code look ugly by introducing explicit cast all around.
|
||||
* Here these warning are pointless anyway.
|
||||
*/
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( disable : 4305 4244 4267 4838 )
|
||||
#endif
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic ignored "-Wshorten-64-to-32"
|
||||
#endif
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace optflow
|
||||
{
|
||||
namespace
|
||||
{
|
||||
|
||||
#ifndef M_SQRT2
|
||||
const float M_SQRT2 = 1.41421356237309504880;
|
||||
#endif
|
||||
|
||||
template <typename T> inline int mathSign( T val ) { return ( T( 0 ) < val ) - ( val < T( 0 ) ); }
|
||||
|
||||
/* Stable symmetric Householder reflection that gives c and s such that
|
||||
* [ c s ][a] = [d],
|
||||
* [ s -c ][b] [0]
|
||||
*
|
||||
* Output:
|
||||
* c -- cosine(theta), where theta is the implicit angle of rotation
|
||||
* (counter-clockwise) in a plane-rotation
|
||||
* s -- sine(theta)
|
||||
* r -- two-norm of [a; b]
|
||||
*/
|
||||
inline void symOrtho( double a, double b, double &c, double &s, double &r )
|
||||
{
|
||||
if ( b == 0 )
|
||||
{
|
||||
c = mathSign( a );
|
||||
s = 0;
|
||||
r = std::abs( a );
|
||||
}
|
||||
else if ( a == 0 )
|
||||
{
|
||||
c = 0;
|
||||
s = mathSign( b );
|
||||
r = std::abs( b );
|
||||
}
|
||||
else if ( std::abs( b ) > std::abs( a ) )
|
||||
{
|
||||
const double tau = a / b;
|
||||
s = mathSign( b ) / std::sqrt( 1 + tau * tau );
|
||||
c = s * tau;
|
||||
r = b / s;
|
||||
}
|
||||
else
|
||||
{
|
||||
const double tau = b / a;
|
||||
c = mathSign( a ) / std::sqrt( 1 + tau * tau );
|
||||
s = c * tau;
|
||||
r = a / c;
|
||||
}
|
||||
}
|
||||
|
||||
/* Iterative LSQR algorithm for solving least squares problems.
|
||||
*
|
||||
* [1] Paige, C. C. and M. A. Saunders,
|
||||
* LSQR: An Algorithm for Sparse Linear Equations And Sparse Least Squares
|
||||
* ACM Trans. Math. Soft., Vol.8, 1982, pp. 43-71.
|
||||
*
|
||||
* Solves the following problem:
|
||||
* argmin_x ||Ax - b|| + damp||x||
|
||||
*
|
||||
* Output:
|
||||
* x -- approximate solution
|
||||
*/
|
||||
void solveLSQR( const Mat &A, const Mat &b, OutputArray xOut, const double damp = 0.0, const unsigned iter_lim = 10 )
|
||||
{
|
||||
const int n = A.size().width;
|
||||
CV_Assert( A.size().height == b.size().height );
|
||||
CV_Assert( A.type() == CV_32F );
|
||||
CV_Assert( b.type() == CV_32F );
|
||||
xOut.create( n, 1, CV_32F );
|
||||
|
||||
Mat v( n, 1, CV_32F, 0.0f );
|
||||
Mat u = b;
|
||||
Mat x = xOut.getMat();
|
||||
x = Mat::zeros( x.size(), x.type() );
|
||||
double alfa = 0;
|
||||
double beta = cv::norm( u, NORM_L2 );
|
||||
Mat w( n, 1, CV_32F, 0.0f );
|
||||
const Mat AT = A.t();
|
||||
|
||||
if ( beta > 0 )
|
||||
{
|
||||
u *= 1 / beta;
|
||||
v = AT * u;
|
||||
alfa = cv::norm( v, NORM_L2 );
|
||||
}
|
||||
|
||||
if ( alfa > 0 )
|
||||
{
|
||||
v *= 1 / alfa;
|
||||
w = v.clone();
|
||||
}
|
||||
|
||||
double rhobar = alfa;
|
||||
double phibar = beta;
|
||||
if ( alfa * beta == 0 )
|
||||
return;
|
||||
|
||||
for ( unsigned itn = 0; itn < iter_lim; ++itn )
|
||||
{
|
||||
u *= -alfa;
|
||||
u += A * v;
|
||||
beta = cv::norm( u, NORM_L2 );
|
||||
|
||||
if ( beta > 0 )
|
||||
{
|
||||
u *= 1 / beta;
|
||||
v *= -beta;
|
||||
v += AT * u;
|
||||
alfa = cv::norm( v, NORM_L2 );
|
||||
if ( alfa > 0 )
|
||||
v *= 1 / alfa;
|
||||
}
|
||||
|
||||
double rhobar1 = sqrt( rhobar * rhobar + damp * damp );
|
||||
double cs1 = rhobar / rhobar1;
|
||||
phibar = cs1 * phibar;
|
||||
|
||||
double cs, sn, rho;
|
||||
symOrtho( rhobar1, beta, cs, sn, rho );
|
||||
|
||||
double theta = sn * alfa;
|
||||
rhobar = -cs * alfa;
|
||||
double phi = cs * phibar;
|
||||
phibar = sn * phibar;
|
||||
|
||||
double t1 = phi / rho;
|
||||
double t2 = -theta / rho;
|
||||
|
||||
x += t1 * w;
|
||||
w *= t2;
|
||||
w += v;
|
||||
}
|
||||
}
|
||||
|
||||
inline void _cpu_fillDCTSampledPoints( float *row, const Point2f &p, const Size &basisSize, const Size &size )
|
||||
{
|
||||
for ( int n1 = 0; n1 < basisSize.width; ++n1 )
|
||||
for ( int n2 = 0; n2 < basisSize.height; ++n2 )
|
||||
row[n1 * basisSize.height + n2] =
|
||||
cosf( ( n1 * CV_PI / size.width ) * ( p.x + 0.5 ) ) * cosf( ( n2 * CV_PI / size.height ) * ( p.y + 0.5 ) );
|
||||
}
|
||||
|
||||
ocl::ProgramSource _ocl_fillDCTSampledPointsSource(
|
||||
"__kernel void fillDCTSampledPoints(__global const uchar* features, int fstep, int foff, __global "
|
||||
"uchar* A, int Astep, int Aoff, int fs, int bsw, int bsh, int sw, int sh) {"
|
||||
"const int i = get_global_id(0);"
|
||||
"const int n1 = get_global_id(1);"
|
||||
"const int n2 = get_global_id(2);"
|
||||
"if (i >= fs || n1 >= bsw || n2 >= bsh) return;"
|
||||
"__global const float2* f = (__global const float2*)(features + (fstep * i + foff));"
|
||||
"__global float* a = (__global float*)(A + (Astep * i + Aoff + (n1 * bsh + n2) * sizeof(float)));"
|
||||
"const float2 p = f[0];"
|
||||
"const float pi = 3.14159265358979323846;"
|
||||
"a[0] = cos((n1 * pi / sw) * (p.x + 0.5)) * cos((n2 * pi / sh) * (p.y + 0.5));"
|
||||
"}" );
|
||||
|
||||
void applyCLAHE( UMat &img, float claheClip )
|
||||
{
|
||||
Ptr<CLAHE> clahe = createCLAHE();
|
||||
clahe->setClipLimit( claheClip );
|
||||
clahe->apply( img, img );
|
||||
}
|
||||
|
||||
void reduceToFlow( const Mat &w1, const Mat &w2, Mat &flow, const Size &basisSize )
|
||||
{
|
||||
const Size size = flow.size();
|
||||
Mat flowX( size, CV_32F, 0.0f );
|
||||
Mat flowY( size, CV_32F, 0.0f );
|
||||
|
||||
const float mult = sqrt( static_cast<float>(size.area()) ) * 0.5;
|
||||
|
||||
for ( int i = 0; i < basisSize.width; ++i )
|
||||
for ( int j = 0; j < basisSize.height; ++j )
|
||||
{
|
||||
flowX.at<float>( j, i ) = w1.at<float>( i * basisSize.height + j ) * mult;
|
||||
flowY.at<float>( j, i ) = w2.at<float>( i * basisSize.height + j ) * mult;
|
||||
}
|
||||
for ( int i = 0; i < basisSize.height; ++i )
|
||||
{
|
||||
flowX.at<float>( i, 0 ) *= M_SQRT2;
|
||||
flowY.at<float>( i, 0 ) *= M_SQRT2;
|
||||
}
|
||||
for ( int i = 0; i < basisSize.width; ++i )
|
||||
{
|
||||
flowX.at<float>( 0, i ) *= M_SQRT2;
|
||||
flowY.at<float>( 0, i ) *= M_SQRT2;
|
||||
}
|
||||
|
||||
dct( flowX, flowX, DCT_INVERSE );
|
||||
dct( flowY, flowY, DCT_INVERSE );
|
||||
for ( int i = 0; i < size.height; ++i )
|
||||
for ( int j = 0; j < size.width; ++j )
|
||||
flow.at<Point2f>( i, j ) = Point2f( flowX.at<float>( i, j ), flowY.at<float>( i, j ) );
|
||||
}
|
||||
}
|
||||
|
||||
void OpticalFlowPCAFlow::findSparseFeatures( UMat &from, UMat &to, std::vector<Point2f> &features,
|
||||
std::vector<Point2f> &predictedFeatures ) const
|
||||
{
|
||||
Size size = from.size();
|
||||
const unsigned maxFeatures = size.area() * sparseRate;
|
||||
goodFeaturesToTrack( from, features, maxFeatures * retainedCornersFraction, 0.005, 3 );
|
||||
|
||||
// Add points along the grid if not enough features
|
||||
if ( maxFeatures > features.size() )
|
||||
{
|
||||
const unsigned missingPoints = maxFeatures - features.size();
|
||||
const unsigned blockSize = sqrt( (float)size.area() / missingPoints );
|
||||
for ( int x = blockSize / 2; x < size.width; x += blockSize )
|
||||
for ( int y = blockSize / 2; y < size.height; y += blockSize )
|
||||
features.push_back( Point2f( x, y ) );
|
||||
}
|
||||
std::vector<uchar> predictedStatus;
|
||||
std::vector<float> predictedError;
|
||||
calcOpticalFlowPyrLK( from, to, features, predictedFeatures, predictedStatus, predictedError );
|
||||
|
||||
size_t j = 0;
|
||||
for ( size_t i = 0; i < features.size(); ++i )
|
||||
{
|
||||
if ( predictedStatus[i] )
|
||||
{
|
||||
features[j] = features[i];
|
||||
predictedFeatures[j] = predictedFeatures[i];
|
||||
++j;
|
||||
}
|
||||
}
|
||||
features.resize( j );
|
||||
predictedFeatures.resize( j );
|
||||
}
|
||||
|
||||
void OpticalFlowPCAFlow::removeOcclusions( UMat &from, UMat &to, std::vector<Point2f> &features,
|
||||
std::vector<Point2f> &predictedFeatures ) const
|
||||
{
|
||||
std::vector<uchar> predictedStatus;
|
||||
std::vector<float> predictedError;
|
||||
std::vector<Point2f> backwardFeatures;
|
||||
calcOpticalFlowPyrLK( to, from, predictedFeatures, backwardFeatures, predictedStatus, predictedError );
|
||||
|
||||
size_t j = 0;
|
||||
const float threshold = occlusionsThreshold * sqrt( static_cast<float>(from.size().area()) );
|
||||
for ( size_t i = 0; i < predictedFeatures.size(); ++i )
|
||||
{
|
||||
if ( predictedStatus[i] )
|
||||
{
|
||||
Point2f flowDiff = features[i] - backwardFeatures[i];
|
||||
if ( flowDiff.dot( flowDiff ) <= threshold )
|
||||
{
|
||||
features[j] = features[i];
|
||||
predictedFeatures[j] = predictedFeatures[i];
|
||||
++j;
|
||||
}
|
||||
}
|
||||
}
|
||||
features.resize( j );
|
||||
predictedFeatures.resize( j );
|
||||
}
|
||||
|
||||
void OpticalFlowPCAFlow::getSystem( OutputArray AOut, OutputArray b1Out, OutputArray b2Out,
|
||||
const std::vector<Point2f> &features, const std::vector<Point2f> &predictedFeatures,
|
||||
const Size size )
|
||||
{
|
||||
AOut.create( features.size(), basisSize.area(), CV_32F );
|
||||
b1Out.create( features.size(), 1, CV_32F );
|
||||
b2Out.create( features.size(), 1, CV_32F );
|
||||
if ( useOpenCL )
|
||||
{
|
||||
UMat A = AOut.getUMat();
|
||||
Mat b1 = b1Out.getMat();
|
||||
Mat b2 = b2Out.getMat();
|
||||
|
||||
ocl::Kernel kernel( "fillDCTSampledPoints", _ocl_fillDCTSampledPointsSource );
|
||||
CV_Assert(basisSize.width > 0 && basisSize.height > 0);
|
||||
size_t globSize[] = {features.size(), (size_t)basisSize.width, (size_t)basisSize.height};
|
||||
kernel
|
||||
.args( cv::ocl::KernelArg::ReadOnlyNoSize( Mat( features ).getUMat( ACCESS_READ ) ),
|
||||
cv::ocl::KernelArg::WriteOnlyNoSize( A ), (int)features.size(), (int)basisSize.width,
|
||||
(int)basisSize.height, (int)size.width, (int)size.height )
|
||||
.run( 3, globSize, 0, true );
|
||||
|
||||
for ( size_t i = 0; i < features.size(); ++i )
|
||||
{
|
||||
const Point2f flow = predictedFeatures[i] - features[i];
|
||||
b1.at<float>( i ) = flow.x;
|
||||
b2.at<float>( i ) = flow.y;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Mat A = AOut.getMat();
|
||||
Mat b1 = b1Out.getMat();
|
||||
Mat b2 = b2Out.getMat();
|
||||
|
||||
for ( size_t i = 0; i < features.size(); ++i )
|
||||
{
|
||||
_cpu_fillDCTSampledPoints( A.ptr<float>( i ), features[i], basisSize, size );
|
||||
const Point2f flow = predictedFeatures[i] - features[i];
|
||||
b1.at<float>( i ) = flow.x;
|
||||
b2.at<float>( i ) = flow.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpticalFlowPCAFlow::getSystem( OutputArray A1Out, OutputArray A2Out, OutputArray b1Out, OutputArray b2Out,
|
||||
const std::vector<Point2f> &features, const std::vector<Point2f> &predictedFeatures,
|
||||
const Size size )
|
||||
{
|
||||
CV_Assert( prior->getBasisSize() == basisSize.area() );
|
||||
|
||||
A1Out.create( features.size() + prior->getPadding(), basisSize.area(), CV_32F );
|
||||
A2Out.create( features.size() + prior->getPadding(), basisSize.area(), CV_32F );
|
||||
b1Out.create( features.size() + prior->getPadding(), 1, CV_32F );
|
||||
b2Out.create( features.size() + prior->getPadding(), 1, CV_32F );
|
||||
|
||||
if ( useOpenCL )
|
||||
{
|
||||
UMat A = A1Out.getUMat();
|
||||
Mat b1 = b1Out.getMat();
|
||||
Mat b2 = b2Out.getMat();
|
||||
|
||||
ocl::Kernel kernel( "fillDCTSampledPoints", _ocl_fillDCTSampledPointsSource );
|
||||
CV_Assert(basisSize.width > 0 && basisSize.height > 0);
|
||||
size_t globSize[] = {features.size(), (size_t)basisSize.width, (size_t)basisSize.height};
|
||||
kernel
|
||||
.args( cv::ocl::KernelArg::ReadOnlyNoSize( Mat( features ).getUMat( ACCESS_READ ) ),
|
||||
cv::ocl::KernelArg::WriteOnlyNoSize( A ), (int)features.size(), (int)basisSize.width,
|
||||
(int)basisSize.height, (int)size.width, (int)size.height )
|
||||
.run( 3, globSize, 0, true );
|
||||
|
||||
for ( size_t i = 0; i < features.size(); ++i )
|
||||
{
|
||||
const Point2f flow = predictedFeatures[i] - features[i];
|
||||
b1.at<float>( i ) = flow.x;
|
||||
b2.at<float>( i ) = flow.y;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Mat A1 = A1Out.getMat();
|
||||
Mat b1 = b1Out.getMat();
|
||||
Mat b2 = b2Out.getMat();
|
||||
|
||||
for ( size_t i = 0; i < features.size(); ++i )
|
||||
{
|
||||
_cpu_fillDCTSampledPoints( A1.ptr<float>( i ), features[i], basisSize, size );
|
||||
const Point2f flow = predictedFeatures[i] - features[i];
|
||||
b1.at<float>( i ) = flow.x;
|
||||
b2.at<float>( i ) = flow.y;
|
||||
}
|
||||
}
|
||||
|
||||
Mat A1 = A1Out.getMat();
|
||||
Mat A2 = A2Out.getMat();
|
||||
Mat b1 = b1Out.getMat();
|
||||
Mat b2 = b2Out.getMat();
|
||||
|
||||
memcpy( A2.ptr<float>(), A1.ptr<float>(), features.size() * basisSize.area() * sizeof( float ) );
|
||||
prior->fillConstraints( A1.ptr<float>( features.size(), 0 ), A2.ptr<float>( features.size(), 0 ),
|
||||
b1.ptr<float>( features.size(), 0 ), b2.ptr<float>( features.size(), 0 ) );
|
||||
}
|
||||
|
||||
void OpticalFlowPCAFlow::calc( InputArray I0, InputArray I1, InputOutputArray flowOut )
|
||||
{
|
||||
const Size size = I0.size();
|
||||
CV_Assert( size == I1.size() );
|
||||
|
||||
UMat from, to;
|
||||
if ( I0.channels() == 3 )
|
||||
{
|
||||
cvtColor( I0, from, COLOR_BGR2GRAY );
|
||||
from.convertTo( from, CV_8U );
|
||||
}
|
||||
else
|
||||
{
|
||||
I0.getMat().convertTo( from, CV_8U );
|
||||
}
|
||||
if ( I1.channels() == 3 )
|
||||
{
|
||||
cvtColor( I1, to, COLOR_BGR2GRAY );
|
||||
to.convertTo( to, CV_8U );
|
||||
}
|
||||
else
|
||||
{
|
||||
I1.getMat().convertTo( to, CV_8U );
|
||||
}
|
||||
|
||||
CV_Assert( from.channels() == 1 );
|
||||
CV_Assert( to.channels() == 1 );
|
||||
|
||||
const Mat fromOrig = from.getMat( ACCESS_READ ).clone();
|
||||
useOpenCL = flowOut.isUMat() && ocl::useOpenCL();
|
||||
|
||||
applyCLAHE( from, claheClip );
|
||||
applyCLAHE( to, claheClip );
|
||||
|
||||
std::vector<Point2f> features, predictedFeatures;
|
||||
findSparseFeatures( from, to, features, predictedFeatures );
|
||||
removeOcclusions( from, to, features, predictedFeatures );
|
||||
|
||||
flowOut.create( size, CV_32FC2 );
|
||||
Mat flow = flowOut.getMat();
|
||||
|
||||
Mat w1, w2;
|
||||
if ( prior.get() )
|
||||
{
|
||||
Mat A1, A2, b1, b2;
|
||||
getSystem( A1, A2, b1, b2, features, predictedFeatures, size );
|
||||
solveLSQR( A1, b1, w1, dampingFactor * size.area() );
|
||||
solveLSQR( A2, b2, w2, dampingFactor * size.area() );
|
||||
}
|
||||
else
|
||||
{
|
||||
Mat A, b1, b2;
|
||||
getSystem( A, b1, b2, features, predictedFeatures, size );
|
||||
solveLSQR( A, b1, w1, dampingFactor * size.area() );
|
||||
solveLSQR( A, b2, w2, dampingFactor * size.area() );
|
||||
}
|
||||
Mat flowSmall( ( size / 8 ) * 2, CV_32FC2 );
|
||||
reduceToFlow( w1, w2, flowSmall, basisSize );
|
||||
resize( flowSmall, flow, size, 0, 0, INTER_LINEAR );
|
||||
ximgproc::fastGlobalSmootherFilter( fromOrig, flow, flow, 500, 2 );
|
||||
}
|
||||
|
||||
OpticalFlowPCAFlow::OpticalFlowPCAFlow( Ptr<const PCAPrior> _prior, const Size _basisSize, float _sparseRate,
|
||||
float _retainedCornersFraction, float _occlusionsThreshold,
|
||||
float _dampingFactor, float _claheClip )
|
||||
: prior( _prior ), basisSize( _basisSize ), sparseRate( _sparseRate ),
|
||||
retainedCornersFraction( _retainedCornersFraction ), occlusionsThreshold( _occlusionsThreshold ),
|
||||
dampingFactor( _dampingFactor ), claheClip( _claheClip ), useOpenCL( false )
|
||||
{
|
||||
CV_Assert( sparseRate > 0 && sparseRate <= 0.1 );
|
||||
CV_Assert( retainedCornersFraction >= 0 && retainedCornersFraction <= 1.0 );
|
||||
CV_Assert( occlusionsThreshold > 0 );
|
||||
}
|
||||
|
||||
void OpticalFlowPCAFlow::collectGarbage() {}
|
||||
|
||||
Ptr<DenseOpticalFlow> createOptFlow_PCAFlow() { return makePtr<OpticalFlowPCAFlow>(); }
|
||||
|
||||
PCAPrior::PCAPrior( const char *pathToPrior )
|
||||
{
|
||||
FILE *f = fopen( pathToPrior, "rb" );
|
||||
CV_Assert( f );
|
||||
|
||||
unsigned n = 0, m = 0;
|
||||
CV_Assert( fread( &n, sizeof( n ), 1, f ) == 1 );
|
||||
CV_Assert( fread( &m, sizeof( m ), 1, f ) == 1 );
|
||||
|
||||
L1.create( n, m, CV_32F );
|
||||
L2.create( n, m, CV_32F );
|
||||
c1.create( n, 1, CV_32F );
|
||||
c2.create( n, 1, CV_32F );
|
||||
|
||||
CV_Assert( fread( L1.ptr<float>(), n * m * sizeof( float ), 1, f ) == 1 );
|
||||
CV_Assert( fread( L2.ptr<float>(), n * m * sizeof( float ), 1, f ) == 1 );
|
||||
CV_Assert( fread( c1.ptr<float>(), n * sizeof( float ), 1, f ) == 1 );
|
||||
CV_Assert( fread( c2.ptr<float>(), n * sizeof( float ), 1, f ) == 1 );
|
||||
|
||||
fclose( f );
|
||||
}
|
||||
|
||||
void PCAPrior::fillConstraints( float *A1, float *A2, float *b1, float *b2 ) const
|
||||
{
|
||||
memcpy( A1, L1.ptr<float>(), L1.size().area() * sizeof( float ) );
|
||||
memcpy( A2, L2.ptr<float>(), L2.size().area() * sizeof( float ) );
|
||||
memcpy( b1, c1.ptr<float>(), c1.size().area() * sizeof( float ) );
|
||||
memcpy( b2, c2.ptr<float>(), c2.size().area() * sizeof( float ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
|
||||
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
|
||||
(3-clause BSD License)
|
||||
|
||||
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:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions 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.
|
||||
|
||||
* Neither the names of the copyright holders nor the names of the contributors
|
||||
may 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 copyright holders 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.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __OPENCV_OPTFLOW_PRECOMP_HPP__
|
||||
#define __OPENCV_OPTFLOW_PRECOMP_HPP__
|
||||
|
||||
#include <opencv2/optflow.hpp>
|
||||
#include <opencv2/video.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/features.hpp>
|
||||
#include "opencv2/core/utility.hpp"
|
||||
#include "opencv2/core/private.hpp"
|
||||
#include "opencv2/core/ocl.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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.
|
||||
// This functions have been contributed by Jonas Geisters <geistert@nue.tu-berlin.de>
|
||||
|
||||
#include "../precomp.hpp"
|
||||
|
||||
#include "geo_interpolation.hpp"
|
||||
#include <string>
|
||||
#include <map>
|
||||
namespace cv {
|
||||
namespace optflow {
|
||||
|
||||
struct Graph_helper {
|
||||
std::vector<int> mem;
|
||||
int e_size;
|
||||
Graph_helper(int k, int num_nodes) {
|
||||
e_size = (2 * k + 1);
|
||||
mem.resize(e_size * num_nodes, 0);
|
||||
}
|
||||
inline int size(int id) {
|
||||
int r_addr = id * (e_size);
|
||||
return mem[r_addr];
|
||||
}
|
||||
inline int * data(int id) {
|
||||
int r_addr = id * (e_size)+1;
|
||||
return &mem[r_addr];
|
||||
}
|
||||
inline void add(int id, std::pair<float, int> data) {
|
||||
int r_addr = id * (e_size);
|
||||
int size = ++mem[r_addr];
|
||||
r_addr += 2 * size - 1;//== 1 + 2*(size-1);
|
||||
*(float*)&mem[r_addr] = data.first;
|
||||
mem[r_addr + 1] = data.second;
|
||||
}
|
||||
inline bool color_in_target(int id, int color) {
|
||||
int r_addr = id * (e_size);
|
||||
int size = mem[r_addr];
|
||||
r_addr += 2;
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (mem[r_addr] == color) {
|
||||
return true;
|
||||
}
|
||||
r_addr += 2;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Mat sgeo_dist(const Mat& gra, int y, int x, float max, Mat &prev)
|
||||
{
|
||||
std::vector <Point2f> points;
|
||||
points.push_back(Point2f(static_cast<float>(x), static_cast<float>(y)));
|
||||
return sgeo_dist(gra, points, max, prev);
|
||||
}
|
||||
Mat sgeo_dist(const Mat& gra, const std::vector<Point2f> & points, float max, Mat &prev)
|
||||
{
|
||||
int Dx[] = { -1,0,1,-1,1,-1,0,1 };
|
||||
int Dy[] = { -1,-1,-1,0,0,1,1,1 };
|
||||
Mat dm(gra.rows, gra.cols, CV_32F, Scalar(max));
|
||||
prev = Mat(gra.rows, gra.cols, CV_8U, Scalar(255));
|
||||
|
||||
std::multimap<float, Vec2i > not_visited_with_value;
|
||||
|
||||
for (auto i = points.begin(); i != points.end(); i++)
|
||||
{
|
||||
int y = static_cast<int>(i->y);
|
||||
int x = static_cast<int>(i->x);
|
||||
not_visited_with_value.insert(std::pair<float, Vec2i >(0.f, Vec2i(y, x)));
|
||||
dm.at<float>(y, x) = 0;
|
||||
}
|
||||
|
||||
bool done = false;
|
||||
while (!done)
|
||||
{
|
||||
if (not_visited_with_value.begin() == not_visited_with_value.end()) {
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
std::multimap<float, Vec2i >::iterator current_it = not_visited_with_value.begin();
|
||||
std::pair<float, Vec2i > current_p = *current_it;
|
||||
not_visited_with_value.erase(current_it);
|
||||
|
||||
int y = current_p.second[0];
|
||||
int x = current_p.second[1];
|
||||
float cur_d = current_p.first;
|
||||
|
||||
if (dm.at<float>(y, x) != cur_d) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Vec8f gra_e = gra.at<Vec8f>(y, x);
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (gra_e[i] < 0) {
|
||||
continue;
|
||||
}
|
||||
int dx = Dx[i];
|
||||
int dy = Dy[i];
|
||||
|
||||
if (dm.at<float>(y + dy, x + dx) > cur_d + gra_e[i]) {
|
||||
dm.at<float>(y + dy, x + dx) = cur_d + gra_e[i];
|
||||
prev.at<uchar>(y + dy, x + dx) = static_cast<uchar>(7 - i);
|
||||
not_visited_with_value.insert(std::pair<float, Vec2i >(cur_d + gra_e[i], Vec2i(y + dy, x + dx)));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return dm;
|
||||
}
|
||||
|
||||
Mat interpolate_irregular_nn_raster(const std::vector<Point2f> & prevPoints,
|
||||
const std::vector<Point2f> & nextPoints,
|
||||
const std::vector<uchar> & status,
|
||||
const Mat & i1)
|
||||
{
|
||||
Mat gra = getGraph(i1, 0.1f);
|
||||
int Dx[] = { -1,0,1,-1,1,-1,0,1 };
|
||||
int Dy[] = { -1,-1,-1,0,0,1,1,1 };
|
||||
int max_rounds = 10;
|
||||
Mat dirt = Mat(gra.rows, gra.cols, CV_8U, Scalar(0));
|
||||
Mat quellknoten = Mat(gra.rows, gra.cols, CV_32S, Scalar(-1));
|
||||
Mat dist = Mat(gra.rows, gra.cols, CV_32F, Scalar(std::numeric_limits<float>::max()));
|
||||
/*
|
||||
* assign quellknoten ids.
|
||||
*/
|
||||
for (int i = 0; i < static_cast<int>(prevPoints.size()); i++)
|
||||
{
|
||||
int x = (int)prevPoints[i].x;
|
||||
int y = (int)prevPoints[i].y;
|
||||
if (status[i] == 0)
|
||||
continue;
|
||||
dirt.at<uchar>(y, x) = 1;
|
||||
dist.at<float>(y, x) = 0;
|
||||
quellknoten.at<int>(y, x) = i;
|
||||
}
|
||||
|
||||
bool clean = true;
|
||||
bool done = false;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int rounds = 0;
|
||||
while (!done) {
|
||||
/*
|
||||
* Update x and y
|
||||
* on even rounds go rasterscanorder , on odd round inverse rasterscanorder
|
||||
*/
|
||||
if (rounds % 2 == 0) {
|
||||
x++;
|
||||
if (x >= gra.cols) {
|
||||
x = 0;
|
||||
y++;
|
||||
if (y >= gra.rows) {
|
||||
y = 0;
|
||||
rounds++;
|
||||
y = gra.rows - 1;
|
||||
x = gra.cols - 1;
|
||||
if (rounds >= max_rounds || clean) {
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
x--;
|
||||
if (x < 0) {
|
||||
x = gra.cols - 1;
|
||||
y--;
|
||||
if (y < 0) {
|
||||
y = gra.rows - 1;
|
||||
rounds++;
|
||||
y = 0;
|
||||
x = 0;
|
||||
if (rounds >= max_rounds || clean) {
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dirt.at<uchar>(y, x) == 0) {
|
||||
continue;
|
||||
}
|
||||
dirt.at<uchar>(y, x) = 0;
|
||||
|
||||
float c_dist = dist.at<float>(y, x);
|
||||
Vec8f gra_e = gra.at<Vec8f>(y, x);
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int tx = Dx[i];
|
||||
int ty = Dy[i];
|
||||
if (ty == 0 && tx == 0) {
|
||||
continue;
|
||||
}
|
||||
if (x + tx < 0 || x + tx >= gra.cols) {
|
||||
continue;
|
||||
}
|
||||
if (y + ty < 0 || y + ty >= gra.rows) {
|
||||
continue;
|
||||
}
|
||||
if (c_dist > dist.at<float>(y + ty, x + tx)) {
|
||||
if (c_dist > dist.at<float>(y + ty, x + tx) + gra_e[i]) {
|
||||
quellknoten.at<int>(y, x) = quellknoten.at<int>(y + ty, x + tx);
|
||||
dist.at<float>(y, x) = dist.at<float>(y + ty, x + tx) + gra_e[i];
|
||||
dirt.at<uchar>(y, x) = 1;
|
||||
clean = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (c_dist + gra_e[i] < dist.at<float>(y + ty, x + tx)) {
|
||||
quellknoten.at<int>(y + ty, x + tx) = quellknoten.at<int>(y, x);
|
||||
dist.at<float>(y + ty, x + tx) = dist.at<float>(y, x) + gra_e[i];
|
||||
dirt.at<uchar>(y + ty, x + tx) = 1;
|
||||
clean = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
Mat nnFlow(i1.rows, i1.cols, CV_32FC2, Scalar(0));
|
||||
for (y = 0; y < i1.rows; y++) {
|
||||
for (x = 0; x < i1.cols; x++) {
|
||||
|
||||
int id = quellknoten.at<int>(y, x);
|
||||
if (id != -1)
|
||||
{
|
||||
nnFlow.at<Point2f>(y, x) = nextPoints[id] - prevPoints[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
return nnFlow;
|
||||
}
|
||||
|
||||
Mat interpolate_irregular_knn(
|
||||
const std::vector<Point2f> & _prevPoints,
|
||||
const std::vector<Point2f> & _nextPoints,
|
||||
const std::vector<uchar> & status,
|
||||
const Mat &color_img,
|
||||
int k,
|
||||
float pixeldistance)
|
||||
{
|
||||
Mat in(color_img.rows, color_img.cols, CV_32FC2);
|
||||
Mat mask = Mat::zeros(color_img.rows, color_img.cols, CV_8UC1);
|
||||
|
||||
for (unsigned n = 0; n < _prevPoints.size(); n++)
|
||||
{
|
||||
if (_prevPoints[n].x >= 0 && _prevPoints[n].y >= 0 && _prevPoints[n].x < color_img.cols && _prevPoints[n].y < color_img.rows)
|
||||
{
|
||||
in.at<Point2f>(_prevPoints[n]) = _nextPoints[n] - _prevPoints[n];
|
||||
mask.at<uchar>(_prevPoints[n]) = status[n];
|
||||
}
|
||||
|
||||
}
|
||||
int Dx[] = { -1,0,1,-1,1,-1,0,1 };
|
||||
int Dy[] = { -1,-1,-1,0,0,1,1,1 };
|
||||
Mat gra = getGraph(color_img, pixeldistance);
|
||||
Mat nnFlow(in.rows, in.cols, CV_32FC2, Scalar(0));
|
||||
|
||||
std::multimap<float, Vec2i > my_agents; // <arrivaltim , < target, color >>
|
||||
Graph_helper graph_helper(k, in.rows*in.cols); //< arrivaltime, color>
|
||||
|
||||
|
||||
int color = 0;
|
||||
std::vector<Vec2i> flow_point_list;
|
||||
for (int y = 0; y < in.rows; y++) {
|
||||
for (int x = 0; x < in.cols; x++) {
|
||||
if (mask.at<uchar>(y, x) > 0) {
|
||||
flow_point_list.push_back(Vec2i(y, x));
|
||||
nnFlow.at<Vec2f>(y, x) = in.at<Vec2f>(y, x);
|
||||
|
||||
int v_id = (y * in.cols + x);
|
||||
graph_helper.add(v_id, std::pair<float, int>(0.f, color));
|
||||
|
||||
|
||||
Vec8f gra_e = gra.at<Vec8f>(y, x);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (gra_e[i] < 0)
|
||||
continue;
|
||||
int dx = Dx[i];
|
||||
int dy = Dy[i];
|
||||
int target = (y + dy) * in.cols + (x + dx);
|
||||
Vec2i agent(target, color);
|
||||
my_agents.insert(std::pair<float, Vec2i >(gra_e[i], agent));
|
||||
|
||||
}
|
||||
color++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
if (my_agents.size() == 0) {
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
|
||||
std::multimap<float, Vec2i >::iterator current_it = my_agents.begin();
|
||||
std::pair<float, Vec2i > current_p = *current_it;
|
||||
my_agents.erase(current_it);
|
||||
|
||||
int target = current_p.second[0];
|
||||
color = current_p.second[1];
|
||||
float arriv_time = current_p.first;
|
||||
|
||||
Vec8f gra_e = gra.at<Vec8f>(target);// (y*cols+x)
|
||||
if (graph_helper.size(target) >= k) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool color_found_in_target = graph_helper.color_in_target(target, color);
|
||||
if (color_found_in_target) {
|
||||
continue;
|
||||
}
|
||||
graph_helper.add(target, std::pair<float, int>(arriv_time, color));
|
||||
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (gra_e[i] < 0)
|
||||
continue;
|
||||
|
||||
int dx = Dx[i];
|
||||
int dy = Dy[i];
|
||||
int new_target = target + dx + (dy*in.cols);
|
||||
if (graph_helper.size(new_target) >= k) {
|
||||
continue;
|
||||
}
|
||||
color_found_in_target = graph_helper.color_in_target(new_target, color);
|
||||
if (color_found_in_target) {
|
||||
continue;
|
||||
}
|
||||
Vec2i new_agent(new_target, color);
|
||||
my_agents.insert(std::pair<float, Vec2i >(arriv_time + gra_e[i], new_agent));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Mat ret(in.rows, in.cols*k, CV_32FC2);
|
||||
for (int y = 0; y < in.rows; y++) {
|
||||
for (int x = 0; x < in.cols; x++) {
|
||||
for (int i = 0; i < k; i++) {
|
||||
float dist = *((float*)(graph_helper.data(y*in.cols + x) + 2 * i));
|
||||
float id = *((float*)(graph_helper.data(y*in.cols + x) + 2 * i + 1));
|
||||
ret.at<Vec2f>(y, k*x + i) = Vec2f(dist, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Mat getGraph(const Mat &image, float edge_length)
|
||||
{
|
||||
|
||||
int Dx[] = { -1,0,1,-1,1,-1,0,1 };
|
||||
int Dy[] = { -1,-1,-1,0,0,1,1,1 };
|
||||
Mat gra(image.rows, image.cols, CV_32FC(8));
|
||||
|
||||
for (int y = 0; y < gra.rows; y++) {
|
||||
for (int x = 0; x < gra.cols; x++) {
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int dx = Dx[i];
|
||||
int dy = Dy[i];
|
||||
gra.at<Vec8f>(y, x)[i] = -1;
|
||||
|
||||
if (x + dx < 0 || y + dy < 0 || x + dx >= gra.cols || y + dy >= gra.rows) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i < 4) {
|
||||
int si = 7 - i;
|
||||
gra.at<Vec8f>(y, x)[i] = gra.at<Vec8f>(y + dy, x + dx)[si];
|
||||
}
|
||||
else {
|
||||
float p1 = dx * dx*edge_length*edge_length + dy * dy*edge_length*edge_length;
|
||||
float p2 = static_cast<float>(image.at<Vec3b>(y, x)[0] - image.at<Vec3b>(y + dy, x + dx)[0]);
|
||||
float p3 = static_cast<float>(image.at<Vec3b>(y, x)[1] - image.at<Vec3b>(y + dy, x + dx)[1]);
|
||||
float p4 = static_cast<float>(image.at<Vec3b>(y, x)[2] - image.at<Vec3b>(y + dy, x + dx)[2]);
|
||||
gra.at<Vec8f>(y, x)[i] = sqrt(p1 + p2 * p2 + p3 * p3 + p4 * p4);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return gra;
|
||||
}
|
||||
|
||||
|
||||
Mat interpolate_irregular_nn(
|
||||
const std::vector<Point2f> & _prevPoints,
|
||||
const std::vector<Point2f> & _nextPoints,
|
||||
const std::vector<uchar> & status,
|
||||
const Mat &color_img,
|
||||
float pixeldistance)
|
||||
{
|
||||
int Dx[] = { -1,0,1,-1,1,-1,0,1 };
|
||||
int Dy[] = { -1,-1,-1,0,0,1,1,1 };
|
||||
std::vector<Point2f> prevPoints, nextPoints;
|
||||
std::map<std::pair<float, float>, std::pair<float, float>> flowMap;
|
||||
for (unsigned n = 0; n < _prevPoints.size(); n++)
|
||||
{
|
||||
if (status[n] != 0)
|
||||
{
|
||||
flowMap.insert(std::make_pair(
|
||||
std::make_pair(_prevPoints[n].x, _prevPoints[n].y),
|
||||
std::make_pair(_nextPoints[n].x, _nextPoints[n].y)));
|
||||
prevPoints.push_back(_prevPoints[n]);
|
||||
nextPoints.push_back(_nextPoints[n]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Mat gra = getGraph(color_img, pixeldistance);
|
||||
|
||||
Mat prev;
|
||||
Mat geo_dist = sgeo_dist(gra, prevPoints, std::numeric_limits<float>::max(), prev);
|
||||
|
||||
|
||||
Mat nnFlow = Mat::zeros(color_img.size(), CV_32FC2);
|
||||
for (int y = 0; y < nnFlow.rows; y++)
|
||||
{
|
||||
for (int x = 0; x < nnFlow.cols; x++)
|
||||
{
|
||||
int cx = x;
|
||||
int cy = y;
|
||||
while (prev.at<uchar>(cy, cx) != 255)
|
||||
{
|
||||
int i = prev.at<uchar>(cy, cx);
|
||||
cx += Dx[i];
|
||||
cy += Dy[i];
|
||||
}
|
||||
auto val = flowMap[std::make_pair(static_cast<float>(cx), static_cast<float>(cy))];
|
||||
nnFlow.at<Vec2f>(y, x) = Vec2f(val.first - cx, val.second - cy);
|
||||
}
|
||||
}
|
||||
return nnFlow;
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,35 @@
|
||||
// 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 _GEO_INTERPOLATION_HPP_
|
||||
#define _GEO_INTERPOLATION_HPP_
|
||||
|
||||
namespace cv {
|
||||
namespace optflow {
|
||||
|
||||
typedef Vec<float, 8> Vec8f;
|
||||
Mat getGraph(const Mat & image, float edge_length);
|
||||
Mat sgeo_dist(const Mat& gra, int y, int x, float max, Mat &prev);
|
||||
Mat sgeo_dist(const Mat& gra, const std::vector<Point2f> & points, float max, Mat &prev);
|
||||
Mat interpolate_irregular_nw(const Mat &in, const Mat &mask, const Mat &color_img, float max_d, float bandwidth, float pixeldistance);
|
||||
Mat interpolate_irregular_nn(
|
||||
const std::vector<Point2f> & prevPoints,
|
||||
const std::vector<Point2f> & nextPoints,
|
||||
const std::vector<uchar> & status,
|
||||
const Mat &color_img,
|
||||
float pixeldistance);
|
||||
Mat interpolate_irregular_knn(
|
||||
const std::vector<Point2f> & _prevPoints,
|
||||
const std::vector<Point2f> & _nextPoints,
|
||||
const std::vector<uchar> & status,
|
||||
const Mat &color_img,
|
||||
int k,
|
||||
float pixeldistance);
|
||||
|
||||
Mat interpolate_irregular_nn_raster(const std::vector<Point2f> & prevPoints,
|
||||
const std::vector<Point2f> & nextPoints,
|
||||
const std::vector<uchar> & status,
|
||||
const Mat & i1);
|
||||
|
||||
}} // namespace
|
||||
#endif
|
||||
@@ -0,0 +1,794 @@
|
||||
// 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 _PLK_INVOKER_HPP_
|
||||
#define _PLK_INVOKER_HPP_
|
||||
#include "rlof_invokerbase.hpp"
|
||||
|
||||
|
||||
namespace cv {
|
||||
namespace optflow {
|
||||
namespace plk {
|
||||
|
||||
// implementierung ohne SSE
|
||||
namespace radial {
|
||||
|
||||
class TrackerInvoker : public cv::ParallelLoopBody
|
||||
{
|
||||
public:
|
||||
TrackerInvoker(
|
||||
const Mat& _prevImg,
|
||||
const Mat& _prevDeriv,
|
||||
const Mat& _nextImg,
|
||||
const Mat& _rgbPrevImg,
|
||||
const Mat& _rgbNextImg,
|
||||
const Point2f* _prevPts,
|
||||
Point2f* _nextPts,
|
||||
uchar* _status,
|
||||
float* _err,
|
||||
Point2f* _gainVecs,
|
||||
int _level,
|
||||
int _maxLevel,
|
||||
int _winSize[2],
|
||||
int _maxIteration,
|
||||
bool _useInitialFlow,
|
||||
int _supportRegionType,
|
||||
float _minEigenValue,
|
||||
int _crossSegmentationThreshold)
|
||||
{
|
||||
prevImg = &_prevImg;
|
||||
prevDeriv = &_prevDeriv;
|
||||
nextImg = &_nextImg;
|
||||
rgbPrevImg = &_rgbPrevImg;
|
||||
rgbNextImg = &_rgbNextImg;
|
||||
prevPts = _prevPts;
|
||||
nextPts = _nextPts;
|
||||
status = _status;
|
||||
err = _err;
|
||||
gainVecs = _gainVecs;
|
||||
minWinSize = _winSize[0];
|
||||
maxWinSize = _winSize[1];
|
||||
criteria.maxCount = _maxIteration;
|
||||
criteria.epsilon = 0.01;
|
||||
level = _level;
|
||||
maxLevel = _maxLevel;
|
||||
windowType = _supportRegionType;
|
||||
minEigThreshold = _minEigenValue;
|
||||
useInitialFlow = _useInitialFlow;
|
||||
crossSegmentationThreshold = _crossSegmentationThreshold;
|
||||
}
|
||||
|
||||
void operator()(const cv::Range& range) const CV_OVERRIDE
|
||||
{
|
||||
cv::Size winSize;
|
||||
cv::Point2f halfWin;
|
||||
|
||||
const Mat& I = *prevImg;
|
||||
const Mat& J = *nextImg;
|
||||
const Mat& derivI = *prevDeriv;
|
||||
const Mat& BI = *rgbPrevImg;
|
||||
|
||||
winSize = cv::Size(maxWinSize,maxWinSize);
|
||||
int winMaskwidth = roundUp(winSize.width, 16);
|
||||
cv::Mat winMaskMatBuf(winMaskwidth, winMaskwidth, tCVMaskType);
|
||||
winMaskMatBuf.setTo(1);
|
||||
const float FLT_SCALE = (1.f/(1 << 16));
|
||||
|
||||
int cn = I.channels(), cn2 = cn*2;
|
||||
int winbufwidth = roundUp(winSize.width, 16);
|
||||
cv::Size winBufSize(winbufwidth,winbufwidth);
|
||||
|
||||
cv::Matx44f invTensorMat;
|
||||
Vec4f mismatchMat;
|
||||
Vec4f resultMat;
|
||||
|
||||
cv::AutoBuffer<deriv_type> _buf(winBufSize.area()*(cn + cn2));
|
||||
int derivDepth = DataType<deriv_type>::depth;
|
||||
|
||||
Mat IWinBuf(winBufSize, CV_MAKETYPE(derivDepth, cn), (deriv_type*)_buf.data());
|
||||
Mat derivIWinBuf(winBufSize, CV_MAKETYPE(derivDepth, cn2), (deriv_type*)_buf.data() + winBufSize.area()*cn);
|
||||
|
||||
for( int ptidx = range.start; ptidx < range.end; ptidx++ )
|
||||
{
|
||||
Point2f prevPt = prevPts[ptidx]*(float)(1./(1 << level));
|
||||
Point2f nextPt;
|
||||
if( level == maxLevel )
|
||||
{
|
||||
if( useInitialFlow )
|
||||
{
|
||||
nextPt = nextPts[ptidx]*(float)(1./(1 << level));
|
||||
}
|
||||
else
|
||||
nextPt = prevPt;
|
||||
}
|
||||
else
|
||||
nextPt = nextPts[ptidx]*2.f;
|
||||
nextPts[ptidx] = nextPt;
|
||||
|
||||
Point2i iprevPt, inextPt;
|
||||
iprevPt.x = cvFloor(prevPt.x);
|
||||
iprevPt.y = cvFloor(prevPt.y);
|
||||
int winArea = maxWinSize * maxWinSize;
|
||||
cv::Mat winMaskMat(winMaskMatBuf, cv::Rect(0,0, maxWinSize,maxWinSize));
|
||||
winMaskMatBuf.setTo(0);
|
||||
if( calcWinMaskMat(BI, windowType, iprevPt,
|
||||
winMaskMat,winSize,halfWin,winArea,
|
||||
minWinSize,maxWinSize) == false)
|
||||
continue;
|
||||
halfWin = Point2f(static_cast<float>(maxWinSize) ,static_cast<float>(maxWinSize) ) - halfWin;
|
||||
prevPt += halfWin;
|
||||
iprevPt.x = cvFloor(prevPt.x);
|
||||
iprevPt.y = cvFloor(prevPt.y);
|
||||
|
||||
|
||||
if( iprevPt.x < 0 || iprevPt.x >= derivI.cols - winSize.width ||
|
||||
iprevPt.y < 0 || iprevPt.y >= derivI.rows - winSize.height - 1)
|
||||
{
|
||||
if( level == 0 )
|
||||
{
|
||||
if( status )
|
||||
status[ptidx] = 3;
|
||||
if( err )
|
||||
err[ptidx] = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
float a = prevPt.x - iprevPt.x;
|
||||
float b = prevPt.y - iprevPt.y;
|
||||
const int W_BITS = 14;
|
||||
int iw00 = cvRound((1.f - a)*(1.f - b)*(1 << W_BITS));
|
||||
int iw01 = cvRound(a*(1.f - b)*(1 << W_BITS));
|
||||
int iw10 = cvRound((1.f - a)*b*(1 << W_BITS));
|
||||
int iw11 = (1 << W_BITS) - iw00 - iw01 - iw10;
|
||||
float A11 = 0, A12 = 0, A22 = 0;
|
||||
// tensor
|
||||
float sumIx = 0;
|
||||
float sumIy = 0;
|
||||
float sumI = 0;
|
||||
float sumW = 0;
|
||||
float w1 = 0, w2 = 0; // -IyI
|
||||
float dI = 0; // I^2
|
||||
float D = 0;
|
||||
|
||||
copyWinBuffers(iw00, iw01, iw10, iw11, winSize, I, derivI, winMaskMat, IWinBuf, derivIWinBuf, iprevPt);
|
||||
|
||||
cv::Point2f backUpNextPt = nextPt;
|
||||
nextPt += halfWin;
|
||||
Point2f prevDelta(0,0); //relates to h(t-1)
|
||||
Point2f prevGain(1,0);
|
||||
cv::Point2f gainVec = gainVecs[ptidx];
|
||||
cv::Point2f backUpGain = gainVec;
|
||||
int j;
|
||||
for( j = 0; j < criteria.maxCount; j++ )
|
||||
{
|
||||
status[ptidx] = static_cast<uchar>(j);
|
||||
inextPt.x = cvFloor(nextPt.x);
|
||||
inextPt.y = cvFloor(nextPt.y);
|
||||
|
||||
if( inextPt.x < 0 || inextPt.x >= J.cols - winSize.width ||
|
||||
inextPt.y < 0 || inextPt.y >= J.rows - winSize.height - 1)
|
||||
{
|
||||
if( level == 0 && status )
|
||||
status[ptidx] = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
a = nextPt.x - inextPt.x;
|
||||
b = nextPt.y - inextPt.y;
|
||||
iw00 = cvRound((1.f - a)*(1.f - b)*(1 << W_BITS));
|
||||
iw01 = cvRound(a*(1.f - b)*(1 << W_BITS));
|
||||
iw10 = cvRound((1.f - a)*b*(1 << W_BITS));
|
||||
iw11 = (1 << W_BITS) - iw00 - iw01 - iw10;
|
||||
float b1,b2,b3,b4;
|
||||
b1 = 0;
|
||||
b2 = 0;
|
||||
b3 = 0;
|
||||
b4 = 0;
|
||||
|
||||
if( j == 0 )
|
||||
{
|
||||
// tensor
|
||||
w1 = 0; // -IxI
|
||||
w2 = 0; // -IyI
|
||||
dI = 0; // I^2
|
||||
sumIx = 0;
|
||||
sumIy = 0;
|
||||
sumI = 0;
|
||||
sumW = 0;
|
||||
A11 = 0;
|
||||
A12 = 0;
|
||||
A22 = 0;
|
||||
}
|
||||
#if CV_SIMD128
|
||||
v_int16x8 vqw0 = v_int16x8((short)(iw00), (short)(iw01), (short)(iw00), (short)(iw01), (short)(iw00), (short)(iw01), (short)(iw00), (short)(iw01));
|
||||
v_int16x8 vqw1 = v_int16x8((short)(iw10), (short)(iw11), (short)(iw10), (short)(iw11), (short)(iw10), (short)(iw11), (short)(iw10), (short)(iw11));
|
||||
v_float32x4 vqb0 = v_setzero_f32(), vqb1 = v_setzero_f32(), vqb2 = v_setzero_f32(), vqb3 = v_setzero_f32();
|
||||
v_float32x4 vsumW1 = v_setzero_f32(), vsumW2 = v_setzero_f32();
|
||||
v_float32x4 vsumIy = v_setzero_f32(), vsumIx = v_setzero_f32(), vsumI = v_setzero_f32(), vsumDI = v_setzero_f32();
|
||||
v_float32x4 vAxx = v_setzero_f32(), vAxy = v_setzero_f32(), vAyy = v_setzero_f32();
|
||||
v_int32x4 vdelta = v_setall_s32(1 << (W_BITS - 5 - 1));
|
||||
v_int16x8 vmax_val_16 = v_setall_s16(std::numeric_limits<unsigned short>::max());
|
||||
float gainVal = gainVec.x > 0 ? gainVec.x : -gainVec.x;
|
||||
int bitShift = gainVec.x == 0 ? 1 : cvCeil(log(200.f / gainVal) / log(2.f));
|
||||
v_int16x8 vgain_value = v_setall_s16(static_cast<short>(gainVec.x * (float)(1 << bitShift)));
|
||||
v_int16x8 vconst_value = v_setall_s16(static_cast<short>(gainVec.y));
|
||||
#endif
|
||||
for(int y = 0; y < winSize.height; y++ )
|
||||
{
|
||||
const uchar* Jptr = J.ptr<uchar>(y + inextPt.y, inextPt.x*cn);
|
||||
const uchar* Jptr1 = J.ptr<uchar>(y + inextPt.y + 1, inextPt.x*cn);
|
||||
const short* Iptr = IWinBuf.ptr<short>(y, 0);
|
||||
const short* dIptr = derivIWinBuf.ptr<short>(y, 0);
|
||||
const tMaskType* maskPtr = winMaskMat.ptr<tMaskType>(y, 0);
|
||||
#if CV_SIMD128
|
||||
for(int x = 0 ; x <= winSize.width*cn; x += 8, dIptr += 8*2 )
|
||||
{
|
||||
v_int16x8 vI = v_reinterpret_as_s16(v_load(Iptr + x)), diff0, diff1, diff2;
|
||||
v_int16x8 v00 = v_reinterpret_as_s16(v_load_expand(Jptr + x));
|
||||
v_int16x8 v01 = v_reinterpret_as_s16(v_load_expand(Jptr + x + cn));
|
||||
v_int16x8 v10 = v_reinterpret_as_s16(v_load_expand(Jptr1 + x));
|
||||
v_int16x8 v11 = v_reinterpret_as_s16(v_load_expand(Jptr1 + x + cn));
|
||||
v_int16x8 vmask = v_mul(v_reinterpret_as_s16(v_load_expand(maskPtr + x)), vmax_val_16);
|
||||
|
||||
v_int32x4 t0, t1;
|
||||
v_int16x8 t00, t01, t10, t11;
|
||||
v_zip(v00, v01, t00, t01);
|
||||
v_zip(v10, v11, t10, t11);
|
||||
|
||||
//subpixel interpolation
|
||||
t0 = v_add(v_dotprod(t00, vqw0, vdelta), v_dotprod(t10, vqw1));
|
||||
t1 = v_add(v_dotprod(t01, vqw0, vdelta), v_dotprod(t11, vqw1));
|
||||
t0 = v_shr(t0, W_BITS - 5);
|
||||
t1 = v_shr(t1, W_BITS - 5);
|
||||
|
||||
// diff = J - I
|
||||
diff0 = v_sub(v_pack(t0, t1), vI);
|
||||
// I*gain.x + gain.x
|
||||
v_mul_expand(vI, vgain_value, t0, t1);
|
||||
diff0 = v_add(v_add(diff0, v_pack(v_shr(t0, bitShift), v_shr(t1, bitShift))), vconst_value);
|
||||
diff0 = v_and(diff0, vmask);
|
||||
v_zip(diff0, diff0, diff2, diff1);
|
||||
|
||||
v_int32x4 diff0_0;
|
||||
v_int32x4 diff0_1;
|
||||
v_expand(diff0, diff0_0, diff0_1);
|
||||
|
||||
v_int16x8 vIxy_0 = v_reinterpret_as_s16(v_load(dIptr)); // Ix0 Iy0 Ix1 Iy1 ...
|
||||
v_int16x8 vIxy_1 = v_reinterpret_as_s16(v_load(dIptr + 8));
|
||||
v_zip(vIxy_0, vIxy_1, v10, v11);
|
||||
v_zip(diff2, diff1, v00, v01);
|
||||
|
||||
vqb0 = v_add(vqb0, v_cvt_f32(v_dotprod(v00, v10)));
|
||||
vqb1 = v_add(vqb1, v_cvt_f32(v_dotprod(v01, v11)));
|
||||
|
||||
v_int32x4 vI0, vI1;
|
||||
v_expand(vI, vI0, vI1);
|
||||
vqb2 = v_add(vqb2, v_cvt_f32(v_mul(diff0_0, vI0)));
|
||||
vqb2 = v_add(vqb2, v_cvt_f32(v_mul(diff0_1, vI1)));
|
||||
|
||||
vqb3 = v_add(vqb3, v_cvt_f32(diff0_0));
|
||||
vqb3 = v_add(vqb3, v_cvt_f32(diff0_1));
|
||||
|
||||
if (j == 0)
|
||||
{
|
||||
v00 = v_reinterpret_as_s16(v_interleave_pairs(v_reinterpret_as_s32(v_interleave_pairs(vIxy_0))));
|
||||
v_expand(v00, t1, t0);
|
||||
|
||||
v_float32x4 vI_ps = v_cvt_f32(vI0);
|
||||
|
||||
v_float32x4 fy = v_cvt_f32(t0);
|
||||
v_float32x4 fx = v_cvt_f32(t1);
|
||||
|
||||
vAyy = v_muladd(fy, fy, vAyy);
|
||||
vAxy = v_muladd(fx, fy, vAxy);
|
||||
vAxx = v_muladd(fx, fx, vAxx);
|
||||
|
||||
// sumIx und sumIy
|
||||
vsumIx = v_add(vsumIx, fx);
|
||||
vsumIy = v_add(vsumIy, fy);
|
||||
|
||||
vsumW1 = v_add(vsumW1, v_mul(vI_ps, fx));
|
||||
vsumW2 = v_add(vsumW2, v_mul(vI_ps, fy));
|
||||
|
||||
// sumI
|
||||
vsumI = v_add(vsumI, vI_ps);
|
||||
|
||||
// sumDI
|
||||
vsumDI = v_add(vsumDI, v_mul(vI_ps, vI_ps));
|
||||
|
||||
v01 = v_reinterpret_as_s16(v_interleave_pairs(v_reinterpret_as_s32(v_interleave_pairs(vIxy_1))));
|
||||
v_expand(v01, t1, t0);
|
||||
vI_ps = v_cvt_f32(vI1);
|
||||
|
||||
fy = v_cvt_f32(t0);
|
||||
fx = v_cvt_f32(t1);
|
||||
|
||||
vAyy = v_muladd(fy, fy, vAyy);
|
||||
vAxy = v_muladd(fx, fy, vAxy);
|
||||
vAxx = v_muladd(fx, fx, vAxx);
|
||||
|
||||
// sumIx und sumIy
|
||||
vsumIx = v_add(vsumIx, fx);
|
||||
vsumIy = v_add(vsumIy, fy);
|
||||
|
||||
vsumW1 = v_add(vsumW1, v_mul(vI_ps, fx));
|
||||
vsumW2 = v_add(vsumW2, v_mul(vI_ps, fy));
|
||||
|
||||
// sumI
|
||||
vsumI = v_add(vsumI, vI_ps);
|
||||
|
||||
// sumDI
|
||||
vsumDI = v_add(vsumDI, v_mul(vI_ps, vI_ps));
|
||||
}
|
||||
}
|
||||
#else
|
||||
for(int x = 0 ; x < winSize.width*cn; x++, dIptr += 2 )
|
||||
{
|
||||
if( maskPtr[x] == 0)
|
||||
continue;
|
||||
int J_val = CV_DESCALE(Jptr[x]*iw00 + Jptr[x+cn]*iw01 +
|
||||
Jptr1[x]*iw10 + Jptr1[x+cn]*iw11,
|
||||
W_BITS-5);
|
||||
int diff = static_cast<int>(J_val - Iptr[x] + Iptr[x] * gainVec.x + gainVec.y);
|
||||
|
||||
b1 += (float)(diff*dIptr[0]) * FLT_RESCALE;
|
||||
b2 += (float)(diff*dIptr[1]) * FLT_RESCALE;
|
||||
b3 += (float)(diff) * Iptr[x] * FLT_RESCALE;
|
||||
b4 += (float)(diff);
|
||||
|
||||
// compute the Gradient Matrice
|
||||
if( j == 0 )
|
||||
{
|
||||
A11 += (float)(dIptr[0]*dIptr[0]);
|
||||
A12 += (float)(dIptr[0]*dIptr[1]);
|
||||
A22 += (float)(dIptr[1]*dIptr[1]);
|
||||
|
||||
|
||||
dI += Iptr[x] * Iptr[x] * FLT_RESCALE;
|
||||
float dx = static_cast<float>(dIptr[0]) * FLT_RESCALE;
|
||||
float dy = static_cast<float>(dIptr[1]) * FLT_RESCALE;
|
||||
sumIx += dx;
|
||||
sumIy += dy;
|
||||
w1 += dx * Iptr[x];
|
||||
w2 += dy * Iptr[x];
|
||||
sumI += Iptr[x] * FLT_RESCALE;
|
||||
//sumW += FLT_RESCALE;
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if( j == 0 )
|
||||
{
|
||||
#if CV_SIMD128
|
||||
|
||||
w1 = v_reduce_sum(vsumW1);
|
||||
w2 = v_reduce_sum(vsumW2);
|
||||
dI = v_reduce_sum(vsumDI);
|
||||
sumI = v_reduce_sum(vsumI);
|
||||
sumIx = v_reduce_sum(vsumIx);
|
||||
sumIy = v_reduce_sum(vsumIy);
|
||||
A11 = v_reduce_sum(vAxx);
|
||||
A12 = v_reduce_sum(vAxy);
|
||||
A22 = v_reduce_sum(vAyy);
|
||||
#endif
|
||||
sumIx *= -FLT_SCALE;
|
||||
sumIy *= -FLT_SCALE;
|
||||
sumI *=FLT_SCALE;
|
||||
sumW = winArea * FLT_SCALE;
|
||||
w1 *= -FLT_SCALE;
|
||||
w2 *= -FLT_SCALE;
|
||||
dI *= FLT_SCALE;
|
||||
|
||||
A11 *= FLT_SCALE;
|
||||
A12 *= FLT_SCALE;
|
||||
A22 *= FLT_SCALE;
|
||||
}
|
||||
|
||||
#if CV_SIMD128
|
||||
float CV_DECL_ALIGNED(16) bbuf[4];
|
||||
v_store_aligned(bbuf, v_add(vqb0, vqb1));
|
||||
b1 = bbuf[0] + bbuf[2];
|
||||
b2 = bbuf[1] + bbuf[3];
|
||||
b3 = v_reduce_sum(vqb2);
|
||||
b4 = v_reduce_sum(vqb3);
|
||||
#endif
|
||||
|
||||
mismatchMat(0) = b1 * FLT_SCALE;
|
||||
mismatchMat(1) = b2 * FLT_SCALE;
|
||||
mismatchMat(2) = -b3 * FLT_SCALE;
|
||||
mismatchMat(3) = -b4 * FLT_SCALE;
|
||||
|
||||
D = - A12*A12*sumI*sumI + dI*sumW*A12*A12 + 2*A12*sumI*sumIx*w2 + 2*A12*sumI*sumIy*w1
|
||||
- 2*dI*A12*sumIx*sumIy - 2*sumW*A12*w1*w2 + A11*A22*sumI*sumI - 2*A22*sumI*sumIx*w1
|
||||
- 2*A11*sumI*sumIy*w2 - sumIx*sumIx*w2*w2 + A22*dI*sumIx*sumIx + 2*sumIx*sumIy*w1*w2
|
||||
- sumIy*sumIy*w1*w1 + A11*dI*sumIy*sumIy + A22*sumW*w1*w1 + A11*sumW*w2*w2 - A11*A22*dI*sumW;
|
||||
|
||||
|
||||
float minEig = (A22 + A11 - std::sqrt((A11-A22)*(A11-A22) +
|
||||
4.f*A12*A12))/(2*winArea);
|
||||
if( minEig < minEigThreshold )
|
||||
{
|
||||
if( (level == 0 && status) || std::abs(D) < FLT_EPSILON)
|
||||
status[ptidx] = 0;
|
||||
if( level > 0)
|
||||
{
|
||||
nextPts[ptidx] = backUpNextPt;
|
||||
gainVecs[ptidx] = backUpGain;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
D = (1.f / D);
|
||||
invTensorMat(0,0) = (A22*sumI*sumI - 2*sumI*sumIy*w2 + dI*sumIy*sumIy + sumW*w2*w2 - A22*dI*sumW)* D;
|
||||
invTensorMat(0,1) = (A12*dI*sumW - A12*sumI * sumI - dI*sumIx*sumIy + sumI*sumIx*w2 + sumI*sumIy*w1 - sumW*w1*w2)* D;
|
||||
invTensorMat(0,2) = (A12*sumI*sumIy - sumIy*sumIy*w1 - A22*sumI*sumIx - A12*sumW*w2 + A22*sumW*w1 + sumIx*sumIy*w2)* D;
|
||||
invTensorMat(0,3) = (A22*dI*sumIx - A12*dI*sumIy - sumIx*w2*w2 + A12*sumI*w2 - A22*sumI*w1 + sumIy*w1*w2) * D;
|
||||
invTensorMat(1,0) = invTensorMat(0,1);
|
||||
invTensorMat(1,1) = (A11*sumI * sumI - 2*sumI*sumIx*w1 + dI*sumIx * sumIx + sumW*w1*w1 - A11*dI*sumW) * D;
|
||||
invTensorMat(1,2) = (A12*sumI*sumIx - A11*sumI*sumIy - sumIx * sumIx*w2 + A11*sumW*w2 - A12*sumW*w1 + sumIx*sumIy*w1) * D;
|
||||
invTensorMat(1,3) = (A11*dI*sumIy - sumIy*w1*w1 - A12*dI*sumIx - A11*sumI*w2 + A12*sumI*w1 + sumIx*w1*w2)* D;
|
||||
invTensorMat(2,0) = invTensorMat(0,2);
|
||||
invTensorMat(2,1) = invTensorMat(1,2);
|
||||
invTensorMat(2,2) = (sumW*A12*A12 - 2*A12*sumIx*sumIy + A22*sumIx*sumIx + A11*sumIy*sumIy - A11*A22*sumW)* D;
|
||||
invTensorMat(2,3) = (A11*A22*sumI - A12*A12*sumI - A11*sumIy*w2 + A12*sumIx*w2 + A12*sumIy*w1 - A22*sumIx*w1)* D;
|
||||
invTensorMat(3,0) = invTensorMat(0,3);
|
||||
invTensorMat(3,1) = invTensorMat(1,3);
|
||||
invTensorMat(3,2) = invTensorMat(2,3);
|
||||
invTensorMat(3,3) = (dI*A12*A12 - 2*A12*w1*w2 + A22*w1*w1 + A11*w2*w2 - A11*A22*dI)* D;
|
||||
|
||||
resultMat = invTensorMat * mismatchMat;
|
||||
|
||||
// 0.08 -12.10
|
||||
Point2f delta(-resultMat(0), -resultMat(1));
|
||||
Point2f deltaGain(-resultMat(2), -resultMat(3));
|
||||
|
||||
|
||||
if( j == 0)
|
||||
prevGain = deltaGain;
|
||||
nextPt += delta;
|
||||
nextPts[ptidx] = nextPt - halfWin;
|
||||
gainVecs[ptidx]= gainVec;
|
||||
if( delta.ddot(delta) <= criteria.epsilon)
|
||||
break;
|
||||
if ((
|
||||
std::abs(delta.x - prevDelta.x) < 0.01 &&
|
||||
std::abs(delta.y - prevDelta.y) < 0.01
|
||||
) || (
|
||||
delta.ddot(delta) <= 0.001 &&
|
||||
std::abs(prevGain.x - deltaGain.x) < 0.01
|
||||
))
|
||||
{
|
||||
nextPts[ptidx] -= delta*0.5f;
|
||||
gainVecs[ptidx] -= deltaGain* 0.5f;
|
||||
break;
|
||||
}
|
||||
|
||||
if(j > 0 && std::abs(delta.x - prevDelta.x) < 0.01 &&
|
||||
std::abs(delta.y - prevDelta.y) < 0.01)
|
||||
{
|
||||
nextPts[ptidx] -= delta*0.5f;
|
||||
break;
|
||||
}
|
||||
|
||||
prevDelta = delta;
|
||||
prevGain = deltaGain;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const Mat* prevImg;
|
||||
const Mat* nextImg;
|
||||
const Mat* prevDeriv;
|
||||
const Mat* rgbPrevImg;
|
||||
const Mat* rgbNextImg;
|
||||
const Point2f* prevPts;
|
||||
Point2f* nextPts;
|
||||
uchar* status;
|
||||
cv::Point2f* gainVecs; // gain vector x -> multiplier y -> offset
|
||||
float* err;
|
||||
int maxWinSize;
|
||||
int minWinSize;
|
||||
TermCriteria criteria;
|
||||
int level;
|
||||
int maxLevel;
|
||||
int windowType;
|
||||
float minEigThreshold;
|
||||
bool useInitialFlow;
|
||||
int crossSegmentationThreshold;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
namespace ica {
|
||||
|
||||
class TrackerInvoker : public cv::ParallelLoopBody
|
||||
{
|
||||
public:
|
||||
TrackerInvoker(
|
||||
const Mat& _prevImg,
|
||||
const Mat& _prevDeriv,
|
||||
const Mat& _nextImg,
|
||||
const Mat& _rgbPrevImg,
|
||||
const Mat& _rgbNextImg,
|
||||
const Point2f* _prevPts,
|
||||
Point2f* _nextPts,
|
||||
uchar* _status,
|
||||
float* _err,
|
||||
int _level,
|
||||
int _maxLevel,
|
||||
int _winSize[2],
|
||||
int _maxIteration,
|
||||
bool _useInitialFlow,
|
||||
int _supportRegionType,
|
||||
int _crossSegmentationThreshold,
|
||||
float _minEigenValue)
|
||||
{
|
||||
prevImg = &_prevImg;
|
||||
prevDeriv = &_prevDeriv;
|
||||
nextImg = &_nextImg;
|
||||
rgbPrevImg = &_rgbPrevImg;
|
||||
rgbNextImg = &_rgbNextImg;
|
||||
prevPts = _prevPts;
|
||||
nextPts = _nextPts;
|
||||
status = _status;
|
||||
err = _err;
|
||||
minWinSize = _winSize[0];
|
||||
maxWinSize = _winSize[1];
|
||||
criteria.maxCount = _maxIteration;
|
||||
criteria.epsilon = 0.01;
|
||||
level = _level;
|
||||
maxLevel = _maxLevel;
|
||||
windowType = _supportRegionType;
|
||||
minEigThreshold = _minEigenValue;
|
||||
useInitialFlow = _useInitialFlow;
|
||||
crossSegmentationThreshold = _crossSegmentationThreshold;
|
||||
}
|
||||
|
||||
void operator()(const cv::Range& range) const CV_OVERRIDE
|
||||
{
|
||||
cv::Size winSize;
|
||||
cv::Point2f halfWin;
|
||||
|
||||
const Mat& I = *prevImg;
|
||||
const Mat& J = *nextImg;
|
||||
const Mat& derivI = *prevDeriv;
|
||||
const Mat& BI = *rgbPrevImg;
|
||||
|
||||
winSize = cv::Size(maxWinSize,maxWinSize);
|
||||
int winMaskwidth = roundUp(winSize.width, 8) * 2;
|
||||
cv::Mat winMaskMatBuf(winMaskwidth, winMaskwidth, tCVMaskType);
|
||||
winMaskMatBuf.setTo(1);
|
||||
|
||||
const float FLT_SCALE = (1.f/(1 << 20)); // 20
|
||||
|
||||
int j, cn = I.channels(), cn2 = cn*2;
|
||||
int winbufwidth = roundUp(winSize.width, 8);
|
||||
cv::Size winBufSize(winbufwidth,winbufwidth);
|
||||
|
||||
std::vector<short> _buf(winBufSize.area()*(cn + cn2));
|
||||
Mat IWinBuf(winBufSize, CV_MAKETYPE(CV_16S, cn), &_buf[0]);
|
||||
Mat derivIWinBuf(winBufSize, CV_MAKETYPE(CV_16S, cn2), &_buf[winBufSize.area()*cn]);
|
||||
|
||||
for( int ptidx = range.start; ptidx < range.end; ptidx++ )
|
||||
{
|
||||
Point2f prevPt = prevPts[ptidx]*(float)(1./(1 << level));
|
||||
Point2f nextPt;
|
||||
if( level == maxLevel )
|
||||
{
|
||||
if( useInitialFlow )
|
||||
nextPt = nextPts[ptidx]*(float)(1./(1 << level));
|
||||
else
|
||||
nextPt = prevPt;
|
||||
}
|
||||
else
|
||||
nextPt = nextPts[ptidx]*2.f;
|
||||
nextPts[ptidx] = nextPt;
|
||||
|
||||
Point2i iprevPt, inextPt;
|
||||
iprevPt.x = cvFloor(prevPt.x);
|
||||
iprevPt.y = cvFloor(prevPt.y);
|
||||
int winArea = maxWinSize * maxWinSize;
|
||||
cv::Mat winMaskMat(winMaskMatBuf, cv::Rect(0,0, maxWinSize,maxWinSize));
|
||||
|
||||
if( calcWinMaskMat(BI, windowType, iprevPt,
|
||||
winMaskMat,winSize,halfWin,winArea,
|
||||
minWinSize,maxWinSize) == false)
|
||||
continue;
|
||||
halfWin = Point2f(static_cast<float>(maxWinSize), static_cast<float>(maxWinSize)) - halfWin;
|
||||
prevPt += halfWin;
|
||||
iprevPt.x = cvFloor(prevPt.x);
|
||||
iprevPt.y = cvFloor(prevPt.y);
|
||||
|
||||
if( iprevPt.x < 0 || iprevPt.x >= derivI.cols - winSize.width ||
|
||||
iprevPt.y < 0 || iprevPt.y >= derivI.rows - winSize.height - 1)
|
||||
{
|
||||
if( level == 0 )
|
||||
{
|
||||
if( status )
|
||||
status[ptidx] = 3;
|
||||
if( err )
|
||||
err[ptidx] = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
float a = prevPt.x - iprevPt.x;
|
||||
float b = prevPt.y - iprevPt.y;
|
||||
const int W_BITS = 14;
|
||||
|
||||
int iw00 = cvRound((1.f - a)*(1.f - b)*(1 << W_BITS));
|
||||
int iw01 = cvRound(a*(1.f - b)*(1 << W_BITS));
|
||||
int iw10 = cvRound((1.f - a)*b*(1 << W_BITS));
|
||||
int iw11 = (1 << W_BITS) - iw00 - iw01 - iw10;
|
||||
float A11 = 0, A12 = 0, A22 = 0;
|
||||
|
||||
// extract the patch from the first image, compute covariation matrix of derivatives
|
||||
int x, y;
|
||||
copyWinBuffers(iw00, iw01, iw10, iw11, winSize, I, derivI, winMaskMat, IWinBuf, derivIWinBuf, A11, A22, A12, iprevPt);
|
||||
|
||||
float D = A11*A22 - A12*A12;
|
||||
float minEig = (A22 + A11 - std::sqrt((A11-A22)*(A11-A22) +
|
||||
4.f*A12*A12))/(2 * winArea);
|
||||
|
||||
if( err )
|
||||
err[ptidx] = (float)minEig;
|
||||
|
||||
if( minEig < minEigThreshold || D < FLT_EPSILON )
|
||||
{
|
||||
if( level == 0 && status )
|
||||
status[ptidx] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
D = 1.f/D;
|
||||
|
||||
nextPt += halfWin;
|
||||
Point2f prevDelta(0,0); //relates to h(t-1)
|
||||
for( j = 0; j < criteria.maxCount; j++ )
|
||||
{
|
||||
status[ptidx] = static_cast<uchar>(j);
|
||||
inextPt.x = cvFloor(nextPt.x);
|
||||
inextPt.y = cvFloor(nextPt.y);
|
||||
|
||||
if( inextPt.x < 0 || inextPt.x >= J.cols - winSize.width ||
|
||||
inextPt.y < 0 || inextPt.y >= J.rows - winSize.height - 1)
|
||||
{
|
||||
if( level == 0 && status )
|
||||
status[ptidx] = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
a = nextPt.x - inextPt.x;
|
||||
b = nextPt.y - inextPt.y;
|
||||
iw00 = cvRound((1.f - a)*(1.f - b)*(1 << W_BITS));
|
||||
iw01 = cvRound(a*(1.f - b)*(1 << W_BITS));
|
||||
iw10 = cvRound((1.f - a)*b*(1 << W_BITS));
|
||||
iw11 = (1 << W_BITS) - iw00 - iw01 - iw10;
|
||||
float b1 = 0, b2 = 0;
|
||||
#if CV_SIMD128
|
||||
v_int16x8 vqw0 = v_int16x8((short)(iw00), (short)(iw01), (short)(iw00), (short)(iw01), (short)(iw00), (short)(iw01), (short)(iw00), (short)(iw01));
|
||||
v_int16x8 vqw1 = v_int16x8((short)(iw10), (short)(iw11), (short)(iw10), (short)(iw11), (short)(iw10), (short)(iw11), (short)(iw10), (short)(iw11));
|
||||
v_float32x4 vqb0 = v_setzero_f32(), vqb1 = v_setzero_f32();
|
||||
v_int16x8 vmax_val_16 = v_setall_s16(std::numeric_limits<unsigned short>::max());
|
||||
v_int32x4 vdelta = v_setall_s32(1 << (W_BITS - 5 - 1));
|
||||
#endif
|
||||
for( y = 0; y < winSize.height; y++ )
|
||||
{
|
||||
const uchar* Jptr = J.ptr<uchar>(y + inextPt.y, inextPt.x*cn);
|
||||
const uchar* Jptr1 = J.ptr<uchar>(y + inextPt.y + 1, inextPt.x*cn);
|
||||
const short* Iptr = IWinBuf.ptr<short>(y, 0);
|
||||
const short* dIptr = derivIWinBuf.ptr<short>(y, 0);
|
||||
|
||||
x = 0;
|
||||
#if CV_SIMD128
|
||||
const tMaskType* maskPtr = winMaskMat.ptr<tMaskType>(y, 0);
|
||||
for( ; x <= winSize.width*cn; x += 8, dIptr += 8*2 )
|
||||
{
|
||||
v_int16x8 diff0 = v_reinterpret_as_s16(v_load(Iptr + x)), diff1, diff2;
|
||||
v_int16x8 v00 = v_reinterpret_as_s16(v_load_expand(Jptr + x));
|
||||
v_int16x8 v01 = v_reinterpret_as_s16(v_load_expand(Jptr + x + cn));
|
||||
v_int16x8 v10 = v_reinterpret_as_s16(v_load_expand(Jptr1 + x));
|
||||
v_int16x8 v11 = v_reinterpret_as_s16(v_load_expand(Jptr1 + x + cn));
|
||||
v_int16x8 vmask = v_mul(v_reinterpret_as_s16(v_load_expand(maskPtr + x)), vmax_val_16);
|
||||
|
||||
v_int32x4 t0, t1;
|
||||
v_int16x8 t00, t01, t10, t11;
|
||||
v_zip(v00, v01, t00, t01);
|
||||
v_zip(v10, v11, t10, t11);
|
||||
|
||||
t0 = v_add(v_dotprod(t00, vqw0, vdelta), v_dotprod(t10, vqw1));
|
||||
t1 = v_add(v_dotprod(t01, vqw0, vdelta), v_dotprod(t11, vqw1));
|
||||
t0 = v_shr(t0, W_BITS - 5);
|
||||
t1 = v_shr(t1, W_BITS - 5);
|
||||
diff0 = v_sub(v_pack(t0, t1), diff0);
|
||||
diff0 = v_and(diff0, vmask);
|
||||
|
||||
v_zip(diff0, diff0, diff2, diff1); // It0 It0 It1 It1 ...
|
||||
|
||||
v_int16x8 vIxy_0 = v_reinterpret_as_s16(v_load(dIptr)); // Ix0 Iy0 Ix1 Iy1 ...
|
||||
v_int16x8 vIxy_1 = v_reinterpret_as_s16(v_load(dIptr + 8));
|
||||
v_zip(vIxy_0, vIxy_1, v10, v11);
|
||||
v_zip(diff2, diff1, v00, v01);
|
||||
|
||||
vqb0 = v_add(vqb0, v_cvt_f32(v_dotprod(v00, v10)));
|
||||
vqb1 = v_add(vqb1, v_cvt_f32(v_dotprod(v01, v11)));
|
||||
}
|
||||
#else
|
||||
for( ; x < winSize.width*cn; x++, dIptr += 2 )
|
||||
{
|
||||
if( dIptr[0] == 0 && dIptr[1] == 0)
|
||||
continue;
|
||||
int diff = CV_DESCALE(Jptr[x]*iw00 + Jptr[x+cn]*iw01 +
|
||||
Jptr1[x]*iw10 + Jptr1[x+cn]*iw11,
|
||||
W_BITS-5) - Iptr[x];
|
||||
|
||||
b1 += (float)(diff*dIptr[0]) * FLT_RESCALE;
|
||||
b2 += (float)(diff*dIptr[1]) * FLT_RESCALE;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if CV_SIMD128
|
||||
float CV_DECL_ALIGNED(16) bbuf[4];
|
||||
v_store_aligned(bbuf, v_add(vqb0, vqb1));
|
||||
b1 = bbuf[0] + bbuf[2];
|
||||
b2 = bbuf[1] + bbuf[3];
|
||||
#endif
|
||||
|
||||
b1 *= FLT_SCALE;
|
||||
b2 *= FLT_SCALE;
|
||||
|
||||
Point2f delta( (float)((A12*b2 - A22*b1) * D),
|
||||
(float)((A12*b1 - A11*b2) * D));
|
||||
|
||||
|
||||
|
||||
nextPt += delta;
|
||||
nextPts[ptidx] = nextPt - halfWin;
|
||||
|
||||
if( delta.ddot(delta) <= criteria.epsilon)
|
||||
break;
|
||||
if(j > 0 && std::abs(delta.x - prevDelta.x) < 0.01 &&
|
||||
std::abs(delta.y - prevDelta.y) < 0.01)
|
||||
{
|
||||
nextPts[ptidx] -= delta*0.5f;
|
||||
break;
|
||||
}
|
||||
|
||||
prevDelta = delta;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const Mat* prevImg;
|
||||
const Mat* nextImg;
|
||||
const Mat* prevDeriv;
|
||||
const Mat* rgbPrevImg;
|
||||
const Mat* rgbNextImg;
|
||||
const Point2f* prevPts;
|
||||
Point2f* nextPts;
|
||||
uchar* status;
|
||||
float* err;
|
||||
int maxWinSize;
|
||||
int minWinSize;
|
||||
TermCriteria criteria;
|
||||
int level;
|
||||
int maxLevel;
|
||||
int windowType;
|
||||
float minEigThreshold;
|
||||
bool useInitialFlow;
|
||||
int crossSegmentationThreshold;
|
||||
};
|
||||
|
||||
}}}} // namespace
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,422 @@
|
||||
// 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 _RLOF_INVOKERBASE_HPP_
|
||||
#define _RLOF_INVOKERBASE_HPP_
|
||||
|
||||
#ifndef CV_DESCALE
|
||||
#define CV_DESCALE(x, n) (((x) + (1 << ((n)-1))) >> (n))
|
||||
#endif
|
||||
#define FLT_RESCALE 1
|
||||
|
||||
#include "rlof_localflow.h"
|
||||
#include <unordered_map>
|
||||
#include "opencv2/core/hal/intrin.hpp"
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
namespace cv {
|
||||
namespace optflow {
|
||||
|
||||
|
||||
typedef short deriv_type;
|
||||
#if CV_SIMD128
|
||||
|
||||
static inline void getVBitMask(const int & width, v_int32x4 & mask0, v_int32x4 & mask1)
|
||||
{
|
||||
int noBits = width - static_cast<int>(floor(width / 8.f) * 8.f);
|
||||
unsigned int val[8];
|
||||
for (int n = 0; n < 8; n++)
|
||||
{
|
||||
val[n] = (noBits > n) ? (std::numeric_limits<unsigned int>::max()) : 0;
|
||||
}
|
||||
mask0 = v_int32x4(val[0], val[1], val[2], val[3]);
|
||||
mask1 = v_int32x4(val[4], val[5], val[6], val[7]);
|
||||
}
|
||||
#endif
|
||||
typedef uchar tMaskType;
|
||||
#define tCVMaskType CV_8UC1
|
||||
#define MaskSet 0xffffffff
|
||||
|
||||
static inline void copyWinBuffers(int iw00, int iw01, int iw10, int iw11,
|
||||
Size winSize,
|
||||
const Mat & I, const Mat & derivI, const Mat & winMaskMat,
|
||||
Mat & IWinBuf, Mat & derivIWinBuf,
|
||||
Point iprevPt)
|
||||
{
|
||||
int cn = I.channels(), cn2 = cn * 2;
|
||||
const int W_BITS = 14;
|
||||
#if CV_SIMD128
|
||||
v_int16x8 vqw0((short)(iw00), (short)(iw01), (short)(iw00), (short)(iw01), (short)(iw00), (short)(iw01), (short)(iw00), (short)(iw01));
|
||||
v_int16x8 vqw1((short)(iw10), (short)(iw11), (short)(iw10), (short)(iw11), (short)(iw10), (short)(iw11), (short)(iw10), (short)(iw11));
|
||||
v_int32x4 vdelta_d = v_setall_s32(1 << (W_BITS - 1));
|
||||
v_int32x4 vdelta = v_setall_s32(1 << (W_BITS - 5 - 1));
|
||||
v_int32x4 vmax_val_32 = v_setall_s32(std::numeric_limits<unsigned int>::max());
|
||||
v_int32x4 vmask_border_0, vmask_border_1;
|
||||
getVBitMask(winSize.width, vmask_border_0, vmask_border_1);
|
||||
#endif
|
||||
|
||||
// extract the patch from the first image, compute covariation matrix of derivatives
|
||||
int x, y;
|
||||
for (y = 0; y < winSize.height; y++)
|
||||
{
|
||||
const uchar* src = I.ptr<uchar>(y + iprevPt.y, 0) + iprevPt.x*cn;
|
||||
const uchar* src1 = I.ptr<uchar>(y + iprevPt.y + 1, 0) + iprevPt.x*cn;
|
||||
const short* dsrc = derivI.ptr<short>(y + iprevPt.y, 0) + iprevPt.x*cn2;
|
||||
const short* dsrc1 = derivI.ptr<short>(y + iprevPt.y + 1, 0) + iprevPt.x*cn2;
|
||||
short* Iptr = IWinBuf.ptr<short>(y, 0);
|
||||
short* dIptr = derivIWinBuf.ptr<short>(y, 0);
|
||||
const tMaskType* maskPtr = winMaskMat.ptr<tMaskType>(y, 0);
|
||||
x = 0;
|
||||
#if CV_SIMD128
|
||||
|
||||
for (; x <= winSize.width*cn; x += 8, dsrc += 8 * 2, dsrc1 += 8 * 2, dIptr += 8 * 2)
|
||||
{
|
||||
v_int32x4 vmask0 = v_mul(v_reinterpret_as_s32(v_load_expand_q(maskPtr + x)), vmax_val_32);
|
||||
v_int32x4 vmask1 = v_mul(v_reinterpret_as_s32(v_load_expand_q(maskPtr + x + 4)), vmax_val_32);
|
||||
if (x + 4 > winSize.width)
|
||||
{
|
||||
vmask0 = v_and(vmask0, vmask_border_0);
|
||||
}
|
||||
if (x + 8 > winSize.width)
|
||||
{
|
||||
vmask1 = v_and(vmask1, vmask_border_1);
|
||||
}
|
||||
|
||||
v_int32x4 t0, t1;
|
||||
v_int16x8 v00, v01, v10, v11, t00, t01, t10, t11;
|
||||
v00 = v_reinterpret_as_s16(v_load_expand(src + x));
|
||||
v01 = v_reinterpret_as_s16(v_load_expand(src + x + cn));
|
||||
v10 = v_reinterpret_as_s16(v_load_expand(src1 + x));
|
||||
v11 = v_reinterpret_as_s16(v_load_expand(src1 + x + cn));
|
||||
|
||||
v_zip(v00, v01, t00, t01);
|
||||
v_zip(v10, v11, t10, t11);
|
||||
t0 = v_add(v_dotprod(t00, vqw0, vdelta), v_dotprod(t10, vqw1));
|
||||
t1 = v_add(v_dotprod(t01, vqw0, vdelta), v_dotprod(t11, vqw1));
|
||||
t0 = v_and(v_shr(t0, W_BITS - 5), vmask0);
|
||||
t1 = v_and(v_shr(t1, W_BITS - 5), vmask1);
|
||||
v_store(Iptr + x, v_pack(t0, t1));
|
||||
|
||||
v00 = v_reinterpret_as_s16(v_load(dsrc));
|
||||
v01 = v_reinterpret_as_s16(v_load(dsrc + cn2));
|
||||
v10 = v_reinterpret_as_s16(v_load(dsrc1));
|
||||
v11 = v_reinterpret_as_s16(v_load(dsrc1 + cn2));
|
||||
|
||||
v_zip(v00, v01, t00, t01);
|
||||
v_zip(v10, v11, t10, t11);
|
||||
|
||||
t0 = v_add(v_dotprod(t00, vqw0, vdelta_d), v_dotprod(t10, vqw1));
|
||||
t1 = v_add(v_dotprod(t01, vqw0, vdelta_d), v_dotprod(t11, vqw1));
|
||||
t0 = v_shr(t0, W_BITS);
|
||||
t1 = v_shr(t1, W_BITS);
|
||||
v00 = v_pack(t0, t1); // Ix0 Iy0 Ix1 Iy1 ...
|
||||
v00 = v_and(v00, v_reinterpret_as_s16(vmask0));
|
||||
v_store(dIptr, v00);
|
||||
|
||||
v00 = v_reinterpret_as_s16(v_load(dsrc + 4 * 2));
|
||||
v01 = v_reinterpret_as_s16(v_load(dsrc + 4 * 2 + cn2));
|
||||
v10 = v_reinterpret_as_s16(v_load(dsrc1 + 4 * 2));
|
||||
v11 = v_reinterpret_as_s16(v_load(dsrc1 + 4 * 2 + cn2));
|
||||
|
||||
v_zip(v00, v01, t00, t01);
|
||||
v_zip(v10, v11, t10, t11);
|
||||
|
||||
t0 = v_add(v_dotprod(t00, vqw0, vdelta_d), v_dotprod(t10, vqw1));
|
||||
t1 = v_add(v_dotprod(t01, vqw0, vdelta_d), v_dotprod(t11, vqw1));
|
||||
t0 = v_shr(t0, W_BITS);
|
||||
t1 = v_shr(t1, W_BITS);
|
||||
v00 = v_pack(t0, t1); // Ix0 Iy0 Ix1 Iy1 ...
|
||||
v00 = v_and(v00, v_reinterpret_as_s16(vmask1));
|
||||
v_store(dIptr + 4 * 2, v00);
|
||||
}
|
||||
#else
|
||||
for (; x < winSize.width*cn; x++, dsrc += 2, dsrc1 += 2, dIptr += 2)
|
||||
{
|
||||
if (maskPtr[x] == 0)
|
||||
{
|
||||
dIptr[0] = 0;
|
||||
dIptr[1] = 0;
|
||||
continue;
|
||||
}
|
||||
int ival = CV_DESCALE(src[x] * iw00 + src[x + cn] * iw01 +
|
||||
src1[x] * iw10 + src1[x + cn] * iw11, W_BITS - 5);
|
||||
int ixval = CV_DESCALE(dsrc[0] * iw00 + dsrc[cn2] * iw01 +
|
||||
dsrc1[0] * iw10 + dsrc1[cn2] * iw11, W_BITS);
|
||||
int iyval = CV_DESCALE(dsrc[1] * iw00 + dsrc[cn2 + 1] * iw01 + dsrc1[1] * iw10 +
|
||||
dsrc1[cn2 + 1] * iw11, W_BITS);
|
||||
|
||||
Iptr[x] = (short)ival;
|
||||
dIptr[0] = (short)ixval;
|
||||
dIptr[1] = (short)iyval;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static inline void copyWinBuffers(int iw00, int iw01, int iw10, int iw11,
|
||||
Size winSize,
|
||||
const Mat & I, const Mat & derivI, const Mat & winMaskMat,
|
||||
Mat & IWinBuf, Mat & derivIWinBuf,
|
||||
float & A11, float & A22, float & A12,
|
||||
Point iprevPt)
|
||||
{
|
||||
const float FLT_SCALE = (1.f / (1 << 20));
|
||||
int cn = I.channels(), cn2 = cn * 2;
|
||||
const int W_BITS = 14;
|
||||
#if CV_SIMD128
|
||||
v_int16x8 vqw0((short)(iw00), (short)(iw01), (short)(iw00), (short)(iw01), (short)(iw00), (short)(iw01), (short)(iw00), (short)(iw01));
|
||||
v_int16x8 vqw1((short)(iw10), (short)(iw11), (short)(iw10), (short)(iw11), (short)(iw10), (short)(iw11), (short)(iw10), (short)(iw11));
|
||||
v_int32x4 vdelta_d = v_setall_s32(1 << (W_BITS - 1));
|
||||
v_int32x4 vdelta = v_setall_s32(1 << (W_BITS - 5 - 1));
|
||||
v_int32x4 vmax_val_32 = v_setall_s32(std::numeric_limits<unsigned int>::max());
|
||||
v_int32x4 vmask_border0, vmask_border1;
|
||||
v_float32x4 vA11 = v_setzero_f32(), vA22 = v_setzero_f32(), vA12 = v_setzero_f32();
|
||||
getVBitMask(winSize.width, vmask_border0, vmask_border1);
|
||||
#endif
|
||||
|
||||
// extract the patch from the first image, compute covariation matrix of derivatives
|
||||
for (int y = 0; y < winSize.height; y++)
|
||||
{
|
||||
const uchar* src = I.ptr<uchar>(y + iprevPt.y, 0) + iprevPt.x*cn;
|
||||
const uchar* src1 = I.ptr<uchar>(y + iprevPt.y + 1, 0) + iprevPt.x*cn;
|
||||
const short* dsrc = derivI.ptr<short>(y + iprevPt.y, 0) + iprevPt.x*cn2;
|
||||
const short* dsrc1 = derivI.ptr<short>(y + iprevPt.y + 1, 0) + iprevPt.x*cn2;
|
||||
short* Iptr = IWinBuf.ptr<short>(y, 0);
|
||||
short* dIptr = derivIWinBuf.ptr<short>(y, 0);
|
||||
const tMaskType* maskPtr = winMaskMat.ptr<tMaskType>(y, 0);
|
||||
#if CV_SIMD128
|
||||
for (int x = 0; x <= winSize.width*cn; x += 8, dsrc += 8 * 2, dsrc1 += 8 * 2, dIptr += 8 * 2)
|
||||
{
|
||||
v_int32x4 vmask0 = v_mul(v_reinterpret_as_s32(v_load_expand_q(maskPtr + x)), vmax_val_32);
|
||||
v_int32x4 vmask1 = v_mul(v_reinterpret_as_s32(v_load_expand_q(maskPtr + x + 4)), vmax_val_32);
|
||||
if (x + 4 > winSize.width)
|
||||
{
|
||||
vmask0 = v_and(vmask0, vmask_border0);
|
||||
}
|
||||
if (x + 8 > winSize.width)
|
||||
{
|
||||
vmask1 = v_and(vmask1, vmask_border1);
|
||||
}
|
||||
|
||||
v_int32x4 t0, t1;
|
||||
v_int16x8 v00, v01, v10, v11, t00, t01, t10, t11;
|
||||
v00 = v_reinterpret_as_s16(v_load_expand(src + x));
|
||||
v01 = v_reinterpret_as_s16(v_load_expand(src + x + cn));
|
||||
v10 = v_reinterpret_as_s16(v_load_expand(src1 + x));
|
||||
v11 = v_reinterpret_as_s16(v_load_expand(src1 + x + cn));
|
||||
|
||||
v_zip(v00, v01, t00, t01);
|
||||
v_zip(v10, v11, t10, t11);
|
||||
t0 = v_add(v_dotprod(t00, vqw0, vdelta), v_dotprod(t10, vqw1));
|
||||
t1 = v_add(v_dotprod(t01, vqw0, vdelta), v_dotprod(t11, vqw1));
|
||||
t0 = v_shr(t0, W_BITS - 5);
|
||||
t1 = v_shr(t1, W_BITS - 5);
|
||||
t0 = v_and(t0, vmask0);
|
||||
t1 = v_and(t1, vmask1);
|
||||
v_store(Iptr + x, v_pack(t0, t1));
|
||||
|
||||
v00 = v_reinterpret_as_s16(v_load(dsrc));
|
||||
v01 = v_reinterpret_as_s16(v_load(dsrc + cn2));
|
||||
v10 = v_reinterpret_as_s16(v_load(dsrc1));
|
||||
v11 = v_reinterpret_as_s16(v_load(dsrc1 + cn2));
|
||||
|
||||
v_zip(v00, v01, t00, t01);
|
||||
v_zip(v10, v11, t10, t11);
|
||||
|
||||
t0 = v_add(v_dotprod(t00, vqw0, vdelta_d), v_dotprod(t10, vqw1));
|
||||
t1 = v_add(v_dotprod(t01, vqw0, vdelta_d), v_dotprod(t11, vqw1));
|
||||
t0 = v_shr(t0, W_BITS);
|
||||
t1 = v_shr(t1, W_BITS);
|
||||
v00 = v_pack(t0, t1); // Ix0 Iy0 Ix1 Iy1 ...
|
||||
v00 = v_and(v00, v_reinterpret_as_s16(vmask0));
|
||||
v_store(dIptr, v00);
|
||||
|
||||
v00 = v_reinterpret_as_s16(v_interleave_pairs(v_reinterpret_as_s32(v_interleave_pairs(v00))));
|
||||
v_expand(v00, t1, t0);
|
||||
|
||||
v_float32x4 fy = v_cvt_f32(t0);
|
||||
v_float32x4 fx = v_cvt_f32(t1);
|
||||
|
||||
vA22 = v_muladd(fy, fy, vA22);
|
||||
vA12 = v_muladd(fx, fy, vA12);
|
||||
vA11 = v_muladd(fx, fx, vA11);
|
||||
|
||||
v00 = v_reinterpret_as_s16(v_load(dsrc + 4 * 2));
|
||||
v01 = v_reinterpret_as_s16(v_load(dsrc + 4 * 2 + cn2));
|
||||
v10 = v_reinterpret_as_s16(v_load(dsrc1 + 4 * 2));
|
||||
v11 = v_reinterpret_as_s16(v_load(dsrc1 + 4 * 2 + cn2));
|
||||
|
||||
v_zip(v00, v01, t00, t01);
|
||||
v_zip(v10, v11, t10, t11);
|
||||
|
||||
t0 = v_add(v_dotprod(t00, vqw0, vdelta_d), v_dotprod(t10, vqw1));
|
||||
t1 = v_add(v_dotprod(t01, vqw0, vdelta_d), v_dotprod(t11, vqw1));
|
||||
t0 = v_shr(t0, W_BITS);
|
||||
t1 = v_shr(t1, W_BITS);
|
||||
v00 = v_pack(t0, t1); // Ix0 Iy0 Ix1 Iy1 ...
|
||||
v00 = v_and(v00, v_reinterpret_as_s16(vmask1));
|
||||
v_store(dIptr + 4 * 2, v00);
|
||||
|
||||
v00 = v_reinterpret_as_s16(v_interleave_pairs(v_reinterpret_as_s32(v_interleave_pairs(v00))));
|
||||
v_expand(v00, t1, t0);
|
||||
|
||||
fy = v_cvt_f32(t0);
|
||||
fx = v_cvt_f32(t1);
|
||||
|
||||
vA22 = v_muladd(fy, fy, vA22);
|
||||
vA12 = v_muladd(fx, fy, vA12);
|
||||
vA11 = v_muladd(fx, fx, vA11);
|
||||
}
|
||||
#else
|
||||
for (int x = 0; x < winSize.width*cn; x++, dsrc += 2, dsrc1 += 2, dIptr += 2)
|
||||
{
|
||||
if (maskPtr[x] == 0)
|
||||
{
|
||||
dIptr[0] = 0;
|
||||
dIptr[1] = 0;
|
||||
continue;
|
||||
}
|
||||
int ival = CV_DESCALE(src[x] * iw00 + src[x + cn] * iw01 +
|
||||
src1[x] * iw10 + src1[x + cn] * iw11, W_BITS - 5);
|
||||
int ixval = CV_DESCALE(dsrc[0] * iw00 + dsrc[cn2] * iw01 +
|
||||
dsrc1[0] * iw10 + dsrc1[cn2] * iw11, W_BITS);
|
||||
int iyval = CV_DESCALE(dsrc[1] * iw00 + dsrc[cn2 + 1] * iw01 + dsrc1[1] * iw10 +
|
||||
dsrc1[cn2 + 1] * iw11, W_BITS);
|
||||
|
||||
Iptr[x] = (short)ival;
|
||||
dIptr[0] = (short)ixval;
|
||||
dIptr[1] = (short)iyval;
|
||||
A11 += (float)(ixval*ixval);
|
||||
A12 += (float)(ixval*iyval);
|
||||
A22 += (float)(iyval*iyval);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#if CV_SIMD128
|
||||
A11 += v_reduce_sum(vA11);
|
||||
A12 += v_reduce_sum(vA12);
|
||||
A22 += v_reduce_sum(vA22);
|
||||
#endif
|
||||
|
||||
A11 *= FLT_SCALE;
|
||||
A12 *= FLT_SCALE;
|
||||
A22 *= FLT_SCALE;
|
||||
}
|
||||
|
||||
static void getLocalPatch(
|
||||
const cv::Mat & src,
|
||||
const cv::Point2i & prevPoint, // feature points
|
||||
cv::Mat & winPointMask,
|
||||
int & noPoints,
|
||||
cv::Rect & winRoi,
|
||||
int minWinSize)
|
||||
{
|
||||
int maxWinSizeH = (winPointMask.cols - 1) / 2;
|
||||
winRoi.x = prevPoint.x;// - maxWinSizeH;
|
||||
winRoi.y = prevPoint.y;// - maxWinSizeH;
|
||||
winRoi.width = winPointMask.cols;
|
||||
winRoi.height = winPointMask.rows;
|
||||
|
||||
if( minWinSize == winPointMask.cols || prevPoint.x < 0 || prevPoint.y < 0
|
||||
|| prevPoint.x + 2*maxWinSizeH >= src.cols || prevPoint.y + 2*maxWinSizeH >= src.rows)
|
||||
{
|
||||
winRoi.x = prevPoint.x - maxWinSizeH;
|
||||
winRoi.y = prevPoint.y - maxWinSizeH;
|
||||
winPointMask.setTo(1);
|
||||
noPoints = winPointMask.size().area();
|
||||
return;
|
||||
}
|
||||
winPointMask.setTo(0);
|
||||
noPoints = 0;
|
||||
int c = prevPoint.x + maxWinSizeH;
|
||||
int r = prevPoint.y + maxWinSizeH;
|
||||
int min_c = c;
|
||||
int max_c = c;
|
||||
int border_left = c - maxWinSizeH;
|
||||
int border_top = r - maxWinSizeH;
|
||||
cv::Vec4i bounds = src.at<cv::Vec4i>(r,c);
|
||||
int min_r = bounds.val[2];
|
||||
int max_r = bounds.val[3];
|
||||
|
||||
for( int _r = min_r; _r <= max_r; _r++)
|
||||
{
|
||||
cv::Rect roi(maxWinSizeH, _r - border_top, winPointMask.cols, 1);
|
||||
if( _r >= 0 && _r < src.cols)
|
||||
{
|
||||
bounds = src.at<cv::Vec4i>(_r,c);
|
||||
roi.x = bounds.val[0] - border_left;
|
||||
roi.width = bounds.val[1] - bounds.val[0];
|
||||
cv::Mat(winPointMask, roi).setTo(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
bounds.val[0] = border_left;
|
||||
bounds.val[1] = border_left + roi.width;
|
||||
}
|
||||
min_c = MIN(min_c, bounds.val[0]);
|
||||
max_c = MAX(max_c, bounds.val[1]);
|
||||
noPoints += roi.width;
|
||||
}
|
||||
|
||||
if( noPoints < minWinSize * minWinSize)
|
||||
{
|
||||
cv::Rect roi( winPointMask.cols / 2 - (minWinSize-1)/2,
|
||||
winPointMask.rows / 2 - (minWinSize-1)/2,
|
||||
minWinSize, minWinSize);
|
||||
cv::Mat(winPointMask, roi).setTo(1);
|
||||
roi.x += border_left;
|
||||
roi.y += border_top;
|
||||
min_c = MIN(MIN(min_c, roi.tl().x),roi.br().x);
|
||||
max_c = MAX(MAX(max_c, roi.tl().x),roi.br().x);
|
||||
min_r = MIN(MIN(min_r, roi.tl().y),roi.br().y);
|
||||
max_r = MAX(MAX(max_r, roi.tl().y),roi.br().y);
|
||||
noPoints += minWinSize * minWinSize;
|
||||
}
|
||||
winRoi.x = min_c - maxWinSizeH;
|
||||
winRoi.y = min_r - maxWinSizeH;
|
||||
winRoi.width = max_c - min_c;
|
||||
winRoi.height = max_r - min_r;
|
||||
winPointMask = winPointMask(cv::Rect(min_c - border_left, min_r - border_top, winRoi.width, winRoi.height));
|
||||
}
|
||||
|
||||
static inline
|
||||
bool calcWinMaskMat(
|
||||
const cv::Mat & BI,
|
||||
const int windowType,
|
||||
const cv::Point2i & iprevPt,
|
||||
cv::Mat & winMaskMat,
|
||||
cv::Size & winSize,
|
||||
cv::Point2f & halfWin,
|
||||
int & winArea,
|
||||
const int minWinSize,
|
||||
const int maxWinSize)
|
||||
{
|
||||
if (windowType == SR_CROSS && maxWinSize != minWinSize)
|
||||
{
|
||||
// patch generation
|
||||
cv::Rect winRoi;
|
||||
getLocalPatch(BI, iprevPt, winMaskMat, winArea, winRoi, minWinSize);
|
||||
if (winArea == 0)
|
||||
return false;
|
||||
winSize = winRoi.size();
|
||||
halfWin = Point2f(static_cast<float>(iprevPt.x - winRoi.tl().x),
|
||||
static_cast<float>(iprevPt.y - winRoi.tl().y));
|
||||
}
|
||||
else
|
||||
{
|
||||
winSize = cv::Size(maxWinSize, maxWinSize);
|
||||
halfWin = Point2f((winSize.width - 1) / 2.f, (winSize.height - 1) / 2.f);
|
||||
winMaskMat.setTo(1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline
|
||||
short estimateScale(cv::Mat & residuals)
|
||||
{
|
||||
cv::Mat absMat = cv::abs(residuals);
|
||||
return quickselect<short>(absMat, absMat.rows / 2);
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
#endif
|
||||
@@ -0,0 +1,711 @@
|
||||
// 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 "opencv2/geometry.hpp" // findHomography
|
||||
#include "rlof_localflow.h"
|
||||
#include "berlof_invoker.hpp"
|
||||
#include "rlof_invoker.hpp"
|
||||
#include "plk_invoker.hpp"
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
typedef short deriv_type;
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
static void calcSharrDeriv(const cv::Mat& src, cv::Mat& dst)
|
||||
{
|
||||
using namespace cv;
|
||||
using cv::detail::deriv_type;
|
||||
int rows = src.rows, cols = src.cols, cn = src.channels(), colsn = cols * cn, depth = src.depth();
|
||||
CV_Assert(depth == CV_8U);
|
||||
dst.create(rows, cols, CV_MAKETYPE(DataType<deriv_type>::depth, cn * 2));
|
||||
|
||||
int x, y, delta = (int)alignSize((cols + 2)*cn, 16);
|
||||
AutoBuffer<deriv_type> _tempBuf(delta * 2 + 64);
|
||||
deriv_type *trow0 = alignPtr(_tempBuf.data() + cn, 16), *trow1 = alignPtr(trow0 + delta, 16);
|
||||
|
||||
#if CV_SIMD128
|
||||
v_int16x8 c3 = v_setall_s16(3), c10 = v_setall_s16(10);
|
||||
bool haveSIMD = checkHardwareSupport(CV_CPU_SSE2) || checkHardwareSupport(CV_CPU_NEON);
|
||||
#endif
|
||||
|
||||
for (y = 0; y < rows; y++)
|
||||
{
|
||||
const uchar* srow0 = src.ptr<uchar>(y > 0 ? y - 1 : rows > 1 ? 1 : 0);
|
||||
const uchar* srow1 = src.ptr<uchar>(y);
|
||||
const uchar* srow2 = src.ptr<uchar>(y < rows - 1 ? y + 1 : rows > 1 ? rows - 2 : 0);
|
||||
deriv_type* drow = dst.ptr<deriv_type>(y);
|
||||
|
||||
// do vertical convolution
|
||||
x = 0;
|
||||
#if CV_SIMD128
|
||||
if (haveSIMD)
|
||||
{
|
||||
for (; x <= colsn - 8; x += 8)
|
||||
{
|
||||
v_int16x8 s0 = v_reinterpret_as_s16(v_load_expand(srow0 + x));
|
||||
v_int16x8 s1 = v_reinterpret_as_s16(v_load_expand(srow1 + x));
|
||||
v_int16x8 s2 = v_reinterpret_as_s16(v_load_expand(srow2 + x));
|
||||
|
||||
v_int16x8 t1 = v_sub(s2, s0);
|
||||
v_int16x8 t0 = v_add(v_mul_wrap(v_add(s0, s2), c3), v_mul_wrap(s1, c10));
|
||||
|
||||
v_store(trow0 + x, t0);
|
||||
v_store(trow1 + x, t1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
for (; x < colsn; x++)
|
||||
{
|
||||
int t0 = (srow0[x] + srow2[x]) * 3 + srow1[x] * 10;
|
||||
int t1 = srow2[x] - srow0[x];
|
||||
trow0[x] = (deriv_type)t0;
|
||||
trow1[x] = (deriv_type)t1;
|
||||
}
|
||||
|
||||
// make border
|
||||
int x0 = (cols > 1 ? 1 : 0)*cn, x1 = (cols > 1 ? cols - 2 : 0)*cn;
|
||||
for (int k = 0; k < cn; k++)
|
||||
{
|
||||
trow0[-cn + k] = trow0[x0 + k]; trow0[colsn + k] = trow0[x1 + k];
|
||||
trow1[-cn + k] = trow1[x0 + k]; trow1[colsn + k] = trow1[x1 + k];
|
||||
}
|
||||
|
||||
// do horizontal convolution, interleave the results and store them to dst
|
||||
x = 0;
|
||||
#if CV_SIMD128
|
||||
if (haveSIMD)
|
||||
{
|
||||
for (; x <= colsn - 8; x += 8)
|
||||
{
|
||||
v_int16x8 s0 = v_load(trow0 + x - cn);
|
||||
v_int16x8 s1 = v_load(trow0 + x + cn);
|
||||
v_int16x8 s2 = v_load(trow1 + x - cn);
|
||||
v_int16x8 s3 = v_load(trow1 + x);
|
||||
v_int16x8 s4 = v_load(trow1 + x + cn);
|
||||
|
||||
v_int16x8 t0 = v_sub(s1, s0);
|
||||
v_int16x8 t1 = v_add(v_mul_wrap(v_add(s2, s4), c3), v_mul_wrap(s3, c10));
|
||||
|
||||
v_store_interleave((drow + x * 2), t0, t1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for (; x < colsn; x++)
|
||||
{
|
||||
deriv_type t0 = (deriv_type)(trow0[x + cn] - trow0[x - cn]);
|
||||
deriv_type t1 = (deriv_type)((trow1[x + cn] + trow1[x - cn]) * 3 + trow1[x] * 10);
|
||||
drow[x * 2] = t0; drow[x * 2 + 1] = t1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
namespace optflow {
|
||||
|
||||
/*! Helper function for preCalcCrossSegmentation. Everything is performed on the large
|
||||
*\param data CV_8UC3 image ( use extended image mit winSize)
|
||||
*\param winSize
|
||||
*\param dst CV_32SC1 bounding map
|
||||
*\param threshold
|
||||
*\param stride if true store into first two bounding maps
|
||||
*/
|
||||
class HorizontalCrossSegmentation : public cv::ParallelLoopBody
|
||||
{
|
||||
public:
|
||||
HorizontalCrossSegmentation(
|
||||
const cv::Point2f * ptList,
|
||||
int npoints,
|
||||
float pointScale,
|
||||
const cv::Mat * data,
|
||||
const int winSize,
|
||||
cv::Mat * dst,
|
||||
int threshold,
|
||||
bool stride,
|
||||
const cv::Mat * mask
|
||||
)
|
||||
{
|
||||
m_ptList = ptList;
|
||||
m_npoints = npoints;
|
||||
m_pointScale = pointScale;
|
||||
m_data = data;
|
||||
m_winSize = winSize;
|
||||
m_dst = dst;
|
||||
m_threshold = threshold;
|
||||
m_stride = stride;
|
||||
m_mask = mask;
|
||||
}
|
||||
|
||||
void operator()(const cv::Range& range) const CV_OVERRIDE
|
||||
{
|
||||
uchar channel[2];
|
||||
channel[0] = m_stride ? 2 : 0;
|
||||
channel[1] = m_stride ? 3 : 1;
|
||||
int hWinSize = (m_winSize - 1) / 2;
|
||||
std::vector<int> differenz(m_winSize);
|
||||
for( int r = range.start; r < range.end; r++ )
|
||||
{
|
||||
for(int c = hWinSize; c < m_data->cols - hWinSize; c++)
|
||||
{
|
||||
if( m_mask->at<uchar>(r,c) == 0)
|
||||
continue;
|
||||
const Point3_<uchar> & ucval = m_data->at<Point3_<uchar>>(r,c);
|
||||
Point3i val(static_cast<int>(ucval.x), static_cast<int>(ucval.y), static_cast<int>(ucval.z));
|
||||
int x = c - hWinSize;
|
||||
Point dstPos = m_stride ? Point(r,c) : Point(c,r);
|
||||
for(int ix = 0; ix < m_winSize; ix++, x++)
|
||||
{
|
||||
const Point3_<uchar> & valref = m_data->at<Point3_<uchar>>(r,x);
|
||||
differenz[ix] = MAX(std::abs(static_cast<int>(valref.x) - val.x),
|
||||
MAX(std::abs(static_cast<int>(valref.y) - val.y),
|
||||
(std::abs(static_cast<int>(valref.z) - val.z))));
|
||||
|
||||
}
|
||||
cv::Vec4i & bounds = m_dst->at<cv::Vec4i>(dstPos);
|
||||
bounds.val[channel[0]] = c - hWinSize;
|
||||
bounds.val[channel[1]] = c + hWinSize;
|
||||
int * diffPtr = &differenz[hWinSize];
|
||||
bool useUpperBound = false;
|
||||
bool useLowerBound = false;
|
||||
for(int ix = 1; ix <= hWinSize; ix++)
|
||||
{
|
||||
if( !useUpperBound && diffPtr[-ix] > m_threshold)
|
||||
{
|
||||
useUpperBound = true;
|
||||
bounds.val[channel[0]] = c - ix;
|
||||
}
|
||||
if( !useLowerBound && diffPtr[ix-1] > m_threshold)
|
||||
{
|
||||
useLowerBound = true;
|
||||
bounds.val[channel[1]] = c + ix - 1;
|
||||
}
|
||||
if( useUpperBound && useLowerBound)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const cv::Point2f * m_ptList;
|
||||
int m_npoints;
|
||||
float m_pointScale;
|
||||
const cv::Mat * m_data;
|
||||
int m_winSize;
|
||||
cv::Mat * m_dst;
|
||||
int m_threshold;
|
||||
bool m_stride;
|
||||
const cv::Mat * m_mask;
|
||||
};
|
||||
|
||||
static
|
||||
void preCalcCrossSegmentation(
|
||||
const cv::Point2f * ptList,
|
||||
int npoints,
|
||||
float pointScale,
|
||||
const cv::Mat & img,
|
||||
const int winSize,
|
||||
cv::Mat & dst,
|
||||
int threshold
|
||||
)
|
||||
{
|
||||
int hWinSize = (winSize - 1) / 2;
|
||||
cv::Mat data = img;
|
||||
data.adjustROI(hWinSize, hWinSize, hWinSize, hWinSize);
|
||||
if( dst.size() != dst.size() || dst.type() != CV_32SC4)
|
||||
{
|
||||
dst.release();
|
||||
dst.create(data.size(), CV_32SC4);
|
||||
}
|
||||
cv::Mat mask(data.cols, data.rows, CV_8UC1);
|
||||
mask.setTo(0);
|
||||
for( unsigned int n = 0; n < static_cast<unsigned int>(npoints); n++)
|
||||
{
|
||||
cv::Point ipos( static_cast<int>(floor(ptList[n].y * pointScale)),
|
||||
static_cast<int>(floor(ptList[n].x * pointScale) + hWinSize));
|
||||
ipos.x = MAX( MIN(ipos.x, mask.cols - 1), 0);
|
||||
int to = MIN( mask.cols - 1, ipos.x + winSize );
|
||||
int ypos = MAX( MIN(ipos.y, mask.rows - 1), 0);
|
||||
for(int x = ipos.x; x <= to ; x++)
|
||||
{
|
||||
mask.at<uchar>(ypos, x) = 255;
|
||||
}
|
||||
}
|
||||
cv::Mat datat = data.t();
|
||||
cv::Mat maskt = mask.t();
|
||||
parallel_for_(cv::Range(0, datat.rows), HorizontalCrossSegmentation(ptList, npoints, pointScale, &datat, winSize, &dst, threshold, true, &mask));
|
||||
parallel_for_(cv::Range(0, data.rows), HorizontalCrossSegmentation(ptList, npoints, pointScale, &data, winSize, &dst, threshold, false, &maskt));
|
||||
|
||||
}
|
||||
|
||||
|
||||
static inline
|
||||
bool isrobust(const RLOFOpticalFlowParameter & param)
|
||||
{
|
||||
return (param.normSigma0 < 255 && param.normSigma1 < 255);
|
||||
}
|
||||
static inline
|
||||
std::vector<float> get_norm(float sigma0, float sigma1)
|
||||
{
|
||||
std::vector<float> result = { sigma0, sigma1, sigma0 / (sigma0 - sigma1), sigma0 * sigma1 };
|
||||
return result;
|
||||
}
|
||||
|
||||
static
|
||||
int buildOpticalFlowPyramidScale(InputArray _img, OutputArrayOfArrays pyramid, Size winSize, int maxLevel, bool withDerivatives,
|
||||
int pyrBorder, int derivBorder, bool tryReuseInputImage, float levelScale[2])
|
||||
{
|
||||
Mat img = _img.getMat();
|
||||
CV_Assert(img.depth() == CV_8U && winSize.width > 2 && winSize.height > 2);
|
||||
int pyrstep = withDerivatives ? 2 : 1;
|
||||
|
||||
pyramid.create(1, (maxLevel + 1) * pyrstep, 0 /*type*/, -1, true);
|
||||
|
||||
int derivType = CV_MAKETYPE(DataType<short>::depth, img.channels() * 2);
|
||||
|
||||
//level 0
|
||||
bool lvl0IsSet = false;
|
||||
if (tryReuseInputImage && img.isSubmatrix() && (pyrBorder & BORDER_ISOLATED) == 0)
|
||||
{
|
||||
Size wholeSize;
|
||||
Point ofs;
|
||||
img.locateROI(wholeSize, ofs);
|
||||
if (ofs.x >= winSize.width && ofs.y >= winSize.height
|
||||
&& ofs.x + img.cols + winSize.width <= wholeSize.width
|
||||
&& ofs.y + img.rows + winSize.height <= wholeSize.height)
|
||||
{
|
||||
pyramid.getMatRef(0) = img;
|
||||
lvl0IsSet = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!lvl0IsSet)
|
||||
{
|
||||
Mat& temp = pyramid.getMatRef(0);
|
||||
|
||||
if (!temp.empty())
|
||||
temp.adjustROI(winSize.height, winSize.height, winSize.width, winSize.width);
|
||||
if (temp.type() != img.type() || temp.cols != winSize.width * 2 + img.cols || temp.rows != winSize.height * 2 + img.rows)
|
||||
temp.create(img.rows + winSize.height * 2, img.cols + winSize.width * 2, img.type());
|
||||
|
||||
if (pyrBorder == BORDER_TRANSPARENT)
|
||||
img.copyTo(temp(Rect(winSize.width, winSize.height, img.cols, img.rows)));
|
||||
else
|
||||
copyMakeBorder(img, temp, winSize.height, winSize.height, winSize.width, winSize.width, pyrBorder);
|
||||
temp.adjustROI(-winSize.height, -winSize.height, -winSize.width, -winSize.width);
|
||||
}
|
||||
|
||||
Size sz = img.size();
|
||||
Mat prevLevel = pyramid.getMatRef(0);
|
||||
Mat thisLevel = prevLevel;
|
||||
|
||||
for (int level = 0; level <= maxLevel; ++level)
|
||||
{
|
||||
if (level != 0)
|
||||
{
|
||||
Mat& temp = pyramid.getMatRef(level * pyrstep);
|
||||
|
||||
if (!temp.empty())
|
||||
temp.adjustROI(winSize.height, winSize.height, winSize.width, winSize.width);
|
||||
if (temp.type() != img.type() || temp.cols != winSize.width * 2 + sz.width || temp.rows != winSize.height * 2 + sz.height)
|
||||
temp.create(sz.height + winSize.height * 2, sz.width + winSize.width * 2, img.type());
|
||||
|
||||
thisLevel = temp(Rect(winSize.width, winSize.height, sz.width, sz.height));
|
||||
pyrDown(prevLevel, thisLevel, sz);
|
||||
|
||||
if (pyrBorder != BORDER_TRANSPARENT)
|
||||
copyMakeBorder(thisLevel, temp, winSize.height, winSize.height, winSize.width, winSize.width, pyrBorder | BORDER_ISOLATED);
|
||||
temp.adjustROI(-winSize.height, -winSize.height, -winSize.width, -winSize.width);
|
||||
}
|
||||
|
||||
if (withDerivatives)
|
||||
{
|
||||
Mat& deriv = pyramid.getMatRef(level * pyrstep + 1);
|
||||
|
||||
if (!deriv.empty())
|
||||
deriv.adjustROI(winSize.height, winSize.height, winSize.width, winSize.width);
|
||||
if (deriv.type() != derivType || deriv.cols != winSize.width * 2 + sz.width || deriv.rows != winSize.height * 2 + sz.height)
|
||||
deriv.create(sz.height + winSize.height * 2, sz.width + winSize.width * 2, derivType);
|
||||
|
||||
Mat derivI = deriv(Rect(winSize.width, winSize.height, sz.width, sz.height));
|
||||
calcSharrDeriv(thisLevel, derivI);
|
||||
|
||||
if (derivBorder != BORDER_TRANSPARENT)
|
||||
copyMakeBorder(derivI, deriv, winSize.height, winSize.height, winSize.width, winSize.width, derivBorder | BORDER_ISOLATED);
|
||||
deriv.adjustROI(-winSize.height, -winSize.height, -winSize.width, -winSize.width);
|
||||
}
|
||||
|
||||
sz = Size(static_cast<int>((sz.width + 1) / levelScale[0]),
|
||||
static_cast<int>((sz.height + 1) / levelScale[1]));
|
||||
if (sz.width <= winSize.width || sz.height <= winSize.height)
|
||||
{
|
||||
pyramid.create(1, (level + 1) * pyrstep, 0 /*type*/, -1, true);//check this
|
||||
return level;
|
||||
}
|
||||
|
||||
prevLevel = thisLevel;
|
||||
}
|
||||
|
||||
return maxLevel;
|
||||
}
|
||||
|
||||
int CImageBuffer::buildPyramid(cv::Size winSize, int maxLevel, float levelScale[2],bool withBlurredImage )
|
||||
{
|
||||
if (! m_Overwrite)
|
||||
return m_maxLevel;
|
||||
if (withBlurredImage)
|
||||
m_maxLevel = buildOpticalFlowPyramidScale(m_BlurredImage, m_ImagePyramid, winSize, maxLevel, false, 4, 0, true, levelScale);
|
||||
else
|
||||
m_maxLevel = buildOpticalFlowPyramidScale(m_Image, m_ImagePyramid, winSize, maxLevel, false, 4, 0, true, levelScale);
|
||||
return m_maxLevel;
|
||||
}
|
||||
|
||||
static
|
||||
void calcLocalOpticalFlowCore(
|
||||
Ptr<CImageBuffer> prevPyramids[2],
|
||||
Ptr<CImageBuffer> currPyramids[2],
|
||||
InputArray _prevPts,
|
||||
InputOutputArray _nextPts,
|
||||
const RLOFOpticalFlowParameter & param)
|
||||
{
|
||||
|
||||
bool useAdditionalRGB = param.supportRegionType == SR_CROSS;
|
||||
int iWinSize = param.largeWinSize;
|
||||
int winSizes[2] = { iWinSize, iWinSize };
|
||||
if (param.supportRegionType != SR_FIXED)
|
||||
{
|
||||
winSizes[0] = param.smallWinSize;
|
||||
}
|
||||
//cv::Size winSize(iWinSize, iWinSize);
|
||||
cv::TermCriteria criteria(cv::TermCriteria::COUNT | cv::TermCriteria::EPS, param.maxIteration, 0.01);
|
||||
std::vector<float> rlofNorm = get_norm(param.normSigma0, param.normSigma1);
|
||||
CV_Assert(winSizes[0] <= winSizes[1]);
|
||||
|
||||
bool usePreComputedCross = winSizes[0] != winSizes[1];
|
||||
Mat prevPtsMat = _prevPts.getMat();
|
||||
const int derivDepth = DataType<detail::deriv_type>::depth;
|
||||
|
||||
CV_Assert(param.maxLevel >= 0 && iWinSize > 2);
|
||||
|
||||
int level = 0, npoints;
|
||||
CV_Assert((npoints = prevPtsMat.checkVector(2, CV_32F, true)) >= 0);
|
||||
|
||||
if (!(param.useInitialFlow))
|
||||
_nextPts.create(prevPtsMat.size(), prevPtsMat.type(), -1, true);
|
||||
|
||||
Mat nextPtsMat = _nextPts.getMat();
|
||||
CV_Assert(nextPtsMat.checkVector(2, CV_32F, true) == npoints);
|
||||
|
||||
const Point2f* prevPts = (const Point2f*)prevPtsMat.data;
|
||||
Point2f* nextPts = (Point2f*)nextPtsMat.data;
|
||||
std::vector<uchar> status(npoints);
|
||||
std::vector<float> err(npoints);
|
||||
std::vector<Point2f> gainPts(npoints);
|
||||
|
||||
float levelScale[2] = { 2.f,2.f };
|
||||
|
||||
int maxLevel = prevPyramids[0]->buildPyramid(cv::Size(iWinSize, iWinSize), param.maxLevel, levelScale);
|
||||
maxLevel = currPyramids[0]->buildPyramid(cv::Size(iWinSize, iWinSize), maxLevel, levelScale);
|
||||
|
||||
if (useAdditionalRGB)
|
||||
{
|
||||
prevPyramids[1]->buildPyramid(cv::Size(iWinSize, iWinSize), maxLevel, levelScale, true);
|
||||
currPyramids[1]->buildPyramid(cv::Size(iWinSize, iWinSize), maxLevel, levelScale, true);
|
||||
}
|
||||
|
||||
if ((criteria.type & TermCriteria::COUNT) == 0)
|
||||
criteria.maxCount = 30;
|
||||
else
|
||||
criteria.maxCount = std::min(std::max(criteria.maxCount, 0), 100);
|
||||
if ((criteria.type & TermCriteria::EPS) == 0)
|
||||
criteria.epsilon = 0.001;
|
||||
else
|
||||
criteria.epsilon = std::min(std::max(criteria.epsilon, 0.), 10.);
|
||||
criteria.epsilon *= criteria.epsilon;
|
||||
|
||||
// dI/dx ~ Ix, dI/dy ~ Iy
|
||||
Mat derivIBuf;
|
||||
derivIBuf.create(prevPyramids[0]->m_ImagePyramid[0].rows + iWinSize * 2, prevPyramids[0]->m_ImagePyramid[0].cols + iWinSize * 2, CV_MAKETYPE(derivDepth, prevPyramids[0]->m_ImagePyramid[0].channels() * 2));
|
||||
|
||||
for (level = maxLevel; level >= 0; level--)
|
||||
{
|
||||
Mat derivI;
|
||||
Size imgSize = prevPyramids[0]->getImage(level).size();
|
||||
Mat _derivI(imgSize.height + iWinSize * 2, imgSize.width + iWinSize * 2, derivIBuf.type(), derivIBuf.data);
|
||||
derivI = _derivI(Rect(iWinSize, iWinSize, imgSize.width, imgSize.height));
|
||||
calcSharrDeriv(prevPyramids[0]->getImage(level), derivI);
|
||||
copyMakeBorder(derivI, _derivI, iWinSize, iWinSize, iWinSize, iWinSize, BORDER_CONSTANT | BORDER_ISOLATED);
|
||||
|
||||
cv::Mat tRGBPrevPyr;
|
||||
cv::Mat tRGBNextPyr;
|
||||
if (useAdditionalRGB)
|
||||
{
|
||||
tRGBPrevPyr = prevPyramids[1]->getImage(level);
|
||||
tRGBNextPyr = prevPyramids[1]->getImage(level);
|
||||
|
||||
prevPyramids[1]->m_Overwrite = false;
|
||||
currPyramids[1]->m_Overwrite = false;
|
||||
}
|
||||
|
||||
cv::Mat prevImage = prevPyramids[0]->getImage(level);
|
||||
cv::Mat currImage = currPyramids[0]->getImage(level);
|
||||
|
||||
cv::Mat preCrossMap;
|
||||
if( usePreComputedCross )
|
||||
{
|
||||
preCalcCrossSegmentation(prevPts, npoints, (float)(1./(1 << level)), tRGBPrevPyr, winSizes[1], preCrossMap, param.crossSegmentationThreshold);
|
||||
tRGBNextPyr = cv::Mat();
|
||||
tRGBPrevPyr = preCrossMap;
|
||||
}
|
||||
// apply plk like tracker
|
||||
prevImage.adjustROI(iWinSize, iWinSize, iWinSize, iWinSize);
|
||||
currImage.adjustROI(iWinSize, iWinSize, iWinSize, iWinSize);
|
||||
derivI.adjustROI(iWinSize, iWinSize, iWinSize, iWinSize);
|
||||
if (isrobust(param) == false)
|
||||
{
|
||||
if (param.useIlluminationModel)
|
||||
{
|
||||
if (param.solverType == SolverType::ST_STANDART)
|
||||
{
|
||||
cv::parallel_for_(cv::Range(0, npoints),
|
||||
plk::radial::TrackerInvoker(
|
||||
prevImage, derivI, currImage, tRGBPrevPyr, tRGBNextPyr,
|
||||
prevPts, nextPts, &status[0], &err[0], &gainPts[0],
|
||||
level, maxLevel, winSizes,
|
||||
param.maxIteration,
|
||||
param.useInitialFlow,
|
||||
param.supportRegionType,
|
||||
param.minEigenValue,
|
||||
param.crossSegmentationThreshold));
|
||||
}
|
||||
else
|
||||
{
|
||||
cv::parallel_for_(cv::Range(0, npoints),
|
||||
beplk::radial::TrackerInvoker(
|
||||
prevImage, derivI, currImage, tRGBPrevPyr, tRGBNextPyr,
|
||||
prevPts, nextPts, &status[0], &err[0], &gainPts[0],
|
||||
level, maxLevel, winSizes,
|
||||
param.maxIteration,
|
||||
param.useInitialFlow,
|
||||
param.supportRegionType,
|
||||
param.crossSegmentationThreshold,
|
||||
param.minEigenValue));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (param.solverType == SolverType::ST_STANDART)
|
||||
{
|
||||
cv::parallel_for_(cv::Range(0, npoints),
|
||||
plk::ica::TrackerInvoker(
|
||||
prevImage, derivI, currImage, tRGBPrevPyr, tRGBNextPyr,
|
||||
prevPts, nextPts, &status[0], &err[0],
|
||||
level, maxLevel, winSizes,
|
||||
param.maxIteration,
|
||||
param.useInitialFlow,
|
||||
param.supportRegionType,
|
||||
param.crossSegmentationThreshold,
|
||||
param.minEigenValue));
|
||||
}
|
||||
else
|
||||
{
|
||||
cv::parallel_for_(cv::Range(0, npoints),
|
||||
beplk::ica::TrackerInvoker(prevImage, derivI, currImage, tRGBPrevPyr, tRGBNextPyr,
|
||||
prevPts, nextPts, &status[0], &err[0],
|
||||
level, maxLevel, winSizes,
|
||||
param.maxIteration,
|
||||
param.useInitialFlow,
|
||||
param.supportRegionType,
|
||||
param.crossSegmentationThreshold,
|
||||
param.minEigenValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
// for robust models
|
||||
else
|
||||
{
|
||||
if (param.useIlluminationModel)
|
||||
{
|
||||
if (param.solverType == SolverType::ST_STANDART)
|
||||
{
|
||||
cv::parallel_for_(cv::Range(0, npoints),
|
||||
rlof::radial::TrackerInvoker(
|
||||
prevImage, derivI, currImage, tRGBPrevPyr, tRGBNextPyr,
|
||||
prevPts, nextPts, &status[0], &err[0], &gainPts[0],
|
||||
level, maxLevel, winSizes,
|
||||
param.maxIteration,
|
||||
param.useInitialFlow,
|
||||
param.supportRegionType,
|
||||
rlofNorm,
|
||||
param.minEigenValue,
|
||||
param.crossSegmentationThreshold));
|
||||
}
|
||||
else
|
||||
{
|
||||
cv::parallel_for_(cv::Range(0, npoints),
|
||||
berlof::radial::TrackerInvoker(prevImage, derivI, currImage, tRGBPrevPyr, tRGBNextPyr,
|
||||
prevPts, nextPts, &status[0], &err[0], &gainPts[0],
|
||||
level, maxLevel, winSizes,
|
||||
param.maxIteration,
|
||||
param.useInitialFlow,
|
||||
param.supportRegionType,
|
||||
param.crossSegmentationThreshold,
|
||||
rlofNorm,
|
||||
param.minEigenValue));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (param.solverType == SolverType::ST_STANDART)
|
||||
{
|
||||
cv::parallel_for_(cv::Range(0, npoints),
|
||||
rlof::ica::TrackerInvoker(
|
||||
prevImage, derivI, currImage, tRGBPrevPyr, tRGBNextPyr,
|
||||
prevPts, nextPts, &status[0], &err[0],
|
||||
level, maxLevel, winSizes,
|
||||
param.maxIteration,
|
||||
param.useInitialFlow,
|
||||
param.supportRegionType,
|
||||
rlofNorm,
|
||||
param.minEigenValue,
|
||||
param.crossSegmentationThreshold));
|
||||
}
|
||||
else
|
||||
{
|
||||
cv::parallel_for_(cv::Range(0, npoints),
|
||||
berlof::ica::TrackerInvoker(prevImage, derivI, currImage, tRGBPrevPyr, tRGBNextPyr,
|
||||
prevPts, nextPts, &status[0], &err[0],
|
||||
level, maxLevel, winSizes,
|
||||
param.maxIteration,
|
||||
param.useInitialFlow,
|
||||
param.supportRegionType,
|
||||
param.crossSegmentationThreshold,
|
||||
rlofNorm,
|
||||
param.minEigenValue));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
prevPyramids[0]->m_Overwrite = true;
|
||||
currPyramids[0]->m_Overwrite = true;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void preprocess(Ptr<CImageBuffer> prevPyramids[2],
|
||||
Ptr<CImageBuffer> currPyramids[2],
|
||||
const std::vector<cv::Point2f> & prevPoints,
|
||||
std::vector<cv::Point2f> & currPoints,
|
||||
const RLOFOpticalFlowParameter & param)
|
||||
{
|
||||
cv::Mat mask, homography;
|
||||
if (param.useGlobalMotionPrior == false)
|
||||
return;
|
||||
|
||||
currPoints.resize(prevPoints.size());
|
||||
|
||||
RLOFOpticalFlowParameter gmeTrackerParam = param;
|
||||
gmeTrackerParam.useGlobalMotionPrior = false;
|
||||
gmeTrackerParam.largeWinSize = 17;
|
||||
// use none robust tracker for global motion estimation since it is faster
|
||||
gmeTrackerParam.normSigma0 = std::numeric_limits<float>::max();
|
||||
gmeTrackerParam.maxIteration = MAX(15, param.maxIteration);
|
||||
gmeTrackerParam.minEigenValue = 0.000001f;
|
||||
|
||||
std::vector<cv::Point2f> gmPrevPoints, gmCurrPoints;
|
||||
|
||||
// Initialize point grid
|
||||
int stepr = prevPyramids[0]->m_Image.rows / 30;
|
||||
int stepc = prevPyramids[0]->m_Image.cols / 40;
|
||||
for (int r = stepr / 2; r < prevPyramids[0]->m_Image.rows; r += stepr)
|
||||
{
|
||||
for (int c = stepc / 2; c < prevPyramids[0]->m_Image.cols; c += stepc)
|
||||
{
|
||||
gmPrevPoints.push_back(cv::Point2f(static_cast<float>(c), static_cast<float>(r)));
|
||||
}
|
||||
}
|
||||
|
||||
// perform motion estimation
|
||||
calcLocalOpticalFlowCore(prevPyramids, currPyramids, gmPrevPoints, gmCurrPoints, gmeTrackerParam);
|
||||
|
||||
cv::Mat prevPointsMat(static_cast<int>(gmPrevPoints.size()), 1, CV_32FC2);
|
||||
cv::Mat currPointsMat(static_cast<int>(gmPrevPoints.size()), 1, CV_32FC2);
|
||||
cv::Mat distMat(static_cast<int>(gmPrevPoints.size()), 1, CV_32FC1);
|
||||
|
||||
// Forward backward confidence to estimate optimal ransac reprojection error
|
||||
int noPoints = 0;
|
||||
for (unsigned int n = 0; n < gmPrevPoints.size(); n++)
|
||||
{
|
||||
cv::Point2f flow = gmCurrPoints[n] - gmPrevPoints[n];
|
||||
prevPointsMat.at<cv::Point2f>(noPoints) = gmPrevPoints[n];
|
||||
currPointsMat.at<cv::Point2f>(noPoints) = gmCurrPoints[n];
|
||||
distMat.at<float>(noPoints) = flow.x * flow.x + flow.y* flow.y;
|
||||
if (isnan(distMat.at<float>(noPoints)) == false)
|
||||
noPoints++;
|
||||
}
|
||||
|
||||
float medianDist = (param.globalMotionRansacThreshold == 0) ? 1.f :
|
||||
quickselect<float>(distMat, static_cast<int>(noPoints * static_cast<float>(param.globalMotionRansacThreshold) / 100.f));
|
||||
medianDist = sqrt(medianDist);
|
||||
|
||||
if (noPoints < 8)
|
||||
return;
|
||||
|
||||
cv::findHomography(prevPointsMat(cv::Rect(0, 0, 1, noPoints)), currPointsMat(cv::Rect(0, 0, 1, noPoints)), cv::RANSAC, medianDist, mask).convertTo(homography, CV_32FC1);
|
||||
|
||||
if (homography.empty())
|
||||
return;
|
||||
|
||||
cv::perspectiveTransform(prevPoints, currPoints, homography);
|
||||
}
|
||||
|
||||
void calcLocalOpticalFlow(
|
||||
const Mat prevImage,
|
||||
const Mat currImage,
|
||||
Ptr<CImageBuffer> prevPyramids[2],
|
||||
Ptr<CImageBuffer> currPyramids[2],
|
||||
const std::vector<Point2f> & prevPoints,
|
||||
std::vector<Point2f> & currPoints,
|
||||
const RLOFOpticalFlowParameter & param)
|
||||
{
|
||||
if (prevImage.empty() == false && currImage.empty()== false)
|
||||
{
|
||||
prevPyramids[0]->m_Overwrite = true;
|
||||
currPyramids[0]->m_Overwrite = true;
|
||||
prevPyramids[1]->m_Overwrite = true;
|
||||
// perform blurring and build blur pyramid only for the prev image
|
||||
currPyramids[1]->m_Overwrite = false;
|
||||
if (prevImage.type() == CV_8UC3)
|
||||
{
|
||||
prevPyramids[0]->setGrayFromRGB(prevImage);
|
||||
currPyramids[0]->setGrayFromRGB(currImage);
|
||||
prevPyramids[1]->setImage(prevImage);
|
||||
currPyramids[1]->setImage(currImage);
|
||||
|
||||
if (param.supportRegionType == SR_CROSS)
|
||||
{
|
||||
prevPyramids[1]->setBlurFromRGB(prevImage);
|
||||
currPyramids[1]->setBlurFromRGB(currImage);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
prevPyramids[0]->setImage(prevImage);
|
||||
currPyramids[0]->setImage(currImage);
|
||||
}
|
||||
}
|
||||
preprocess(prevPyramids, currPyramids, prevPoints, currPoints, param);
|
||||
RLOFOpticalFlowParameter internParam = param;
|
||||
if (param.useGlobalMotionPrior == true)
|
||||
internParam.useInitialFlow = true;
|
||||
calcLocalOpticalFlowCore(prevPyramids, currPyramids, prevPoints, currPoints, internParam);
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,117 @@
|
||||
// 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 _RLOF_LOCALFLOW_H_
|
||||
#define _RLOF_LOCALFLOW_H_
|
||||
#include <limits>
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
#include <stdio.h>
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/optflow/rlofflow.hpp"
|
||||
//! Fast median estimation method based on @cite Tibshirani2008. This implementation relates to http://www.stat.cmu.edu/~ryantibs/median/
|
||||
using namespace cv;
|
||||
template<typename T>
|
||||
T quickselect(const Mat & inp, int k)
|
||||
{
|
||||
unsigned long i;
|
||||
unsigned long ir;
|
||||
unsigned long j;
|
||||
unsigned long l;
|
||||
unsigned long mid;
|
||||
Mat values = inp.clone();
|
||||
T a;
|
||||
|
||||
l = 0;
|
||||
ir = MAX(values.rows, values.cols) - 1;
|
||||
while(true)
|
||||
{
|
||||
if (ir <= l + 1)
|
||||
{
|
||||
if (ir == l + 1 && values.at<T>(ir) < values.at<T>(l))
|
||||
std::swap(values.at<T>(l), values.at<T>(ir));
|
||||
return values.at<T>(k);
|
||||
}
|
||||
else
|
||||
{
|
||||
mid = (l + ir) >> 1;
|
||||
std::swap(values.at<T>(mid), values.at<T>(l+1));
|
||||
if (values.at<T>(l) > values.at<T>(ir))
|
||||
std::swap(values.at<T>(l), values.at<T>(ir));
|
||||
if (values.at<T>(l+1) > values.at<T>(ir))
|
||||
std::swap(values.at<T>(l+1), values.at<T>(ir));
|
||||
if (values.at<T>(l) > values.at<T>(l+1))
|
||||
std::swap(values.at<T>(l), values.at<T>(l+1));
|
||||
i = l + 1;
|
||||
j = ir;
|
||||
a = values.at<T>(l+1);
|
||||
while (true)
|
||||
{
|
||||
do
|
||||
{
|
||||
i++;
|
||||
}
|
||||
while (values.at<T>(i) < a);
|
||||
do
|
||||
{
|
||||
j--;
|
||||
}
|
||||
while (values.at<T>(j) > a);
|
||||
if (j < i) break;
|
||||
std::swap(values.at<T>(i), values.at<T>(j));
|
||||
}
|
||||
values.at<T>(l+1) = values.at<T>(j);
|
||||
values.at<T>(j) = a;
|
||||
if (j >= static_cast<unsigned long>(k)) ir = j - 1;
|
||||
if (j <= static_cast<unsigned long>(k)) l = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace cv {
|
||||
namespace optflow {
|
||||
|
||||
class CImageBuffer
|
||||
{
|
||||
public:
|
||||
CImageBuffer()
|
||||
: m_Overwrite(true)
|
||||
{}
|
||||
void setGrayFromRGB(const cv::Mat & inp)
|
||||
{
|
||||
if(m_Overwrite)
|
||||
cv::cvtColor(inp, m_Image, cv::COLOR_BGR2GRAY);
|
||||
}
|
||||
void setImage(const cv::Mat & inp)
|
||||
{
|
||||
if(m_Overwrite)
|
||||
inp.copyTo(m_Image);
|
||||
}
|
||||
void setBlurFromRGB(const cv::Mat & inp)
|
||||
{
|
||||
if(m_Overwrite)
|
||||
cv::GaussianBlur(inp, m_BlurredImage, cv::Size(7,7), -1);
|
||||
}
|
||||
|
||||
int buildPyramid(cv::Size winSize, int maxLevel, float levelScale[2], bool withBlurredImage = false);
|
||||
cv::Mat & getImage(int level) {return m_ImagePyramid[level];}
|
||||
|
||||
std::vector<cv::Mat> m_ImagePyramid;
|
||||
cv::Mat m_BlurredImage;
|
||||
cv::Mat m_Image;
|
||||
std::vector<cv::Mat> m_CrossPyramid;
|
||||
int m_maxLevel;
|
||||
bool m_Overwrite;
|
||||
};
|
||||
|
||||
void calcLocalOpticalFlow(
|
||||
const Mat prevImage,
|
||||
const Mat currImage,
|
||||
Ptr<CImageBuffer> prevPyramids[2],
|
||||
Ptr<CImageBuffer> currPyramids[2],
|
||||
const std::vector<Point2f> & prevPoints,
|
||||
std::vector<Point2f> & currPoints,
|
||||
const RLOFOpticalFlowParameter & param);
|
||||
|
||||
}} // namespace
|
||||
#endif
|
||||
@@ -0,0 +1,482 @@
|
||||
// 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 "rlof/rlof_localflow.h"
|
||||
#include "rlof/geo_interpolation.hpp"
|
||||
#include "opencv2/ximgproc.hpp"
|
||||
|
||||
|
||||
namespace cv {
|
||||
namespace optflow {
|
||||
|
||||
Ptr<RLOFOpticalFlowParameter> RLOFOpticalFlowParameter::create()
|
||||
{
|
||||
return Ptr<RLOFOpticalFlowParameter>(new RLOFOpticalFlowParameter);
|
||||
}
|
||||
|
||||
void RLOFOpticalFlowParameter::setUseMEstimator(bool val)
|
||||
{
|
||||
if (val)
|
||||
{
|
||||
normSigma0 = 3.2f;
|
||||
normSigma1 = 7.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
normSigma0 = std::numeric_limits<float>::max();
|
||||
normSigma1 = std::numeric_limits<float>::max();
|
||||
}
|
||||
}
|
||||
void RLOFOpticalFlowParameter::setSolverType(SolverType val){ solverType = val;}
|
||||
SolverType RLOFOpticalFlowParameter::getSolverType() const { return solverType;}
|
||||
|
||||
void RLOFOpticalFlowParameter::setSupportRegionType(SupportRegionType val){ supportRegionType = val;}
|
||||
SupportRegionType RLOFOpticalFlowParameter::getSupportRegionType() const { return supportRegionType;}
|
||||
|
||||
void RLOFOpticalFlowParameter::setNormSigma0(float val){ normSigma0 = val;}
|
||||
float RLOFOpticalFlowParameter::getNormSigma0() const { return normSigma0;}
|
||||
|
||||
void RLOFOpticalFlowParameter::setNormSigma1(float val){ normSigma1 = val;}
|
||||
float RLOFOpticalFlowParameter::getNormSigma1() const { return normSigma1;}
|
||||
|
||||
void RLOFOpticalFlowParameter::setSmallWinSize(int val){ smallWinSize = val;}
|
||||
int RLOFOpticalFlowParameter::getSmallWinSize() const { return smallWinSize;}
|
||||
|
||||
void RLOFOpticalFlowParameter::setLargeWinSize(int val){ largeWinSize = val;}
|
||||
int RLOFOpticalFlowParameter::getLargeWinSize() const { return largeWinSize;}
|
||||
|
||||
void RLOFOpticalFlowParameter::setCrossSegmentationThreshold(int val){ crossSegmentationThreshold = val;}
|
||||
int RLOFOpticalFlowParameter::getCrossSegmentationThreshold() const { return crossSegmentationThreshold;}
|
||||
|
||||
void RLOFOpticalFlowParameter::setMaxLevel(int val){ maxLevel = val;}
|
||||
int RLOFOpticalFlowParameter::getMaxLevel() const { return maxLevel;}
|
||||
|
||||
void RLOFOpticalFlowParameter::setUseInitialFlow(bool val){ useInitialFlow = val;}
|
||||
bool RLOFOpticalFlowParameter::getUseInitialFlow() const { return useInitialFlow;}
|
||||
|
||||
void RLOFOpticalFlowParameter::setUseIlluminationModel(bool val){ useIlluminationModel = val;}
|
||||
bool RLOFOpticalFlowParameter::getUseIlluminationModel() const { return useIlluminationModel;}
|
||||
|
||||
void RLOFOpticalFlowParameter::setUseGlobalMotionPrior(bool val){ useGlobalMotionPrior = val;}
|
||||
bool RLOFOpticalFlowParameter::getUseGlobalMotionPrior() const { return useGlobalMotionPrior;}
|
||||
|
||||
void RLOFOpticalFlowParameter::setMaxIteration(int val){ maxIteration = val;}
|
||||
int RLOFOpticalFlowParameter::getMaxIteration() const { return maxIteration;}
|
||||
|
||||
void RLOFOpticalFlowParameter::setMinEigenValue(float val){ minEigenValue = val;}
|
||||
float RLOFOpticalFlowParameter::getMinEigenValue() const { return minEigenValue;}
|
||||
|
||||
void RLOFOpticalFlowParameter::setGlobalMotionRansacThreshold(float val){ globalMotionRansacThreshold = val;}
|
||||
float RLOFOpticalFlowParameter::getGlobalMotionRansacThreshold() const { return globalMotionRansacThreshold;}
|
||||
|
||||
class DenseOpticalFlowRLOFImpl : public DenseRLOFOpticalFlow
|
||||
{
|
||||
public:
|
||||
DenseOpticalFlowRLOFImpl()
|
||||
: param(Ptr<RLOFOpticalFlowParameter>(new RLOFOpticalFlowParameter))
|
||||
, forwardBackwardThreshold(1.f)
|
||||
, gridStep(6, 6)
|
||||
, interp_type(InterpolationType::INTERP_GEO)
|
||||
, k(128)
|
||||
, sigma(0.05f)
|
||||
, lambda(999.f)
|
||||
, fgs_lambda(500.0f)
|
||||
, fgs_sigma(1.5f)
|
||||
, use_post_proc(true)
|
||||
, use_variational_refinement(false)
|
||||
, sp_size(15)
|
||||
, slic_type(ximgproc::SLIC)
|
||||
|
||||
{
|
||||
prevPyramid[0] = cv::Ptr<CImageBuffer>(new CImageBuffer);
|
||||
prevPyramid[1] = cv::Ptr<CImageBuffer>(new CImageBuffer);
|
||||
currPyramid[0] = cv::Ptr<CImageBuffer>(new CImageBuffer);
|
||||
currPyramid[1] = cv::Ptr<CImageBuffer>(new CImageBuffer);
|
||||
}
|
||||
virtual void setRLOFOpticalFlowParameter(Ptr<RLOFOpticalFlowParameter> val) CV_OVERRIDE { param = val; }
|
||||
virtual Ptr<RLOFOpticalFlowParameter> getRLOFOpticalFlowParameter() const CV_OVERRIDE { return param; }
|
||||
|
||||
virtual float getForwardBackward() const CV_OVERRIDE { return forwardBackwardThreshold; }
|
||||
virtual void setForwardBackward(float val) CV_OVERRIDE { forwardBackwardThreshold = val; }
|
||||
|
||||
virtual void setInterpolation(InterpolationType val) CV_OVERRIDE { interp_type = val; }
|
||||
virtual InterpolationType getInterpolation() const CV_OVERRIDE { return interp_type; }
|
||||
|
||||
virtual Size getGridStep() const CV_OVERRIDE { return gridStep; }
|
||||
virtual void setGridStep(Size val) CV_OVERRIDE { gridStep = val; }
|
||||
|
||||
virtual int getEPICK() const CV_OVERRIDE { return k; }
|
||||
virtual void setEPICK(int val) CV_OVERRIDE { k = val; }
|
||||
|
||||
virtual float getEPICSigma() const CV_OVERRIDE { return sigma; }
|
||||
virtual void setEPICSigma(float val) CV_OVERRIDE { sigma = val; }
|
||||
|
||||
virtual float getEPICLambda() const CV_OVERRIDE { return lambda; }
|
||||
virtual void setEPICLambda(float val) CV_OVERRIDE { lambda = val; }
|
||||
|
||||
virtual float getFgsLambda() const CV_OVERRIDE { return fgs_lambda; }
|
||||
virtual void setFgsLambda(float val) CV_OVERRIDE { fgs_lambda = val; }
|
||||
|
||||
virtual float getFgsSigma() const CV_OVERRIDE { return fgs_sigma; }
|
||||
virtual void setFgsSigma(float val) CV_OVERRIDE { fgs_sigma = val; }
|
||||
|
||||
virtual bool getUsePostProc() const CV_OVERRIDE { return use_post_proc; }
|
||||
virtual void setUsePostProc(bool val) CV_OVERRIDE { use_post_proc = val; }
|
||||
|
||||
virtual void setUseVariationalRefinement(bool val) CV_OVERRIDE { use_variational_refinement = val; }
|
||||
virtual bool getUseVariationalRefinement() const CV_OVERRIDE { return use_variational_refinement; }
|
||||
|
||||
virtual void setRICSPSize(int val) CV_OVERRIDE { sp_size = val; }
|
||||
virtual int getRICSPSize() const CV_OVERRIDE { return sp_size; }
|
||||
|
||||
virtual void setRICSLICType(int val) CV_OVERRIDE { slic_type = static_cast<ximgproc::SLICType>(val); }
|
||||
virtual int getRICSLICType() const CV_OVERRIDE { return slic_type; }
|
||||
|
||||
virtual void calc(InputArray I0, InputArray I1, InputOutputArray flow) CV_OVERRIDE
|
||||
{
|
||||
CV_Assert(!I0.empty() && I0.depth() == CV_8U && (I0.channels() == 3 || I0.channels() == 1));
|
||||
CV_Assert(!I1.empty() && I1.depth() == CV_8U && (I1.channels() == 3 || I1.channels() == 1));
|
||||
CV_Assert(I0.sameSize(I1));
|
||||
if (param.empty())
|
||||
param = Ptr<RLOFOpticalFlowParameter>(new RLOFOpticalFlowParameter());
|
||||
if (param->supportRegionType == SR_CROSS)
|
||||
CV_Assert( I0.channels() == 3 && I1.channels() == 3);
|
||||
CV_Assert(interp_type == InterpolationType::INTERP_EPIC || interp_type == InterpolationType::INTERP_GEO || interp_type == InterpolationType::INTERP_RIC);
|
||||
// if no parameter is used use the default parameter
|
||||
|
||||
Mat prevImage = I0.getMat();
|
||||
Mat currImage = I1.getMat();
|
||||
int noPoints = prevImage.cols * prevImage.rows;
|
||||
std::vector<cv::Point2f> prevPoints(noPoints);
|
||||
std::vector<cv::Point2f> currPoints, refPoints;
|
||||
noPoints = 0;
|
||||
cv::Size grid_h = gridStep / 2;
|
||||
for (int r = grid_h.height; r < prevImage.rows - grid_h.height; r += gridStep.height)
|
||||
{
|
||||
for (int c = grid_h.width; c < prevImage.cols - grid_h.width; c += gridStep.width)
|
||||
{
|
||||
prevPoints[noPoints++] = cv::Point2f(static_cast<float>(c), static_cast<float>(r));
|
||||
}
|
||||
}
|
||||
prevPoints.erase(prevPoints.begin() + noPoints, prevPoints.end());
|
||||
currPoints.resize(prevPoints.size());
|
||||
calcLocalOpticalFlow(prevImage, currImage, prevPyramid, currPyramid, prevPoints, currPoints, *(param.get()));
|
||||
flow.create(prevImage.size(), CV_32FC2);
|
||||
Mat dense_flow = flow.getMat();
|
||||
|
||||
std::vector<Point2f> filtered_prevPoints;
|
||||
std::vector<Point2f> filtered_currPoints;
|
||||
if (gridStep == cv::Size(1, 1) && forwardBackwardThreshold <= 0)
|
||||
{
|
||||
for (unsigned int n = 0; n < prevPoints.size(); n++)
|
||||
{
|
||||
dense_flow.at<Point2f>(prevPoints[n]) = currPoints[n] - prevPoints[n];
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (forwardBackwardThreshold > 0)
|
||||
{
|
||||
// reuse image pyramids
|
||||
calcLocalOpticalFlow(currImage, prevImage, currPyramid, prevPyramid, currPoints, refPoints, *(param.get()));
|
||||
|
||||
filtered_prevPoints.resize(prevPoints.size());
|
||||
filtered_currPoints.resize(prevPoints.size());
|
||||
float sqrForwardBackwardThreshold = forwardBackwardThreshold * forwardBackwardThreshold;
|
||||
noPoints = 0;
|
||||
for (unsigned int r = 0; r < refPoints.size(); r++)
|
||||
{
|
||||
Point2f diff = refPoints[r] - prevPoints[r];
|
||||
if (diff.x * diff.x + diff.y * diff.y < sqrForwardBackwardThreshold)
|
||||
{
|
||||
filtered_prevPoints[noPoints] = prevPoints[r];
|
||||
filtered_currPoints[noPoints++] = currPoints[r];
|
||||
}
|
||||
}
|
||||
|
||||
filtered_prevPoints.erase(filtered_prevPoints.begin() + noPoints, filtered_prevPoints.end());
|
||||
filtered_currPoints.erase(filtered_currPoints.begin() + noPoints, filtered_currPoints.end());
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
filtered_prevPoints = prevPoints;
|
||||
filtered_currPoints = currPoints;
|
||||
}
|
||||
// Interpolators below expect non empty matches
|
||||
if (filtered_prevPoints.empty()) {
|
||||
flow.setTo(0);
|
||||
return;
|
||||
}
|
||||
if (interp_type == InterpolationType::INTERP_EPIC)
|
||||
{
|
||||
Ptr<ximgproc::EdgeAwareInterpolator> gd = ximgproc::createEdgeAwareInterpolator();
|
||||
gd->setK(k);
|
||||
gd->setSigma(sigma);
|
||||
gd->setLambda(lambda);
|
||||
gd->setFGSLambda(fgs_lambda);
|
||||
gd->setFGSSigma(fgs_sigma);
|
||||
gd->setUsePostProcessing(use_post_proc);
|
||||
gd->interpolate(prevImage, filtered_prevPoints, currImage, filtered_currPoints, dense_flow);
|
||||
}
|
||||
else if (interp_type == InterpolationType::INTERP_RIC)
|
||||
{
|
||||
Ptr<ximgproc::RICInterpolator> gd = ximgproc::createRICInterpolator();
|
||||
gd->setK(k);
|
||||
gd->setFGSLambda(fgs_lambda);
|
||||
gd->setFGSSigma(fgs_sigma);
|
||||
gd->setSuperpixelSize(sp_size);
|
||||
gd->setSuperpixelMode(slic_type);
|
||||
gd->setUseGlobalSmootherFilter(use_post_proc);
|
||||
gd->setUseVariationalRefinement(false);
|
||||
gd->interpolate(prevImage, filtered_prevPoints, currImage, filtered_currPoints, dense_flow);
|
||||
}
|
||||
else
|
||||
{
|
||||
Mat blurredPrevImage, blurredCurrImage;
|
||||
GaussianBlur(prevImage, blurredPrevImage, cv::Size(5, 5), -1);
|
||||
std::vector<uchar> status(filtered_currPoints.size(), 1);
|
||||
interpolate_irregular_nn_raster(filtered_prevPoints, filtered_currPoints, status, blurredPrevImage).copyTo(dense_flow);
|
||||
std::vector<Mat> vecMats;
|
||||
std::vector<Mat> vecMats2(2);
|
||||
cv::split(dense_flow, vecMats);
|
||||
cv::bilateralFilter(vecMats[0], vecMats2[0], 5, 2, 20);
|
||||
cv::bilateralFilter(vecMats[1], vecMats2[1], 5, 2, 20);
|
||||
cv::merge(vecMats2, dense_flow);
|
||||
if (use_post_proc)
|
||||
{
|
||||
ximgproc::fastGlobalSmootherFilter(prevImage, flow, flow, fgs_lambda, fgs_sigma);
|
||||
}
|
||||
}
|
||||
if (use_variational_refinement)
|
||||
{
|
||||
Mat prevGrey, currGrey;
|
||||
Ptr<VariationalRefinement > variationalrefine = VariationalRefinement::create();
|
||||
cvtColor(prevImage, prevGrey, COLOR_BGR2GRAY);
|
||||
cvtColor(currImage, currGrey, COLOR_BGR2GRAY);
|
||||
variationalrefine->setOmega(1.9f);
|
||||
variationalrefine->calc(prevGrey, currGrey, flow);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void collectGarbage() CV_OVERRIDE
|
||||
{
|
||||
prevPyramid[0].release();
|
||||
prevPyramid[1].release();
|
||||
currPyramid[0].release();
|
||||
currPyramid[1].release();
|
||||
}
|
||||
|
||||
protected:
|
||||
Ptr<RLOFOpticalFlowParameter> param;
|
||||
float forwardBackwardThreshold;
|
||||
Ptr<CImageBuffer> prevPyramid[2];
|
||||
Ptr<CImageBuffer> currPyramid[2];
|
||||
cv::Size gridStep;
|
||||
InterpolationType interp_type;
|
||||
int k;
|
||||
float sigma;
|
||||
float lambda;
|
||||
float fgs_lambda;
|
||||
float fgs_sigma;
|
||||
bool use_post_proc;
|
||||
bool use_variational_refinement;
|
||||
int sp_size;
|
||||
ximgproc::SLICType slic_type;
|
||||
};
|
||||
|
||||
Ptr<DenseRLOFOpticalFlow> DenseRLOFOpticalFlow::create(
|
||||
Ptr<RLOFOpticalFlowParameter> rlofParam,
|
||||
float forwardBackwardThreshold,
|
||||
cv::Size gridStep,
|
||||
InterpolationType interp_type,
|
||||
int epicK,
|
||||
float epicSigma,
|
||||
float epicLambda,
|
||||
int ricSPSize,
|
||||
int ricSLICType,
|
||||
bool use_post_proc,
|
||||
float fgs_lambda,
|
||||
float fgs_sigma,
|
||||
bool use_variational_refinement)
|
||||
{
|
||||
Ptr<DenseRLOFOpticalFlow> algo = makePtr<DenseOpticalFlowRLOFImpl>();
|
||||
algo->setRLOFOpticalFlowParameter(rlofParam);
|
||||
algo->setForwardBackward(forwardBackwardThreshold);
|
||||
algo->setGridStep(gridStep);
|
||||
algo->setInterpolation(interp_type);
|
||||
algo->setEPICK(epicK);
|
||||
algo->setEPICSigma(epicSigma);
|
||||
algo->setEPICLambda(epicLambda);
|
||||
algo->setUsePostProc(use_post_proc);
|
||||
algo->setFgsLambda(fgs_lambda);
|
||||
algo->setFgsSigma(fgs_sigma);
|
||||
algo->setRICSLICType(ricSLICType);
|
||||
algo->setRICSPSize(ricSPSize);
|
||||
algo->setUseVariationalRefinement(use_variational_refinement);
|
||||
return algo;
|
||||
}
|
||||
|
||||
class SparseRLOFOpticalFlowImpl : public SparseRLOFOpticalFlow
|
||||
{
|
||||
public:
|
||||
SparseRLOFOpticalFlowImpl()
|
||||
: param(Ptr<RLOFOpticalFlowParameter>(new RLOFOpticalFlowParameter))
|
||||
, forwardBackwardThreshold(1.f)
|
||||
{
|
||||
prevPyramid[0] = cv::Ptr< CImageBuffer>(new CImageBuffer);
|
||||
prevPyramid[1] = cv::Ptr< CImageBuffer>(new CImageBuffer);
|
||||
currPyramid[0] = cv::Ptr< CImageBuffer>(new CImageBuffer);
|
||||
currPyramid[1] = cv::Ptr< CImageBuffer>(new CImageBuffer);
|
||||
}
|
||||
virtual void setRLOFOpticalFlowParameter(Ptr<RLOFOpticalFlowParameter> val) CV_OVERRIDE { param = val; }
|
||||
virtual Ptr<RLOFOpticalFlowParameter> getRLOFOpticalFlowParameter() const CV_OVERRIDE { return param; }
|
||||
|
||||
virtual float getForwardBackward() const CV_OVERRIDE { return forwardBackwardThreshold; }
|
||||
virtual void setForwardBackward(float val) CV_OVERRIDE { forwardBackwardThreshold = val; }
|
||||
|
||||
virtual void calc(InputArray prevImg, InputArray nextImg,
|
||||
InputArray prevPts, InputOutputArray nextPts,
|
||||
OutputArray status,
|
||||
OutputArray err) CV_OVERRIDE
|
||||
{
|
||||
CV_Assert(!prevImg.empty() && prevImg.depth() == CV_8U && (prevImg.channels() == 3 || prevImg.channels() == 1));
|
||||
CV_Assert(!nextImg.empty() && nextImg.depth() == CV_8U && (nextImg.channels() == 3 || nextImg.channels() == 1));
|
||||
CV_Assert(prevImg.sameSize(nextImg));
|
||||
|
||||
if (param.empty())
|
||||
{
|
||||
param = makePtr<RLOFOpticalFlowParameter>();
|
||||
}
|
||||
CV_DbgAssert(!param.empty());
|
||||
|
||||
if (param->supportRegionType == SR_CROSS)
|
||||
{
|
||||
CV_CheckChannelsEQ(prevImg.channels(), 3, "SR_CROSS mode requires images with 3 channels");
|
||||
CV_CheckChannelsEQ(nextImg.channels(), 3, "SR_CROSS mode requires images with 3 channels");
|
||||
}
|
||||
|
||||
Mat prevImage = prevImg.getMat();
|
||||
Mat nextImage = nextImg.getMat();
|
||||
Mat prevPtsMat = prevPts.getMat();
|
||||
|
||||
if (param->useInitialFlow == false)
|
||||
nextPts.create(prevPtsMat.size(), prevPtsMat.type(), -1, true);
|
||||
|
||||
int npoints = 0;
|
||||
CV_Assert((npoints = prevPtsMat.checkVector(2, CV_32F, true)) >= 0);
|
||||
if (npoints == 0)
|
||||
{
|
||||
nextPts.release();
|
||||
status.release();
|
||||
err.release();
|
||||
return;
|
||||
}
|
||||
Mat nextPtsMat = nextPts.getMat();
|
||||
CV_Assert(nextPtsMat.checkVector(2, CV_32F, true) == npoints);
|
||||
|
||||
std::vector<cv::Point2f> prevPoints(npoints), nextPoints(npoints), refPoints;
|
||||
|
||||
if (prevPtsMat.channels() != 2)
|
||||
prevPtsMat = prevPtsMat.reshape(2, npoints);
|
||||
|
||||
prevPtsMat.copyTo(prevPoints);
|
||||
|
||||
if (param->useInitialFlow )
|
||||
{
|
||||
if (nextPtsMat.channels() != 2)
|
||||
nextPtsMat = nextPtsMat.reshape(2, npoints);
|
||||
nextPtsMat.copyTo(nextPoints);
|
||||
}
|
||||
cv::Mat statusMat;
|
||||
cv::Mat errorMat;
|
||||
if (status.needed() || forwardBackwardThreshold > 0)
|
||||
{
|
||||
status.create((int)npoints, 1, CV_8U, -1, true);
|
||||
statusMat = status.getMat();
|
||||
statusMat.setTo(1);
|
||||
}
|
||||
|
||||
if (err.needed() || forwardBackwardThreshold > 0)
|
||||
{
|
||||
err.create((int)npoints, 1, CV_32F, -1, true);
|
||||
errorMat = err.getMat();
|
||||
errorMat.setTo(0);
|
||||
}
|
||||
|
||||
calcLocalOpticalFlow(prevImage, nextImage, prevPyramid, currPyramid, prevPoints, nextPoints, *(param.get()));
|
||||
cv::Mat(1,npoints , CV_32FC2, &nextPoints[0]).copyTo(nextPtsMat);
|
||||
if (forwardBackwardThreshold > 0)
|
||||
{
|
||||
// use temp variable to properly initialize refPoints
|
||||
// inside 'calcLocalOpticalFlow' when 'use_init_flow' and 'fwd_bwd_thresh' parameters are used
|
||||
bool temp_param = param->getUseInitialFlow();
|
||||
param->setUseInitialFlow(false);
|
||||
// reuse image pyramids
|
||||
calcLocalOpticalFlow(nextImage, prevImage, currPyramid, prevPyramid, nextPoints, refPoints, *(param.get()));
|
||||
param->setUseInitialFlow(temp_param);
|
||||
}
|
||||
for (unsigned int r = 0; r < refPoints.size(); r++)
|
||||
{
|
||||
Point2f diff = refPoints[r] - prevPoints[r];
|
||||
errorMat.at<float>(r) = sqrt(diff.x * diff.x + diff.y * diff.y);
|
||||
if (errorMat.at<float>(r) > forwardBackwardThreshold)
|
||||
statusMat.at<uchar>(r) = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected:
|
||||
Ptr<RLOFOpticalFlowParameter> param;
|
||||
float forwardBackwardThreshold;
|
||||
Ptr<CImageBuffer> prevPyramid[2];
|
||||
Ptr<CImageBuffer> currPyramid[2];
|
||||
};
|
||||
|
||||
Ptr<SparseRLOFOpticalFlow> SparseRLOFOpticalFlow::create(
|
||||
Ptr<RLOFOpticalFlowParameter> rlofParam,
|
||||
float forwardBackwardThreshold)
|
||||
{
|
||||
Ptr<SparseRLOFOpticalFlow> algo = makePtr<SparseRLOFOpticalFlowImpl>();
|
||||
algo->setRLOFOpticalFlowParameter(rlofParam);
|
||||
algo->setForwardBackward(forwardBackwardThreshold);
|
||||
return algo;
|
||||
}
|
||||
|
||||
void calcOpticalFlowDenseRLOF(InputArray I0, InputArray I1, InputOutputArray flow,
|
||||
Ptr<RLOFOpticalFlowParameter> rlofParam ,
|
||||
float forewardBackwardThreshold, Size gridStep,
|
||||
InterpolationType interp_type,
|
||||
int epicK, float epicSigma, float epicLambda,
|
||||
int superpixelSize, int superpixelType,
|
||||
bool use_post_proc, float fgsLambda, float fgsSigma, bool use_variational_refinement)
|
||||
{
|
||||
Ptr<DenseRLOFOpticalFlow> algo = DenseRLOFOpticalFlow::create(
|
||||
rlofParam, forewardBackwardThreshold, gridStep, interp_type,
|
||||
epicK, epicSigma, epicLambda, superpixelSize, superpixelType,
|
||||
use_post_proc, fgsLambda, fgsSigma, use_variational_refinement);
|
||||
algo->calc(I0, I1, flow);
|
||||
algo->collectGarbage();
|
||||
}
|
||||
|
||||
void calcOpticalFlowSparseRLOF(InputArray prevImg, InputArray nextImg,
|
||||
InputArray prevPts, InputOutputArray nextPts,
|
||||
OutputArray status, OutputArray err,
|
||||
Ptr<RLOFOpticalFlowParameter> rlofParam,
|
||||
float forewardBackwardThreshold)
|
||||
{
|
||||
Ptr<SparseRLOFOpticalFlow> algo = SparseRLOFOpticalFlow::create(
|
||||
rlofParam, forewardBackwardThreshold);
|
||||
algo->calc(prevImg, nextImg, prevPts, nextPts, status, err);
|
||||
}
|
||||
Ptr<DenseOpticalFlow> createOptFlow_DenseRLOF()
|
||||
{
|
||||
return DenseRLOFOpticalFlow::create();
|
||||
}
|
||||
|
||||
Ptr<SparseOpticalFlow> createOptFlow_SparseRLOF()
|
||||
{
|
||||
return SparseRLOFOpticalFlow::create();
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,779 @@
|
||||
/*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) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., 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"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(disable: 4512)
|
||||
#endif
|
||||
|
||||
//
|
||||
// 2D dense optical flow algorithm from the following paper:
|
||||
// Michael Tao, Jiamin Bai, Pushmeet Kohli, and Sylvain Paris.
|
||||
// "SimpleFlow: A Non-iterative, Sublinear Optical Flow Algorithm"
|
||||
// Computer Graphics Forum (Eurographics 2012)
|
||||
// http://graphics.berkeley.edu/papers/Tao-SAN-2012-05/
|
||||
//
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace optflow
|
||||
{
|
||||
|
||||
static const uchar MASK_TRUE_VALUE = (uchar)255;
|
||||
|
||||
inline static int dist(const Vec3b &p1, const Vec3b &p2) {
|
||||
int a = p1[0] - p2[0];
|
||||
int b = p1[1] - p2[1];
|
||||
int c = p1[2] - p2[2];
|
||||
|
||||
return a*a+b*b+c*c;
|
||||
}
|
||||
|
||||
inline static float dist(const Vec2f &p1, const Vec2f &p2) {
|
||||
float a = p1[0] - p2[0];
|
||||
float b = p1[1] - p2[1];
|
||||
|
||||
return a*a+b*b;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline static T min(T t1, T t2, T t3) {
|
||||
return (t1 <= t2 && t1 <= t3) ? t1 : min(t2, t3);
|
||||
}
|
||||
|
||||
static void removeOcclusions(const Mat& flow,
|
||||
const Mat& flow_inv,
|
||||
float occ_thr,
|
||||
Mat& confidence) {
|
||||
const int rows = flow.rows;
|
||||
const int cols = flow.cols;
|
||||
if (!confidence.data) {
|
||||
confidence = Mat::zeros(rows, cols, CV_32F);
|
||||
}
|
||||
for (int r = 0; r < rows; ++r) {
|
||||
for (int c = 0; c < cols; ++c) {
|
||||
if (dist(flow.at<Vec2f>(r, c), -flow_inv.at<Vec2f>(r, c)) > occ_thr) {
|
||||
confidence.at<float>(r, c) = 0;
|
||||
} else {
|
||||
confidence.at<float>(r, c) = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void wd(Mat& d, int top_shift, int bottom_shift, int left_shift, int right_shift, double sigma) {
|
||||
for (int dr = -top_shift, r = 0; dr <= bottom_shift; ++dr, ++r) {
|
||||
for (int dc = -left_shift, c = 0; dc <= right_shift; ++dc, ++c) {
|
||||
d.at<float>(r, c) = (float)-(dr*dr + dc*dc);
|
||||
}
|
||||
}
|
||||
d *= 1.0 / (2.0 * sigma * sigma);
|
||||
exp(d, d);
|
||||
}
|
||||
|
||||
template<typename JointVec, typename SrcVec>
|
||||
class CrossBilateralFilter : public ParallelLoopBody {
|
||||
Mat &joint, &confidence, &src;
|
||||
Mat &dst;
|
||||
int radius;
|
||||
bool flag;
|
||||
Mat &spaceWeights;
|
||||
std::vector<double> &expLut;
|
||||
|
||||
public:
|
||||
CrossBilateralFilter(Mat &joint_, Mat &confidence_, Mat &src_, Mat &dst_, int radius_, bool flag_, Mat &spaceWeights_, std::vector<double> &expLut_)
|
||||
:
|
||||
joint(joint_),
|
||||
confidence(confidence_),
|
||||
src(src_),
|
||||
dst(dst_),
|
||||
radius(radius_),
|
||||
flag(flag_),
|
||||
spaceWeights(spaceWeights_),
|
||||
expLut(expLut_) {
|
||||
CV_DbgAssert(joint.type() == traits::Type<JointVec>::value && confidence.type() == CV_32F && src.type() == dst.type() && src.type() == traits::Type<SrcVec>::value);
|
||||
CV_DbgAssert(joint.rows == src.rows && confidence.rows == src.rows && src.rows == dst.rows + 2 * radius);
|
||||
CV_DbgAssert(joint.cols == src.cols && confidence.cols == src.cols && src.cols == dst.cols + 2 * radius);
|
||||
}
|
||||
|
||||
void operator()(const Range &range) const CV_OVERRIDE {
|
||||
const int d = 2*radius +1;
|
||||
for (int i = range.start; i < range.end; i++) {
|
||||
SrcVec* dstRow = dst.ptr<SrcVec>(i);
|
||||
for (int j = 0; j < dst.cols; j++) {
|
||||
const JointVec& centeralPoint = joint.at<JointVec>(i+radius, j+radius);
|
||||
|
||||
Scalar totalSum = Scalar::all(0);
|
||||
double weightsSum = 0;
|
||||
for (int dr = i, r = 0; dr < i + d; ++dr, ++r) {
|
||||
const JointVec *jointRow = joint.ptr<JointVec>(dr);
|
||||
const SrcVec *srcRow = src.ptr<SrcVec>(dr);
|
||||
const float *confidenceRow = confidence.ptr<float>(dr);
|
||||
const float *spaceWeightsRow = spaceWeights.ptr<float>(r);
|
||||
for (int dc = j, c = 0; dc < j + d; ++dc, ++c) {
|
||||
double weight = spaceWeightsRow[c]*confidenceRow[dc];
|
||||
for (int cn = 0; cn < JointVec::channels; cn++) {
|
||||
weight *= expLut[std::abs(centeralPoint[cn] - jointRow[dc][cn])];
|
||||
}
|
||||
for (int cn = 0; cn < SrcVec::channels; cn++) {
|
||||
totalSum[cn] += weight * srcRow[dc][cn];
|
||||
}
|
||||
weightsSum += weight;
|
||||
}
|
||||
}
|
||||
|
||||
SrcVec& srcSum = dstRow[j];
|
||||
for (int cn = 0; cn < SrcVec::channels; cn++) {
|
||||
srcSum[cn] = (flag && fabs(weightsSum) < 1e-9)
|
||||
? src.at<SrcVec>(i+radius, j+radius)[cn]
|
||||
: static_cast<float>(totalSum[cn] / weightsSum);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static void crossBilateralFilter(InputArray joint_,
|
||||
InputArray confidence_,
|
||||
InputOutputArray src_,
|
||||
int radius,
|
||||
double sigmaColor, double sigmaSpace,
|
||||
bool flag = false) {
|
||||
CV_Assert(!src_.empty());
|
||||
CV_Assert(!confidence_.empty());
|
||||
CV_Assert(!joint_.empty());
|
||||
|
||||
Mat src = src_.getMat();
|
||||
Mat joint = joint_.getMat();
|
||||
Mat confidence = confidence_.getMat();
|
||||
|
||||
CV_Assert(src.size() == joint.size() && confidence.size() == src.size());
|
||||
CV_Assert(joint.depth() == CV_8U && confidence.type() == CV_32F);
|
||||
|
||||
if (sigmaColor <= 0)
|
||||
sigmaColor = 1;
|
||||
if (sigmaSpace <= 0)
|
||||
sigmaSpace = 1;
|
||||
|
||||
if (radius <= 0)
|
||||
radius = cvRound(sigmaSpace * 1.5);
|
||||
radius = std::max(radius, 1);
|
||||
|
||||
if (src.data == joint.data)
|
||||
joint = joint.clone();
|
||||
|
||||
int d = 2 * radius + 1;
|
||||
|
||||
Mat jointTemp, confidenceTemp, srcTemp;
|
||||
copyMakeBorder(joint, jointTemp, radius, radius, radius, radius, BORDER_DEFAULT);
|
||||
copyMakeBorder(confidence, confidenceTemp, radius, radius, radius, radius, BORDER_CONSTANT, Scalar(0));
|
||||
copyMakeBorder(src, srcTemp, radius, radius, radius, radius, BORDER_DEFAULT);
|
||||
|
||||
Mat spaceWeights(d, d, CV_32F);
|
||||
wd(spaceWeights, radius, radius, radius, radius, sigmaSpace);
|
||||
|
||||
double gaussColorCoeff = -0.5 / (sigmaColor * sigmaColor);
|
||||
std::vector<double> expLut (256);
|
||||
for (size_t i = 0; i < expLut.size(); i++) {
|
||||
expLut[i] = std::exp(i * i * gaussColorCoeff);
|
||||
}
|
||||
|
||||
Range range(0, src.rows);
|
||||
parallel_for_(range, CrossBilateralFilter<Vec3b, Vec2f>(jointTemp, confidenceTemp, srcTemp, src, radius, flag, spaceWeights, expLut));
|
||||
}
|
||||
|
||||
static void calcConfidence(const Mat& prev,
|
||||
const Mat& next,
|
||||
const Mat& flow,
|
||||
Mat& confidence,
|
||||
int max_flow) {
|
||||
const int rows = prev.rows;
|
||||
const int cols = prev.cols;
|
||||
confidence = Mat::zeros(rows, cols, CV_32F);
|
||||
|
||||
for (int r0 = 0; r0 < rows; ++r0) {
|
||||
for (int c0 = 0; c0 < cols; ++c0) {
|
||||
Vec2f flow_at_point = flow.at<Vec2f>(r0, c0);
|
||||
int u0 = cvRound(flow_at_point[0]);
|
||||
if (r0 + u0 < 0) { u0 = -r0; }
|
||||
if (r0 + u0 >= rows) { u0 = rows - 1 - r0; }
|
||||
int v0 = cvRound(flow_at_point[1]);
|
||||
if (c0 + v0 < 0) { v0 = -c0; }
|
||||
if (c0 + v0 >= cols) { v0 = cols - 1 - c0; }
|
||||
|
||||
const int top_row_shift = -std::min(r0 + u0, max_flow);
|
||||
const int bottom_row_shift = std::min(rows - 1 - (r0 + u0), max_flow);
|
||||
const int left_col_shift = -std::min(c0 + v0, max_flow);
|
||||
const int right_col_shift = std::min(cols - 1 - (c0 + v0), max_flow);
|
||||
|
||||
bool first_flow_iteration = true;
|
||||
int sum_e = 0, min_e = 0;
|
||||
|
||||
for (int u = top_row_shift; u <= bottom_row_shift; ++u) {
|
||||
for (int v = left_col_shift; v <= right_col_shift; ++v) {
|
||||
int e = dist(prev.at<Vec3b>(r0, c0), next.at<Vec3b>(r0 + u0 + u, c0 + v0 + v));
|
||||
if (first_flow_iteration) {
|
||||
sum_e = e;
|
||||
min_e = e;
|
||||
first_flow_iteration = false;
|
||||
} else {
|
||||
sum_e += e;
|
||||
min_e = std::min(min_e, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
int windows_square = (bottom_row_shift - top_row_shift + 1) *
|
||||
(right_col_shift - left_col_shift + 1);
|
||||
confidence.at<float>(r0, c0) = (windows_square == 0) ? 0
|
||||
: static_cast<float>(sum_e) / windows_square - min_e;
|
||||
CV_Assert(confidence.at<float>(r0, c0) >= 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename SrcVec, typename DstVec>
|
||||
class CalcOpticalFlowSingleScaleSF : public ParallelLoopBody {
|
||||
Mat &prev, &next;
|
||||
Mat &mask;
|
||||
Mat &dst;
|
||||
int radius, maxFlow;
|
||||
Mat &spaceWeights;
|
||||
std::vector<double> &expLut;
|
||||
|
||||
public:
|
||||
CalcOpticalFlowSingleScaleSF(Mat &prev_, Mat &next_, Mat &mask_, Mat &dst_, int radius_, int maxFlow_, Mat &spaceWeights_, std::vector<double> &expLut_)
|
||||
:
|
||||
prev(prev_),
|
||||
next(next_),
|
||||
mask(mask_),
|
||||
dst(dst_),
|
||||
radius(radius_),
|
||||
maxFlow(maxFlow_),
|
||||
spaceWeights(spaceWeights_),
|
||||
expLut(expLut_){
|
||||
CV_DbgAssert(prev.type() == next.type());
|
||||
CV_DbgAssert(prev.rows == next.rows && prev.rows == dst.rows + 2 * radius);
|
||||
CV_DbgAssert(prev.cols == next.cols && next.cols == dst.cols + 2 * radius);
|
||||
}
|
||||
|
||||
void operator()(const Range &range) const CV_OVERRIDE {
|
||||
int d = 2 * radius + 1;
|
||||
Mat weights(d, d, CV_32F);
|
||||
for (int i = range.start; i < range.end; i++) {
|
||||
const uchar *maskRow = mask.ptr<uchar>(i);
|
||||
const DstVec *dstRow = dst.ptr<DstVec>(i);
|
||||
for (int j = 0; j < dst.cols; j++) {
|
||||
if (!maskRow[j]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: do smth with this creepy staff
|
||||
const DstVec &flowAtPoint = dstRow[j];
|
||||
int u0 = cvRound(flowAtPoint[0]);
|
||||
if (i + u0 < 0) {u0 = -i;}
|
||||
if (i + u0 >= dst.rows) {u0 = dst.rows - 1 - i;}
|
||||
int v0 = cvRound(flowAtPoint[1]);
|
||||
if (j + v0 < 0) {v0 = -j;}
|
||||
if (j + v0 >= dst.cols) {v0 = dst.cols - 1 - j;}
|
||||
|
||||
const int topRowShift = -std::min(i + u0, maxFlow);
|
||||
const int bottomRowShift = std::min(dst.rows - 1 - (i + u0), maxFlow);
|
||||
const int leftColShift = -std::min(j + v0, maxFlow);
|
||||
const int rightColShift = std::min(dst.cols - 1 - (j + v0), maxFlow);
|
||||
|
||||
float minCost = FLT_MAX, bestU = (float) u0, bestV = (float) v0;
|
||||
|
||||
const SrcVec& centeralPoint = prev.at<SrcVec>(i+radius, j+radius);
|
||||
|
||||
int left_border = j, right_border = j + d;
|
||||
for (int dr = i, r = 0; dr < i + d; ++dr, ++r) {
|
||||
const SrcVec *prevRow = prev.ptr<SrcVec>(dr);
|
||||
const float* spaceWeightsRow = spaceWeights.ptr<float>(r);
|
||||
float *weightsRow = weights.ptr<float>(r);
|
||||
for (int dc = left_border, c = 0; dc < right_border; ++dc, ++c) {
|
||||
double weight = spaceWeightsRow[c];
|
||||
for(int cn=0;cn<SrcVec::channels;cn++){
|
||||
weight *= expLut[std::abs(centeralPoint[cn]-prevRow[dc][cn])];
|
||||
}
|
||||
weightsRow[c] = static_cast<float>(weight);
|
||||
}
|
||||
}
|
||||
|
||||
for (int u = topRowShift; u <= bottomRowShift; ++u) {
|
||||
const int next_extended_top_window_row = i + u0 + u;
|
||||
for (int v = leftColShift; v <= rightColShift; ++v) {
|
||||
const int next_extended_left_window_col = j + v0 + v;
|
||||
|
||||
float cost = 0;
|
||||
for (int r = 0; r < d; ++r) {
|
||||
const SrcVec *prev_extended_window_row = prev.ptr<SrcVec>(i + r);
|
||||
const SrcVec *next_extended_window_row = next.ptr<SrcVec>(next_extended_top_window_row + r);
|
||||
const float *weight_window_row = weights.ptr<float>(r);
|
||||
for (int c = 0; c < d; ++c) {
|
||||
cost += weight_window_row[c] *
|
||||
dist(prev_extended_window_row[j + c],
|
||||
next_extended_window_row[next_extended_left_window_col + c]);
|
||||
}
|
||||
}
|
||||
// cost should be divided by sum(weight_window), but because
|
||||
// we interested only in min(cost) and sum(weight_window) is constant
|
||||
// for every point - we remove it
|
||||
|
||||
if (cost < minCost) {
|
||||
minCost = cost;
|
||||
bestU = (float) (u + u0);
|
||||
bestV = (float) (v + v0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dst.at<DstVec>(i, j) = DstVec(bestU, bestV);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static void calcOpticalFlowSingleScaleSF(InputArray prev_,
|
||||
InputArray next_,
|
||||
InputArray mask_,
|
||||
InputOutputArray dst_,
|
||||
int radius,
|
||||
int max_flow,
|
||||
float sigmaSpace,
|
||||
float sigmaColor) {
|
||||
Mat prev = prev_.getMat();
|
||||
Mat next = next_.getMat();
|
||||
Mat mask = mask_.getMat();
|
||||
Mat dst = dst_.getMat();
|
||||
|
||||
Mat prevTemp, nextTemp;
|
||||
copyMakeBorder(prev, prevTemp, radius, radius, radius, radius, BORDER_DEFAULT);
|
||||
copyMakeBorder(next, nextTemp, radius, radius, radius, radius, BORDER_DEFAULT);
|
||||
|
||||
int d = 2 * radius + 1;
|
||||
|
||||
Mat spaceWeights(d, d, CV_32F);
|
||||
wd(spaceWeights, radius, radius, radius, radius, sigmaSpace);
|
||||
|
||||
double gaussColorCoeff = -0.5 / (sigmaColor * sigmaColor);
|
||||
std::vector<double> expLut (256);
|
||||
for (size_t i = 0; i < expLut.size(); i++) {
|
||||
expLut[i] = std::exp(i * i * gaussColorCoeff);
|
||||
}
|
||||
|
||||
Range range(0, dst.rows);
|
||||
parallel_for_(range, CalcOpticalFlowSingleScaleSF<Vec3b, Vec2f>(prevTemp, nextTemp, mask, dst, radius, max_flow, spaceWeights, expLut));
|
||||
}
|
||||
|
||||
static Mat upscaleOpticalFlow(int new_rows,
|
||||
int new_cols,
|
||||
const Mat& image,
|
||||
const Mat& confidence,
|
||||
Mat& flow,
|
||||
int averaging_radius,
|
||||
float sigma_dist,
|
||||
float sigma_color) {
|
||||
crossBilateralFilter(image, confidence, flow, averaging_radius, sigma_color, sigma_dist, true);
|
||||
Mat new_flow;
|
||||
resize(flow, new_flow, Size(new_cols, new_rows), 0, 0, INTER_NEAREST);
|
||||
new_flow *= 2;
|
||||
return new_flow;
|
||||
}
|
||||
|
||||
static Mat calcIrregularityMat(const Mat& flow, int radius) {
|
||||
const int rows = flow.rows;
|
||||
const int cols = flow.cols;
|
||||
Mat irregularity = Mat::zeros(rows, cols, CV_32F);
|
||||
for (int r = 0; r < rows; ++r) {
|
||||
const int start_row = std::max(0, r - radius);
|
||||
const int end_row = std::min(rows - 1, r + radius);
|
||||
for (int c = 0; c < cols; ++c) {
|
||||
const int start_col = std::max(0, c - radius);
|
||||
const int end_col = std::min(cols - 1, c + radius);
|
||||
for (int dr = start_row; dr <= end_row; ++dr) {
|
||||
for (int dc = start_col; dc <= end_col; ++dc) {
|
||||
const float diff = dist(flow.at<Vec2f>(r, c), flow.at<Vec2f>(dr, dc));
|
||||
if (diff > irregularity.at<float>(r, c)) {
|
||||
irregularity.at<float>(r, c) = diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return irregularity;
|
||||
}
|
||||
|
||||
static void selectPointsToRecalcFlow(const Mat& flow,
|
||||
int irregularity_metric_radius,
|
||||
float speed_up_thr,
|
||||
int curr_rows,
|
||||
int curr_cols,
|
||||
const Mat& prev_speed_up,
|
||||
Mat& speed_up,
|
||||
Mat& mask) {
|
||||
const int prev_rows = flow.rows;
|
||||
const int prev_cols = flow.cols;
|
||||
|
||||
Mat is_flow_regular = calcIrregularityMat(flow, irregularity_metric_radius)
|
||||
< speed_up_thr;
|
||||
Mat done = Mat::zeros(prev_rows, prev_cols, CV_8U);
|
||||
speed_up = Mat::zeros(curr_rows, curr_cols, CV_8U);
|
||||
mask = Mat::zeros(curr_rows, curr_cols, CV_8U);
|
||||
|
||||
for (int r = 0; r < is_flow_regular.rows; ++r) {
|
||||
for (int c = 0; c < is_flow_regular.cols; ++c) {
|
||||
if (!done.at<uchar>(r, c)) {
|
||||
if (is_flow_regular.at<uchar>(r, c) &&
|
||||
2*r + 1 < curr_rows && 2*c + 1< curr_cols) {
|
||||
|
||||
bool all_flow_in_region_regular = true;
|
||||
int speed_up_at_this_point = prev_speed_up.at<uchar>(r, c);
|
||||
int step = (1 << speed_up_at_this_point) - 1;
|
||||
int prev_top = r;
|
||||
int prev_bottom = std::min(r + step, prev_rows - 1);
|
||||
int prev_left = c;
|
||||
int prev_right = std::min(c + step, prev_cols - 1);
|
||||
|
||||
for (int rr = prev_top; rr <= prev_bottom; ++rr) {
|
||||
for (int cc = prev_left; cc <= prev_right; ++cc) {
|
||||
done.at<uchar>(rr, cc) = 1;
|
||||
if (!is_flow_regular.at<uchar>(rr, cc)) {
|
||||
all_flow_in_region_regular = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int curr_top = std::min(2 * r, curr_rows - 1);
|
||||
int curr_bottom = std::min(2*(r + step) + 1, curr_rows - 1);
|
||||
int curr_left = std::min(2 * c, curr_cols - 1);
|
||||
int curr_right = std::min(2*(c + step) + 1, curr_cols - 1);
|
||||
|
||||
if (all_flow_in_region_regular &&
|
||||
curr_top != curr_bottom &&
|
||||
curr_left != curr_right) {
|
||||
mask.at<uchar>(curr_top, curr_left) = MASK_TRUE_VALUE;
|
||||
mask.at<uchar>(curr_bottom, curr_left) = MASK_TRUE_VALUE;
|
||||
mask.at<uchar>(curr_top, curr_right) = MASK_TRUE_VALUE;
|
||||
mask.at<uchar>(curr_bottom, curr_right) = MASK_TRUE_VALUE;
|
||||
for (int rr = curr_top; rr <= curr_bottom; ++rr) {
|
||||
for (int cc = curr_left; cc <= curr_right; ++cc) {
|
||||
speed_up.at<uchar>(rr, cc) = (uchar)(speed_up_at_this_point + 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int rr = curr_top; rr <= curr_bottom; ++rr) {
|
||||
for (int cc = curr_left; cc <= curr_right; ++cc) {
|
||||
mask.at<uchar>(rr, cc) = MASK_TRUE_VALUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
done.at<uchar>(r, c) = 1;
|
||||
for (int dr = 0; dr <= 1; ++dr) {
|
||||
int nr = 2*r + dr;
|
||||
for (int dc = 0; dc <= 1; ++dc) {
|
||||
int nc = 2*c + dc;
|
||||
if (nr < curr_rows && nc < curr_cols) {
|
||||
mask.at<uchar>(nr, nc) = MASK_TRUE_VALUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline float extrapolateValueInRect(int height, int width,
|
||||
float v11, float v12,
|
||||
float v21, float v22,
|
||||
int r, int c) {
|
||||
if (r == 0 && c == 0) { return v11;}
|
||||
if (r == 0 && c == width) { return v12;}
|
||||
if (r == height && c == 0) { return v21;}
|
||||
if (r == height && c == width) { return v22;}
|
||||
|
||||
CV_Assert(height > 0 && width > 0);
|
||||
float qr = float(r) / height;
|
||||
float pr = 1.0f - qr;
|
||||
float qc = float(c) / width;
|
||||
float pc = 1.0f - qc;
|
||||
|
||||
return v11*pr*pc + v12*pr*qc + v21*qr*pc + v22*qc*qr;
|
||||
}
|
||||
|
||||
static void extrapolateFlow(Mat& flow,
|
||||
const Mat& speed_up) {
|
||||
const int rows = flow.rows;
|
||||
const int cols = flow.cols;
|
||||
Mat done = Mat::zeros(rows, cols, CV_8U);
|
||||
for (int r = 0; r < rows; ++r) {
|
||||
for (int c = 0; c < cols; ++c) {
|
||||
if (!done.at<uchar>(r, c) && speed_up.at<uchar>(r, c) > 1) {
|
||||
int step = (1 << speed_up.at<uchar>(r, c)) - 1;
|
||||
int top = r;
|
||||
int bottom = std::min(r + step, rows - 1);
|
||||
int left = c;
|
||||
int right = std::min(c + step, cols - 1);
|
||||
|
||||
int height = bottom - top;
|
||||
int width = right - left;
|
||||
for (int rr = top; rr <= bottom; ++rr) {
|
||||
for (int cc = left; cc <= right; ++cc) {
|
||||
done.at<uchar>(rr, cc) = 1;
|
||||
Vec2f flow_at_point;
|
||||
Vec2f top_left = flow.at<Vec2f>(top, left);
|
||||
Vec2f top_right = flow.at<Vec2f>(top, right);
|
||||
Vec2f bottom_left = flow.at<Vec2f>(bottom, left);
|
||||
Vec2f bottom_right = flow.at<Vec2f>(bottom, right);
|
||||
|
||||
flow_at_point[0] = extrapolateValueInRect(height, width,
|
||||
top_left[0], top_right[0],
|
||||
bottom_left[0], bottom_right[0],
|
||||
rr-top, cc-left);
|
||||
|
||||
flow_at_point[1] = extrapolateValueInRect(height, width,
|
||||
top_left[1], top_right[1],
|
||||
bottom_left[1], bottom_right[1],
|
||||
rr-top, cc-left);
|
||||
flow.at<Vec2f>(rr, cc) = flow_at_point;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void buildPyramidWithResizeMethod(const Mat& src,
|
||||
std::vector<Mat>& pyramid,
|
||||
int layers,
|
||||
int interpolation_type) {
|
||||
pyramid.push_back(src);
|
||||
for (int i = 1; i <= layers; ++i) {
|
||||
Mat prev = pyramid[i - 1];
|
||||
if (prev.rows <= 1 || prev.cols <= 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
Mat next;
|
||||
resize(prev, next, Size((prev.cols + 1) / 2, (prev.rows + 1) / 2), 0, 0, interpolation_type);
|
||||
pyramid.push_back(next);
|
||||
}
|
||||
}
|
||||
|
||||
CV_EXPORTS_W void calcOpticalFlowSF(InputArray _from,
|
||||
InputArray _to,
|
||||
OutputArray _resulted_flow,
|
||||
int layers,
|
||||
int averaging_radius,
|
||||
int max_flow,
|
||||
double sigma_dist,
|
||||
double sigma_color,
|
||||
int postprocess_window,
|
||||
double sigma_dist_fix,
|
||||
double sigma_color_fix,
|
||||
double occ_thr,
|
||||
int upscale_averaging_radius,
|
||||
double upscale_sigma_dist,
|
||||
double upscale_sigma_color,
|
||||
double speed_up_thr)
|
||||
{
|
||||
Mat from = _from.getMat();
|
||||
Mat to = _to.getMat();
|
||||
|
||||
std::vector<Mat> pyr_from_images;
|
||||
std::vector<Mat> pyr_to_images;
|
||||
|
||||
buildPyramidWithResizeMethod(from, pyr_from_images, layers - 1, INTER_CUBIC);
|
||||
buildPyramidWithResizeMethod(to, pyr_to_images, layers - 1, INTER_CUBIC);
|
||||
|
||||
CV_Assert((int)pyr_from_images.size() == layers && (int)pyr_to_images.size() == layers);
|
||||
|
||||
Mat curr_from, curr_to, prev_from, prev_to;
|
||||
|
||||
curr_from = pyr_from_images[layers - 1];
|
||||
curr_to = pyr_to_images[layers - 1];
|
||||
|
||||
Mat mask = Mat::ones(curr_from.size(), CV_8U);
|
||||
Mat mask_inv = Mat::ones(curr_from.size(), CV_8U);
|
||||
|
||||
Mat flow = Mat::zeros(curr_from.size(), CV_32FC2);
|
||||
Mat flow_inv = Mat::zeros(curr_to.size(), CV_32FC2);
|
||||
|
||||
Mat confidence;
|
||||
Mat confidence_inv;
|
||||
|
||||
|
||||
calcOpticalFlowSingleScaleSF(curr_from,
|
||||
curr_to,
|
||||
mask,
|
||||
flow,
|
||||
averaging_radius,
|
||||
max_flow,
|
||||
(float)sigma_dist,
|
||||
(float)sigma_color);
|
||||
|
||||
calcOpticalFlowSingleScaleSF(curr_to,
|
||||
curr_from,
|
||||
mask_inv,
|
||||
flow_inv,
|
||||
averaging_radius,
|
||||
max_flow,
|
||||
(float)sigma_dist,
|
||||
(float)sigma_color);
|
||||
|
||||
removeOcclusions(flow,
|
||||
flow_inv,
|
||||
(float)occ_thr,
|
||||
confidence);
|
||||
|
||||
removeOcclusions(flow_inv,
|
||||
flow,
|
||||
(float)occ_thr,
|
||||
confidence_inv);
|
||||
|
||||
Mat speed_up = Mat::zeros(curr_from.size(), CV_8U);
|
||||
Mat speed_up_inv = Mat::zeros(curr_from.size(), CV_8U);
|
||||
|
||||
for (int curr_layer = layers - 2; curr_layer >= 0; --curr_layer) {
|
||||
curr_from = pyr_from_images[curr_layer];
|
||||
curr_to = pyr_to_images[curr_layer];
|
||||
prev_from = pyr_from_images[curr_layer + 1];
|
||||
prev_to = pyr_to_images[curr_layer + 1];
|
||||
|
||||
const int curr_rows = curr_from.rows;
|
||||
const int curr_cols = curr_from.cols;
|
||||
|
||||
Mat new_speed_up, new_speed_up_inv;
|
||||
|
||||
selectPointsToRecalcFlow(flow,
|
||||
averaging_radius,
|
||||
(float)speed_up_thr,
|
||||
curr_rows,
|
||||
curr_cols,
|
||||
speed_up,
|
||||
new_speed_up,
|
||||
mask);
|
||||
|
||||
selectPointsToRecalcFlow(flow_inv,
|
||||
averaging_radius,
|
||||
(float)speed_up_thr,
|
||||
curr_rows,
|
||||
curr_cols,
|
||||
speed_up_inv,
|
||||
new_speed_up_inv,
|
||||
mask_inv);
|
||||
|
||||
speed_up = new_speed_up;
|
||||
speed_up_inv = new_speed_up_inv;
|
||||
|
||||
flow = upscaleOpticalFlow(curr_rows,
|
||||
curr_cols,
|
||||
prev_from,
|
||||
confidence,
|
||||
flow,
|
||||
upscale_averaging_radius,
|
||||
(float)upscale_sigma_dist,
|
||||
(float)upscale_sigma_color);
|
||||
|
||||
flow_inv = upscaleOpticalFlow(curr_rows,
|
||||
curr_cols,
|
||||
prev_to,
|
||||
confidence_inv,
|
||||
flow_inv,
|
||||
upscale_averaging_radius,
|
||||
(float)upscale_sigma_dist,
|
||||
(float)upscale_sigma_color);
|
||||
|
||||
calcConfidence(curr_from, curr_to, flow, confidence, max_flow);
|
||||
calcOpticalFlowSingleScaleSF(curr_from,
|
||||
curr_to,
|
||||
mask,
|
||||
flow,
|
||||
averaging_radius,
|
||||
max_flow,
|
||||
(float)sigma_dist,
|
||||
(float)sigma_color);
|
||||
|
||||
calcConfidence(curr_to, curr_from, flow_inv, confidence_inv, max_flow);
|
||||
calcOpticalFlowSingleScaleSF(curr_to,
|
||||
curr_from,
|
||||
mask_inv,
|
||||
flow_inv,
|
||||
averaging_radius,
|
||||
max_flow,
|
||||
(float)sigma_dist,
|
||||
(float)sigma_color);
|
||||
|
||||
extrapolateFlow(flow, speed_up);
|
||||
extrapolateFlow(flow_inv, speed_up_inv);
|
||||
|
||||
//TODO: should we remove occlusions for the last stage?
|
||||
removeOcclusions(flow, flow_inv, (float)occ_thr, confidence);
|
||||
removeOcclusions(flow_inv, flow, (float)occ_thr, confidence_inv);
|
||||
}
|
||||
|
||||
crossBilateralFilter(curr_from, confidence, flow,
|
||||
postprocess_window, (float)sigma_color_fix, (float)sigma_dist_fix);
|
||||
|
||||
GaussianBlur(flow, flow, Size(3, 3), 5);
|
||||
|
||||
_resulted_flow.create(flow.size(), CV_32FC2);
|
||||
Mat resulted_flow = _resulted_flow.getMat();
|
||||
int from_to[] = {0,1 , 1,0};
|
||||
mixChannels(&flow, 1, &resulted_flow, 1, from_to, 2);
|
||||
}
|
||||
|
||||
CV_EXPORTS_W void calcOpticalFlowSF(InputArray from,
|
||||
InputArray to,
|
||||
OutputArray flow,
|
||||
int layers,
|
||||
int averaging_block_size,
|
||||
int max_flow) {
|
||||
calcOpticalFlowSF(from, to, flow, layers, averaging_block_size, max_flow,
|
||||
4.1, 25.5, 18, 55.0, 25.5, 0.35, 18, 55.0, 25.5, 10);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,788 @@
|
||||
/*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) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., 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"
|
||||
#undef CV_FORCE_SIMD128_CPP // mixed HAL SIMD/SSE code
|
||||
#include "opencv2/core/private.hpp"
|
||||
#include "opencv2/flann/miniflann.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencl_kernels_optflow.hpp"
|
||||
#include "opencv2/core/hal/intrin.hpp"
|
||||
#ifdef CV_CXX11
|
||||
#include <random> // std::mt19937
|
||||
#endif
|
||||
|
||||
/* Disable "from double to float" and "from size_t to int" warnings.
|
||||
* Fixing these would make the code look ugly by introducing explicit cast all around.
|
||||
* Here these warning are pointless anyway.
|
||||
*/
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( disable : 4244 4267 4838 )
|
||||
#endif
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic ignored "-Wshorten-64-to-32"
|
||||
#endif
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace optflow
|
||||
{
|
||||
namespace
|
||||
{
|
||||
|
||||
#define PATCH_RADIUS 10
|
||||
#define PATCH_RADIUS_DOUBLED 20
|
||||
#define SQRT2_INV 0.7071067811865475
|
||||
|
||||
const int patchRadius = PATCH_RADIUS;
|
||||
const int globalIters = 3;
|
||||
const int localIters = 500;
|
||||
const double thresholdOutliers = 0.98;
|
||||
const double thresholdMagnitudeFrac = 0.8;
|
||||
const double epsTolerance = 1e-12;
|
||||
const unsigned scoreGainPos = 5;
|
||||
const unsigned scoreGainNeg = 1;
|
||||
const unsigned negSearchKNN = 5;
|
||||
const double simulatedAnnealingTemperatureCoef = 200.0;
|
||||
const double sigmaGrowthRate = 0.2;
|
||||
|
||||
RNG rng;
|
||||
|
||||
struct Magnitude
|
||||
{
|
||||
float val;
|
||||
int i;
|
||||
int j;
|
||||
|
||||
Magnitude( float _val, int _i, int _j ) : val( _val ), i( _i ), j( _j ) {}
|
||||
Magnitude() {}
|
||||
|
||||
bool operator<( const Magnitude &m ) const { return val > m.val; }
|
||||
};
|
||||
|
||||
struct PartitionPredicate1
|
||||
{
|
||||
Vec< double, GPCPatchDescriptor::nFeatures > coef;
|
||||
double rhs;
|
||||
|
||||
PartitionPredicate1( const Vec< double, GPCPatchDescriptor::nFeatures > &_coef, double _rhs ) : coef( _coef ), rhs( _rhs ) {}
|
||||
|
||||
bool operator()( const GPCPatchSample &sample ) const
|
||||
{
|
||||
bool refdir, posdir, negdir;
|
||||
sample.getDirections( refdir, posdir, negdir, coef, rhs );
|
||||
return refdir == false && ( posdir == false || negdir == true );
|
||||
}
|
||||
};
|
||||
|
||||
struct PartitionPredicate2
|
||||
{
|
||||
Vec< double, GPCPatchDescriptor::nFeatures > coef;
|
||||
double rhs;
|
||||
|
||||
PartitionPredicate2( const Vec< double, GPCPatchDescriptor::nFeatures > &_coef, double _rhs ) : coef( _coef ), rhs( _rhs ) {}
|
||||
|
||||
bool operator()( const GPCPatchSample &sample ) const
|
||||
{
|
||||
bool refdir, posdir, negdir;
|
||||
sample.getDirections( refdir, posdir, negdir, coef, rhs );
|
||||
return refdir != posdir && refdir == negdir;
|
||||
}
|
||||
};
|
||||
|
||||
struct CompareWithTolerance
|
||||
{
|
||||
double val;
|
||||
|
||||
CompareWithTolerance( double _val ) : val( _val ) {};
|
||||
|
||||
bool operator()( const double &elem ) const
|
||||
{
|
||||
const double diff = ( val + elem == 0 ) ? std::abs( val - elem ) : std::abs( ( val - elem ) / ( val + elem ) );
|
||||
return diff <= epsTolerance;
|
||||
}
|
||||
};
|
||||
|
||||
float normL2Sqr( const Vec2f &v ) { return v[0] * v[0] + v[1] * v[1]; }
|
||||
|
||||
int normL2Sqr( const Point2i &v ) { return v.x * v.x + v.y * v.y; }
|
||||
|
||||
bool checkBounds( int i, int j, Size sz )
|
||||
{
|
||||
return i >= patchRadius && j >= patchRadius && i + patchRadius < sz.height && j + patchRadius < sz.width;
|
||||
}
|
||||
|
||||
void getDCTPatchDescriptor( GPCPatchDescriptor &patchDescr, const Mat *imgCh, int i, int j )
|
||||
{
|
||||
Rect roi( j - patchRadius, i - patchRadius, 2 * patchRadius, 2 * patchRadius );
|
||||
Mat freqDomain;
|
||||
dct( imgCh[0]( roi ), freqDomain );
|
||||
|
||||
double *feature = patchDescr.feature.val;
|
||||
feature[0] = freqDomain.at< float >( 0, 0 );
|
||||
feature[1] = freqDomain.at< float >( 0, 1 );
|
||||
feature[2] = freqDomain.at< float >( 0, 2 );
|
||||
feature[3] = freqDomain.at< float >( 0, 3 );
|
||||
|
||||
feature[4] = freqDomain.at< float >( 1, 0 );
|
||||
feature[5] = freqDomain.at< float >( 1, 1 );
|
||||
feature[6] = freqDomain.at< float >( 1, 2 );
|
||||
feature[7] = freqDomain.at< float >( 1, 3 );
|
||||
|
||||
feature[8] = freqDomain.at< float >( 2, 0 );
|
||||
feature[9] = freqDomain.at< float >( 2, 1 );
|
||||
feature[10] = freqDomain.at< float >( 2, 2 );
|
||||
feature[11] = freqDomain.at< float >( 2, 3 );
|
||||
|
||||
feature[12] = freqDomain.at< float >( 3, 0 );
|
||||
feature[13] = freqDomain.at< float >( 3, 1 );
|
||||
feature[14] = freqDomain.at< float >( 3, 2 );
|
||||
feature[15] = freqDomain.at< float >( 3, 3 );
|
||||
|
||||
feature[16] = cv::sum( imgCh[1]( roi ) )[0] / ( 2 * patchRadius );
|
||||
feature[17] = cv::sum( imgCh[2]( roi ) )[0] / ( 2 * patchRadius );
|
||||
}
|
||||
|
||||
double sumInt( const Mat &integ, int i, int j, int h, int w )
|
||||
{
|
||||
return integ.at< double >( i + h, j + w ) - integ.at< double >( i + h, j ) - integ.at< double >( i, j + w ) + integ.at< double >( i, j );
|
||||
}
|
||||
|
||||
void getWHTPatchDescriptor( GPCPatchDescriptor &patchDescr, const Mat *imgCh, int i, int j )
|
||||
{
|
||||
i -= patchRadius;
|
||||
j -= patchRadius;
|
||||
const int k = 2 * patchRadius;
|
||||
const double s = sumInt( imgCh[0], i, j, k, k );
|
||||
double *feature = patchDescr.feature.val;
|
||||
|
||||
feature[0] = s;
|
||||
feature[1] = s - 2 * sumInt( imgCh[0], i, j + k / 2, k, k / 2 );
|
||||
feature[2] = s - 2 * sumInt( imgCh[0], i, j + k / 4, k, k / 2 );
|
||||
feature[3] = s - 2 * sumInt( imgCh[0], i, j + k / 4, k, k / 4 ) - 2 * sumInt( imgCh[0], i, j + 3 * k / 4, k, k / 4 );
|
||||
|
||||
feature[4] = s - 2 * sumInt( imgCh[0], i + k / 2, j, k / 2, k );
|
||||
feature[5] = s - 2 * sumInt( imgCh[0], i, j + k / 2, k / 2, k / 2 ) - 2 * sumInt( imgCh[0], i + k / 2, j, k / 2, k / 2 );
|
||||
feature[6] = s - 2 * sumInt( imgCh[0], i, j + k / 4, k / 2, k / 2 ) - 2 * sumInt( imgCh[0], i + k / 2, j, k / 2, k / 4 ) -
|
||||
2 * sumInt( imgCh[0], i + k / 2, j + 3 * k / 4, k / 2, k / 4 );
|
||||
feature[7] = s - 2 * sumInt( imgCh[0], i, j + k / 4, k / 2, k / 4 ) - 2 * sumInt( imgCh[0], i, j + 3 * k / 4, k / 2, k / 4 ) -
|
||||
2 * sumInt( imgCh[0], i + k / 2, j, k / 2, k / 4 ) - 2 * sumInt( imgCh[0], i + k / 2, j + k / 2, k / 2, k / 4 );
|
||||
|
||||
feature[8] = s - 2 * sumInt( imgCh[0], i + k / 4, j, k / 2, k );
|
||||
feature[9] = s - 2 * sumInt( imgCh[0], i + k / 4, j, k / 2, k / 2 ) - 2 * sumInt( imgCh[0], i, j + k / 2, k / 4, k / 2 ) -
|
||||
2 * sumInt( imgCh[0], i + 3 * k / 4, j + k / 2, k / 4, k / 2 );
|
||||
feature[10] = s - 2 * sumInt( imgCh[0], i + k / 4, j, k / 2, k / 4 ) - 2 * sumInt( imgCh[0], i + k / 4, j + 3 * k / 4, k / 2, k / 4 ) -
|
||||
2 * sumInt( imgCh[0], i, j + k / 4, k / 4, k / 2 ) - 2 * sumInt( imgCh[0], i + 3 * k / 4, j + k / 4, k / 4, k / 2 );
|
||||
feature[11] = s - 2 * sumInt( imgCh[0], i, j + k / 4, k / 4, k / 4 ) - 2 * sumInt( imgCh[0], i, j + 3 * k / 4, k / 4, k / 4 ) -
|
||||
2 * sumInt( imgCh[0], i + k / 4, j, k / 2, k / 4 ) - 2 * sumInt( imgCh[0], i + k / 4, j + k / 2, k / 2, k / 4 ) -
|
||||
2 * sumInt( imgCh[0], i + 3 * k / 4, j + k / 4, k / 4, k / 4 ) -
|
||||
2 * sumInt( imgCh[0], i + 3 * k / 4, j + 3 * k / 4, k / 4, k / 4 );
|
||||
|
||||
feature[12] = s - 2 * sumInt( imgCh[0], i + k / 4, j, k / 4, k ) - 2 * sumInt( imgCh[0], i + 3 * k / 4, j, k / 4, k );
|
||||
feature[13] = s - 2 * sumInt( imgCh[0], i + k / 4, j, k / 4, k / 2 ) - 2 * sumInt( imgCh[0], i + 3 * k / 4, j, k / 4, k / 2 ) -
|
||||
2 * sumInt( imgCh[0], i, j + k / 2, k / 4, k / 2 ) - 2 * sumInt( imgCh[0], i + k / 2, j + k / 2, k / 4, k / 2 );
|
||||
feature[14] = s - 2 * sumInt( imgCh[0], i + k / 4, j, k / 4, k / 4 ) - 2 * sumInt( imgCh[0], i + 3 * k / 4, j, k / 4, k / 4 ) -
|
||||
2 * sumInt( imgCh[0], i, j + k / 4, k / 4, k / 2 ) - 2 * sumInt( imgCh[0], i + k / 2, j + k / 4, k / 4, k / 2 ) -
|
||||
2 * sumInt( imgCh[0], i + k / 4, j + 3 * k / 4, k / 4, k / 4 ) -
|
||||
2 * sumInt( imgCh[0], i + 3 * k / 4, j + 3 * k / 4, k / 4, k / 4 );
|
||||
feature[15] = s - 2 * sumInt( imgCh[0], i, j + k / 4, k / 4, k / 4 ) - 2 * sumInt( imgCh[0], i, j + 3 * k / 4, k / 4, k / 4 ) -
|
||||
2 * sumInt( imgCh[0], i + k / 4, j, k / 4, k / 4 ) - 2 * sumInt( imgCh[0], i + k / 4, j + k / 2, k / 4, k / 4 ) -
|
||||
2 * sumInt( imgCh[0], i + k / 2, j + k / 4, k / 4, k / 4 ) -
|
||||
2 * sumInt( imgCh[0], i + k / 2, j + 3 * k / 4, k / 4, k / 4 ) - 2 * sumInt( imgCh[0], i + 3 * k / 4, j, k / 4, k / 4 ) -
|
||||
2 * sumInt( imgCh[0], i + 3 * k / 4, j + k / 2, k / 4, k / 4 );
|
||||
|
||||
feature[16] = sumInt( imgCh[1], i, j, k, k );
|
||||
feature[17] = sumInt( imgCh[2], i, j, k, k );
|
||||
|
||||
patchDescr.feature /= patchRadius;
|
||||
}
|
||||
|
||||
class ParallelDCTFiller : public ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
const Size sz;
|
||||
const Mat *imgCh;
|
||||
std::vector< GPCPatchDescriptor > *descr;
|
||||
|
||||
ParallelDCTFiller &operator=( const ParallelDCTFiller & );
|
||||
|
||||
public:
|
||||
ParallelDCTFiller( const Size &_sz, const Mat *_imgCh, std::vector< GPCPatchDescriptor > *_descr )
|
||||
: sz( _sz ), imgCh( _imgCh ), descr( _descr ){};
|
||||
|
||||
void operator()( const Range &range ) const CV_OVERRIDE
|
||||
{
|
||||
for ( int i = range.start; i < range.end; ++i )
|
||||
{
|
||||
int x, y;
|
||||
GPCDetails::getCoordinatesFromIndex( i, sz, x, y );
|
||||
getDCTPatchDescriptor( descr->at( i ), imgCh, y, x );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
|
||||
bool ocl_getAllDCTDescriptorsForImage( const Mat *imgCh, std::vector< GPCPatchDescriptor > &descr )
|
||||
{
|
||||
const Size sz = imgCh[0].size();
|
||||
ocl::Kernel kernel( "getPatchDescriptor", ocl::optflow::sparse_matching_gpc_oclsrc,
|
||||
format( "-DPATCH_RADIUS_DOUBLED=%d -DCV_PI=%f -DSQRT2_INV=%f", PATCH_RADIUS_DOUBLED, CV_PI, SQRT2_INV ) );
|
||||
CV_Assert(sz.height - 2 * patchRadius > 0);
|
||||
CV_Assert(sz.width - 2 * patchRadius > 0);
|
||||
size_t globSize[] = {(size_t)(sz.height - 2 * patchRadius), (size_t)(sz.width - 2 * patchRadius)};
|
||||
UMat out( globSize[0] * globSize[1], GPCPatchDescriptor::nFeatures, CV_64F );
|
||||
if (
|
||||
kernel
|
||||
.args( cv::ocl::KernelArg::ReadOnlyNoSize( imgCh[0].getUMat( ACCESS_READ ) ),
|
||||
cv::ocl::KernelArg::ReadOnlyNoSize( imgCh[1].getUMat( ACCESS_READ ) ),
|
||||
cv::ocl::KernelArg::ReadOnlyNoSize( imgCh[2].getUMat( ACCESS_READ ) ),
|
||||
cv::ocl::KernelArg::WriteOnlyNoSize( out ),
|
||||
(int)globSize[0], (int)globSize[1], (int)patchRadius )
|
||||
.run( 2, globSize, 0, true ) == false )
|
||||
return false;
|
||||
Mat cpuOut = out.getMat( ACCESS_READ );
|
||||
for ( int i = 0; i + 2 * patchRadius < sz.height; ++i )
|
||||
for ( int j = 0; j + 2 * patchRadius < sz.width; ++j )
|
||||
descr.push_back( *cpuOut.ptr< GPCPatchDescriptor >( i * globSize[1] + j ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void getAllDCTDescriptorsForImage( const Mat *imgCh, std::vector< GPCPatchDescriptor > &descr, const GPCMatchingParams &mp )
|
||||
{
|
||||
const Size sz = imgCh[0].size();
|
||||
descr.reserve( ( sz.height - 2 * patchRadius ) * ( sz.width - 2 * patchRadius ) );
|
||||
|
||||
CV_UNUSED(mp); // Fix unused parameter warning in case OpenCL is not available
|
||||
CV_OCL_RUN( mp.useOpenCL, ocl_getAllDCTDescriptorsForImage( imgCh, descr ) )
|
||||
|
||||
descr.resize( ( sz.height - 2 * patchRadius ) * ( sz.width - 2 * patchRadius ) );
|
||||
parallel_for_( Range( 0, descr.size() ), ParallelDCTFiller( sz, imgCh, &descr ) );
|
||||
}
|
||||
|
||||
class ParallelWHTFiller : public ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
const Size sz;
|
||||
const Mat *imgChInt;
|
||||
std::vector< GPCPatchDescriptor > *descr;
|
||||
|
||||
ParallelWHTFiller &operator=( const ParallelWHTFiller & );
|
||||
|
||||
public:
|
||||
ParallelWHTFiller( const Size &_sz, const Mat *_imgChInt, std::vector< GPCPatchDescriptor > *_descr )
|
||||
: sz( _sz ), imgChInt( _imgChInt ), descr( _descr ){};
|
||||
|
||||
void operator()( const Range &range ) const CV_OVERRIDE
|
||||
{
|
||||
for ( int i = range.start; i < range.end; ++i )
|
||||
{
|
||||
int x, y;
|
||||
GPCDetails::getCoordinatesFromIndex( i, sz, x, y );
|
||||
getWHTPatchDescriptor( descr->at( i ), imgChInt, y, x );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void getAllWHTDescriptorsForImage( const Mat *imgCh, std::vector< GPCPatchDescriptor > &descr, const GPCMatchingParams & )
|
||||
{
|
||||
const Size sz = imgCh[0].size();
|
||||
descr.resize( ( sz.height - 2 * patchRadius ) * ( sz.width - 2 * patchRadius ) );
|
||||
|
||||
Mat imgChInt[3];
|
||||
integral( imgCh[0], imgChInt[0], CV_64F );
|
||||
integral( imgCh[1], imgChInt[1], CV_64F );
|
||||
integral( imgCh[2], imgChInt[2], CV_64F );
|
||||
|
||||
parallel_for_( Range( 0, descr.size() ), ParallelWHTFiller( sz, imgChInt, &descr ) );
|
||||
}
|
||||
|
||||
void buildIndex( OutputArray featuresOut, flann::Index &index, const Mat *imgCh,
|
||||
void ( *getAllDescrFn )( const Mat *, std::vector< GPCPatchDescriptor > &, const GPCMatchingParams & ) )
|
||||
{
|
||||
std::vector< GPCPatchDescriptor > descriptors;
|
||||
getAllDescrFn( imgCh, descriptors, GPCMatchingParams() );
|
||||
|
||||
featuresOut.create( descriptors.size(), GPCPatchDescriptor::nFeatures, CV_32F );
|
||||
Mat features = featuresOut.getMat();
|
||||
|
||||
for ( size_t i = 0; i < descriptors.size(); ++i )
|
||||
*features.ptr< Vec< float, GPCPatchDescriptor::nFeatures > >( i ) = descriptors[i].feature;
|
||||
|
||||
cv::flann::KDTreeIndexParams indexParams;
|
||||
index.build( features, indexParams, cvflann::FLANN_DIST_L2 );
|
||||
}
|
||||
|
||||
void getTriplet( const Magnitude &mag, const Mat >, const Mat *fromCh, const Mat *toCh, GPCSamplesVector &samples, flann::Index &index,
|
||||
void ( *getDescFn )( GPCPatchDescriptor &, const Mat *, int, int ) )
|
||||
{
|
||||
const Size sz = gt.size();
|
||||
const int i0 = mag.i;
|
||||
const int j0 = mag.j;
|
||||
const int i1 = i0 + cvRound( gt.at< Vec2f >( i0, j0 )[1] );
|
||||
const int j1 = j0 + cvRound( gt.at< Vec2f >( i0, j0 )[0] );
|
||||
if ( checkBounds( i1, j1, sz ) )
|
||||
{
|
||||
GPCPatchSample ps;
|
||||
getDescFn( ps.ref, fromCh, i0, j0 );
|
||||
getDescFn( ps.pos, toCh, i1, j1 );
|
||||
ps.neg.markAsSeparated();
|
||||
|
||||
Matx< float, 1, GPCPatchDescriptor::nFeatures > ref32;
|
||||
Matx< int, 1, negSearchKNN > indices;
|
||||
int maxDist = 0;
|
||||
|
||||
for ( unsigned i = 0; i < GPCPatchDescriptor::nFeatures; ++i )
|
||||
ref32( 0, i ) = ps.ref.feature[i];
|
||||
|
||||
index.knnSearch( ref32, indices, noArray(), negSearchKNN );
|
||||
|
||||
for ( unsigned i = 0; i < negSearchKNN; ++i )
|
||||
{
|
||||
int i2, j2;
|
||||
GPCDetails::getCoordinatesFromIndex( indices( 0, i ), sz, j2, i2 );
|
||||
const int dist = ( i2 - i1 ) * ( i2 - i1 ) + ( j2 - j1 ) * ( j2 - j1 );
|
||||
if ( maxDist < dist )
|
||||
{
|
||||
maxDist = dist;
|
||||
getDescFn( ps.neg, toCh, i2, j2 );
|
||||
}
|
||||
}
|
||||
|
||||
samples.push_back( ps );
|
||||
}
|
||||
}
|
||||
|
||||
void getTrainingSamples( const Mat &from, const Mat &to, const Mat >, GPCSamplesVector &samples, const int type )
|
||||
{
|
||||
const Size sz = gt.size();
|
||||
std::vector< Magnitude > mag;
|
||||
|
||||
for ( int i = patchRadius; i + patchRadius < sz.height; ++i )
|
||||
for ( int j = patchRadius; j + patchRadius < sz.width; ++j )
|
||||
mag.push_back( Magnitude( normL2Sqr( gt.at< Vec2f >( i, j ) ), i, j ) );
|
||||
|
||||
size_t n = size_t( mag.size() * thresholdMagnitudeFrac ); // As suggested in the paper, we discard part of the training samples
|
||||
// with a small displacement and train to better distinguish hard pairs.
|
||||
std::nth_element( mag.begin(), mag.begin() + n, mag.end() );
|
||||
mag.resize( n );
|
||||
#ifdef CV_CXX11
|
||||
std::mt19937 std_rng(cv::theRNG()());
|
||||
std::shuffle(mag.begin(), mag.end(), std_rng);
|
||||
#else
|
||||
std::random_shuffle( mag.begin(), mag.end() );
|
||||
#endif
|
||||
n /= patchRadius;
|
||||
mag.resize( n );
|
||||
|
||||
if ( type == GPC_DESCRIPTOR_DCT )
|
||||
{
|
||||
Mat fromCh[3], toCh[3];
|
||||
split( from, fromCh );
|
||||
split( to, toCh );
|
||||
|
||||
Mat allDescriptors;
|
||||
flann::Index index;
|
||||
buildIndex( allDescriptors, index, toCh, getAllDCTDescriptorsForImage );
|
||||
|
||||
for ( size_t k = 0; k < n; ++k )
|
||||
getTriplet( mag[k], gt, fromCh, toCh, samples, index, getDCTPatchDescriptor );
|
||||
}
|
||||
else if ( type == GPC_DESCRIPTOR_WHT )
|
||||
{
|
||||
Mat fromCh[3], toCh[3], fromChInt[3], toChInt[3];
|
||||
split( from, fromCh );
|
||||
split( to, toCh );
|
||||
integral( fromCh[0], fromChInt[0], CV_64F );
|
||||
integral( fromCh[1], fromChInt[1], CV_64F );
|
||||
integral( fromCh[2], fromChInt[2], CV_64F );
|
||||
integral( toCh[0], toChInt[0], CV_64F );
|
||||
integral( toCh[1], toChInt[1], CV_64F );
|
||||
integral( toCh[2], toChInt[2], CV_64F );
|
||||
|
||||
Mat allDescriptors;
|
||||
flann::Index index;
|
||||
buildIndex( allDescriptors, index, toCh, getAllWHTDescriptorsForImage );
|
||||
|
||||
for ( size_t k = 0; k < n; ++k )
|
||||
getTriplet( mag[k], gt, fromChInt, toChInt, samples, index, getWHTPatchDescriptor );
|
||||
}
|
||||
else
|
||||
CV_Error( Error::StsBadArg, "Unknown descriptor type" );
|
||||
}
|
||||
|
||||
/* Sample random number from Cauchy distribution. */
|
||||
double getRandomCauchyScalar()
|
||||
{
|
||||
return tan( rng.uniform( -1.54, 1.54 ) ); // I intentionally used the value slightly less than PI/2 to enforce strictly
|
||||
// zero probability for large numbers. Resulting PDF for Cauchy has
|
||||
// truncated "tails".
|
||||
}
|
||||
|
||||
/* Sample random vector from Cauchy distribution (pointwise, i.e. vector whose components are independent random
|
||||
* variables from Cauchy distribution) */
|
||||
void getRandomCauchyVector( Vec< double, GPCPatchDescriptor::nFeatures > &v )
|
||||
{
|
||||
for ( unsigned i = 0; i < GPCPatchDescriptor::nFeatures; ++i )
|
||||
v[i] = getRandomCauchyScalar();
|
||||
}
|
||||
|
||||
double getRobustMedian( double m ) { return m < 0 ? m * ( 1.0 + epsTolerance ) : m * ( 1.0 - epsTolerance ); }
|
||||
}
|
||||
|
||||
double GPCPatchDescriptor::dot( const Vec< double, nFeatures > &coef ) const
|
||||
{
|
||||
#if CV_SIMD128_64F
|
||||
v_float64x2 sum = v_setzero_f64();
|
||||
for ( unsigned i = 0; i < nFeatures; i += 2 )
|
||||
{
|
||||
v_float64x2 x = v_load( &feature.val[i] );
|
||||
v_float64x2 y = v_load( &coef.val[i] );
|
||||
sum = v_muladd( x, y, sum );
|
||||
}
|
||||
#if CV_SSE2
|
||||
__m128d sumrev = _mm_shuffle_pd( sum.val, sum.val, _MM_SHUFFLE2( 0, 1 ) );
|
||||
return _mm_cvtsd_f64( _mm_add_pd( sum.val, sumrev ) );
|
||||
#else
|
||||
double CV_DECL_ALIGNED( 16 ) buf[2];
|
||||
v_store_aligned( buf, sum );
|
||||
return OPENCV_HAL_ADD( buf[0], buf[1] );
|
||||
#endif
|
||||
|
||||
#else
|
||||
return feature.dot( coef );
|
||||
#endif
|
||||
}
|
||||
|
||||
void GPCPatchSample::getDirections( bool &refdir, bool &posdir, bool &negdir, const Vec< double, GPCPatchDescriptor::nFeatures > &coef, double rhs ) const
|
||||
{
|
||||
refdir = ( ref.dot( coef ) < rhs );
|
||||
posdir = pos.isSeparated() ? ( !refdir ) : ( pos.dot( coef ) < rhs );
|
||||
negdir = neg.isSeparated() ? ( !refdir ) : ( neg.dot( coef ) < rhs );
|
||||
}
|
||||
|
||||
void GPCDetails::getAllDescriptorsForImage( const Mat *imgCh, std::vector< GPCPatchDescriptor > &descr, const GPCMatchingParams &mp,
|
||||
int type )
|
||||
{
|
||||
if ( type == GPC_DESCRIPTOR_DCT )
|
||||
getAllDCTDescriptorsForImage( imgCh, descr, mp );
|
||||
else if ( type == GPC_DESCRIPTOR_WHT )
|
||||
getAllWHTDescriptorsForImage( imgCh, descr, mp );
|
||||
else
|
||||
CV_Error( Error::StsBadArg, "Unknown descriptor type" );
|
||||
}
|
||||
|
||||
void GPCDetails::getCoordinatesFromIndex( size_t index, Size sz, int &x, int &y )
|
||||
{
|
||||
const size_t stride = sz.width - patchRadius * 2;
|
||||
y = int( index / stride );
|
||||
x = int( index - y * stride + patchRadius );
|
||||
y += patchRadius;
|
||||
}
|
||||
|
||||
bool GPCTree::trainNode( size_t nodeId, SIter begin, SIter end, unsigned depth )
|
||||
{
|
||||
const int nSamples = (int)std::distance( begin, end );
|
||||
|
||||
if ( nSamples < params.minNumberOfSamples || depth >= params.maxTreeDepth )
|
||||
return false;
|
||||
|
||||
if ( nodeId >= nodes.size() )
|
||||
nodes.resize( nodeId + 1 );
|
||||
|
||||
Node &node = nodes[nodeId];
|
||||
|
||||
// Select the best hyperplane
|
||||
unsigned globalBestScore = 0;
|
||||
std::vector< double > values;
|
||||
values.reserve( nSamples * 2 );
|
||||
|
||||
for ( int j = 0; j < globalIters; ++j )
|
||||
{ // Global search step
|
||||
Vec< double, GPCPatchDescriptor::nFeatures > coef;
|
||||
unsigned localBestScore = 0;
|
||||
getRandomCauchyVector( coef );
|
||||
|
||||
for ( int i = 0; i < localIters; ++i )
|
||||
{ // Local search step
|
||||
double randomModification = getRandomCauchyScalar() * ( 1.0 + sigmaGrowthRate * int( i / GPCPatchDescriptor::nFeatures ) );
|
||||
const int pos = i % GPCPatchDescriptor::nFeatures;
|
||||
std::swap( coef[pos], randomModification );
|
||||
values.clear();
|
||||
|
||||
for ( SIter iter = begin; iter != end; ++iter )
|
||||
values.push_back( iter->ref.dot( coef ) );
|
||||
|
||||
std::nth_element( values.begin(), values.begin() + nSamples / 2, values.end() );
|
||||
double median = values[nSamples / 2];
|
||||
|
||||
// Skip obviously malformed division. This may happen in case there are a large number of equal samples.
|
||||
// Most likely this won't happen with samples collected from a good dataset.
|
||||
// Happens in case dataset contains plain (or close to plain) images.
|
||||
if ( std::count_if( values.begin(), values.end(), CompareWithTolerance( median ) ) > std::max( 1, nSamples / 4 ) )
|
||||
continue;
|
||||
|
||||
median = getRobustMedian( median );
|
||||
|
||||
unsigned score = 0;
|
||||
for ( SIter iter = begin; iter != end; ++iter )
|
||||
{
|
||||
bool refdir, posdir, negdir;
|
||||
iter->getDirections( refdir, posdir, negdir, coef, median );
|
||||
if ( refdir == posdir )
|
||||
score += scoreGainPos;
|
||||
if ( refdir != negdir )
|
||||
score += scoreGainNeg;
|
||||
}
|
||||
|
||||
if ( score > localBestScore )
|
||||
localBestScore = score;
|
||||
else
|
||||
{
|
||||
const double beta = simulatedAnnealingTemperatureCoef * std::sqrt( static_cast<float>(i) ) / ( nSamples * ( scoreGainPos + scoreGainNeg ) );
|
||||
if ( rng.uniform( 0.0, 1.0 ) > std::exp( -beta * ( localBestScore - score) ) )
|
||||
coef[pos] = randomModification;
|
||||
}
|
||||
|
||||
if ( score > globalBestScore )
|
||||
{
|
||||
globalBestScore = score;
|
||||
node.coef = coef;
|
||||
node.rhs = median;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( globalBestScore == 0 )
|
||||
return false;
|
||||
|
||||
if ( params.printProgress )
|
||||
{
|
||||
const int maxScore = nSamples * ( scoreGainPos + scoreGainNeg );
|
||||
const double correctRatio = double( globalBestScore ) / maxScore;
|
||||
printf( "[%u] Correct %.2f (%u/%d)\nWeights:", depth, correctRatio, globalBestScore, maxScore );
|
||||
for ( unsigned k = 0; k < GPCPatchDescriptor::nFeatures; ++k )
|
||||
printf( " %.3f", node.coef[k] );
|
||||
printf( "\n" );
|
||||
}
|
||||
|
||||
for ( SIter iter = begin; iter != end; ++iter )
|
||||
{
|
||||
bool refdir, posdir, negdir;
|
||||
iter->getDirections( refdir, posdir, negdir, node.coef, node.rhs );
|
||||
// We shouldn't account for positive sample in the scoring in case it was separated before. So mark it as separated.
|
||||
// After all, we can't bring back samples which were separated from reference on early levels.
|
||||
if ( refdir != posdir )
|
||||
iter->pos.markAsSeparated();
|
||||
// The same for negative sample.
|
||||
if ( refdir != negdir )
|
||||
iter->neg.markAsSeparated();
|
||||
// If both positive and negative were separated before then such triplet doesn't make sense on deeper levels. We discard it.
|
||||
}
|
||||
|
||||
// Partition vector with samples according to the hyperplane in QuickSort-like manner.
|
||||
// Unlike QuickSort, we need to partition it into 3 parts (left subtree samples; undefined samples; right subtree
|
||||
// samples), so we call it two times.
|
||||
SIter leftEnd = std::partition( begin, end, PartitionPredicate1( node.coef, node.rhs ) ); // Separate left subtree samples from others.
|
||||
SIter rightBegin =
|
||||
std::partition( leftEnd, end, PartitionPredicate2( node.coef, node.rhs ) ); // Separate undefined samples from right subtree samples.
|
||||
|
||||
node.left = ( trainNode( nodeId * 2 + 1, begin, leftEnd, depth + 1 ) ) ? unsigned( nodeId * 2 + 1 ) : 0;
|
||||
node.right = ( trainNode( nodeId * 2 + 2, rightBegin, end, depth + 1 ) ) ? unsigned( nodeId * 2 + 2 ) : 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GPCTree::train( GPCTrainingSamples &samples, const GPCTrainingParams _params )
|
||||
{
|
||||
if ( _params.descriptorType != samples.type() )
|
||||
CV_Error( Error::StsBadArg, "Descriptor type mismatch! Check that samples are collected with the same descriptor type." );
|
||||
nodes.clear();
|
||||
nodes.reserve( samples.size() * 2 - 1 ); // set upper bound for the possible number of nodes so all subsequent resize() will be no-op
|
||||
params = _params;
|
||||
GPCSamplesVector &sv = samples;
|
||||
trainNode( 0, sv.begin(), sv.end(), 0 );
|
||||
}
|
||||
|
||||
void GPCTree::write( FileStorage &fs ) const
|
||||
{
|
||||
if ( nodes.empty() )
|
||||
CV_Error( Error::StsBadArg, "Tree have not been trained" );
|
||||
fs << "nodes" << nodes;
|
||||
fs << "dtype" << (int)params.descriptorType;
|
||||
}
|
||||
|
||||
void GPCTree::read( const FileNode &fn )
|
||||
{
|
||||
fn["nodes"] >> nodes;
|
||||
fn["dtype"] >> (int &)params.descriptorType;
|
||||
}
|
||||
|
||||
unsigned GPCTree::findLeafForPatch( const GPCPatchDescriptor &descr ) const
|
||||
{
|
||||
unsigned id = 0, prevId;
|
||||
do
|
||||
{
|
||||
prevId = id;
|
||||
if ( descr.dot( nodes[id].coef ) < nodes[id].rhs )
|
||||
id = nodes[id].right;
|
||||
else
|
||||
id = nodes[id].left;
|
||||
} while ( id );
|
||||
return prevId;
|
||||
}
|
||||
|
||||
Ptr< GPCTrainingSamples > GPCTrainingSamples::create( const std::vector< String > &imagesFrom, const std::vector< String > &imagesTo,
|
||||
const std::vector< String > >, int _descriptorType )
|
||||
{
|
||||
CV_Assert( imagesFrom.size() == imagesTo.size() );
|
||||
CV_Assert( imagesFrom.size() == gt.size() );
|
||||
|
||||
Ptr< GPCTrainingSamples > ts = makePtr< GPCTrainingSamples >();
|
||||
|
||||
ts->descriptorType = _descriptorType;
|
||||
|
||||
for ( size_t i = 0; i < imagesFrom.size(); ++i )
|
||||
{
|
||||
Mat from = imread( imagesFrom[i] );
|
||||
Mat to = imread( imagesTo[i] );
|
||||
Mat gtFlow = readOpticalFlow( gt[i] );
|
||||
|
||||
CV_Assert( from.size == to.size );
|
||||
CV_Assert( from.size == gtFlow.size );
|
||||
CV_Assert( from.channels() == 3 );
|
||||
CV_Assert( to.channels() == 3 );
|
||||
|
||||
from.convertTo( from, CV_32FC3 );
|
||||
to.convertTo( to, CV_32FC3 );
|
||||
cvtColor( from, from, COLOR_BGR2YCrCb );
|
||||
cvtColor( to, to, COLOR_BGR2YCrCb );
|
||||
|
||||
getTrainingSamples( from, to, gtFlow, ts->samples, ts->descriptorType );
|
||||
}
|
||||
|
||||
return ts;
|
||||
}
|
||||
|
||||
Ptr< GPCTrainingSamples > GPCTrainingSamples::create( InputArrayOfArrays imagesFrom, InputArrayOfArrays imagesTo,
|
||||
InputArrayOfArrays gt, int _descriptorType )
|
||||
{
|
||||
CV_Assert( imagesFrom.total() == imagesTo.total() );
|
||||
CV_Assert( imagesFrom.total() == gt.total() );
|
||||
|
||||
Ptr< GPCTrainingSamples > ts = makePtr< GPCTrainingSamples >();
|
||||
|
||||
ts->descriptorType = _descriptorType;
|
||||
|
||||
for ( size_t i = 0; i < imagesFrom.total(); ++i )
|
||||
{
|
||||
Mat from = imagesFrom.getMat( static_cast<int>( i ) );
|
||||
Mat to = imagesTo.getMat( static_cast<int>( i ) );
|
||||
Mat gtFlow = gt.getMat( static_cast<int>( i ) );
|
||||
|
||||
CV_Assert( from.size == to.size );
|
||||
CV_Assert( from.size == gtFlow.size );
|
||||
CV_Assert( from.channels() == 3 );
|
||||
CV_Assert( to.channels() == 3 );
|
||||
|
||||
from.convertTo( from, CV_32FC3 );
|
||||
to.convertTo( to, CV_32FC3 );
|
||||
cvtColor( from, from, COLOR_BGR2YCrCb );
|
||||
cvtColor( to, to, COLOR_BGR2YCrCb );
|
||||
|
||||
getTrainingSamples( from, to, gtFlow, ts->samples, ts->descriptorType );
|
||||
}
|
||||
|
||||
return ts;
|
||||
}
|
||||
|
||||
void GPCDetails::dropOutliers( std::vector< std::pair< Point2i, Point2i > > &corr )
|
||||
{
|
||||
if ( corr.size() == 0 )
|
||||
return;
|
||||
|
||||
std::vector< float > mag( corr.size() );
|
||||
|
||||
for ( size_t i = 0; i < corr.size(); ++i )
|
||||
mag[i] = normL2Sqr( corr[i].first - corr[i].second );
|
||||
|
||||
const size_t threshold = size_t( mag.size() * thresholdOutliers );
|
||||
std::nth_element( mag.begin(), mag.begin() + threshold, mag.end() );
|
||||
const float percentile = mag[threshold];
|
||||
size_t i = 0, j = 0;
|
||||
|
||||
while ( i < corr.size() )
|
||||
{
|
||||
if ( normL2Sqr( corr[i].first - corr[i].second ) <= percentile )
|
||||
{
|
||||
corr[j] = corr[i];
|
||||
++j;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
corr.resize( j );
|
||||
}
|
||||
|
||||
} // namespace optflow
|
||||
|
||||
void write( FileStorage &fs, const String &name, const optflow::GPCTree::Node &node )
|
||||
{
|
||||
cv::internal::WriteStructContext ws( fs, name, FileNode::SEQ + FileNode::FLOW );
|
||||
for ( unsigned i = 0; i < optflow::GPCPatchDescriptor::nFeatures; ++i )
|
||||
write( fs, node.coef[i] );
|
||||
write( fs, node.rhs );
|
||||
write( fs, (int)node.left );
|
||||
write( fs, (int)node.right );
|
||||
}
|
||||
|
||||
void read( const FileNode &fn, optflow::GPCTree::Node &node, optflow::GPCTree::Node )
|
||||
{
|
||||
FileNodeIterator it = fn.begin();
|
||||
for ( unsigned i = 0; i < optflow::GPCPatchDescriptor::nFeatures; ++i )
|
||||
it >> node.coef[i];
|
||||
it >> node.rhs >> (int &)node.left >> (int &)node.right;
|
||||
}
|
||||
|
||||
} // namespace cv
|
||||
@@ -0,0 +1,112 @@
|
||||
/*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) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., 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/ximgproc/sparse_match_interpolator.hpp"
|
||||
using namespace std;
|
||||
|
||||
namespace cv {
|
||||
namespace optflow {
|
||||
|
||||
CV_EXPORTS_W void calcOpticalFlowSparseToDense(InputArray from, InputArray to, OutputArray flow,
|
||||
int grid_step, int k,
|
||||
float sigma, bool use_post_proc,
|
||||
float fgs_lambda, float fgs_sigma)
|
||||
{
|
||||
CV_Assert( grid_step>1 && k>3 && sigma>0.0001f && fgs_lambda>1.0f && fgs_sigma>0.01f );
|
||||
CV_Assert( !from.empty() && from.depth() == CV_8U && (from.channels() == 3 || from.channels() == 1) );
|
||||
CV_Assert( !to .empty() && to .depth() == CV_8U && (to .channels() == 3 || to .channels() == 1) );
|
||||
CV_Assert( from.sameSize(to) );
|
||||
|
||||
Mat prev = from.getMat();
|
||||
Mat cur = to.getMat();
|
||||
Mat prev_grayscale, cur_grayscale;
|
||||
|
||||
while( (prev.cols/grid_step)*(prev.rows/grid_step) > SHRT_MAX ) //ensure that the number matches is not too big
|
||||
grid_step*=2;
|
||||
|
||||
if(prev.channels()==3)
|
||||
{
|
||||
cvtColor(prev,prev_grayscale,COLOR_BGR2GRAY);
|
||||
cvtColor(cur, cur_grayscale, COLOR_BGR2GRAY);
|
||||
}
|
||||
else
|
||||
{
|
||||
prev.copyTo(prev_grayscale);
|
||||
cur .copyTo(cur_grayscale);
|
||||
}
|
||||
|
||||
vector<Point2f> points;
|
||||
vector<Point2f> dst_points;
|
||||
vector<unsigned char> status;
|
||||
vector<float> err;
|
||||
vector<Point2f> points_filtered, dst_points_filtered;
|
||||
|
||||
for(int i=0;i<prev.rows;i+=grid_step)
|
||||
for(int j=0;j<prev.cols;j+=grid_step)
|
||||
points.push_back(Point2f((float)j,(float)i));
|
||||
|
||||
calcOpticalFlowPyrLK(prev_grayscale,cur_grayscale,points,dst_points,status,err,Size(21,21));
|
||||
|
||||
for(unsigned int i=0;i<points.size();i++)
|
||||
{
|
||||
if(status[i]!=0)
|
||||
{
|
||||
points_filtered.push_back(points[i]);
|
||||
dst_points_filtered.push_back(dst_points[i]);
|
||||
}
|
||||
}
|
||||
|
||||
flow.create(from.size(),CV_32FC2);
|
||||
Mat dense_flow = flow.getMat();
|
||||
|
||||
Ptr<ximgproc::EdgeAwareInterpolator> gd = ximgproc::createEdgeAwareInterpolator();
|
||||
gd->setK(k);
|
||||
gd->setSigma(sigma);
|
||||
gd->setUsePostProcessing(use_post_proc);
|
||||
gd->setFGSLambda(fgs_lambda);
|
||||
gd->setFGSSigma (fgs_sigma);
|
||||
gd->interpolate(prev,points_filtered,cur,dst_points_filtered,dense_flow);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,66 @@
|
||||
// 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.
|
||||
|
||||
#include "../test_precomp.hpp"
|
||||
#include "opencv2/ts/ocl_test.hpp"
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
PARAM_TEST_CASE(UpdateMotionHistory, bool)
|
||||
{
|
||||
double timestamp, duration;
|
||||
bool use_roi;
|
||||
|
||||
TEST_DECLARE_INPUT_PARAMETER(silhouette);
|
||||
TEST_DECLARE_OUTPUT_PARAMETER(mhi);
|
||||
|
||||
virtual void SetUp()
|
||||
{
|
||||
use_roi = GET_PARAM(0);
|
||||
}
|
||||
|
||||
virtual void generateTestData()
|
||||
{
|
||||
Size roiSize = randomSize(1, MAX_VALUE);
|
||||
Border silhouetteBorder = randomBorder(0, use_roi ? MAX_VALUE : 0);
|
||||
randomSubMat(silhouette, silhouette_roi, roiSize, silhouetteBorder, CV_8UC1, -11, 11);
|
||||
|
||||
Border mhiBorder = randomBorder(0, use_roi ? MAX_VALUE : 0);
|
||||
randomSubMat(mhi, mhi_roi, roiSize, mhiBorder, CV_32FC1, 0, 1);
|
||||
|
||||
timestamp = randomDouble(0, 1);
|
||||
duration = randomDouble(0, 1);
|
||||
if (timestamp < duration)
|
||||
std::swap(timestamp, duration);
|
||||
|
||||
UMAT_UPLOAD_INPUT_PARAMETER(silhouette);
|
||||
UMAT_UPLOAD_OUTPUT_PARAMETER(mhi);
|
||||
}
|
||||
};
|
||||
|
||||
OCL_TEST_P(UpdateMotionHistory, Mat)
|
||||
{
|
||||
for (int j = 0; j < cvtest::ocl::test_loop_times; j++)
|
||||
{
|
||||
generateTestData();
|
||||
|
||||
OCL_OFF(cv::motempl::updateMotionHistory(silhouette_roi, mhi_roi, timestamp, duration));
|
||||
OCL_ON(cv::motempl::updateMotionHistory(usilhouette_roi, umhi_roi, timestamp, duration));
|
||||
|
||||
OCL_EXPECT_MATS_NEAR(mhi, 0);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////// Instantiation /////////////////////////////////////////
|
||||
|
||||
OCL_INSTANTIATE_TEST_CASE_P(Video, UpdateMotionHistory, Values(false, true));
|
||||
|
||||
}} // namespace
|
||||
|
||||
#endif // HAVE_OPENCL
|
||||
@@ -0,0 +1,117 @@
|
||||
/*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) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved.
|
||||
// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved.
|
||||
// Copyright (C) 2010-2012, Multicoreware, Inc., 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/ts/ocl_test.hpp"
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
|
||||
namespace opencv_test {
|
||||
namespace ocl {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Optical_flow_tvl1
|
||||
namespace
|
||||
{
|
||||
IMPLEMENT_PARAM_CLASS(UseInitFlow, bool)
|
||||
IMPLEMENT_PARAM_CLASS(MedianFiltering, int)
|
||||
IMPLEMENT_PARAM_CLASS(ScaleStep, double)
|
||||
}
|
||||
|
||||
PARAM_TEST_CASE(OpticalFlowTVL1, UseInitFlow, MedianFiltering, ScaleStep)
|
||||
{
|
||||
bool useInitFlow;
|
||||
int medianFiltering;
|
||||
double scaleStep;
|
||||
virtual void SetUp()
|
||||
{
|
||||
useInitFlow = GET_PARAM(0);
|
||||
medianFiltering = GET_PARAM(1);
|
||||
scaleStep = GET_PARAM(2);
|
||||
}
|
||||
};
|
||||
|
||||
OCL_TEST_P(OpticalFlowTVL1, Mat)
|
||||
{
|
||||
Mat frame0 = readImage("optflow/RubberWhale1.png", IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame0.empty());
|
||||
|
||||
Mat frame1 = readImage("optflow/RubberWhale2.png", IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame1.empty());
|
||||
|
||||
Mat flow; UMat uflow;
|
||||
|
||||
//create algorithm
|
||||
Ptr<DualTVL1OpticalFlow> alg = createOptFlow_DualTVL1();
|
||||
|
||||
//set parameters
|
||||
alg->setScaleStep(scaleStep);
|
||||
alg->setMedianFiltering(medianFiltering);
|
||||
|
||||
//create initial flow as result of algorithm calculation
|
||||
if (useInitFlow)
|
||||
{
|
||||
OCL_ON(alg->calc(frame0, frame1, uflow));
|
||||
uflow.copyTo(flow);
|
||||
}
|
||||
|
||||
//set flag to use initial flow as it is ready to use
|
||||
alg->setUseInitialFlow(useInitFlow);
|
||||
|
||||
OCL_OFF(alg->calc(frame0, frame1, flow));
|
||||
OCL_ON(alg->calc(frame0, frame1, uflow));
|
||||
|
||||
EXPECT_MAT_SIMILAR(flow, uflow, 1e-2);
|
||||
}
|
||||
|
||||
OCL_INSTANTIATE_TEST_CASE_P(Contrib, OpticalFlowTVL1,
|
||||
Combine(
|
||||
Values(UseInitFlow(false), UseInitFlow(true)),
|
||||
Values(MedianFiltering(3), MedianFiltering(-1)),
|
||||
Values(ScaleStep(0.3),ScaleStep(0.5))
|
||||
)
|
||||
);
|
||||
|
||||
} } // namespace opencv_test::ocl
|
||||
|
||||
#endif // HAVE_OPENCL
|
||||
@@ -0,0 +1,353 @@
|
||||
/*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.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, 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 Intel Corporation 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"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
static string getDataDir() { return TS::ptr()->get_data_path(); }
|
||||
|
||||
static string getRubberWhaleFrame1() { return getDataDir() + "optflow/RubberWhale1.png"; }
|
||||
|
||||
static string getRubberWhaleFrame2() { return getDataDir() + "optflow/RubberWhale2.png"; }
|
||||
|
||||
static string getRubberWhaleGroundTruth() { return getDataDir() + "optflow/RubberWhale.flo"; }
|
||||
|
||||
static bool isFlowCorrect(float u) { return !cvIsNaN(u) && (fabs(u) < 1e9); }
|
||||
|
||||
static bool isFlowCorrect(double u) { return !cvIsNaN(u) && (fabs(u) < 1e9); }
|
||||
|
||||
static float calcRMSE(Mat flow1, Mat flow2)
|
||||
{
|
||||
float sum = 0;
|
||||
int counter = 0;
|
||||
const int rows = flow1.rows;
|
||||
const int cols = flow1.cols;
|
||||
|
||||
for (int y = 0; y < rows; ++y)
|
||||
{
|
||||
for (int x = 0; x < cols; ++x)
|
||||
{
|
||||
Vec2f flow1_at_point = flow1.at<Vec2f>(y, x);
|
||||
Vec2f flow2_at_point = flow2.at<Vec2f>(y, x);
|
||||
|
||||
float u1 = flow1_at_point[0];
|
||||
float v1 = flow1_at_point[1];
|
||||
float u2 = flow2_at_point[0];
|
||||
float v2 = flow2_at_point[1];
|
||||
|
||||
if (isFlowCorrect(u1) && isFlowCorrect(u2) && isFlowCorrect(v1) && isFlowCorrect(v2))
|
||||
{
|
||||
sum += (u1 - u2) * (u1 - u2) + (v1 - v2) * (v1 - v2);
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (float)sqrt(sum / (1e-9 + counter));
|
||||
}
|
||||
static float calcRMSE(vector<Point2f> prevPts, vector<Point2f> currPts, Mat flow)
|
||||
{
|
||||
vector<float> ee;
|
||||
for (unsigned int n = 0; n < prevPts.size(); n++)
|
||||
{
|
||||
Point2f gtFlow = flow.at<Point2f>(prevPts[n]);
|
||||
if (isFlowCorrect(gtFlow.x) && isFlowCorrect(gtFlow.y))
|
||||
{
|
||||
Point2f diffFlow = (currPts[n] - prevPts[n]) - gtFlow;
|
||||
ee.push_back(sqrt(diffFlow.x * diffFlow.x + diffFlow.y * diffFlow.y));
|
||||
}
|
||||
}
|
||||
return static_cast<float>(mean(ee).val[0]);
|
||||
}
|
||||
static float calcAvgEPE(vector< pair<Point2i, Point2i> > corr, Mat flow)
|
||||
{
|
||||
double sum = 0;
|
||||
int counter = 0;
|
||||
|
||||
for (size_t i = 0; i < corr.size(); ++i)
|
||||
{
|
||||
Vec2f flow1_at_point = Point2f(corr[i].second - corr[i].first);
|
||||
Vec2f flow2_at_point = flow.at<Vec2f>(corr[i].first.y, corr[i].first.x);
|
||||
|
||||
double u1 = (double)flow1_at_point[0];
|
||||
double v1 = (double)flow1_at_point[1];
|
||||
double u2 = (double)flow2_at_point[0];
|
||||
double v2 = (double)flow2_at_point[1];
|
||||
|
||||
if (isFlowCorrect(u1) && isFlowCorrect(u2) && isFlowCorrect(v1) && isFlowCorrect(v2))
|
||||
{
|
||||
sum += sqrt((u1 - u2) * (u1 - u2) + (v1 - v2) * (v1 - v2));
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
|
||||
return (float)(sum / counter);
|
||||
}
|
||||
|
||||
bool readRubberWhale(Mat &dst_frame_1, Mat &dst_frame_2, Mat &dst_GT)
|
||||
{
|
||||
string frame1_path = getRubberWhaleFrame1();
|
||||
string frame2_path = getRubberWhaleFrame2();
|
||||
string gt_flow_path = getRubberWhaleGroundTruth();
|
||||
// removing space may be an issue on windows machines
|
||||
frame1_path.erase(std::remove_if(frame1_path.begin(), frame1_path.end(), isspace), frame1_path.end());
|
||||
frame2_path.erase(std::remove_if(frame2_path.begin(), frame2_path.end(), isspace), frame2_path.end());
|
||||
gt_flow_path.erase(std::remove_if(gt_flow_path.begin(), gt_flow_path.end(), isspace), gt_flow_path.end());
|
||||
|
||||
dst_frame_1 = imread(frame1_path);
|
||||
dst_frame_2 = imread(frame2_path);
|
||||
dst_GT = readOpticalFlow(gt_flow_path);
|
||||
|
||||
if (dst_frame_1.empty() || dst_frame_2.empty() || dst_GT.empty())
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
TEST(DenseOpticalFlow_SimpleFlow, ReferenceAccuracy)
|
||||
{
|
||||
Mat frame1, frame2, GT;
|
||||
ASSERT_TRUE(readRubberWhale(frame1, frame2, GT));
|
||||
float target_RMSE = 0.37f;
|
||||
|
||||
Mat flow;
|
||||
Ptr<DenseOpticalFlow> algo;
|
||||
algo = createOptFlow_SimpleFlow();
|
||||
algo->calc(frame1, frame2, flow);
|
||||
ASSERT_EQ(GT.rows, flow.rows);
|
||||
ASSERT_EQ(GT.cols, flow.cols);
|
||||
EXPECT_LE(calcRMSE(GT, flow), target_RMSE);
|
||||
}
|
||||
|
||||
TEST(DenseOpticalFlow_DeepFlow, ReferenceAccuracy)
|
||||
{
|
||||
Mat frame1, frame2, GT;
|
||||
ASSERT_TRUE(readRubberWhale(frame1, frame2, GT));
|
||||
float target_RMSE = 0.35f;
|
||||
cvtColor(frame1, frame1, COLOR_BGR2GRAY);
|
||||
cvtColor(frame2, frame2, COLOR_BGR2GRAY);
|
||||
|
||||
Mat flow;
|
||||
Ptr<DenseOpticalFlow> algo;
|
||||
algo = createOptFlow_DeepFlow();
|
||||
algo->calc(frame1, frame2, flow);
|
||||
ASSERT_EQ(GT.rows, flow.rows);
|
||||
ASSERT_EQ(GT.cols, flow.cols);
|
||||
EXPECT_LE(calcRMSE(GT, flow), target_RMSE);
|
||||
}
|
||||
|
||||
TEST(SparseOpticalFlow, ReferenceAccuracy)
|
||||
{
|
||||
// with the following test each invoker class should be tested once
|
||||
Mat frame1, frame2, GT;
|
||||
ASSERT_TRUE(readRubberWhale(frame1, frame2, GT));
|
||||
vector<Point2f> prevPts, currPts;
|
||||
for (int r = 0; r < frame1.rows; r+=10)
|
||||
{
|
||||
for (int c = 0; c < frame1.cols; c+=10)
|
||||
{
|
||||
prevPts.push_back(Point2f(static_cast<float>(c), static_cast<float>(r)));
|
||||
}
|
||||
}
|
||||
vector<uchar> status(prevPts.size());
|
||||
vector<float> err(prevPts.size());
|
||||
Ptr<SparseRLOFOpticalFlow> algo = SparseRLOFOpticalFlow::create();
|
||||
algo->setForwardBackward(0.0f);
|
||||
Ptr<RLOFOpticalFlowParameter> param = Ptr<RLOFOpticalFlowParameter>(new RLOFOpticalFlowParameter);
|
||||
param->supportRegionType = SR_CROSS;
|
||||
param->useIlluminationModel = true;
|
||||
param->solverType = ST_BILINEAR;
|
||||
param->setUseMEstimator(true);
|
||||
algo->setRLOFOpticalFlowParameter(param);
|
||||
algo->calc(frame1, frame2, prevPts, currPts, status, err);
|
||||
EXPECT_LE(calcRMSE(prevPts, currPts, GT), 0.3f);
|
||||
|
||||
param->solverType = ST_STANDART;
|
||||
algo->setRLOFOpticalFlowParameter(param);
|
||||
algo->calc(frame1, frame2, prevPts, currPts, status, err);
|
||||
EXPECT_LE(calcRMSE(prevPts, currPts, GT), 0.34f);
|
||||
|
||||
param->useIlluminationModel = false;
|
||||
param->solverType = ST_BILINEAR;
|
||||
algo->setRLOFOpticalFlowParameter(param);
|
||||
algo->calc(frame1, frame2, prevPts, currPts, status, err);
|
||||
EXPECT_LE(calcRMSE(prevPts, currPts, GT), 0.27f);
|
||||
|
||||
param->solverType = ST_STANDART;
|
||||
algo->setRLOFOpticalFlowParameter(param);
|
||||
algo->calc(frame1, frame2, prevPts, currPts, status, err);
|
||||
EXPECT_LE(calcRMSE(prevPts, currPts, GT), 0.27f);
|
||||
|
||||
param->setUseMEstimator(false);
|
||||
param->useIlluminationModel = true;
|
||||
|
||||
param->solverType = ST_BILINEAR;
|
||||
algo->setRLOFOpticalFlowParameter(param);
|
||||
algo->calc(frame1, frame2, prevPts, currPts, status, err);
|
||||
EXPECT_LE(calcRMSE(prevPts, currPts, GT), 0.28f);
|
||||
|
||||
param->solverType = ST_STANDART;
|
||||
algo->setRLOFOpticalFlowParameter(param);
|
||||
algo->calc(frame1, frame2, prevPts, currPts, status, err);
|
||||
EXPECT_LE(calcRMSE(prevPts, currPts, GT), 0.28f);
|
||||
|
||||
param->useIlluminationModel = false;
|
||||
|
||||
param->solverType = ST_BILINEAR;
|
||||
algo->setRLOFOpticalFlowParameter(param);
|
||||
algo->calc(frame1, frame2, prevPts, currPts, status, err);
|
||||
EXPECT_LE(calcRMSE(prevPts, currPts, GT), 0.80f);
|
||||
|
||||
param->solverType = ST_STANDART;
|
||||
algo->setRLOFOpticalFlowParameter(param);
|
||||
algo->calc(frame1, frame2, prevPts, currPts, status, err);
|
||||
EXPECT_LE(calcRMSE(prevPts, currPts, GT), 0.28f);
|
||||
}
|
||||
|
||||
TEST(DenseOpticalFlow_RLOF, ReferenceAccuracy)
|
||||
{
|
||||
Mat frame1, frame2, GT;
|
||||
ASSERT_TRUE(readRubberWhale(frame1, frame2, GT));
|
||||
Mat flow;
|
||||
Ptr<DenseRLOFOpticalFlow> algo = DenseRLOFOpticalFlow::create();
|
||||
Ptr<RLOFOpticalFlowParameter> param = Ptr<RLOFOpticalFlowParameter>(new RLOFOpticalFlowParameter);
|
||||
param->setUseMEstimator(true);
|
||||
param->supportRegionType = SR_CROSS;
|
||||
param->solverType = ST_BILINEAR;
|
||||
algo->setRLOFOpticalFlowParameter(param);
|
||||
algo->setForwardBackward(1.0f);
|
||||
algo->setGridStep(cv::Size(4, 4));
|
||||
algo->setInterpolation(INTERP_EPIC);
|
||||
algo->calc(frame1, frame2, flow);
|
||||
|
||||
ASSERT_EQ(GT.rows, flow.rows);
|
||||
ASSERT_EQ(GT.cols, flow.cols);
|
||||
EXPECT_LE(calcRMSE(GT, flow), 0.46f);
|
||||
|
||||
algo->setInterpolation(INTERP_GEO);
|
||||
algo->calc(frame1, frame2, flow);
|
||||
|
||||
ASSERT_EQ(GT.rows, flow.rows);
|
||||
ASSERT_EQ(GT.cols, flow.cols);
|
||||
EXPECT_LE(calcRMSE(GT, flow), 0.55f);
|
||||
|
||||
}
|
||||
|
||||
TEST(DenseOpticalFlow_SparseToDenseFlow, ReferenceAccuracy)
|
||||
{
|
||||
Mat frame1, frame2, GT;
|
||||
ASSERT_TRUE(readRubberWhale(frame1, frame2, GT));
|
||||
float target_RMSE = 0.52f;
|
||||
|
||||
Mat flow;
|
||||
Ptr<DenseOpticalFlow> algo;
|
||||
algo = createOptFlow_SparseToDense();
|
||||
algo->calc(frame1, frame2, flow);
|
||||
ASSERT_EQ(GT.rows, flow.rows);
|
||||
ASSERT_EQ(GT.cols, flow.cols);
|
||||
EXPECT_LE(calcRMSE(GT, flow), target_RMSE);
|
||||
}
|
||||
|
||||
TEST(DenseOpticalFlow_PCAFlow, ReferenceAccuracy)
|
||||
{
|
||||
Mat frame1, frame2, GT;
|
||||
ASSERT_TRUE(readRubberWhale(frame1, frame2, GT));
|
||||
const float target_RMSE = 0.55f;
|
||||
|
||||
Mat flow;
|
||||
Ptr<DenseOpticalFlow> algo = createOptFlow_PCAFlow();
|
||||
algo->calc(frame1, frame2, flow);
|
||||
ASSERT_EQ(GT.rows, flow.rows);
|
||||
ASSERT_EQ(GT.cols, flow.cols);
|
||||
EXPECT_LE(calcRMSE(GT, flow), target_RMSE);
|
||||
}
|
||||
|
||||
TEST(DenseOpticalFlow_GlobalPatchColliderDCT, ReferenceAccuracy)
|
||||
{
|
||||
Mat frame1, frame2, GT;
|
||||
ASSERT_TRUE(readRubberWhale(frame1, frame2, GT));
|
||||
|
||||
const Size sz = frame1.size() / 2;
|
||||
frame1 = frame1(Rect(0, 0, sz.width, sz.height));
|
||||
frame2 = frame2(Rect(0, 0, sz.width, sz.height));
|
||||
GT = GT(Rect(0, 0, sz.width, sz.height));
|
||||
|
||||
vector<Mat> img1, img2, gt;
|
||||
vector< pair<Point2i, Point2i> > corr;
|
||||
img1.push_back(frame1);
|
||||
img2.push_back(frame2);
|
||||
gt.push_back(GT);
|
||||
|
||||
Ptr< GPCForest<5> > forest = GPCForest<5>::create();
|
||||
forest->train(img1, img2, gt, GPCTrainingParams(8, 3, GPC_DESCRIPTOR_DCT, false));
|
||||
forest->findCorrespondences(frame1, frame2, corr);
|
||||
|
||||
ASSERT_LE(7500U, corr.size());
|
||||
ASSERT_LE(calcAvgEPE(corr, GT), 0.5f);
|
||||
}
|
||||
|
||||
TEST(DenseOpticalFlow_GlobalPatchColliderWHT, ReferenceAccuracy)
|
||||
{
|
||||
Mat frame1, frame2, GT;
|
||||
ASSERT_TRUE(readRubberWhale(frame1, frame2, GT));
|
||||
|
||||
const Size sz = frame1.size() / 2;
|
||||
frame1 = frame1(Rect(0, 0, sz.width, sz.height));
|
||||
frame2 = frame2(Rect(0, 0, sz.width, sz.height));
|
||||
GT = GT(Rect(0, 0, sz.width, sz.height));
|
||||
|
||||
vector<Mat> img1, img2, gt;
|
||||
vector< pair<Point2i, Point2i> > corr;
|
||||
img1.push_back(frame1);
|
||||
img2.push_back(frame2);
|
||||
gt.push_back(GT);
|
||||
|
||||
Ptr< GPCForest<5> > forest = GPCForest<5>::create();
|
||||
forest->train(img1, img2, gt, GPCTrainingParams(8, 3, GPC_DESCRIPTOR_WHT, false));
|
||||
forest->findCorrespondences(frame1, frame2, corr);
|
||||
|
||||
ASSERT_LE(7000U, corr.size());
|
||||
ASSERT_LE(calcAvgEPE(corr, GT), 0.5f);
|
||||
}
|
||||
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,6 @@
|
||||
// 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"
|
||||
|
||||
CV_TEST_MAIN("cv")
|
||||
@@ -0,0 +1,501 @@
|
||||
/*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.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, 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 Intel Corporation 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"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
///////////////////// base MHI class ///////////////////////
|
||||
class CV_MHIBaseTest : public cvtest::ArrayTest
|
||||
{
|
||||
public:
|
||||
CV_MHIBaseTest();
|
||||
|
||||
protected:
|
||||
void get_test_array_types_and_sizes( int test_case_idx, vector<vector<Size> >& sizes, vector<vector<int> >& types );
|
||||
void get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high );
|
||||
int prepare_test_case( int test_case_idx );
|
||||
double timestamp, duration, max_log_duration;
|
||||
int mhi_i, mhi_ref_i;
|
||||
double silh_ratio;
|
||||
};
|
||||
|
||||
|
||||
CV_MHIBaseTest::CV_MHIBaseTest()
|
||||
{
|
||||
timestamp = duration = 0;
|
||||
max_log_duration = 9;
|
||||
mhi_i = mhi_ref_i = -1;
|
||||
|
||||
silh_ratio = 0.25;
|
||||
}
|
||||
|
||||
|
||||
void CV_MHIBaseTest::get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high )
|
||||
{
|
||||
cvtest::ArrayTest::get_minmax_bounds( i, j, type, low, high );
|
||||
if( i == INPUT && CV_MAT_DEPTH(type) == CV_8U )
|
||||
{
|
||||
low = Scalar::all(cvRound(-1./silh_ratio)+2.);
|
||||
high = Scalar::all(2);
|
||||
}
|
||||
else if( i == mhi_i || i == mhi_ref_i )
|
||||
{
|
||||
low = Scalar::all(-exp(max_log_duration));
|
||||
high = Scalar::all(0.);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CV_MHIBaseTest::get_test_array_types_and_sizes( int test_case_idx,
|
||||
vector<vector<Size> >& sizes, vector<vector<int> >& types )
|
||||
{
|
||||
RNG& rng = ts->get_rng();
|
||||
cvtest::ArrayTest::get_test_array_types_and_sizes( test_case_idx, sizes, types );
|
||||
|
||||
types[INPUT][0] = CV_8UC1;
|
||||
types[mhi_i][0] = types[mhi_ref_i][0] = CV_32FC1;
|
||||
duration = exp(cvtest::randReal(rng)*max_log_duration);
|
||||
timestamp = duration + cvtest::randReal(rng)*30.-10.;
|
||||
}
|
||||
|
||||
|
||||
int CV_MHIBaseTest::prepare_test_case( int test_case_idx )
|
||||
{
|
||||
int code = cvtest::ArrayTest::prepare_test_case( test_case_idx );
|
||||
if( code > 0 )
|
||||
{
|
||||
Mat& mat = test_mat[mhi_i][0];
|
||||
mat += Scalar::all(duration);
|
||||
cv::max(mat, 0, mat);
|
||||
if( mhi_i != mhi_ref_i )
|
||||
{
|
||||
Mat& mat0 = test_mat[mhi_ref_i][0];
|
||||
cvtest::copy( mat, mat0 );
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
///////////////////// update motion history ////////////////////////////
|
||||
|
||||
static void test_updateMHI( const Mat& silh, Mat& mhi, double timestamp, double duration )
|
||||
{
|
||||
int i, j;
|
||||
float delbound = (float)(timestamp - duration);
|
||||
for( i = 0; i < mhi.rows; i++ )
|
||||
{
|
||||
const uchar* silh_row = silh.ptr(i);
|
||||
float* mhi_row = mhi.ptr<float>(i);
|
||||
|
||||
for( j = 0; j < mhi.cols; j++ )
|
||||
{
|
||||
if( silh_row[j] )
|
||||
mhi_row[j] = (float)timestamp;
|
||||
else if( mhi_row[j] < delbound )
|
||||
mhi_row[j] = 0.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CV_UpdateMHITest : public CV_MHIBaseTest
|
||||
{
|
||||
public:
|
||||
CV_UpdateMHITest();
|
||||
|
||||
protected:
|
||||
double get_success_error_level( int test_case_idx, int i, int j );
|
||||
void run_func();
|
||||
void prepare_to_validation( int );
|
||||
};
|
||||
|
||||
|
||||
CV_UpdateMHITest::CV_UpdateMHITest()
|
||||
{
|
||||
test_array[INPUT].push_back(NULL);
|
||||
test_array[INPUT_OUTPUT].push_back(NULL);
|
||||
test_array[REF_INPUT_OUTPUT].push_back(NULL);
|
||||
mhi_i = INPUT_OUTPUT; mhi_ref_i = REF_INPUT_OUTPUT;
|
||||
}
|
||||
|
||||
|
||||
double CV_UpdateMHITest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void CV_UpdateMHITest::run_func()
|
||||
{
|
||||
cv::motempl::updateMotionHistory( test_mat[INPUT][0], test_mat[INPUT_OUTPUT][0], timestamp, duration);
|
||||
}
|
||||
|
||||
|
||||
void CV_UpdateMHITest::prepare_to_validation( int /*test_case_idx*/ )
|
||||
{
|
||||
//CvMat m0 = test_mat[REF_INPUT_OUTPUT][0];
|
||||
test_updateMHI( test_mat[INPUT][0], test_mat[REF_INPUT_OUTPUT][0], timestamp, duration );
|
||||
}
|
||||
|
||||
|
||||
///////////////////// calc motion gradient ////////////////////////////
|
||||
|
||||
static void test_MHIGradient( const Mat& mhi, Mat& mask, Mat& orientation,
|
||||
double delta1, double delta2, int aperture_size )
|
||||
{
|
||||
Point anchor( aperture_size/2, aperture_size/2 );
|
||||
double limit = 1e-4*aperture_size*aperture_size;
|
||||
|
||||
Mat dx, dy, min_mhi, max_mhi;
|
||||
|
||||
Mat kernel = cvtest::calcSobelKernel2D( 1, 0, aperture_size );
|
||||
cvtest::filter2D( mhi, dx, CV_32F, kernel, anchor, 0, BORDER_REPLICATE );
|
||||
kernel = cvtest::calcSobelKernel2D( 0, 1, aperture_size );
|
||||
cvtest::filter2D( mhi, dy, CV_32F, kernel, anchor, 0, BORDER_REPLICATE );
|
||||
|
||||
kernel = Mat::ones(aperture_size, aperture_size, CV_8U);
|
||||
cvtest::erode(mhi, min_mhi, kernel, anchor, 0, BORDER_REPLICATE);
|
||||
cvtest::dilate(mhi, max_mhi, kernel, anchor, 0, BORDER_REPLICATE);
|
||||
|
||||
if( delta1 > delta2 )
|
||||
{
|
||||
std::swap( delta1, delta2 );
|
||||
}
|
||||
|
||||
for( int i = 0; i < mhi.rows; i++ )
|
||||
{
|
||||
uchar* mask_row = mask.ptr(i);
|
||||
float* orient_row = orientation.ptr<float>(i);
|
||||
const float* dx_row = dx.ptr<float>(i);
|
||||
const float* dy_row = dy.ptr<float>(i);
|
||||
const float* min_row = min_mhi.ptr<float>(i);
|
||||
const float* max_row = max_mhi.ptr<float>(i);
|
||||
|
||||
for( int j = 0; j < mhi.cols; j++ )
|
||||
{
|
||||
double delta = max_row[j] - min_row[j];
|
||||
double _dx = dx_row[j], _dy = dy_row[j];
|
||||
|
||||
if( delta1 <= delta && delta <= delta2 &&
|
||||
(fabs(_dx) > limit || fabs(_dy) > limit) )
|
||||
{
|
||||
mask_row[j] = 1;
|
||||
double angle = atan2( _dy, _dx ) * (180/CV_PI);
|
||||
if( angle < 0 )
|
||||
angle += 360.;
|
||||
orient_row[j] = (float)angle;
|
||||
}
|
||||
else
|
||||
{
|
||||
mask_row[j] = 0;
|
||||
orient_row[j] = 0.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CV_MHIGradientTest : public CV_MHIBaseTest
|
||||
{
|
||||
public:
|
||||
CV_MHIGradientTest();
|
||||
|
||||
protected:
|
||||
void get_test_array_types_and_sizes( int test_case_idx, vector<vector<Size> >& sizes, vector<vector<int> >& types );
|
||||
double get_success_error_level( int test_case_idx, int i, int j );
|
||||
void run_func();
|
||||
void prepare_to_validation( int );
|
||||
|
||||
double delta1, delta2, delta_range_log;
|
||||
int aperture_size;
|
||||
};
|
||||
|
||||
|
||||
CV_MHIGradientTest::CV_MHIGradientTest()
|
||||
{
|
||||
mhi_i = mhi_ref_i = INPUT;
|
||||
test_array[INPUT].push_back(NULL);
|
||||
test_array[OUTPUT].push_back(NULL);
|
||||
test_array[OUTPUT].push_back(NULL);
|
||||
test_array[REF_OUTPUT].push_back(NULL);
|
||||
test_array[REF_OUTPUT].push_back(NULL);
|
||||
delta1 = delta2 = 0;
|
||||
aperture_size = 0;
|
||||
delta_range_log = 4;
|
||||
}
|
||||
|
||||
|
||||
void CV_MHIGradientTest::get_test_array_types_and_sizes( int test_case_idx, vector<vector<Size> >& sizes, vector<vector<int> >& types )
|
||||
{
|
||||
RNG& rng = ts->get_rng();
|
||||
CV_MHIBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types );
|
||||
|
||||
types[OUTPUT][0] = types[REF_OUTPUT][0] = CV_8UC1;
|
||||
types[OUTPUT][1] = types[REF_OUTPUT][1] = CV_32FC1;
|
||||
delta1 = exp(cvtest::randReal(rng)*delta_range_log + 1.);
|
||||
delta2 = exp(cvtest::randReal(rng)*delta_range_log + 1.);
|
||||
aperture_size = (cvtest::randInt(rng)%3)*2+3;
|
||||
//duration = exp(cvtest::randReal(rng)*max_log_duration);
|
||||
//timestamp = duration + cvtest::randReal(rng)*30.-10.;
|
||||
}
|
||||
|
||||
|
||||
double CV_MHIGradientTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int j )
|
||||
{
|
||||
return j == 0 ? 0 : 2e-1;
|
||||
}
|
||||
|
||||
|
||||
void CV_MHIGradientTest::run_func()
|
||||
{
|
||||
cv::motempl::calcMotionGradient(test_mat[INPUT][0], test_mat[OUTPUT][0],
|
||||
test_mat[OUTPUT][1], delta1, delta2, aperture_size );
|
||||
//cvCalcMotionGradient( test_array[INPUT][0], test_array[OUTPUT][0],
|
||||
// test_array[OUTPUT][1], delta1, delta2, aperture_size );
|
||||
}
|
||||
|
||||
|
||||
void CV_MHIGradientTest::prepare_to_validation( int /*test_case_idx*/ )
|
||||
{
|
||||
test_MHIGradient( test_mat[INPUT][0], test_mat[REF_OUTPUT][0],
|
||||
test_mat[REF_OUTPUT][1], delta1, delta2, aperture_size );
|
||||
test_mat[REF_OUTPUT][0] += Scalar::all(1);
|
||||
test_mat[OUTPUT][0] += Scalar::all(1);
|
||||
}
|
||||
|
||||
|
||||
////////////////////// calc global orientation /////////////////////////
|
||||
|
||||
static double test_calcGlobalOrientation( const Mat& orient, const Mat& mask,
|
||||
const Mat& mhi, double timestamp, double duration )
|
||||
{
|
||||
const int HIST_SIZE = 12;
|
||||
int y, x;
|
||||
int histogram[HIST_SIZE];
|
||||
int max_bin = 0;
|
||||
|
||||
double base_orientation = 0, delta_orientation = 0, weight = 0;
|
||||
double low_time, global_orientation;
|
||||
|
||||
memset( histogram, 0, sizeof( histogram ));
|
||||
timestamp = 0;
|
||||
|
||||
for( y = 0; y < orient.rows; y++ )
|
||||
{
|
||||
const float* orient_data = orient.ptr<float>(y);
|
||||
const uchar* mask_data = mask.ptr(y);
|
||||
const float* mhi_data = mhi.ptr<float>(y);
|
||||
for( x = 0; x < orient.cols; x++ )
|
||||
if( mask_data[x] )
|
||||
{
|
||||
int bin = cvFloor( (orient_data[x]*HIST_SIZE)/360 );
|
||||
histogram[bin < 0 ? 0 : bin >= HIST_SIZE ? HIST_SIZE-1 : bin]++;
|
||||
if( mhi_data[x] > timestamp )
|
||||
timestamp = mhi_data[x];
|
||||
}
|
||||
}
|
||||
|
||||
low_time = timestamp - duration;
|
||||
|
||||
for( x = 1; x < HIST_SIZE; x++ )
|
||||
{
|
||||
if( histogram[x] > histogram[max_bin] )
|
||||
max_bin = x;
|
||||
}
|
||||
|
||||
base_orientation = ((double)max_bin*360)/HIST_SIZE;
|
||||
|
||||
for( y = 0; y < orient.rows; y++ )
|
||||
{
|
||||
const float* orient_data = orient.ptr<float>(y);
|
||||
const float* mhi_data = mhi.ptr<float>(y);
|
||||
const uchar* mask_data = mask.ptr(y);
|
||||
|
||||
for( x = 0; x < orient.cols; x++ )
|
||||
{
|
||||
if( mask_data[x] && mhi_data[x] > low_time )
|
||||
{
|
||||
double diff = orient_data[x] - base_orientation;
|
||||
double delta_weight = (((mhi_data[x] - low_time)/duration)*254 + 1)/255;
|
||||
|
||||
if( diff < -180 ) diff += 360;
|
||||
if( diff > 180 ) diff -= 360;
|
||||
|
||||
if( delta_weight > 0 && fabs(diff) < 45 )
|
||||
{
|
||||
delta_orientation += diff*delta_weight;
|
||||
weight += delta_weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( weight == 0 )
|
||||
global_orientation = base_orientation;
|
||||
else
|
||||
{
|
||||
global_orientation = base_orientation + delta_orientation/weight;
|
||||
if( global_orientation < 0 ) global_orientation += 360;
|
||||
if( global_orientation > 360 ) global_orientation -= 360;
|
||||
}
|
||||
|
||||
return global_orientation;
|
||||
}
|
||||
|
||||
|
||||
class CV_MHIGlobalOrientTest : public CV_MHIBaseTest
|
||||
{
|
||||
public:
|
||||
CV_MHIGlobalOrientTest();
|
||||
|
||||
protected:
|
||||
void get_test_array_types_and_sizes( int test_case_idx, vector<vector<Size> >& sizes, vector<vector<int> >& types );
|
||||
void get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high );
|
||||
double get_success_error_level( int test_case_idx, int i, int j );
|
||||
int validate_test_results( int test_case_idx );
|
||||
void run_func();
|
||||
double angle, min_angle, max_angle;
|
||||
};
|
||||
|
||||
|
||||
CV_MHIGlobalOrientTest::CV_MHIGlobalOrientTest()
|
||||
{
|
||||
mhi_i = mhi_ref_i = INPUT;
|
||||
test_array[INPUT].push_back(NULL);
|
||||
test_array[INPUT].push_back(NULL);
|
||||
test_array[INPUT].push_back(NULL);
|
||||
min_angle = max_angle = 0;
|
||||
}
|
||||
|
||||
|
||||
void CV_MHIGlobalOrientTest::get_test_array_types_and_sizes( int test_case_idx, vector<vector<Size> >& sizes, vector<vector<int> >& types )
|
||||
{
|
||||
RNG& rng = ts->get_rng();
|
||||
CV_MHIBaseTest::get_test_array_types_and_sizes( test_case_idx, sizes, types );
|
||||
Size size = sizes[INPUT][0];
|
||||
|
||||
size.width = MAX( size.width, 16 );
|
||||
size.height = MAX( size.height, 16 );
|
||||
sizes[INPUT][0] = sizes[INPUT][1] = sizes[INPUT][2] = size;
|
||||
|
||||
types[INPUT][1] = CV_8UC1; // mask
|
||||
types[INPUT][2] = CV_32FC1; // orientation
|
||||
|
||||
min_angle = cvtest::randReal(rng)*359.9;
|
||||
max_angle = cvtest::randReal(rng)*359.9;
|
||||
if( min_angle >= max_angle )
|
||||
{
|
||||
std::swap( min_angle, max_angle);
|
||||
}
|
||||
max_angle += 0.1;
|
||||
duration = exp(cvtest::randReal(rng)*max_log_duration);
|
||||
timestamp = duration + cvtest::randReal(rng)*30.-10.;
|
||||
}
|
||||
|
||||
|
||||
void CV_MHIGlobalOrientTest::get_minmax_bounds( int i, int j, int type, Scalar& low, Scalar& high )
|
||||
{
|
||||
CV_MHIBaseTest::get_minmax_bounds( i, j, type, low, high );
|
||||
if( i == INPUT && j == 2 )
|
||||
{
|
||||
low = Scalar::all(min_angle);
|
||||
high = Scalar::all(max_angle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double CV_MHIGlobalOrientTest::get_success_error_level( int /*test_case_idx*/, int /*i*/, int /*j*/ )
|
||||
{
|
||||
return 15;
|
||||
}
|
||||
|
||||
|
||||
void CV_MHIGlobalOrientTest::run_func()
|
||||
{
|
||||
//angle = cvCalcGlobalOrientation( test_array[INPUT][2], test_array[INPUT][1],
|
||||
// test_array[INPUT][0], timestamp, duration );
|
||||
angle = cv::motempl::calcGlobalOrientation(test_mat[INPUT][2], test_mat[INPUT][1],
|
||||
test_mat[INPUT][0], timestamp, duration );
|
||||
}
|
||||
|
||||
|
||||
int CV_MHIGlobalOrientTest::validate_test_results( int test_case_idx )
|
||||
{
|
||||
//printf("%d. rows=%d, cols=%d, nzmask=%d\n", test_case_idx, test_mat[INPUT][1].rows, test_mat[INPUT][1].cols,
|
||||
// cvCountNonZero(test_array[INPUT][1]));
|
||||
|
||||
double ref_angle = test_calcGlobalOrientation( test_mat[INPUT][2], test_mat[INPUT][1],
|
||||
test_mat[INPUT][0], timestamp, duration );
|
||||
double err_level = get_success_error_level( test_case_idx, 0, 0 );
|
||||
int code = cvtest::TS::OK;
|
||||
int nz = countNonZero( test_mat[INPUT][1] );
|
||||
|
||||
if( nz > 32 && !(min_angle - err_level <= angle &&
|
||||
max_angle + err_level >= angle) &&
|
||||
!(min_angle - err_level <= angle+360 &&
|
||||
max_angle + err_level >= angle+360) )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "The angle=%g is outside (%g,%g) range\n",
|
||||
angle, min_angle - err_level, max_angle + err_level );
|
||||
code = cvtest::TS::FAIL_BAD_ACCURACY;
|
||||
}
|
||||
else if( fabs(angle - ref_angle) > err_level &&
|
||||
fabs(360 - fabs(angle - ref_angle)) > err_level )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "The angle=%g differs too much from reference value=%g\n",
|
||||
angle, ref_angle );
|
||||
code = cvtest::TS::FAIL_BAD_ACCURACY;
|
||||
}
|
||||
|
||||
if( code < 0 )
|
||||
ts->set_failed_test_info( code );
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
TEST(Video_MHIUpdate, accuracy) { CV_UpdateMHITest test; test.safe_run(); }
|
||||
TEST(Video_MHIGradient, accuracy) { CV_MHIGradientTest test; test.safe_run(); }
|
||||
TEST(Video_MHIGlobalOrient, accuracy) { CV_MHIGlobalOrientTest test; test.safe_run(); }
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,15 @@
|
||||
// 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/optflow.hpp"
|
||||
|
||||
namespace opencv_test {
|
||||
using namespace cv::optflow;
|
||||
using namespace perf;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -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.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, 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 Intel Corporation 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"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
//#define DUMP
|
||||
|
||||
// first four bytes, should be the same in little endian
|
||||
const float FLO_TAG_FLOAT = 202021.25f; // check for this when READING the file
|
||||
|
||||
#ifdef DUMP
|
||||
// binary file format for flow data specified here:
|
||||
// http://vision.middlebury.edu/flow/data/
|
||||
void writeOpticalFlowToFile(const Mat_<Point2f>& flow, const string& fileName)
|
||||
{
|
||||
const char FLO_TAG_STRING[] = "PIEH"; // use this when WRITING the file
|
||||
ofstream file(fileName.c_str(), ios_base::binary);
|
||||
|
||||
file << FLO_TAG_STRING;
|
||||
|
||||
file.write((const char*) &flow.cols, sizeof(int));
|
||||
file.write((const char*) &flow.rows, sizeof(int));
|
||||
|
||||
for (int i = 0; i < flow.rows; ++i)
|
||||
{
|
||||
for (int j = 0; j < flow.cols; ++j)
|
||||
{
|
||||
const Point2f u = flow(i, j);
|
||||
|
||||
file.write((const char*) &u.x, sizeof(float));
|
||||
file.write((const char*) &u.y, sizeof(float));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// binary file format for flow data specified here:
|
||||
// http://vision.middlebury.edu/flow/data/
|
||||
void readOpticalFlowFromFile(Mat_<Point2f>& flow, const string& fileName)
|
||||
{
|
||||
std::ifstream file(fileName.c_str(), std::ios_base::binary);
|
||||
|
||||
float tag;
|
||||
file.read((char*) &tag, sizeof(float));
|
||||
CV_Assert( tag == FLO_TAG_FLOAT );
|
||||
|
||||
Size size;
|
||||
|
||||
file.read((char*) &size.width, sizeof(int));
|
||||
file.read((char*) &size.height, sizeof(int));
|
||||
|
||||
flow.create(size);
|
||||
|
||||
for (int i = 0; i < flow.rows; ++i)
|
||||
{
|
||||
for (int j = 0; j < flow.cols; ++j)
|
||||
{
|
||||
Point2f u;
|
||||
|
||||
file.read((char*) &u.x, sizeof(float));
|
||||
file.read((char*) &u.y, sizeof(float));
|
||||
|
||||
flow(i, j) = u;
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
bool isFlowCorrect(Point2f u)
|
||||
{
|
||||
return !cvIsNaN(u.x) && !cvIsNaN(u.y) && (fabs(u.x) < 1e9) && (fabs(u.y) < 1e9);
|
||||
}
|
||||
|
||||
void check(const Mat_<Point2f>& gold, const Mat_<Point2f>& flow, double threshold = 0.1, double expectedAccuracy = 0.95)
|
||||
{
|
||||
threshold = threshold*threshold;
|
||||
|
||||
size_t gold_counter = 0;
|
||||
size_t valid_counter = 0;
|
||||
|
||||
for (int i = 0; i < gold.rows; ++i)
|
||||
{
|
||||
for (int j = 0; j < gold.cols; ++j)
|
||||
{
|
||||
const Point2f u1 = gold(i, j);
|
||||
const Point2f u2 = flow(i, j);
|
||||
|
||||
if (isFlowCorrect(u1))
|
||||
{
|
||||
gold_counter++;
|
||||
if (isFlowCorrect(u2))
|
||||
{
|
||||
const Point2f diff = u1 - u2;
|
||||
double err = diff.ddot(diff);
|
||||
if (err <= threshold)
|
||||
valid_counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPECT_GE(valid_counter, expectedAccuracy * gold_counter);
|
||||
}
|
||||
|
||||
TEST(Contrib_calcOpticalFlowDual_TVL1, Regression)
|
||||
{
|
||||
const string frame1_path = TS::ptr()->get_data_path() + "optflow/RubberWhale1.png";
|
||||
const string frame2_path = TS::ptr()->get_data_path() + "optflow/RubberWhale2.png";
|
||||
const string gold_flow_path = TS::ptr()->get_data_path() + "optflow/tvl1_flow.flo";
|
||||
|
||||
Mat frame1 = imread(frame1_path, IMREAD_GRAYSCALE);
|
||||
Mat frame2 = imread(frame2_path, IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame1.empty());
|
||||
ASSERT_FALSE(frame2.empty());
|
||||
|
||||
Mat_<Point2f> flow;
|
||||
Ptr<DualTVL1OpticalFlow> tvl1 = cv::optflow::DualTVL1OpticalFlow::create();
|
||||
|
||||
tvl1->calc(frame1, frame2, flow);
|
||||
|
||||
#ifdef DUMP
|
||||
writeOpticalFlowToFile(flow, gold_flow_path);
|
||||
#else
|
||||
Mat_<Point2f> gold;
|
||||
readOpticalFlowFromFile(gold, gold_flow_path);
|
||||
|
||||
ASSERT_EQ(gold.rows, flow.rows);
|
||||
ASSERT_EQ(gold.cols, flow.cols);
|
||||
|
||||
check(gold, flow);
|
||||
#endif
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
Reference in New Issue
Block a user