Merge pull request #1940 from tsenst:add_robust_optical_flow_implementation
Add robust local optical flow (RLOF) implementations (#1940) * Add robust local optical flow (RLOF) implementations which is an improved pyramidal iterative Lucas-Kanade approach. This implementations contains interfaces for sparse optical flow for feature tracking and dense optical flow based on sparse-to-dense interpolation schemes. Add performance and accuracy tests have been implementation as well as documentation with the related publications * - exchange tabs with spaces - fix optflow.bib indentation - remove optflow_o.hpp - change RLOFOpticalFlowParameter interfaces to Ptr<RLOFOpticalFlowParameter> to remove error on building. Fix warnings * introducing precompiler flag RLOD_SSE * remove header that could not be found * remove whitespaces fix perf and accuracy tests * remove x86intrin.h header * fix ios and arm by removing last sse commands * fix warnings for windows compilation * fix documentation RLOFOpticalFlowParameter * integrate cast to remove last warnings * * add create method and function inferfaces to RLOFOpticalFlowParamter to enable python wrapper interfaces * white space fixes / coding style * fix perf test * other changes: precomp.hpp / static * use Matx44f and Vec4f instead of Mat * normSigmas into constants * replace ceil() calls * maximum level is set to 5 so that it is similar value used in the papers * implement paralellized horizontal cross segmentation as used in Geistert2016 * drop dead code * Avoid using "data" and "step" calculations. Use .ptr<mat_type>(row, col) instead. * Avoid using "data" and "step" calculations. Use .ptr<mat_type>(row, col) instead. * bugfix on BEPLK with ica and adapt the accuracy tests * more 'static' functions * bugfix after changing ptr + step to .ptr(y,x) calls by adjusting ROI of prevImage, currImage and derivI as well as changing the offset of the points in the invoker classes. * add some static_cast to avoid warning * remove 50 grid size sample from perf test. This grid size is to sparse for the epic interpolation * remove notSameColor function since it is not used anymorepull/1965/head
parent
b1e9dd5454
commit
1c9e23745c
15 changed files with 7224 additions and 6 deletions
@ -1,2 +1,2 @@ |
||||
set(the_description "Optical Flow Algorithms") |
||||
ocv_define_module(optflow opencv_core opencv_imgproc opencv_video opencv_ximgproc opencv_imgcodecs opencv_flann WRAP python) |
||||
ocv_define_module(optflow opencv_core opencv_imgproc opencv_calib3d opencv_video opencv_ximgproc opencv_imgcodecs opencv_flann WRAP python) |
||||
|
@ -0,0 +1,493 @@ |
||||
// 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, see @cite Revaud2015,Geistert2016. */ |
||||
}; |
||||
|
||||
/** @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 similar to the pyramidal iterative Lucas-Kanade method as |
||||
* proposed by @cite Bouguet00. 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(3.2f) |
||||
,normSigma1(7.f) |
||||
,smallWinSize(9) |
||||
,largeWinSize(21) |
||||
,crossSegmentationThreshold(25) |
||||
,maxLevel(5) |
||||
,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 paramter 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 paramter 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. |
||||
*/ |
||||
|
||||
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 similar to the pyramidal iterative Lucas-Kanade method as |
||||
* proposed by @cite Bouguet00. 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 SIMD parallelization is only available when compiling with SSE4.1. 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. |
||||
* |
||||
* @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 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 use_post_proc see setUsePostProc |
||||
* @param fgsLambda see setFgsLambda |
||||
* @param fgsSigma see setFgsSigma |
||||
*/ |
||||
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, |
||||
bool use_post_proc = true, |
||||
float fgsLambda = 500.0f, |
||||
float fgsSigma = 1.5f); |
||||
}; |
||||
|
||||
/** @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 similar to the pyramidal iterative Lucas-Kanade method as |
||||
* proposed by @cite Bouguet00. 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 similar to the pyramidal iterative Lucas-Kanade method as |
||||
* proposed by @cite Bouguet00. 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 use_post_proc enables ximgproc::fastGlobalSmootherFilter() parameter. |
||||
* @param fgsLambda sets the respective ximgproc::fastGlobalSmootherFilter() parameter. |
||||
* @param fgsSigma sets the respective ximgproc::fastGlobalSmootherFilter() parameter. |
||||
* |
||||
* 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. |
||||
* |
||||
* @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, |
||||
bool use_post_proc = true, float fgsLambda = 500.0f, float fgsSigma = 1.5f); |
||||
|
||||
/** @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 similar to the pyramidal iterative Lucas-Kanade method as |
||||
* proposed by @cite Bouguet00. 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,72 @@ |
||||
#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"), |
||||
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; |
||||
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
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,453 @@ |
||||
// 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++; |
||||
} |
||||
} |
||||
} |
||||
|
||||
int global_time = 0; |
||||
|
||||
bool done = false; |
||||
while (!done) { |
||||
if (my_agents.size() == 0) { |
||||
done = true; |
||||
break; |
||||
} |
||||
global_time++; |
||||
|
||||
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 |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,691 @@ |
||||
// 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/calib3d.hpp" // findHomography |
||||
#include "opencv2/highgui.hpp" |
||||
#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 = s2 - s0; |
||||
v_int16x8 t0 = v_mul_wrap(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 = s1 - s0; |
||||
v_int16x8 t1 = v_mul_wrap(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]) |
||||
{ |
||||
if (m_Overwrite == false) |
||||
return m_maxLevel; |
||||
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); |
||||
currPyramids[1]->buildPyramid(cv::Size(iWinSize, iWinSize), maxLevel, levelScale); |
||||
} |
||||
|
||||
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) |
||||
{ |
||||
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 |
||||
{ |
||||
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; |
||||
currPyramids[1]->m_Overwrite = true; |
||||
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]); |
||||
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,399 @@ |
||||
// 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::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) |
||||
|
||||
{ |
||||
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 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); |
||||
// 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; |
||||
} |
||||
|
||||
if (interp_type == InterpolationType::INTERP_EPIC) |
||||
{ |
||||
Ptr<ximgproc::EdgeAwareInterpolator> gd = ximgproc::createEdgeAwareInterpolator(); |
||||
gd->setK(k); |
||||
gd->setSigma(sigma); |
||||
gd->setLambda(lambda); |
||||
gd->setUsePostProcessing(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); |
||||
} |
||||
} |
||||
|
||||
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; |
||||
}; |
||||
|
||||
Ptr<DenseRLOFOpticalFlow> DenseRLOFOpticalFlow::create( |
||||
Ptr<RLOFOpticalFlowParameter> rlofParam, |
||||
float forwardBackwardThreshold, |
||||
cv::Size gridStep, |
||||
InterpolationType interp_type, |
||||
int epicK, |
||||
float epicSigma, |
||||
float epicLambda, |
||||
bool use_post_proc, |
||||
float fgs_lambda, |
||||
float fgs_sigma) |
||||
{ |
||||
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); |
||||
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)); |
||||
|
||||
Mat prevImage = prevImg.getMat(); |
||||
Mat nextImage = nextImg.getMat(); |
||||
Mat prevPtsMat = prevPts.getMat(); |
||||
|
||||
if (param.empty()) |
||||
{ |
||||
param = Ptr<RLOFOpticalFlowParameter>(new RLOFOpticalFlowParameter); |
||||
} |
||||
|
||||
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; |
||||
prevPtsMat.copyTo(cv::Mat(1, npoints, CV_32FC2, &prevPoints[0])); |
||||
if (param->useInitialFlow ) |
||||
nextPtsMat.copyTo(cv::Mat(1, nextPtsMat.cols, CV_32FC2, &nextPoints[0])); |
||||
|
||||
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) |
||||
{ |
||||
// reuse image pyramids
|
||||
calcLocalOpticalFlow(nextImage, prevImage, currPyramid, prevPyramid, nextPoints, refPoints, *(param.get())); |
||||
} |
||||
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, |
||||
bool use_post_proc, float fgsLambda, float fgsSigma) |
||||
{ |
||||
Ptr<DenseRLOFOpticalFlow> algo = DenseRLOFOpticalFlow::create( |
||||
rlofParam, forewardBackwardThreshold, gridStep, interp_type, |
||||
epicK, epicSigma, epicLambda, use_post_proc, fgsLambda, fgsSigma); |
||||
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
|
Loading…
Reference in new issue