vendor: OpenCV 5.0.0 snapshot at 40738fb16ceddb5fb3fea747585f7ce6abb0605b
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
set(the_description "Video Analysis")
|
||||
ocv_add_dispatched_file(lkpyramid SSE2 SSE4_1 AVX2 AVX512_SKX AVX512_ICL)
|
||||
ocv_define_module(video
|
||||
opencv_imgproc
|
||||
OPTIONAL
|
||||
opencv_geometry
|
||||
opencv_features
|
||||
opencv_dnn
|
||||
WRAP
|
||||
java
|
||||
objc
|
||||
python
|
||||
js
|
||||
)
|
||||
|
||||
if(HAVE_OPENMP AND DEFINED OpenMP_CXX_LIBRARIES AND OpenMP_CXX_LIBRARIES)
|
||||
ocv_target_link_libraries(${the_module} LINK_PRIVATE "${OpenMP_CXX_LIBRARIES}")
|
||||
endif()
|
||||
@@ -0,0 +1,37 @@
|
||||
@article{AAM,
|
||||
title={Adaptive appearance modeling for video tracking: survey and evaluation},
|
||||
author={Salti, Samuele and Cavallaro, Andrea and Di Stefano, Luigi},
|
||||
journal={Image Processing, IEEE Transactions on},
|
||||
volume={21},
|
||||
number={10},
|
||||
pages={4334--4348},
|
||||
year={2012},
|
||||
publisher={IEEE}
|
||||
}
|
||||
|
||||
@article{AMVOT,
|
||||
title={A survey of appearance models in visual object tracking},
|
||||
author={Li, Xi and Hu, Weiming and Shen, Chunhua and Zhang, Zhongfei and Dick, Anthony and Hengel, Anton Van Den},
|
||||
journal={ACM Transactions on Intelligent Systems and Technology (TIST)},
|
||||
volume={4},
|
||||
number={4},
|
||||
pages={58},
|
||||
year={2013},
|
||||
publisher={ACM}
|
||||
}
|
||||
|
||||
@inproceedings{Kroeger2016,
|
||||
author={Till Kroeger and Radu Timofte and Dengxin Dai and Luc Van Gool},
|
||||
title={Fast Optical Flow using Dense Inverse Search},
|
||||
booktitle={Proceedings of the European Conference on Computer Vision ({ECCV})},
|
||||
year={2016}
|
||||
}
|
||||
|
||||
@inproceedings{MIL,
|
||||
title={Visual tracking with online multiple instance learning},
|
||||
author={Babenko, Boris and Yang, Ming-Hsuan and Belongie, Serge},
|
||||
booktitle={Computer Vision and Pattern Recognition, 2009. CVPR 2009. IEEE Conference on},
|
||||
pages={983--990},
|
||||
year={2009},
|
||||
organization={IEEE}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef OPENCV_VIDEO_HPP
|
||||
#define OPENCV_VIDEO_HPP
|
||||
|
||||
/**
|
||||
@defgroup video Video Analysis
|
||||
@{
|
||||
@defgroup video_motion Motion Analysis
|
||||
@defgroup video_track Object Tracking
|
||||
@}
|
||||
*/
|
||||
|
||||
#include "opencv2/video/tracking.hpp"
|
||||
#include "opencv2/video/background_segm.hpp"
|
||||
|
||||
#endif //OPENCV_VIDEO_HPP
|
||||
@@ -0,0 +1,344 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef OPENCV_BACKGROUND_SEGM_HPP
|
||||
#define OPENCV_BACKGROUND_SEGM_HPP
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
//! @addtogroup video_motion
|
||||
//! @{
|
||||
|
||||
/** @brief Base class for background/foreground segmentation. :
|
||||
|
||||
The class is only used to define the common interface for the whole family of background/foreground
|
||||
segmentation algorithms.
|
||||
*/
|
||||
class CV_EXPORTS_W BackgroundSubtractor : public Algorithm
|
||||
{
|
||||
public:
|
||||
/** @brief Computes a foreground mask.
|
||||
|
||||
@param image Next video frame.
|
||||
@param fgmask The output foreground mask as an 8-bit binary image.
|
||||
@param learningRate The value between 0 and 1 that indicates how fast the background model is
|
||||
learnt. Negative parameter value makes the algorithm to use some automatically chosen learning
|
||||
rate. 0 means that the background model is not updated at all, 1 means that the background model
|
||||
is completely reinitialized from the last frame.
|
||||
*/
|
||||
CV_WRAP virtual void apply(InputArray image, OutputArray fgmask, double learningRate=-1) = 0;
|
||||
|
||||
/** @brief Computes a foreground mask with known foreground mask input.
|
||||
|
||||
@param image Next video frame. Floating point frame will be used without scaling and should be in range \f$[0,255]\f$.
|
||||
@param fgmask The output foreground mask as an 8-bit binary image.
|
||||
@param knownForegroundMask The mask for inputting already known foreground, allows model to ignore pixels.
|
||||
@param learningRate The value between 0 and 1 that indicates how fast the background model is
|
||||
learnt. Negative parameter value makes the algorithm to use some automatically chosen learning
|
||||
rate. 0 means that the background model is not updated at all, 1 means that the background model
|
||||
is completely reinitialized from the last frame.
|
||||
|
||||
@note This method has a default virtual implementation that throws a "not impemented" error.
|
||||
Foreground masking may not be supported by all background subtractors.
|
||||
*/
|
||||
CV_WRAP virtual void apply(InputArray image, InputArray knownForegroundMask, OutputArray fgmask, double learningRate=-1) = 0;
|
||||
|
||||
/** @brief Computes a background image.
|
||||
|
||||
@param backgroundImage The output background image.
|
||||
|
||||
@note Sometimes the background image can be very blurry, as it contain the average background
|
||||
statistics.
|
||||
*/
|
||||
CV_WRAP virtual void getBackgroundImage(OutputArray backgroundImage) const = 0;
|
||||
};
|
||||
|
||||
|
||||
/** @brief Gaussian Mixture-based Background/Foreground Segmentation Algorithm.
|
||||
|
||||
The class implements the Gaussian mixture model background subtraction described in @cite Zivkovic2004
|
||||
and @cite Zivkovic2006 .
|
||||
*/
|
||||
class CV_EXPORTS_W BackgroundSubtractorMOG2 : public BackgroundSubtractor
|
||||
{
|
||||
public:
|
||||
/** @brief Returns the number of last frames that affect the background model
|
||||
*/
|
||||
CV_WRAP virtual int getHistory() const = 0;
|
||||
/** @brief Sets the number of last frames that affect the background model
|
||||
*/
|
||||
CV_WRAP virtual void setHistory(int history) = 0;
|
||||
|
||||
/** @brief Returns the number of gaussian components in the background model
|
||||
*/
|
||||
CV_WRAP virtual int getNMixtures() const = 0;
|
||||
/** @brief Sets the number of gaussian components in the background model.
|
||||
|
||||
The model needs to be reinitialized to reserve memory.
|
||||
*/
|
||||
CV_WRAP virtual void setNMixtures(int nmixtures) = 0;//needs reinitialization!
|
||||
|
||||
/** @brief Returns the "background ratio" parameter of the algorithm
|
||||
|
||||
If a foreground pixel keeps semi-constant value for about backgroundRatio\*history frames, it's
|
||||
considered background and added to the model as a center of a new component. It corresponds to TB
|
||||
parameter in the paper.
|
||||
*/
|
||||
CV_WRAP virtual double getBackgroundRatio() const = 0;
|
||||
/** @brief Sets the "background ratio" parameter of the algorithm
|
||||
*/
|
||||
CV_WRAP virtual void setBackgroundRatio(double ratio) = 0;
|
||||
|
||||
/** @brief Returns the variance threshold for the pixel-model match
|
||||
|
||||
The main threshold on the squared Mahalanobis distance to decide if the sample is well described by
|
||||
the background model or not. Related to Cthr from the paper.
|
||||
*/
|
||||
CV_WRAP virtual double getVarThreshold() const = 0;
|
||||
/** @brief Sets the variance threshold for the pixel-model match
|
||||
*/
|
||||
CV_WRAP virtual void setVarThreshold(double varThreshold) = 0;
|
||||
|
||||
/** @brief Returns the variance threshold for the pixel-model match used for new mixture component generation
|
||||
|
||||
Threshold for the squared Mahalanobis distance that helps decide when a sample is close to the
|
||||
existing components (corresponds to Tg in the paper). If a pixel is not close to any component, it
|
||||
is considered foreground or added as a new component. 3 sigma =\> Tg=3\*3=9 is default. A smaller Tg
|
||||
value generates more components. A higher Tg value may result in a small number of components but
|
||||
they can grow too large.
|
||||
*/
|
||||
CV_WRAP virtual double getVarThresholdGen() const = 0;
|
||||
/** @brief Sets the variance threshold for the pixel-model match used for new mixture component generation
|
||||
*/
|
||||
CV_WRAP virtual void setVarThresholdGen(double varThresholdGen) = 0;
|
||||
|
||||
/** @brief Returns the initial variance of each gaussian component
|
||||
*/
|
||||
CV_WRAP virtual double getVarInit() const = 0;
|
||||
/** @brief Sets the initial variance of each gaussian component
|
||||
*/
|
||||
CV_WRAP virtual void setVarInit(double varInit) = 0;
|
||||
|
||||
CV_WRAP virtual double getVarMin() const = 0;
|
||||
CV_WRAP virtual void setVarMin(double varMin) = 0;
|
||||
|
||||
CV_WRAP virtual double getVarMax() const = 0;
|
||||
CV_WRAP virtual void setVarMax(double varMax) = 0;
|
||||
|
||||
/** @brief Returns the complexity reduction threshold
|
||||
|
||||
This parameter defines the number of samples needed to accept to prove the component exists. CT=0.05
|
||||
is a default value for all the samples. By setting CT=0 you get an algorithm very similar to the
|
||||
standard Stauffer&Grimson algorithm.
|
||||
*/
|
||||
CV_WRAP virtual double getComplexityReductionThreshold() const = 0;
|
||||
/** @brief Sets the complexity reduction threshold
|
||||
*/
|
||||
CV_WRAP virtual void setComplexityReductionThreshold(double ct) = 0;
|
||||
|
||||
/** @brief Returns the shadow detection flag
|
||||
|
||||
If true, the algorithm detects shadows and marks them. See createBackgroundSubtractorMOG2 for
|
||||
details.
|
||||
*/
|
||||
CV_WRAP virtual bool getDetectShadows() const = 0;
|
||||
/** @brief Enables or disables shadow detection
|
||||
*/
|
||||
CV_WRAP virtual void setDetectShadows(bool detectShadows) = 0;
|
||||
|
||||
/** @brief Returns the shadow value
|
||||
|
||||
Shadow value is the value used to mark shadows in the foreground mask. Default value is 127. Value 0
|
||||
in the mask always means background, 255 means foreground.
|
||||
*/
|
||||
CV_WRAP virtual int getShadowValue() const = 0;
|
||||
/** @brief Sets the shadow value
|
||||
*/
|
||||
CV_WRAP virtual void setShadowValue(int value) = 0;
|
||||
|
||||
/** @brief Returns the shadow threshold
|
||||
|
||||
A shadow is detected if pixel is a darker version of the background. The shadow threshold (Tau in
|
||||
the paper) is a threshold defining how much darker the shadow can be. Tau= 0.5 means that if a pixel
|
||||
is more than twice darker then it is not shadow. See Prati, Mikic, Trivedi and Cucchiara,
|
||||
*Detecting Moving Shadows...*, IEEE PAMI,2003.
|
||||
*/
|
||||
CV_WRAP virtual double getShadowThreshold() const = 0;
|
||||
/** @brief Sets the shadow threshold
|
||||
*/
|
||||
CV_WRAP virtual void setShadowThreshold(double threshold) = 0;
|
||||
|
||||
/** @brief Computes a foreground mask.
|
||||
|
||||
@param image Next video frame. Floating point frame will be used without scaling and should be in range \f$[0,255]\f$.
|
||||
@param fgmask The output foreground mask as an 8-bit binary image.
|
||||
@param learningRate The value between 0 and 1 that indicates how fast the background model is
|
||||
learnt. Negative parameter value makes the algorithm to use some automatically chosen learning
|
||||
rate. 0 means that the background model is not updated at all, 1 means that the background model
|
||||
is completely reinitialized from the last frame.
|
||||
*/
|
||||
CV_WRAP virtual void apply(InputArray image, OutputArray fgmask, double learningRate=-1) CV_OVERRIDE = 0;
|
||||
|
||||
/** @brief Computes a foreground mask and skips known foreground in evaluation.
|
||||
|
||||
@param image Next video frame. Floating point frame will be used without scaling and should be in range \f$[0,255]\f$.
|
||||
@param fgmask The output foreground mask as an 8-bit binary image.
|
||||
@param knownForegroundMask The mask for inputting already known foreground, allows model to ignore pixels.
|
||||
@param learningRate The value between 0 and 1 that indicates how fast the background model is
|
||||
learnt. Negative parameter value makes the algorithm to use some automatically chosen learning
|
||||
rate. 0 means that the background model is not updated at all, 1 means that the background model
|
||||
is completely reinitialized from the last frame.
|
||||
*/
|
||||
CV_WRAP virtual void apply(InputArray image, InputArray knownForegroundMask, OutputArray fgmask, double learningRate=-1) CV_OVERRIDE = 0;
|
||||
};
|
||||
|
||||
/** @brief Creates MOG2 Background Subtractor
|
||||
|
||||
@param history Length of the history.
|
||||
@param varThreshold Threshold on the squared Mahalanobis distance between the pixel and the model
|
||||
to decide whether a pixel is well described by the background model. This parameter does not
|
||||
affect the background update.
|
||||
@param detectShadows If true, the algorithm will detect shadows and mark them. It decreases the
|
||||
speed a bit, so if you do not need this feature, set the parameter to false.
|
||||
*/
|
||||
CV_EXPORTS_W Ptr<BackgroundSubtractorMOG2>
|
||||
createBackgroundSubtractorMOG2(int history=500, double varThreshold=16,
|
||||
bool detectShadows=true);
|
||||
|
||||
/** @brief K-nearest neighbours - based Background/Foreground Segmentation Algorithm.
|
||||
|
||||
The class implements the K-nearest neighbours background subtraction described in @cite Zivkovic2006 .
|
||||
Very efficient if number of foreground pixels is low.
|
||||
*/
|
||||
class CV_EXPORTS_W BackgroundSubtractorKNN : public BackgroundSubtractor
|
||||
{
|
||||
public:
|
||||
/** @brief Returns the number of last frames that affect the background model
|
||||
*/
|
||||
CV_WRAP virtual int getHistory() const = 0;
|
||||
/** @brief Sets the number of last frames that affect the background model
|
||||
*/
|
||||
CV_WRAP virtual void setHistory(int history) = 0;
|
||||
|
||||
/** @brief Returns the number of data samples in the background model
|
||||
*/
|
||||
CV_WRAP virtual int getNSamples() const = 0;
|
||||
/** @brief Sets the number of data samples in the background model.
|
||||
|
||||
The model needs to be reinitialized to reserve memory.
|
||||
*/
|
||||
CV_WRAP virtual void setNSamples(int _nN) = 0;//needs reinitialization!
|
||||
|
||||
/** @brief Returns the threshold on the squared distance between the pixel and the sample
|
||||
|
||||
The threshold on the squared distance between the pixel and the sample to decide whether a pixel is
|
||||
close to a data sample.
|
||||
*/
|
||||
CV_WRAP virtual double getDist2Threshold() const = 0;
|
||||
/** @brief Sets the threshold on the squared distance
|
||||
*/
|
||||
CV_WRAP virtual void setDist2Threshold(double _dist2Threshold) = 0;
|
||||
|
||||
/** @brief Returns the number of neighbours, the k in the kNN.
|
||||
|
||||
K is the number of samples that need to be within dist2Threshold in order to decide that that
|
||||
pixel is matching the kNN background model.
|
||||
*/
|
||||
CV_WRAP virtual int getkNNSamples() const = 0;
|
||||
/** @brief Sets the k in the kNN. How many nearest neighbours need to match.
|
||||
*/
|
||||
CV_WRAP virtual void setkNNSamples(int _nkNN) = 0;
|
||||
|
||||
/** @brief Returns the shadow detection flag
|
||||
|
||||
If true, the algorithm detects shadows and marks them. See createBackgroundSubtractorKNN for
|
||||
details.
|
||||
*/
|
||||
CV_WRAP virtual bool getDetectShadows() const = 0;
|
||||
/** @brief Enables or disables shadow detection
|
||||
*/
|
||||
CV_WRAP virtual void setDetectShadows(bool detectShadows) = 0;
|
||||
|
||||
/** @brief Returns the shadow value
|
||||
|
||||
Shadow value is the value used to mark shadows in the foreground mask. Default value is 127. Value 0
|
||||
in the mask always means background, 255 means foreground.
|
||||
*/
|
||||
CV_WRAP virtual int getShadowValue() const = 0;
|
||||
/** @brief Sets the shadow value
|
||||
*/
|
||||
CV_WRAP virtual void setShadowValue(int value) = 0;
|
||||
|
||||
/** @brief Returns the shadow threshold
|
||||
|
||||
A shadow is detected if pixel is a darker version of the background. The shadow threshold (Tau in
|
||||
the paper) is a threshold defining how much darker the shadow can be. Tau= 0.5 means that if a pixel
|
||||
is more than twice darker then it is not shadow. See Prati, Mikic, Trivedi and Cucchiara,
|
||||
*Detecting Moving Shadows...*, IEEE PAMI,2003.
|
||||
*/
|
||||
CV_WRAP virtual double getShadowThreshold() const = 0;
|
||||
/** @brief Sets the shadow threshold
|
||||
*/
|
||||
CV_WRAP virtual void setShadowThreshold(double threshold) = 0;
|
||||
};
|
||||
|
||||
/** @brief Creates KNN Background Subtractor
|
||||
|
||||
@param history Length of the history.
|
||||
@param dist2Threshold Threshold on the squared distance between the pixel and the sample to decide
|
||||
whether a pixel is close to that sample. This parameter does not affect the background update.
|
||||
@param detectShadows If true, the algorithm will detect shadows and mark them. It decreases the
|
||||
speed a bit, so if you do not need this feature, set the parameter to false.
|
||||
*/
|
||||
CV_EXPORTS_W Ptr<BackgroundSubtractorKNN>
|
||||
createBackgroundSubtractorKNN(int history=500, double dist2Threshold=400.0,
|
||||
bool detectShadows=true);
|
||||
|
||||
//! @} video_motion
|
||||
|
||||
} // cv
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,406 @@
|
||||
// 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_VIDEO_DETAIL_TRACKING_HPP
|
||||
#define OPENCV_VIDEO_DETAIL_TRACKING_HPP
|
||||
|
||||
/*
|
||||
* Partially based on:
|
||||
* ====================================================================================================================
|
||||
* - [AAM] S. Salti, A. Cavallaro, L. Di Stefano, Adaptive Appearance Modeling for Video Tracking: Survey and Evaluation
|
||||
* - [AMVOT] X. Li, W. Hu, C. Shen, Z. Zhang, A. Dick, A. van den Hengel, A Survey of Appearance Models in Visual Object Tracking
|
||||
*
|
||||
* This Tracking API has been designed with PlantUML. If you modify this API please change UML files under modules/tracking/doc/uml
|
||||
*
|
||||
*/
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
/** @addtogroup tracking_detail
|
||||
@{
|
||||
*/
|
||||
|
||||
/************************************ TrackerFeature Base Classes ************************************/
|
||||
|
||||
/** @brief Abstract base class for TrackerFeature that represents the feature.
|
||||
*/
|
||||
class CV_EXPORTS TrackerFeature
|
||||
{
|
||||
public:
|
||||
virtual ~TrackerFeature();
|
||||
|
||||
/** @brief Compute the features in the images collection
|
||||
@param images The images
|
||||
@param response The output response
|
||||
*/
|
||||
void compute(const std::vector<Mat>& images, Mat& response);
|
||||
|
||||
protected:
|
||||
virtual bool computeImpl(const std::vector<Mat>& images, Mat& response) = 0;
|
||||
};
|
||||
|
||||
/** @brief Class that manages the extraction and selection of features
|
||||
|
||||
@cite AAM Feature Extraction and Feature Set Refinement (Feature Processing and Feature Selection).
|
||||
See table I and section III C @cite AMVOT Appearance modelling -\> Visual representation (Table II,
|
||||
section 3.1 - 3.2)
|
||||
|
||||
TrackerFeatureSet is an aggregation of TrackerFeature
|
||||
|
||||
@sa
|
||||
TrackerFeature
|
||||
|
||||
*/
|
||||
class CV_EXPORTS TrackerFeatureSet
|
||||
{
|
||||
public:
|
||||
TrackerFeatureSet();
|
||||
|
||||
~TrackerFeatureSet();
|
||||
|
||||
/** @brief Extract features from the images collection
|
||||
@param images The input images
|
||||
*/
|
||||
void extraction(const std::vector<Mat>& images);
|
||||
|
||||
/** @brief Add TrackerFeature in the collection. Return true if TrackerFeature is added, false otherwise
|
||||
@param feature The TrackerFeature class
|
||||
*/
|
||||
bool addTrackerFeature(const Ptr<TrackerFeature>& feature);
|
||||
|
||||
/** @brief Get the TrackerFeature collection (TrackerFeature name, TrackerFeature pointer)
|
||||
*/
|
||||
const std::vector<Ptr<TrackerFeature>>& getTrackerFeatures() const;
|
||||
|
||||
/** @brief Get the responses
|
||||
@note Be sure to call extraction before getResponses Example TrackerFeatureSet::getResponses
|
||||
*/
|
||||
const std::vector<Mat>& getResponses() const;
|
||||
|
||||
private:
|
||||
void clearResponses();
|
||||
bool blockAddTrackerFeature;
|
||||
|
||||
std::vector<Ptr<TrackerFeature>> features; // list of features
|
||||
std::vector<Mat> responses; // list of response after compute
|
||||
};
|
||||
|
||||
/************************************ TrackerSampler Base Classes ************************************/
|
||||
|
||||
/** @brief Abstract base class for TrackerSamplerAlgorithm that represents the algorithm for the specific
|
||||
sampler.
|
||||
*/
|
||||
class CV_EXPORTS TrackerSamplerAlgorithm
|
||||
{
|
||||
public:
|
||||
virtual ~TrackerSamplerAlgorithm();
|
||||
|
||||
/** @brief Computes the regions starting from a position in an image.
|
||||
|
||||
Return true if samples are computed, false otherwise
|
||||
|
||||
@param image The current frame
|
||||
@param boundingBox The bounding box from which regions can be calculated
|
||||
|
||||
@param sample The computed samples @cite AAM Fig. 1 variable Sk
|
||||
*/
|
||||
virtual bool sampling(const Mat& image, const Rect& boundingBox, std::vector<Mat>& sample) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Class that manages the sampler in order to select regions for the update the model of the tracker
|
||||
* [AAM] Sampling e Labeling. See table I and section III B
|
||||
*/
|
||||
|
||||
/** @brief Class that manages the sampler in order to select regions for the update the model of the tracker
|
||||
|
||||
@cite AAM Sampling e Labeling. See table I and section III B
|
||||
|
||||
TrackerSampler is an aggregation of TrackerSamplerAlgorithm
|
||||
@sa
|
||||
TrackerSamplerAlgorithm
|
||||
*/
|
||||
class CV_EXPORTS TrackerSampler
|
||||
{
|
||||
public:
|
||||
TrackerSampler();
|
||||
|
||||
~TrackerSampler();
|
||||
|
||||
/** @brief Computes the regions starting from a position in an image
|
||||
@param image The current frame
|
||||
@param boundingBox The bounding box from which regions can be calculated
|
||||
*/
|
||||
void sampling(const Mat& image, Rect boundingBox);
|
||||
|
||||
/** @brief Return the collection of the TrackerSamplerAlgorithm
|
||||
*/
|
||||
const std::vector<Ptr<TrackerSamplerAlgorithm>>& getSamplers() const;
|
||||
|
||||
/** @brief Return the samples from all TrackerSamplerAlgorithm, @cite AAM Fig. 1 variable Sk
|
||||
*/
|
||||
const std::vector<Mat>& getSamples() const;
|
||||
|
||||
/** @brief Add TrackerSamplerAlgorithm in the collection. Return true if sampler is added, false otherwise
|
||||
@param sampler The TrackerSamplerAlgorithm
|
||||
*/
|
||||
bool addTrackerSamplerAlgorithm(const Ptr<TrackerSamplerAlgorithm>& sampler);
|
||||
|
||||
private:
|
||||
std::vector<Ptr<TrackerSamplerAlgorithm>> samplers;
|
||||
std::vector<Mat> samples;
|
||||
bool blockAddTrackerSampler;
|
||||
|
||||
void clearSamples();
|
||||
};
|
||||
|
||||
/************************************ TrackerModel Base Classes ************************************/
|
||||
|
||||
/** @brief Abstract base class for TrackerTargetState that represents a possible state of the target.
|
||||
|
||||
See @cite AAM \f$\hat{x}^{i}_{k}\f$ all the states candidates.
|
||||
|
||||
Inherits this class with your Target state, In own implementation you can add scale variation,
|
||||
width, height, orientation, etc.
|
||||
*/
|
||||
class CV_EXPORTS TrackerTargetState
|
||||
{
|
||||
public:
|
||||
virtual ~TrackerTargetState() {}
|
||||
/** @brief Get the position
|
||||
* @return The position
|
||||
*/
|
||||
Point2f getTargetPosition() const;
|
||||
|
||||
/** @brief Set the position
|
||||
* @param position The position
|
||||
*/
|
||||
void setTargetPosition(const Point2f& position);
|
||||
/** @brief Get the width of the target
|
||||
* @return The width of the target
|
||||
*/
|
||||
int getTargetWidth() const;
|
||||
|
||||
/** @brief Set the width of the target
|
||||
* @param width The width of the target
|
||||
*/
|
||||
void setTargetWidth(int width);
|
||||
/** @brief Get the height of the target
|
||||
* @return The height of the target
|
||||
*/
|
||||
int getTargetHeight() const;
|
||||
|
||||
/** @brief Set the height of the target
|
||||
* @param height The height of the target
|
||||
*/
|
||||
void setTargetHeight(int height);
|
||||
|
||||
protected:
|
||||
Point2f targetPosition;
|
||||
int targetWidth;
|
||||
int targetHeight;
|
||||
};
|
||||
|
||||
/** @brief Represents the model of the target at frame \f$k\f$ (all states and scores)
|
||||
|
||||
See @cite AAM The set of the pair \f$\langle \hat{x}^{i}_{k}, C^{i}_{k} \rangle\f$
|
||||
@sa TrackerTargetState
|
||||
*/
|
||||
typedef std::vector<std::pair<Ptr<TrackerTargetState>, float>> ConfidenceMap;
|
||||
|
||||
/** @brief Represents the estimate states for all frames
|
||||
|
||||
@cite AAM \f$x_{k}\f$ is the trajectory of the target up to time \f$k\f$
|
||||
|
||||
@sa TrackerTargetState
|
||||
*/
|
||||
typedef std::vector<Ptr<TrackerTargetState>> Trajectory;
|
||||
|
||||
/** @brief Abstract base class for TrackerStateEstimator that estimates the most likely target state.
|
||||
|
||||
See @cite AAM State estimator
|
||||
|
||||
See @cite AMVOT Statistical modeling (Fig. 3), Table III (generative) - IV (discriminative) - V (hybrid)
|
||||
*/
|
||||
class CV_EXPORTS TrackerStateEstimator
|
||||
{
|
||||
public:
|
||||
virtual ~TrackerStateEstimator();
|
||||
|
||||
/** @brief Estimate the most likely target state, return the estimated state
|
||||
@param confidenceMaps The overall appearance model as a list of :cConfidenceMap
|
||||
*/
|
||||
Ptr<TrackerTargetState> estimate(const std::vector<ConfidenceMap>& confidenceMaps);
|
||||
|
||||
/** @brief Update the ConfidenceMap with the scores
|
||||
@param confidenceMaps The overall appearance model as a list of :cConfidenceMap
|
||||
*/
|
||||
void update(std::vector<ConfidenceMap>& confidenceMaps);
|
||||
|
||||
/** @brief Create TrackerStateEstimator by tracker state estimator type
|
||||
@param trackeStateEstimatorType The TrackerStateEstimator name
|
||||
|
||||
The modes available now:
|
||||
|
||||
- "BOOSTING" -- Boosting-based discriminative appearance models. See @cite AMVOT section 4.4
|
||||
|
||||
The modes available soon:
|
||||
|
||||
- "SVM" -- SVM-based discriminative appearance models. See @cite AMVOT section 4.5
|
||||
*/
|
||||
static Ptr<TrackerStateEstimator> create(const String& trackeStateEstimatorType);
|
||||
|
||||
/** @brief Get the name of the specific TrackerStateEstimator
|
||||
*/
|
||||
String getClassName() const;
|
||||
|
||||
protected:
|
||||
virtual Ptr<TrackerTargetState> estimateImpl(const std::vector<ConfidenceMap>& confidenceMaps) = 0;
|
||||
virtual void updateImpl(std::vector<ConfidenceMap>& confidenceMaps) = 0;
|
||||
String className;
|
||||
};
|
||||
|
||||
/** @brief Abstract class that represents the model of the target.
|
||||
|
||||
It must be instantiated by specialized tracker
|
||||
|
||||
See @cite AAM Ak
|
||||
|
||||
Inherits this with your TrackerModel
|
||||
*/
|
||||
class CV_EXPORTS TrackerModel
|
||||
{
|
||||
public:
|
||||
TrackerModel();
|
||||
|
||||
virtual ~TrackerModel();
|
||||
|
||||
/** @brief Set TrackerEstimator, return true if the tracker state estimator is added, false otherwise
|
||||
@param trackerStateEstimator The TrackerStateEstimator
|
||||
@note You can add only one TrackerStateEstimator
|
||||
*/
|
||||
bool setTrackerStateEstimator(Ptr<TrackerStateEstimator> trackerStateEstimator);
|
||||
|
||||
/** @brief Estimate the most likely target location
|
||||
|
||||
@cite AAM ME, Model Estimation table I
|
||||
@param responses Features extracted from TrackerFeatureSet
|
||||
*/
|
||||
void modelEstimation(const std::vector<Mat>& responses);
|
||||
|
||||
/** @brief Update the model
|
||||
|
||||
@cite AAM MU, Model Update table I
|
||||
*/
|
||||
void modelUpdate();
|
||||
|
||||
/** @brief Run the TrackerStateEstimator, return true if is possible to estimate a new state, false otherwise
|
||||
*/
|
||||
bool runStateEstimator();
|
||||
|
||||
/** @brief Set the current TrackerTargetState in the Trajectory
|
||||
@param lastTargetState The current TrackerTargetState
|
||||
*/
|
||||
void setLastTargetState(const Ptr<TrackerTargetState>& lastTargetState);
|
||||
|
||||
/** @brief Get the last TrackerTargetState from Trajectory
|
||||
*/
|
||||
Ptr<TrackerTargetState> getLastTargetState() const;
|
||||
|
||||
/** @brief Get the list of the ConfidenceMap
|
||||
*/
|
||||
const std::vector<ConfidenceMap>& getConfidenceMaps() const;
|
||||
|
||||
/** @brief Get the last ConfidenceMap for the current frame
|
||||
*/
|
||||
const ConfidenceMap& getLastConfidenceMap() const;
|
||||
|
||||
/** @brief Get the TrackerStateEstimator
|
||||
*/
|
||||
Ptr<TrackerStateEstimator> getTrackerStateEstimator() const;
|
||||
|
||||
private:
|
||||
void clearCurrentConfidenceMap();
|
||||
|
||||
protected:
|
||||
std::vector<ConfidenceMap> confidenceMaps;
|
||||
Ptr<TrackerStateEstimator> stateEstimator;
|
||||
ConfidenceMap currentConfidenceMap;
|
||||
Trajectory trajectory;
|
||||
int maxCMLength;
|
||||
|
||||
virtual void modelEstimationImpl(const std::vector<Mat>& responses) = 0;
|
||||
virtual void modelUpdateImpl() = 0;
|
||||
};
|
||||
|
||||
/************************************ Specific TrackerStateEstimator Classes ************************************/
|
||||
|
||||
// None
|
||||
|
||||
/************************************ Specific TrackerSamplerAlgorithm Classes ************************************/
|
||||
|
||||
/** @brief TrackerSampler based on CSC (current state centered), used by MIL algorithm TrackerMIL
|
||||
*/
|
||||
class CV_EXPORTS TrackerSamplerCSC : public TrackerSamplerAlgorithm
|
||||
{
|
||||
public:
|
||||
~TrackerSamplerCSC();
|
||||
|
||||
enum MODE
|
||||
{
|
||||
MODE_INIT_POS = 1, //!< mode for init positive samples
|
||||
MODE_INIT_NEG = 2, //!< mode for init negative samples
|
||||
MODE_TRACK_POS = 3, //!< mode for update positive samples
|
||||
MODE_TRACK_NEG = 4, //!< mode for update negative samples
|
||||
MODE_DETECT = 5 //!< mode for detect samples
|
||||
};
|
||||
|
||||
struct CV_EXPORTS Params
|
||||
{
|
||||
Params();
|
||||
float initInRad; //!< radius for gathering positive instances during init
|
||||
float trackInPosRad; //!< radius for gathering positive instances during tracking
|
||||
float searchWinSize; //!< size of search window
|
||||
int initMaxNegNum; //!< # negative samples to use during init
|
||||
int trackMaxPosNum; //!< # positive samples to use during training
|
||||
int trackMaxNegNum; //!< # negative samples to use during training
|
||||
};
|
||||
|
||||
/** @brief Constructor
|
||||
@param parameters TrackerSamplerCSC parameters TrackerSamplerCSC::Params
|
||||
*/
|
||||
TrackerSamplerCSC(const TrackerSamplerCSC::Params& parameters = TrackerSamplerCSC::Params());
|
||||
|
||||
/** @brief Set the sampling mode of TrackerSamplerCSC
|
||||
@param samplingMode The sampling mode
|
||||
|
||||
The modes are:
|
||||
|
||||
- "MODE_INIT_POS = 1" -- for the positive sampling in initialization step
|
||||
- "MODE_INIT_NEG = 2" -- for the negative sampling in initialization step
|
||||
- "MODE_TRACK_POS = 3" -- for the positive sampling in update step
|
||||
- "MODE_TRACK_NEG = 4" -- for the negative sampling in update step
|
||||
- "MODE_DETECT = 5" -- for the sampling in detection step
|
||||
*/
|
||||
void setMode(int samplingMode);
|
||||
|
||||
bool sampling(const Mat& image, const Rect& boundingBox, std::vector<Mat>& sample) CV_OVERRIDE;
|
||||
|
||||
private:
|
||||
Params params;
|
||||
int mode;
|
||||
RNG rng;
|
||||
|
||||
std::vector<Mat> sampleImage(const Mat& img, int x, int y, int w, int h, float inrad, float outrad = 0, int maxnum = 1000000);
|
||||
};
|
||||
|
||||
//! @}
|
||||
|
||||
}}} // namespace cv::detail::tracking
|
||||
|
||||
#endif // OPENCV_VIDEO_DETAIL_TRACKING_HPP
|
||||
@@ -0,0 +1,16 @@
|
||||
// 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_VIDEO_LEGACY_CONSTANTS_H
|
||||
#define OPENCV_VIDEO_LEGACY_CONSTANTS_H
|
||||
|
||||
enum
|
||||
{
|
||||
CV_LKFLOW_PYR_A_READY = 1,
|
||||
CV_LKFLOW_PYR_B_READY = 2,
|
||||
CV_LKFLOW_INITIAL_GUESSES = 4,
|
||||
CV_LKFLOW_GET_MIN_EIGENVALS = 8
|
||||
};
|
||||
|
||||
#endif // OPENCV_VIDEO_LEGACY_CONSTANTS_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,48 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifdef __OPENCV_BUILD
|
||||
#error this is a compatibility header which should not be used inside the OpenCV library
|
||||
#endif
|
||||
|
||||
#include "opencv2/video.hpp"
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"missing_consts": {
|
||||
"Video" : {
|
||||
"private" : [
|
||||
["CV_LKFLOW_INITIAL_GUESSES", 4 ],
|
||||
["CV_LKFLOW_GET_MIN_EIGENVALS", 8 ]
|
||||
]
|
||||
}
|
||||
},
|
||||
"func_arg_fix" : {
|
||||
"calcOpticalFlowPyrLK" : { "prevPts" : {"ctype" : "vector_Point2f"},
|
||||
"nextPts" : {"ctype" : "vector_Point2f"},
|
||||
"status" : {"ctype" : "vector_uchar"},
|
||||
"err" : {"ctype" : "vector_float"} }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.opencv.test.video;
|
||||
|
||||
import org.opencv.test.OpenCVTestCase;
|
||||
|
||||
public class BackgroundSubtractorMOGTest extends OpenCVTestCase {
|
||||
|
||||
public void testApplyMatMat() {
|
||||
fail("Not yet implemented");
|
||||
/*
|
||||
BackgroundSubtractorMOG backGroundSubtract = new BackgroundSubtractorMOG();
|
||||
|
||||
Point bottomRight = new Point(rgbLena.cols() / 2, rgbLena.rows() / 2);
|
||||
Point topLeft = new Point(0, 0);
|
||||
Scalar color = new Scalar(128);
|
||||
Mat mask = new Mat(rgbLena.size(), CvType.CV_16UC3, new Scalar(1));
|
||||
|
||||
Imgproc.rectangle(rgbLena, bottomRight, topLeft, color, Imgproc.FILLED);
|
||||
|
||||
backGroundSubtract.apply(rgbLena, mask);
|
||||
|
||||
Mat truth = new Mat(rgbLena.size(), rgbLena.type(), new Scalar(0));
|
||||
Imgproc.rectangle(truth, bottomRight, topLeft, color, Imgproc.FILLED);
|
||||
assertMatEqual(truth, rgbLena);
|
||||
*/
|
||||
}
|
||||
|
||||
public void testApplyMatMatDouble() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testBackgroundSubtractorMOG() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testBackgroundSubtractorMOGIntIntDouble() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testBackgroundSubtractorMOGIntIntDoubleDouble() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package org.opencv.test.video;
|
||||
|
||||
import org.opencv.test.OpenCVTestCase;
|
||||
import org.opencv.video.KalmanFilter;
|
||||
|
||||
public class KalmanFilterTest extends OpenCVTestCase {
|
||||
|
||||
public void testCorrect() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testKalmanFilter() {
|
||||
KalmanFilter kf = new KalmanFilter();
|
||||
|
||||
assertNotNull(kf);
|
||||
}
|
||||
|
||||
public void testKalmanFilterIntInt() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testKalmanFilterIntIntInt() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testKalmanFilterIntIntIntInt() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testPredict() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testPredictMat() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package org.opencv.test.video;
|
||||
|
||||
import java.io.File;
|
||||
import org.opencv.core.Core;
|
||||
import org.opencv.core.CvType;
|
||||
import org.opencv.core.CvException;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.Rect;
|
||||
import org.opencv.dnn.Dnn;
|
||||
import org.opencv.dnn.Net;
|
||||
import org.opencv.test.OpenCVTestCase;
|
||||
|
||||
import org.opencv.video.Tracker;
|
||||
import org.opencv.video.TrackerNano;
|
||||
import org.opencv.video.TrackerNano_Params;
|
||||
import org.opencv.video.TrackerVit;
|
||||
import org.opencv.video.TrackerVit_Params;
|
||||
import org.opencv.video.TrackerMIL;
|
||||
|
||||
public class TrackerCreateTest extends OpenCVTestCase {
|
||||
|
||||
private final static String ENV_OPENCV_DNN_TEST_DATA_PATH = "OPENCV_DNN_TEST_DATA_PATH";
|
||||
private final static String ENV_OPENCV_TEST_DATA_PATH = "OPENCV_TEST_DATA_PATH";
|
||||
private String testDataPath;
|
||||
private String modelsDataPath;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
// relys on https://developer.android.com/reference/java/lang/System
|
||||
isTestCaseEnabled = System.getProperties().getProperty("java.vm.name") != "Dalvik";
|
||||
if (!isTestCaseEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
testDataPath = System.getenv(ENV_OPENCV_TEST_DATA_PATH);
|
||||
if (testDataPath == null) {
|
||||
throw new Exception(ENV_OPENCV_TEST_DATA_PATH + " has to be defined!");
|
||||
}
|
||||
|
||||
modelsDataPath = System.getenv(ENV_OPENCV_DNN_TEST_DATA_PATH);
|
||||
if (modelsDataPath == null) {
|
||||
modelsDataPath = testDataPath;
|
||||
}
|
||||
|
||||
if (isTestCaseEnabled) {
|
||||
testDataPath = System.getenv(ENV_OPENCV_DNN_TEST_DATA_PATH);
|
||||
if (testDataPath == null)
|
||||
testDataPath = System.getenv(ENV_OPENCV_TEST_DATA_PATH);
|
||||
if (testDataPath == null)
|
||||
throw new Exception(ENV_OPENCV_TEST_DATA_PATH + " has to be defined!");
|
||||
}
|
||||
}
|
||||
|
||||
public void testCreateTrackerNano() {
|
||||
Net backbone;
|
||||
Net neckhead;
|
||||
try {
|
||||
String backboneFile = new File(modelsDataPath, "dnn/onnx/models/nanotrack_backbone_sim_v2.onnx").toString();
|
||||
String neckheadFile = new File(modelsDataPath, "dnn/onnx/models/nanotrack_head_sim_v2.onnx").toString();
|
||||
backbone = Dnn.readNet(backboneFile);
|
||||
neckhead = Dnn.readNet(neckheadFile);
|
||||
} catch (CvException e) {
|
||||
return;
|
||||
}
|
||||
Tracker tracker = TrackerNano.create(backbone, neckhead);
|
||||
assert(tracker != null);
|
||||
}
|
||||
|
||||
public void testCreateTrackerVit() {
|
||||
Net net;
|
||||
try {
|
||||
String backboneFile = new File(modelsDataPath, "dnn/onnx/models/vitTracker.onnx").toString();
|
||||
net = Dnn.readNet(backboneFile);
|
||||
} catch (CvException e) {
|
||||
return;
|
||||
}
|
||||
Tracker tracker = TrackerVit.create(net);
|
||||
assert(tracker != null);
|
||||
}
|
||||
|
||||
public void testCreateTrackerMIL() {
|
||||
Tracker tracker = TrackerMIL.create();
|
||||
assert(tracker != null);
|
||||
Mat mat = new Mat(100, 100, CvType.CV_8UC1);
|
||||
Rect rect = new Rect(10, 10, 30, 30);
|
||||
tracker.init(mat, rect); // should not crash (https://github.com/opencv/opencv/issues/19915)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package org.opencv.test.video;
|
||||
|
||||
import org.opencv.core.Core;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.core.MatOfByte;
|
||||
import org.opencv.core.MatOfFloat;
|
||||
import org.opencv.core.MatOfPoint2f;
|
||||
import org.opencv.core.Point;
|
||||
import org.opencv.core.Size;
|
||||
import org.opencv.test.OpenCVTestCase;
|
||||
import org.opencv.video.Video;
|
||||
|
||||
public class VideoTest extends OpenCVTestCase {
|
||||
|
||||
private MatOfFloat err = null;
|
||||
private int h;
|
||||
private MatOfPoint2f nextPts = null;
|
||||
private MatOfPoint2f prevPts = null;
|
||||
|
||||
private int shift1;
|
||||
private int shift2;
|
||||
|
||||
private MatOfByte status = null;
|
||||
private Mat subLena1 = null;
|
||||
private Mat subLena2 = null;
|
||||
private int w;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
shift1 = 10;
|
||||
shift2 = 17;
|
||||
w = (int)(rgbLena.cols() / 2);
|
||||
h = (int)(rgbLena.rows() / 2);
|
||||
|
||||
subLena1 = rgbLena.submat(shift1, h + shift1, shift1, w + shift1);
|
||||
subLena2 = rgbLena.submat(shift2, h + shift2, shift2, w + shift2);
|
||||
|
||||
prevPts = new MatOfPoint2f(new Point(11d, 8d), new Point(5d, 5d), new Point(10d, 10d));
|
||||
|
||||
nextPts = new MatOfPoint2f();
|
||||
status = new MatOfByte();
|
||||
err = new MatOfFloat();
|
||||
}
|
||||
|
||||
public void testCalcGlobalOrientation() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testCalcMotionGradientMatMatMatDoubleDouble() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testCalcMotionGradientMatMatMatDoubleDoubleInt() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testCalcOpticalFlowFarneback() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testCalcOpticalFlowPyrLKMatMatListOfPointListOfPointListOfByteListOfFloat() {
|
||||
Video.calcOpticalFlowPyrLK(subLena1, subLena2, prevPts, nextPts, status, err);
|
||||
assertEquals(3, Core.countNonZero(status));
|
||||
}
|
||||
|
||||
public void testCalcOpticalFlowPyrLKMatMatListOfPointListOfPointListOfByteListOfFloatSize() {
|
||||
Size sz = new Size(3, 3);
|
||||
Video.calcOpticalFlowPyrLK(subLena1, subLena2, prevPts, nextPts, status, err, sz, 3);
|
||||
assertEquals(0, Core.countNonZero(status));
|
||||
}
|
||||
|
||||
|
||||
public void testCalcOpticalFlowPyrLKMatMatListOfPointListOfPointListOfByteListOfFloatSizeIntTermCriteriaDoubleIntDouble() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testCamShift() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testEstimateRigidTransform() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testMeanShift() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testSegmentMotion() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
public void testUpdateMotionHistory() {
|
||||
fail("Not yet implemented");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"whitelist":
|
||||
{
|
||||
"": [
|
||||
"CamShift",
|
||||
"calcOpticalFlowFarneback",
|
||||
"calcOpticalFlowPyrLK",
|
||||
"createBackgroundSubtractorMOG2",
|
||||
"findTransformECC",
|
||||
"meanShift"
|
||||
],
|
||||
"BackgroundSubtractorMOG2": ["BackgroundSubtractorMOG2", "apply"],
|
||||
"BackgroundSubtractor": ["apply", "getBackgroundImage"],
|
||||
"TrackerMIL": ["create"],
|
||||
"TrackerMIL_Params": []
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#ifdef HAVE_OPENCV_VIDEO
|
||||
typedef TrackerMIL::Params TrackerMIL_Params;
|
||||
typedef TrackerDaSiamRPN::Params TrackerDaSiamRPN_Params;
|
||||
typedef TrackerNano::Params TrackerNano_Params;
|
||||
#endif
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
'''
|
||||
Lucas-Kanade homography tracker test
|
||||
===============================
|
||||
Uses goodFeaturesToTrack for track initialization and back-tracking for match verification
|
||||
between frames. Finds homography between reference and current views.
|
||||
'''
|
||||
|
||||
# Python 2/3 compatibility
|
||||
from __future__ import print_function
|
||||
|
||||
import numpy as np
|
||||
import cv2 as cv
|
||||
|
||||
#local modules
|
||||
from tst_scene_render import TestSceneRender
|
||||
from tests_common import NewOpenCVTests, isPointInRect
|
||||
|
||||
lk_params = dict( winSize = (19, 19),
|
||||
maxLevel = 2,
|
||||
criteria = (cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 0.03))
|
||||
|
||||
feature_params = dict( maxCorners = 1000,
|
||||
qualityLevel = 0.01,
|
||||
minDistance = 8,
|
||||
blockSize = 19 )
|
||||
|
||||
def checkedTrace(img0, img1, p0, back_threshold = 1.0):
|
||||
p1, _st, _err = cv.calcOpticalFlowPyrLK(img0, img1, p0, None, **lk_params)
|
||||
p0r, _st, _err = cv.calcOpticalFlowPyrLK(img1, img0, p1, None, **lk_params)
|
||||
d = abs(p0-p0r).reshape(-1, 2).max(-1)
|
||||
status = d < back_threshold
|
||||
return p1, status
|
||||
|
||||
class lk_homography_test(NewOpenCVTests):
|
||||
|
||||
render = None
|
||||
framesCounter = 0
|
||||
frame = frame0 = None
|
||||
p0 = None
|
||||
p1 = None
|
||||
gray0 = gray1 = None
|
||||
numFeaturesInRectOnStart = 0
|
||||
|
||||
def test_lk_homography(self):
|
||||
self.render = TestSceneRender(self.get_sample('samples/data/graf1.png'),
|
||||
self.get_sample('samples/data/box.png'), noise = 0.1, speed = 1.0)
|
||||
|
||||
frame = self.render.getNextFrame()
|
||||
frame_gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
|
||||
self.frame0 = frame.copy()
|
||||
self.p0 = cv.goodFeaturesToTrack(frame_gray, **feature_params)
|
||||
|
||||
isForegroundHomographyFound = False
|
||||
|
||||
if self.p0 is not None:
|
||||
self.p1 = self.p0
|
||||
self.gray0 = frame_gray
|
||||
self.gray1 = frame_gray
|
||||
currRect = self.render.getCurrentRect()
|
||||
for (x,y) in self.p0[:,0]:
|
||||
if isPointInRect((x,y), currRect):
|
||||
self.numFeaturesInRectOnStart += 1
|
||||
|
||||
while self.framesCounter < 200:
|
||||
self.framesCounter += 1
|
||||
frame = self.render.getNextFrame()
|
||||
frame_gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
|
||||
if self.p0 is not None:
|
||||
p2, trace_status = checkedTrace(self.gray1, frame_gray, self.p1)
|
||||
|
||||
self.p1 = p2[trace_status].copy()
|
||||
self.p0 = self.p0[trace_status].copy()
|
||||
self.gray1 = frame_gray
|
||||
|
||||
if len(self.p0) < 4:
|
||||
self.p0 = None
|
||||
continue
|
||||
_H, status = cv.findHomography(self.p0, self.p1, cv.RANSAC, 5.0)
|
||||
|
||||
goodPointsInRect = 0
|
||||
goodPointsOutsideRect = 0
|
||||
for (_x0, _y0), (x1, y1), good in zip(self.p0[:,0], self.p1[:,0], status[:,0]):
|
||||
if good:
|
||||
if isPointInRect((x1,y1), self.render.getCurrentRect()):
|
||||
goodPointsInRect += 1
|
||||
else: goodPointsOutsideRect += 1
|
||||
|
||||
if goodPointsOutsideRect < goodPointsInRect:
|
||||
isForegroundHomographyFound = True
|
||||
self.assertGreater(float(goodPointsInRect) / (self.numFeaturesInRectOnStart + 1), 0.6)
|
||||
else:
|
||||
self.p0 = cv.goodFeaturesToTrack(frame_gray, **feature_params)
|
||||
|
||||
self.assertEqual(isForegroundHomographyFound, True)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
NewOpenCVTests.bootstrap()
|
||||
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
'''
|
||||
Lucas-Kanade tracker
|
||||
====================
|
||||
|
||||
Lucas-Kanade sparse optical flow demo. Uses goodFeaturesToTrack
|
||||
for track initialization and back-tracking for match verification
|
||||
between frames.
|
||||
'''
|
||||
|
||||
# Python 2/3 compatibility
|
||||
from __future__ import print_function
|
||||
|
||||
import numpy as np
|
||||
import cv2 as cv
|
||||
|
||||
#local modules
|
||||
from tst_scene_render import TestSceneRender
|
||||
from tests_common import NewOpenCVTests, intersectionRate, isPointInRect
|
||||
|
||||
lk_params = dict( winSize = (15, 15),
|
||||
maxLevel = 2,
|
||||
criteria = (cv.TERM_CRITERIA_EPS | cv.TERM_CRITERIA_COUNT, 10, 0.03))
|
||||
|
||||
feature_params = dict( maxCorners = 500,
|
||||
qualityLevel = 0.3,
|
||||
minDistance = 7,
|
||||
blockSize = 7 )
|
||||
|
||||
def getRectFromPoints(points):
|
||||
|
||||
distances = []
|
||||
for point in points:
|
||||
distances.append(cv.norm(point, cv.NORM_L2))
|
||||
|
||||
x0, y0 = points[np.argmin(distances)]
|
||||
x1, y1 = points[np.argmax(distances)]
|
||||
|
||||
return np.array([x0, y0, x1, y1])
|
||||
|
||||
|
||||
class lk_track_test(NewOpenCVTests):
|
||||
|
||||
track_len = 10
|
||||
detect_interval = 5
|
||||
tracks = []
|
||||
frame_idx = 0
|
||||
render = None
|
||||
|
||||
def test_lk_track(self):
|
||||
|
||||
self.render = TestSceneRender(self.get_sample('samples/data/graf1.png'), self.get_sample('samples/data/box.png'))
|
||||
self.runTracker()
|
||||
|
||||
def runTracker(self):
|
||||
foregroundPointsNum = 0
|
||||
|
||||
while True:
|
||||
frame = self.render.getNextFrame()
|
||||
frame_gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
|
||||
|
||||
if len(self.tracks) > 0:
|
||||
img0, img1 = self.prev_gray, frame_gray
|
||||
p0 = np.float32([tr[-1][0] for tr in self.tracks]).reshape(-1, 1, 2)
|
||||
p1, _st, _err = cv.calcOpticalFlowPyrLK(img0, img1, p0, None, **lk_params)
|
||||
p0r, _st, _err = cv.calcOpticalFlowPyrLK(img1, img0, p1, None, **lk_params)
|
||||
d = abs(p0-p0r).reshape(-1, 2).max(-1)
|
||||
good = d < 1
|
||||
new_tracks = []
|
||||
for tr, (x, y), good_flag in zip(self.tracks, p1.reshape(-1, 2), good):
|
||||
if not good_flag:
|
||||
continue
|
||||
tr.append([(x, y), self.frame_idx])
|
||||
if len(tr) > self.track_len:
|
||||
del tr[0]
|
||||
new_tracks.append(tr)
|
||||
self.tracks = new_tracks
|
||||
|
||||
if self.frame_idx % self.detect_interval == 0:
|
||||
goodTracksCount = 0
|
||||
for tr in self.tracks:
|
||||
oldRect = self.render.getRectInTime(self.render.timeStep * tr[0][1])
|
||||
newRect = self.render.getRectInTime(self.render.timeStep * tr[-1][1])
|
||||
if isPointInRect(tr[0][0], oldRect) and isPointInRect(tr[-1][0], newRect):
|
||||
goodTracksCount += 1
|
||||
|
||||
if self.frame_idx == self.detect_interval:
|
||||
foregroundPointsNum = goodTracksCount
|
||||
|
||||
fgIndex = float(foregroundPointsNum) / (foregroundPointsNum + 1)
|
||||
fgRate = float(goodTracksCount) / (len(self.tracks) + 1)
|
||||
|
||||
if self.frame_idx > 0:
|
||||
self.assertGreater(fgIndex, 0.9)
|
||||
self.assertGreater(fgRate, 0.2)
|
||||
|
||||
mask = np.zeros_like(frame_gray)
|
||||
mask[:] = 255
|
||||
for x, y in [np.int32(tr[-1][0]) for tr in self.tracks]:
|
||||
cv.circle(mask, (x, y), 5, 0, -1)
|
||||
p = cv.goodFeaturesToTrack(frame_gray, mask = mask, **feature_params)
|
||||
if p is not None:
|
||||
for x, y in np.float32(p).reshape(-1, 2):
|
||||
self.tracks.append([[(x, y), self.frame_idx]])
|
||||
|
||||
self.frame_idx += 1
|
||||
self.prev_gray = frame_gray
|
||||
|
||||
if self.frame_idx > 300:
|
||||
break
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
NewOpenCVTests.bootstrap()
|
||||
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env python
|
||||
import os
|
||||
import numpy as np
|
||||
import cv2 as cv
|
||||
|
||||
from tests_common import NewOpenCVTests, unittest
|
||||
|
||||
class tracking_test(NewOpenCVTests):
|
||||
|
||||
def test_createMILTracker(self):
|
||||
t = cv.TrackerMIL.create()
|
||||
self.assertTrue(t is not None)
|
||||
|
||||
def test_createNanoTracker(self):
|
||||
backbone_path = self.find_file("dnn/onnx/models/nanotrack_backbone_sim_v2.onnx", required=False);
|
||||
neckhead_path = self.find_file("dnn/onnx/models/nanotrack_head_sim_v2.onnx", required=False);
|
||||
backbone = cv.dnn.readNet(backbone_path)
|
||||
neckhead = cv.dnn.readNet(neckhead_path)
|
||||
t = cv.TrackerNano.create(backbone, neckhead)
|
||||
self.assertTrue(t is not None)
|
||||
|
||||
def test_createVitTracker(self):
|
||||
model_path = self.find_file("dnn/onnx/models/vitTracker.onnx", required=False);
|
||||
model = cv.dnn.readNet(model_path)
|
||||
t = cv.TrackerVit.create(model)
|
||||
self.assertTrue(t is not None)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
NewOpenCVTests.bootstrap()
|
||||
@@ -0,0 +1,95 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "../perf_precomp.hpp"
|
||||
#include "opencv2/ts/ocl_perf.hpp"
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
#include "../perf_bgfg_utils.hpp"
|
||||
|
||||
namespace cvtest {
|
||||
namespace ocl {
|
||||
|
||||
//////////////////////////// KNN//////////////////////////
|
||||
|
||||
typedef tuple<string, int> VideoKNNParamType;
|
||||
typedef TestBaseWithParam<VideoKNNParamType> KNN_Apply;
|
||||
typedef TestBaseWithParam<VideoKNNParamType> KNN_GetBackgroundImage;
|
||||
|
||||
using namespace opencv_test;
|
||||
|
||||
OCL_PERF_TEST_P(KNN_Apply, KNN, Combine(Values("cv/video/768x576.avi", "cv/video/1920x1080.avi"), Values(1,3)))
|
||||
{
|
||||
VideoKNNParamType params = GetParam();
|
||||
|
||||
const string inputFile = getDataPath(get<0>(params));
|
||||
|
||||
const int cn = get<1>(params);
|
||||
int nFrame = 5;
|
||||
|
||||
vector<Mat> frame_buffer(nFrame);
|
||||
|
||||
cv::VideoCapture cap(inputFile);
|
||||
if (!cap.isOpened())
|
||||
throw SkipTestException("Video file can not be opened");
|
||||
prepareData(cap, cn, frame_buffer);
|
||||
|
||||
UMat u_foreground;
|
||||
|
||||
OCL_TEST_CYCLE()
|
||||
{
|
||||
Ptr<cv::BackgroundSubtractorKNN> knn = createBackgroundSubtractorKNN();
|
||||
knn->setDetectShadows(false);
|
||||
u_foreground.release();
|
||||
for (int i = 0; i < nFrame; i++)
|
||||
{
|
||||
knn->apply(frame_buffer[i], u_foreground);
|
||||
}
|
||||
}
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
OCL_PERF_TEST_P(KNN_GetBackgroundImage, KNN, Values(
|
||||
std::make_pair<string, int>("cv/video/768x576.avi", 5),
|
||||
std::make_pair<string, int>("cv/video/1920x1080.avi", 5)))
|
||||
{
|
||||
VideoKNNParamType params = GetParam();
|
||||
|
||||
const string inputFile = getDataPath(get<0>(params));
|
||||
|
||||
const int cn = 3;
|
||||
const int skipFrames = get<1>(params);
|
||||
int nFrame = 10;
|
||||
|
||||
vector<Mat> frame_buffer(nFrame);
|
||||
|
||||
cv::VideoCapture cap(inputFile);
|
||||
if (!cap.isOpened())
|
||||
throw SkipTestException("Video file can not be opened");
|
||||
prepareData(cap, cn, frame_buffer, skipFrames);
|
||||
|
||||
UMat u_foreground, u_background;
|
||||
|
||||
OCL_TEST_CYCLE()
|
||||
{
|
||||
Ptr<cv::BackgroundSubtractorKNN> knn = createBackgroundSubtractorKNN();
|
||||
knn->setDetectShadows(false);
|
||||
u_foreground.release();
|
||||
u_background.release();
|
||||
for (int i = 0; i < nFrame; i++)
|
||||
{
|
||||
knn->apply(frame_buffer[i], u_foreground);
|
||||
}
|
||||
knn->getBackgroundImage(u_background);
|
||||
}
|
||||
#ifdef DEBUG_BGFG
|
||||
imwrite(format("fg_%d_%d_knn_ocl.png", frame_buffer[0].rows, cn), u_foreground.getMat(ACCESS_READ));
|
||||
imwrite(format("bg_%d_%d_knn_ocl.png", frame_buffer[0].rows, cn), u_background.getMat(ACCESS_READ));
|
||||
#endif
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
}}// namespace cvtest::ocl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,95 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "../perf_precomp.hpp"
|
||||
#include "opencv2/ts/ocl_perf.hpp"
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
#include "../perf_bgfg_utils.hpp"
|
||||
|
||||
namespace opencv_test {
|
||||
namespace ocl {
|
||||
|
||||
//////////////////////////// Mog2//////////////////////////
|
||||
|
||||
typedef tuple<string, int> VideoMOG2ParamType;
|
||||
typedef TestBaseWithParam<VideoMOG2ParamType> MOG2_Apply;
|
||||
typedef TestBaseWithParam<VideoMOG2ParamType> MOG2_GetBackgroundImage;
|
||||
|
||||
using namespace opencv_test;
|
||||
|
||||
OCL_PERF_TEST_P(MOG2_Apply, Mog2, Combine(Values("cv/video/768x576.avi", "cv/video/1920x1080.avi"), Values(1,3)))
|
||||
{
|
||||
VideoMOG2ParamType params = GetParam();
|
||||
|
||||
const string inputFile = getDataPath(get<0>(params));
|
||||
|
||||
const int cn = get<1>(params);
|
||||
int nFrame = 5;
|
||||
|
||||
vector<Mat> frame_buffer(nFrame);
|
||||
|
||||
cv::VideoCapture cap(inputFile);
|
||||
if (!cap.isOpened())
|
||||
throw SkipTestException("Video file can not be opened");
|
||||
prepareData(cap, cn, frame_buffer);
|
||||
|
||||
UMat u_foreground;
|
||||
|
||||
OCL_TEST_CYCLE()
|
||||
{
|
||||
Ptr<cv::BackgroundSubtractorMOG2> mog2 = createBackgroundSubtractorMOG2();
|
||||
mog2->setDetectShadows(false);
|
||||
u_foreground.release();
|
||||
for (int i = 0; i < nFrame; i++)
|
||||
{
|
||||
mog2->apply(frame_buffer[i], u_foreground);
|
||||
}
|
||||
}
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
OCL_PERF_TEST_P(MOG2_GetBackgroundImage, Mog2, Values(
|
||||
std::make_pair<string, int>("cv/video/768x576.avi", 5),
|
||||
std::make_pair<string, int>("cv/video/1920x1080.avi", 5)))
|
||||
{
|
||||
VideoMOG2ParamType params = GetParam();
|
||||
|
||||
const string inputFile = getDataPath(get<0>(params));
|
||||
|
||||
const int cn = 3;
|
||||
const int skipFrames = get<1>(params);
|
||||
int nFrame = 10;
|
||||
|
||||
vector<Mat> frame_buffer(nFrame);
|
||||
|
||||
cv::VideoCapture cap(inputFile);
|
||||
if (!cap.isOpened())
|
||||
throw SkipTestException("Video file can not be opened");
|
||||
prepareData(cap, cn, frame_buffer, skipFrames);
|
||||
|
||||
UMat u_foreground, u_background;
|
||||
|
||||
OCL_TEST_CYCLE()
|
||||
{
|
||||
Ptr<cv::BackgroundSubtractorMOG2> mog2 = createBackgroundSubtractorMOG2();
|
||||
mog2->setDetectShadows(false);
|
||||
u_foreground.release();
|
||||
u_background.release();
|
||||
for (int i = 0; i < nFrame; i++)
|
||||
{
|
||||
mog2->apply(frame_buffer[i], u_foreground);
|
||||
}
|
||||
mog2->getBackgroundImage(u_background);
|
||||
}
|
||||
#ifdef DEBUG_BGFG
|
||||
imwrite(format("fg_%d_%d_mog2_ocl.png", frame_buffer[0].rows, cn), u_foreground.getMat(ACCESS_READ));
|
||||
imwrite(format("bg_%d_%d_mog2_ocl.png", frame_buffer[0].rows, cn), u_background.getMat(ACCESS_READ));
|
||||
#endif
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
}}// namespace opencv_test::ocl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,73 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
#include "../perf_precomp.hpp"
|
||||
#include "opencv2/ts/ocl_perf.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
|
||||
void MakeArtificialExample(UMat &dst_frame1, UMat &dst_frame2);
|
||||
|
||||
typedef tuple<String, Size> DISParams;
|
||||
typedef TestBaseWithParam<DISParams> DenseOpticalFlow_DIS;
|
||||
|
||||
OCL_PERF_TEST_P(DenseOpticalFlow_DIS, perf,
|
||||
Combine(Values("PRESET_ULTRAFAST", "PRESET_FAST", "PRESET_MEDIUM"), Values(szVGA, sz720p, sz1080p)))
|
||||
{
|
||||
DISParams params = GetParam();
|
||||
|
||||
// use strings to print preset names in the perf test results:
|
||||
String preset_string = get<0>(params);
|
||||
int preset = DISOpticalFlow::PRESET_FAST;
|
||||
if (preset_string == "PRESET_ULTRAFAST")
|
||||
preset = DISOpticalFlow::PRESET_ULTRAFAST;
|
||||
else if (preset_string == "PRESET_FAST")
|
||||
preset = DISOpticalFlow::PRESET_FAST;
|
||||
else if (preset_string == "PRESET_MEDIUM")
|
||||
preset = DISOpticalFlow::PRESET_MEDIUM;
|
||||
Size sz = get<1>(params);
|
||||
|
||||
UMat frame1(sz, CV_8U);
|
||||
UMat frame2(sz, CV_8U);
|
||||
UMat flow;
|
||||
|
||||
MakeArtificialExample(frame1, frame2);
|
||||
|
||||
Ptr<DenseOpticalFlow> algo = DISOpticalFlow::create(preset);
|
||||
|
||||
PERF_SAMPLE_BEGIN()
|
||||
{
|
||||
algo->calc(frame1, frame2, flow);
|
||||
}
|
||||
PERF_SAMPLE_END()
|
||||
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
void MakeArtificialExample(UMat &dst_frame1, UMat &dst_frame2)
|
||||
{
|
||||
int src_scale = 2;
|
||||
int OF_scale = 6;
|
||||
double sigma = dst_frame1.cols / 300;
|
||||
|
||||
UMat tmp(Size(dst_frame1.cols / (1 << src_scale), dst_frame1.rows / (1 << src_scale)), CV_8U);
|
||||
randu(tmp, 0, 255);
|
||||
resize(tmp, dst_frame1, dst_frame1.size(), 0.0, 0.0, INTER_LINEAR_EXACT);
|
||||
resize(tmp, dst_frame2, dst_frame2.size(), 0.0, 0.0, INTER_LINEAR_EXACT);
|
||||
|
||||
Mat displacement_field(Size(dst_frame1.cols / (1 << OF_scale), dst_frame1.rows / (1 << OF_scale)),
|
||||
CV_32FC2);
|
||||
randn(displacement_field, 0.0, sigma);
|
||||
resize(displacement_field, displacement_field, dst_frame2.size(), 0.0, 0.0, INTER_CUBIC);
|
||||
for (int i = 0; i < displacement_field.rows; i++)
|
||||
for (int j = 0; j < displacement_field.cols; j++)
|
||||
displacement_field.at<Vec2f>(i, j) += Vec2f((float)j, (float)i);
|
||||
|
||||
remap(dst_frame2, dst_frame2, displacement_field, Mat(), INTER_LINEAR, BORDER_REPLICATE);
|
||||
}
|
||||
|
||||
#endif // HAVE_OPENCL
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,36 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
// Copyright (C) 2014, Advanced Micro Devices, Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
|
||||
#include "../perf_precomp.hpp"
|
||||
#include "opencv2/ts/ocl_perf.hpp"
|
||||
|
||||
#if 0 //def HAVE_OPENCL
|
||||
|
||||
namespace opencv_test {
|
||||
namespace ocl {
|
||||
|
||||
///////////// UpdateMotionHistory ////////////////////////
|
||||
|
||||
typedef TestBaseWithParam<Size> UpdateMotionHistoryFixture;
|
||||
|
||||
OCL_PERF_TEST_P(UpdateMotionHistoryFixture, UpdateMotionHistory, OCL_TEST_SIZES)
|
||||
{
|
||||
const Size size = GetParam();
|
||||
checkDeviceMaxMemoryAllocSize(size, CV_32FC1);
|
||||
|
||||
UMat silhouette(size, CV_8UC1), mhi(size, CV_32FC1);
|
||||
randu(silhouette, -5, 5);
|
||||
declare.in(mhi, WARMUP_RNG);
|
||||
|
||||
OCL_TEST_CYCLE() cv::updateMotionHistory(silhouette, mhi, 1, 0.5);
|
||||
|
||||
SANITY_CHECK(mhi);
|
||||
}
|
||||
|
||||
} } // namespace opencv_test::ocl
|
||||
|
||||
#endif // HAVE_OPENCL
|
||||
@@ -0,0 +1,112 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2010-2012, Multicoreware, Inc., all rights reserved.
|
||||
// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// @Authors
|
||||
// Fangfang Bai, fangfang@multicorewareinc.com
|
||||
// Jin Ma, jin@multicorewareinc.com
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors as is and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "../perf_precomp.hpp"
|
||||
#include "opencv2/ts/ocl_perf.hpp"
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
|
||||
namespace opencv_test {
|
||||
namespace ocl {
|
||||
|
||||
///////////// FarnebackOpticalFlow ////////////////////////
|
||||
CV_ENUM(farneFlagType, 0, OPTFLOW_FARNEBACK_GAUSSIAN)
|
||||
|
||||
typedef tuple< tuple<int, double>, farneFlagType, bool > FarnebackOpticalFlowParams;
|
||||
typedef TestBaseWithParam<FarnebackOpticalFlowParams> FarnebackOpticalFlowFixture;
|
||||
|
||||
OCL_PERF_TEST_P(FarnebackOpticalFlowFixture, FarnebackOpticalFlow,
|
||||
::testing::Combine(
|
||||
::testing::Values(
|
||||
make_tuple<int, double>(5, 1.1),
|
||||
make_tuple<int, double>(7, 1.5)
|
||||
),
|
||||
farneFlagType::all(),
|
||||
::testing::Bool()
|
||||
)
|
||||
)
|
||||
{
|
||||
Mat frame0 = imread(getDataPath("gpu/opticalflow/rubberwhale1.png"), cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame0.empty()) << "can't load rubberwhale1.png";
|
||||
|
||||
Mat frame1 = imread(getDataPath("gpu/opticalflow/rubberwhale2.png"), cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame1.empty()) << "can't load rubberwhale2.png";
|
||||
|
||||
const Size srcSize = frame0.size();
|
||||
|
||||
const int numLevels = 5;
|
||||
const int winSize = 13;
|
||||
const int numIters = 10;
|
||||
|
||||
const FarnebackOpticalFlowParams params = GetParam();
|
||||
const tuple<int, double> polyParams = get<0>(params);
|
||||
const int polyN = get<0>(polyParams);
|
||||
const double polySigma = get<1>(polyParams);
|
||||
const double pyrScale = 0.5;
|
||||
int flags = get<1>(params);
|
||||
const bool useInitFlow = get<2>(params);
|
||||
const double eps = 0.1;
|
||||
|
||||
UMat uFrame0; frame0.copyTo(uFrame0);
|
||||
UMat uFrame1; frame1.copyTo(uFrame1);
|
||||
UMat uFlow(srcSize, CV_32FC2);
|
||||
declare.in(uFrame0, uFrame1, WARMUP_READ).out(uFlow, WARMUP_READ);
|
||||
if (useInitFlow)
|
||||
{
|
||||
cv::calcOpticalFlowFarneback(uFrame0, uFrame1, uFlow, pyrScale, numLevels, winSize, numIters, polyN, polySigma, flags);
|
||||
flags |= OPTFLOW_USE_INITIAL_FLOW;
|
||||
}
|
||||
|
||||
OCL_TEST_CYCLE()
|
||||
cv::calcOpticalFlowFarneback(uFrame0, uFrame1, uFlow, pyrScale, numLevels, winSize, numIters, polyN, polySigma, flags);
|
||||
|
||||
|
||||
SANITY_CHECK(uFlow, eps, ERROR_RELATIVE);
|
||||
}
|
||||
|
||||
} } // namespace opencv_test::ocl
|
||||
|
||||
#endif // HAVE_OPENCL
|
||||
@@ -0,0 +1,107 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2010-2012, Multicoreware, Inc., all rights reserved.
|
||||
// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// @Authors
|
||||
// Fangfang Bai, fangfang@multicorewareinc.com
|
||||
// Jin Ma, jin@multicorewareinc.com
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors as is and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "../perf_precomp.hpp"
|
||||
#include "opencv2/ts/ocl_perf.hpp"
|
||||
#ifdef HAVE_OPENCV_FEATURES
|
||||
#include "opencv2/features.hpp"
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_OPENCL) && defined(HAVE_OPENCV_FEATURES)
|
||||
|
||||
namespace opencv_test {
|
||||
namespace ocl {
|
||||
|
||||
typedef tuple< int > PyrLKOpticalFlowParams;
|
||||
typedef TestBaseWithParam<PyrLKOpticalFlowParams> PyrLKOpticalFlowFixture;
|
||||
|
||||
OCL_PERF_TEST_P(PyrLKOpticalFlowFixture, PyrLKOpticalFlow,
|
||||
::testing::Values(1000, 2000, 4000)
|
||||
)
|
||||
{
|
||||
Mat frame0 = imread(getDataPath("gpu/opticalflow/rubberwhale1.png"), cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame0.empty()) << "can't load rubberwhale1.png";
|
||||
|
||||
Mat frame1 = imread(getDataPath("gpu/opticalflow/rubberwhale2.png"), cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame1.empty()) << "can't load rubberwhale2.png";
|
||||
|
||||
UMat uFrame0; frame0.copyTo(uFrame0);
|
||||
UMat uFrame1; frame1.copyTo(uFrame1);
|
||||
|
||||
const Size winSize = Size(21, 21);
|
||||
const int maxLevel = 3;
|
||||
const TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 0.01);
|
||||
const int flags = 0;
|
||||
const float minEigThreshold = 1e-4f;
|
||||
const double eps = 1.0;
|
||||
|
||||
const PyrLKOpticalFlowParams params = GetParam();
|
||||
const int pointsCount = get<0>(params);
|
||||
|
||||
// SKIP unstable tests
|
||||
#ifdef __linux__
|
||||
if (cvtest::skipUnstableTests && ocl::useOpenCL())
|
||||
{
|
||||
if (ocl::Device::getDefault().isIntel())
|
||||
throw ::perf::TestBase::PerfSkipTestException();
|
||||
}
|
||||
#endif
|
||||
|
||||
vector<Point2f> pts;
|
||||
goodFeaturesToTrack(frame0, pts, pointsCount, 0.01, 0.0);
|
||||
Mat ptsMat(1, static_cast<int>(pts.size()), CV_32FC2, (void *)&pts[0]);
|
||||
|
||||
declare.in(uFrame0, uFrame1, WARMUP_READ);
|
||||
UMat uNextPts, uStatus, uErr;
|
||||
OCL_TEST_CYCLE()
|
||||
cv::calcOpticalFlowPyrLK(uFrame0, uFrame1, pts, uNextPts, uStatus, uErr, winSize, maxLevel, criteria, flags, minEigThreshold);
|
||||
|
||||
SANITY_CHECK(uNextPts, eps);
|
||||
}
|
||||
|
||||
} } // namespace opencv_test::ocl
|
||||
|
||||
#endif // HAVE_OPENCL
|
||||
@@ -0,0 +1,88 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
#include "perf_bgfg_utils.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
//////////////////////////// KNN//////////////////////////
|
||||
|
||||
typedef tuple<std::string, int> VideoKNNParamType;
|
||||
typedef TestBaseWithParam<VideoKNNParamType> KNN_Apply;
|
||||
typedef TestBaseWithParam<VideoKNNParamType> KNN_GetBackgroundImage;
|
||||
|
||||
PERF_TEST_P(KNN_Apply, KNN, Combine(Values("cv/video/768x576.avi", "cv/video/1920x1080.avi"), Values(1,3)))
|
||||
{
|
||||
VideoKNNParamType params = GetParam();
|
||||
|
||||
const string inputFile = getDataPath(get<0>(params));
|
||||
|
||||
const int cn = get<1>(params);
|
||||
int nFrame = 5;
|
||||
|
||||
vector<Mat> frame_buffer(nFrame);
|
||||
|
||||
cv::VideoCapture cap(inputFile);
|
||||
if (!cap.isOpened())
|
||||
throw SkipTestException("Video file can not be opened");
|
||||
prepareData(cap, cn, frame_buffer);
|
||||
|
||||
Mat foreground;
|
||||
|
||||
TEST_CYCLE()
|
||||
{
|
||||
Ptr<cv::BackgroundSubtractorKNN> knn = createBackgroundSubtractorKNN();
|
||||
knn->setDetectShadows(false);
|
||||
foreground.release();
|
||||
for (int i = 0; i < nFrame; i++)
|
||||
{
|
||||
knn->apply(frame_buffer[i], foreground);
|
||||
}
|
||||
}
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
PERF_TEST_P(KNN_GetBackgroundImage, KNN, Values(
|
||||
std::make_pair<string, int>("cv/video/768x576.avi", 5),
|
||||
std::make_pair<string, int>("cv/video/1920x1080.avi", 5)))
|
||||
{
|
||||
VideoKNNParamType params = GetParam();
|
||||
|
||||
const string inputFile = getDataPath(get<0>(params));
|
||||
|
||||
const int cn = 3;
|
||||
const int skipFrames = get<1>(params);
|
||||
int nFrame = 10;
|
||||
|
||||
vector<Mat> frame_buffer(nFrame);
|
||||
|
||||
cv::VideoCapture cap(inputFile);
|
||||
if (!cap.isOpened())
|
||||
throw SkipTestException("Video file can not be opened");
|
||||
prepareData(cap, cn, frame_buffer, skipFrames);
|
||||
|
||||
Mat foreground, background;
|
||||
|
||||
TEST_CYCLE()
|
||||
{
|
||||
Ptr<cv::BackgroundSubtractorKNN> knn = createBackgroundSubtractorKNN();
|
||||
knn->setDetectShadows(false);
|
||||
foreground.release();
|
||||
background.release();
|
||||
for (int i = 0; i < nFrame; i++)
|
||||
{
|
||||
knn->apply(frame_buffer[i], foreground);
|
||||
}
|
||||
knn->getBackgroundImage(background);
|
||||
}
|
||||
#ifdef DEBUG_BGFG
|
||||
imwrite(format("fg_%d_%d_knn.png", frame_buffer[0].rows, cn), foreground);
|
||||
imwrite(format("bg_%d_%d_knn.png", frame_buffer[0].rows, cn), background);
|
||||
#endif
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
}}// namespace
|
||||
@@ -0,0 +1,88 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
#include "perf_bgfg_utils.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
//////////////////////////// Mog2//////////////////////////
|
||||
|
||||
typedef tuple<std::string, int> VideoMOG2ParamType;
|
||||
typedef TestBaseWithParam<VideoMOG2ParamType> MOG2_Apply;
|
||||
typedef TestBaseWithParam<VideoMOG2ParamType> MOG2_GetBackgroundImage;
|
||||
|
||||
PERF_TEST_P(MOG2_Apply, Mog2, Combine(Values("cv/video/768x576.avi", "cv/video/1920x1080.avi"), Values(1,3)))
|
||||
{
|
||||
VideoMOG2ParamType params = GetParam();
|
||||
|
||||
const string inputFile = getDataPath(get<0>(params));
|
||||
|
||||
const int cn = get<1>(params);
|
||||
int nFrame = 5;
|
||||
|
||||
vector<Mat> frame_buffer(nFrame);
|
||||
|
||||
cv::VideoCapture cap(inputFile);
|
||||
if (!cap.isOpened())
|
||||
throw SkipTestException("Video file can not be opened");
|
||||
prepareData(cap, cn, frame_buffer);
|
||||
|
||||
Mat foreground;
|
||||
|
||||
TEST_CYCLE()
|
||||
{
|
||||
Ptr<cv::BackgroundSubtractorMOG2> mog2 = createBackgroundSubtractorMOG2();
|
||||
mog2->setDetectShadows(false);
|
||||
foreground.release();
|
||||
for (int i = 0; i < nFrame; i++)
|
||||
{
|
||||
mog2->apply(frame_buffer[i], foreground);
|
||||
}
|
||||
}
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
PERF_TEST_P(MOG2_GetBackgroundImage, Mog2, Values(
|
||||
std::make_pair<string, int>("cv/video/768x576.avi", 5),
|
||||
std::make_pair<string, int>("cv/video/1920x1080.avi", 5)))
|
||||
{
|
||||
VideoMOG2ParamType params = GetParam();
|
||||
|
||||
const string inputFile = getDataPath(get<0>(params));
|
||||
|
||||
const int cn = 3;
|
||||
const int skipFrames = get<1>(params);
|
||||
int nFrame = 10;
|
||||
|
||||
vector<Mat> frame_buffer(nFrame);
|
||||
|
||||
cv::VideoCapture cap(inputFile);
|
||||
if (!cap.isOpened())
|
||||
throw SkipTestException("Video file can not be opened");
|
||||
prepareData(cap, cn, frame_buffer, skipFrames);
|
||||
|
||||
Mat foreground, background;
|
||||
|
||||
TEST_CYCLE()
|
||||
{
|
||||
Ptr<cv::BackgroundSubtractorMOG2> mog2 = createBackgroundSubtractorMOG2();
|
||||
mog2->setDetectShadows(false);
|
||||
foreground.release();
|
||||
background.release();
|
||||
for (int i = 0; i < nFrame; i++)
|
||||
{
|
||||
mog2->apply(frame_buffer[i], foreground);
|
||||
}
|
||||
mog2->getBackgroundImage(background);
|
||||
}
|
||||
#ifdef DEBUG_BGFG
|
||||
imwrite(format("fg_%d_%d_mog2.png", frame_buffer[0].rows, cn), foreground);
|
||||
imwrite(format("bg_%d_%d_mog2.png", frame_buffer[0].rows, cn), background);
|
||||
#endif
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
}}// namespace
|
||||
@@ -0,0 +1,48 @@
|
||||
// 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.
|
||||
|
||||
namespace opencv_test {
|
||||
|
||||
//#define DEBUG_BGFG
|
||||
|
||||
using namespace testing;
|
||||
using namespace cvtest;
|
||||
using namespace perf;
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace cv;
|
||||
|
||||
static void cvtFrameFmt(std::vector<Mat>& input, std::vector<Mat>& output)
|
||||
{
|
||||
for(int i = 0; i< (int)(input.size()); i++)
|
||||
{
|
||||
cvtColor(input[i], output[i], COLOR_RGB2GRAY);
|
||||
}
|
||||
}
|
||||
|
||||
static void prepareData(VideoCapture& cap, int cn, std::vector<Mat>& frame_buffer, int skipFrames = 0)
|
||||
{
|
||||
std::vector<Mat> frame_buffer_init;
|
||||
int nFrame = (int)frame_buffer.size();
|
||||
for (int i = 0; i < skipFrames; i++)
|
||||
{
|
||||
cv::Mat frame;
|
||||
cap >> frame;
|
||||
}
|
||||
for (int i = 0; i < nFrame; i++)
|
||||
{
|
||||
cv::Mat frame;
|
||||
cap >> frame;
|
||||
ASSERT_FALSE(frame.empty());
|
||||
frame_buffer_init.push_back(frame);
|
||||
}
|
||||
|
||||
if (cn == 1)
|
||||
cvtFrameFmt(frame_buffer_init, frame_buffer);
|
||||
else
|
||||
frame_buffer.swap(frame_buffer_init);
|
||||
}
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,66 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
void MakeArtificialExample(Mat &dst_frame1, Mat &dst_frame2);
|
||||
|
||||
typedef tuple<String, Size> DISParams;
|
||||
typedef TestBaseWithParam<DISParams> DenseOpticalFlow_DIS;
|
||||
|
||||
PERF_TEST_P(DenseOpticalFlow_DIS, perf,
|
||||
Combine(Values("PRESET_ULTRAFAST", "PRESET_FAST", "PRESET_MEDIUM"), Values(szVGA, sz720p, sz1080p)))
|
||||
{
|
||||
DISParams params = GetParam();
|
||||
|
||||
// use strings to print preset names in the perf test results:
|
||||
String preset_string = get<0>(params);
|
||||
int preset = DISOpticalFlow::PRESET_FAST;
|
||||
if (preset_string == "PRESET_ULTRAFAST")
|
||||
preset = DISOpticalFlow::PRESET_ULTRAFAST;
|
||||
else if (preset_string == "PRESET_FAST")
|
||||
preset = DISOpticalFlow::PRESET_FAST;
|
||||
else if (preset_string == "PRESET_MEDIUM")
|
||||
preset = DISOpticalFlow::PRESET_MEDIUM;
|
||||
Size sz = get<1>(params);
|
||||
|
||||
Mat frame1(sz, CV_8U);
|
||||
Mat frame2(sz, CV_8U);
|
||||
Mat flow;
|
||||
|
||||
MakeArtificialExample(frame1, frame2);
|
||||
|
||||
TEST_CYCLE_N(10)
|
||||
{
|
||||
Ptr<DenseOpticalFlow> algo = DISOpticalFlow::create(preset);
|
||||
algo->calc(frame1, frame2, flow);
|
||||
}
|
||||
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
void MakeArtificialExample(Mat &dst_frame1, Mat &dst_frame2)
|
||||
{
|
||||
int src_scale = 2;
|
||||
int OF_scale = 6;
|
||||
double sigma = dst_frame1.cols / 300;
|
||||
|
||||
Mat tmp(Size(dst_frame1.cols / (1 << src_scale), dst_frame1.rows / (1 << src_scale)), CV_8U);
|
||||
randu(tmp, 0, 255);
|
||||
resize(tmp, dst_frame1, dst_frame1.size(), 0.0, 0.0, INTER_LINEAR_EXACT);
|
||||
resize(tmp, dst_frame2, dst_frame2.size(), 0.0, 0.0, INTER_LINEAR_EXACT);
|
||||
|
||||
Mat displacement_field(Size(dst_frame1.cols / (1 << OF_scale), dst_frame1.rows / (1 << OF_scale)),
|
||||
CV_32FC2);
|
||||
randn(displacement_field, 0.0, sigma);
|
||||
resize(displacement_field, displacement_field, dst_frame2.size(), 0.0, 0.0, INTER_CUBIC);
|
||||
for (int i = 0; i < displacement_field.rows; i++)
|
||||
for (int j = 0; j < displacement_field.cols; j++)
|
||||
displacement_field.at<Vec2f>(i, j) += Vec2f((float)j, (float)i);
|
||||
|
||||
remap(dst_frame2, dst_frame2, displacement_field, Mat(), INTER_LINEAR, BORDER_REPLICATE);
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,130 @@
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
namespace opencv_test {
|
||||
using namespace perf;
|
||||
|
||||
CV_ENUM(MotionType, MOTION_TRANSLATION, MOTION_EUCLIDEAN, MOTION_AFFINE, MOTION_HOMOGRAPHY)
|
||||
CV_ENUM(ReadFlag, IMREAD_GRAYSCALE, IMREAD_COLOR)
|
||||
CV_ENUM(MultiScaleFlag, false, true)
|
||||
|
||||
typedef std::tuple<MotionType, ReadFlag> TestParams;
|
||||
typedef std::tuple<MotionType, MultiScaleFlag> TestParamsMS;
|
||||
typedef perf::TestBaseWithParam<TestParams> ECCPerfTest;
|
||||
typedef perf::TestBaseWithParam<TestParamsMS> ECCPerfTestMS;
|
||||
|
||||
typedef std::tuple<MotionType, ReadFlag> TestParams;
|
||||
|
||||
PERF_TEST_P(ECCPerfTest, findTransformECC,
|
||||
testing::Combine(testing::Values(MOTION_TRANSLATION, MOTION_EUCLIDEAN, MOTION_AFFINE, MOTION_HOMOGRAPHY),
|
||||
testing::Values(IMREAD_GRAYSCALE, IMREAD_COLOR))) {
|
||||
int transform_type = get<0>(GetParam());
|
||||
int readFlag = get<1>(GetParam());
|
||||
|
||||
Mat img = imread(getDataPath("cv/shared/fruits_ecc.png"), readFlag);
|
||||
Mat templateImage;
|
||||
|
||||
Mat warpMat;
|
||||
Mat warpGround;
|
||||
|
||||
double angle;
|
||||
switch (transform_type) {
|
||||
case MOTION_TRANSLATION:
|
||||
warpGround = (Mat_<float>(2, 3) << 1.f, 0.f, 7.234f, 0.f, 1.f, 11.839f);
|
||||
|
||||
warpAffine(img, templateImage, warpGround, Size(200, 200), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
break;
|
||||
case MOTION_EUCLIDEAN:
|
||||
angle = CV_PI / 30;
|
||||
|
||||
warpGround = (Mat_<float>(2, 3) << (float)cos(angle), (float)-sin(angle), 12.123f, (float)sin(angle),
|
||||
(float)cos(angle), 14.789f);
|
||||
warpAffine(img, templateImage, warpGround, Size(200, 200), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
break;
|
||||
case MOTION_AFFINE:
|
||||
warpGround = (Mat_<float>(2, 3) << 0.98f, 0.03f, 15.523f, -0.02f, 0.95f, 10.456f);
|
||||
warpAffine(img, templateImage, warpGround, Size(200, 200), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
break;
|
||||
case MOTION_HOMOGRAPHY:
|
||||
warpGround = (Mat_<float>(3, 3) << 0.98f, 0.03f, 15.523f, -0.02f, 0.95f, 10.456f, 0.0002f, 0.0003f, 1.f);
|
||||
warpPerspective(img, templateImage, warpGround, Size(200, 200), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
break;
|
||||
}
|
||||
|
||||
TEST_CYCLE() {
|
||||
if (transform_type < 3)
|
||||
warpMat = Mat::eye(2, 3, CV_32F);
|
||||
else
|
||||
warpMat = Mat::eye(3, 3, CV_32F);
|
||||
|
||||
findTransformECC(templateImage, img, warpMat, transform_type,
|
||||
TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 5, -1));
|
||||
}
|
||||
|
||||
if (transform_type == MOTION_HOMOGRAPHY)
|
||||
{
|
||||
// NOTE: for Mac M1 + KleidiCV
|
||||
// ECCPerfTest_findTransformECC.findTransformECC/6, where GetParam() = (MOTION_HOMOGRAPHY, IMREAD_GRAYSCALE)
|
||||
SANITY_CHECK(warpMat, 8.3e-3);
|
||||
}
|
||||
else
|
||||
{
|
||||
SANITY_CHECK(warpMat, 3e-3);
|
||||
}
|
||||
}
|
||||
|
||||
PERF_TEST_P(ECCPerfTestMS, findTransformECCMultiScale,
|
||||
testing::Combine(testing::Values(MOTION_TRANSLATION, MOTION_EUCLIDEAN, MOTION_AFFINE, MOTION_HOMOGRAPHY),
|
||||
testing::Values(false, true))) {
|
||||
int transform_type = get<0>(GetParam());
|
||||
bool multiscaleFlag = get<1>(GetParam());
|
||||
|
||||
Mat img = imread(getDataPath("cv/shared/3MP.png"), IMREAD_GRAYSCALE);
|
||||
Mat templateImage;
|
||||
|
||||
Mat warpMat;
|
||||
Mat warpGround;
|
||||
|
||||
double angle;
|
||||
switch (transform_type) {
|
||||
case MOTION_TRANSLATION:
|
||||
warpGround = (Mat_<float>(2, 3) << 1.f, 0.f, 7.234f, 0.f, 1.f, 11.839f);
|
||||
warpAffine(img, templateImage, warpGround, img.size(), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
break;
|
||||
case MOTION_EUCLIDEAN:
|
||||
angle = CV_PI / 30;
|
||||
|
||||
warpGround = (Mat_<float>(2, 3) << (float)cos(angle), (float)-sin(angle), 12.123f, (float)sin(angle),
|
||||
(float)cos(angle), 14.789f);
|
||||
warpAffine(img, templateImage, warpGround, img.size(), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
break;
|
||||
case MOTION_AFFINE:
|
||||
warpGround = (Mat_<float>(2, 3) << 0.98f, 0.03f, 15.523f, -0.02f, 0.95f, 10.456f);
|
||||
warpAffine(img, templateImage, warpGround, img.size(), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
break;
|
||||
case MOTION_HOMOGRAPHY:
|
||||
warpGround = (Mat_<float>(3, 3) << 0.98f, 0.03f, 15.523f, -0.02f, 0.95f, 10.456f, 0.0002f, 0.0003f, 1.f);
|
||||
warpPerspective(img, templateImage, warpGround, img.size(), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
break;
|
||||
}
|
||||
|
||||
TEST_CYCLE() {
|
||||
if (transform_type < 3)
|
||||
warpMat = Mat::eye(2, 3, CV_32F);
|
||||
else
|
||||
warpMat = Mat::eye(3, 3, CV_32F);
|
||||
|
||||
if(multiscaleFlag) {
|
||||
ECCParameters params;
|
||||
params.criteria = cv::TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 5, -1);
|
||||
params.motionType = transform_type;
|
||||
params.itersPerLevel = {1, 2, 2, 2};
|
||||
findTransformECCMultiScale(templateImage, img, warpMat, params);
|
||||
}
|
||||
else {
|
||||
findTransformECC(templateImage, img, warpMat, transform_type,
|
||||
TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 5, -1));
|
||||
}
|
||||
}
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
} // namespace opencv_test
|
||||
@@ -0,0 +1,14 @@
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
#if defined(HAVE_HPX)
|
||||
#include <hpx/hpx_main.hpp>
|
||||
#endif
|
||||
|
||||
static
|
||||
void initTests()
|
||||
{
|
||||
cvtest::addDataSearchEnv("OPENCV_DNN_TEST_DATA_PATH");
|
||||
cvtest::addDataSearchSubDirectory(""); // override "cv" prefix below to access without "../dnn" hacks
|
||||
}
|
||||
|
||||
CV_PERF_TEST_MAIN(video, initTests())
|
||||
@@ -0,0 +1,269 @@
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
using namespace perf;
|
||||
|
||||
typedef tuple<std::string, int, int, tuple<int,int>, int> Path_Idx_Cn_NPoints_WSize_t;
|
||||
typedef TestBaseWithParam<Path_Idx_Cn_NPoints_WSize_t> Path_Idx_Cn_NPoints_WSize;
|
||||
|
||||
void FormTrackingPointsArray(vector<Point2f>& points, int width, int height, int nPointsX, int nPointsY)
|
||||
{
|
||||
int stepX = width / nPointsX;
|
||||
int stepY = height / nPointsY;
|
||||
if (stepX < 1 || stepY < 1) FAIL() << "Specified points number is too big";
|
||||
|
||||
points.clear();
|
||||
points.reserve(nPointsX * nPointsY);
|
||||
|
||||
for( int x = stepX / 2; x < width; x += stepX )
|
||||
{
|
||||
for( int y = stepY / 2; y < height; y += stepY )
|
||||
{
|
||||
Point2f pt(static_cast<float>(x), static_cast<float>(y));
|
||||
points.push_back(pt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PERF_TEST_P(Path_Idx_Cn_NPoints_WSize, OpticalFlowPyrLK_full, testing::Combine(
|
||||
testing::Values<std::string>("cv/optflow/frames/VGA_%02d.png", "cv/optflow/frames/720p_%02d.png"),
|
||||
testing::Range(1, 3),
|
||||
testing::Values(1, 3, 4),
|
||||
testing::Values(make_tuple(9, 9), make_tuple(15, 15)),
|
||||
testing::Values(7, 11)
|
||||
)
|
||||
)
|
||||
{
|
||||
string filename1 = getDataPath(cv::format(get<0>(GetParam()).c_str(), get<1>(GetParam())));
|
||||
string filename2 = getDataPath(cv::format(get<0>(GetParam()).c_str(), get<1>(GetParam()) + 1));
|
||||
Mat img1 = imread(filename1);
|
||||
Mat img2 = imread(filename2);
|
||||
if (img1.empty()) FAIL() << "Unable to load source image " << filename1;
|
||||
if (img2.empty()) FAIL() << "Unable to load source image " << filename2;
|
||||
|
||||
int cn = get<2>(GetParam());
|
||||
int nPointsX = std::min(get<0>(get<3>(GetParam())), img1.cols);
|
||||
int nPointsY = std::min(get<1>(get<3>(GetParam())), img1.rows);
|
||||
int winSize = get<4>(GetParam());
|
||||
|
||||
int maxLevel = 2;
|
||||
TermCriteria criteria(TermCriteria::COUNT|TermCriteria::EPS, 7, 0.001);
|
||||
int flags = 0;
|
||||
double minEigThreshold = 1e-4;
|
||||
|
||||
Mat frame1, frame2;
|
||||
switch(cn)
|
||||
{
|
||||
case 1:
|
||||
cvtColor(img1, frame1, COLOR_BGR2GRAY, cn);
|
||||
cvtColor(img2, frame2, COLOR_BGR2GRAY, cn);
|
||||
break;
|
||||
case 3:
|
||||
frame1 = img1;
|
||||
frame2 = img2;
|
||||
break;
|
||||
case 4:
|
||||
cvtColor(img1, frame1, COLOR_BGR2BGRA, cn);
|
||||
cvtColor(img2, frame2, COLOR_BGR2BGRA, cn);
|
||||
break;
|
||||
default:
|
||||
FAIL() << "Unexpected number of channels: " << cn;
|
||||
}
|
||||
|
||||
vector<Point2f> inPoints;
|
||||
vector<Point2f> outPoints;
|
||||
vector<uchar> status;
|
||||
vector<float> err;
|
||||
|
||||
FormTrackingPointsArray(inPoints, frame1.cols, frame1.rows, nPointsX, nPointsY);
|
||||
outPoints.resize(inPoints.size());
|
||||
status.resize(inPoints.size());
|
||||
err.resize(inPoints.size());
|
||||
|
||||
declare.in(frame1, frame2, inPoints).out(outPoints);
|
||||
|
||||
TEST_CYCLE_N(30)
|
||||
{
|
||||
calcOpticalFlowPyrLK(frame1, frame2, inPoints, outPoints, status, err,
|
||||
Size(winSize, winSize), maxLevel, criteria,
|
||||
flags, minEigThreshold);
|
||||
}
|
||||
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
typedef tuple<std::string, int, tuple<int, int>, int> Path_Idx_NPoints_WSize_t;
|
||||
typedef TestBaseWithParam<Path_Idx_NPoints_WSize_t> Path_Idx_NPoints_WSize;
|
||||
|
||||
PERF_TEST_P(Path_Idx_NPoints_WSize, DISABLED_OpticalFlowPyrLK_ovx, testing::Combine(
|
||||
testing::Values<std::string>("cv/optflow/frames/VGA_%02d.png", "cv/optflow/frames/720p_%02d.png"),
|
||||
testing::Range(1, 3),
|
||||
testing::Values(make_tuple(9, 9), make_tuple(15, 15)),
|
||||
testing::Values(7, 11)
|
||||
)
|
||||
)
|
||||
{
|
||||
string filename1 = getDataPath(cv::format(get<0>(GetParam()).c_str(), get<1>(GetParam())));
|
||||
string filename2 = getDataPath(cv::format(get<0>(GetParam()).c_str(), get<1>(GetParam()) + 1));
|
||||
Mat img1 = imread(filename1);
|
||||
Mat img2 = imread(filename2);
|
||||
if (img1.empty()) FAIL() << "Unable to load source image " << filename1;
|
||||
if (img2.empty()) FAIL() << "Unable to load source image " << filename2;
|
||||
|
||||
int nPointsX = std::min(get<0>(get<2>(GetParam())), img1.cols);
|
||||
int nPointsY = std::min(get<1>(get<2>(GetParam())), img1.rows);
|
||||
int winSize = get<3>(GetParam());
|
||||
|
||||
int maxLevel = 2;
|
||||
TermCriteria criteria(TermCriteria::COUNT|TermCriteria::EPS, 7, 0.001);
|
||||
int flags = 0;
|
||||
double minEigThreshold = 1e-4;
|
||||
|
||||
Mat frame1, frame2;
|
||||
cvtColor(img1, frame1, COLOR_BGR2GRAY, 1);
|
||||
cvtColor(img2, frame2, COLOR_BGR2GRAY, 1);
|
||||
|
||||
vector<Point2f> inPoints;
|
||||
vector<Point2f> outPoints;
|
||||
vector<uchar> status;
|
||||
|
||||
FormTrackingPointsArray(inPoints, frame1.cols, frame1.rows, nPointsX, nPointsY);
|
||||
outPoints.resize(inPoints.size());
|
||||
status.resize(inPoints.size());
|
||||
|
||||
declare.in(frame1, frame2, inPoints).out(outPoints);
|
||||
|
||||
TEST_CYCLE_N(30)
|
||||
{
|
||||
calcOpticalFlowPyrLK(frame1, frame2, inPoints, outPoints, status, cv::noArray(),
|
||||
Size(winSize, winSize), maxLevel, criteria,
|
||||
flags, minEigThreshold);
|
||||
}
|
||||
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
typedef tuple<std::string, int, int, tuple<int,int>, int, bool> Path_Idx_Cn_NPoints_WSize_Deriv_t;
|
||||
typedef TestBaseWithParam<Path_Idx_Cn_NPoints_WSize_Deriv_t> Path_Idx_Cn_NPoints_WSize_Deriv;
|
||||
|
||||
PERF_TEST_P(Path_Idx_Cn_NPoints_WSize_Deriv, OpticalFlowPyrLK_self, testing::Combine(
|
||||
testing::Values<std::string>("cv/optflow/frames/VGA_%02d.png", "cv/optflow/frames/720p_%02d.png"),
|
||||
testing::Range(1, 3),
|
||||
testing::Values(1, 3, 4),
|
||||
testing::Values(make_tuple(9, 9), make_tuple(15, 15)),
|
||||
testing::Values(7, 11),
|
||||
testing::Bool()
|
||||
)
|
||||
)
|
||||
{
|
||||
string filename1 = getDataPath(cv::format(get<0>(GetParam()).c_str(), get<1>(GetParam())));
|
||||
string filename2 = getDataPath(cv::format(get<0>(GetParam()).c_str(), get<1>(GetParam()) + 1));
|
||||
Mat img1 = imread(filename1);
|
||||
Mat img2 = imread(filename2);
|
||||
if (img1.empty()) FAIL() << "Unable to load source image " << filename1;
|
||||
if (img2.empty()) FAIL() << "Unable to load source image " << filename2;
|
||||
|
||||
int cn = get<2>(GetParam());
|
||||
int nPointsX = std::min(get<0>(get<3>(GetParam())), img1.cols);
|
||||
int nPointsY = std::min(get<1>(get<3>(GetParam())), img1.rows);
|
||||
int winSize = get<4>(GetParam());
|
||||
bool withDerivatives = get<5>(GetParam());
|
||||
|
||||
int maxLevel = 2;
|
||||
TermCriteria criteria(TermCriteria::COUNT|TermCriteria::EPS, 7, 0.001);
|
||||
int flags = 0;
|
||||
double minEigThreshold = 1e-4;
|
||||
|
||||
Mat frame1, frame2;
|
||||
switch(cn)
|
||||
{
|
||||
case 1:
|
||||
cvtColor(img1, frame1, COLOR_BGR2GRAY, cn);
|
||||
cvtColor(img2, frame2, COLOR_BGR2GRAY, cn);
|
||||
break;
|
||||
case 3:
|
||||
frame1 = img1;
|
||||
frame2 = img2;
|
||||
break;
|
||||
case 4:
|
||||
cvtColor(img1, frame1, COLOR_BGR2BGRA, cn);
|
||||
cvtColor(img2, frame2, COLOR_BGR2BGRA, cn);
|
||||
break;
|
||||
default:
|
||||
FAIL() << "Unexpected number of channels: " << cn;
|
||||
}
|
||||
|
||||
vector<Point2f> inPoints;
|
||||
vector<Point2f> outPoints;
|
||||
vector<uchar> status;
|
||||
vector<float> err;
|
||||
|
||||
FormTrackingPointsArray(inPoints, frame1.cols, frame1.rows, nPointsX, nPointsY);
|
||||
outPoints.resize(inPoints.size());
|
||||
status.resize(inPoints.size());
|
||||
err.resize(inPoints.size());
|
||||
|
||||
std::vector<Mat> pyramid1, pyramid2;
|
||||
|
||||
maxLevel = buildOpticalFlowPyramid(frame1, pyramid1, Size(winSize, winSize), maxLevel, withDerivatives);
|
||||
maxLevel = buildOpticalFlowPyramid(frame2, pyramid2, Size(winSize, winSize), maxLevel, withDerivatives);
|
||||
|
||||
declare.in(pyramid1, pyramid2, inPoints).out(outPoints);
|
||||
declare.time(400);
|
||||
|
||||
int runs = 3;
|
||||
TEST_CYCLE_MULTIRUN(runs)
|
||||
{
|
||||
calcOpticalFlowPyrLK(pyramid1, pyramid2, inPoints, outPoints, status, err,
|
||||
Size(winSize, winSize), maxLevel, criteria,
|
||||
flags, minEigThreshold);
|
||||
}
|
||||
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
CV_ENUM(PyrBorderMode, BORDER_DEFAULT, BORDER_TRANSPARENT)
|
||||
typedef tuple<std::string, int, bool, PyrBorderMode, bool> Path_Win_Deriv_Border_Reuse_t;
|
||||
typedef TestBaseWithParam<Path_Win_Deriv_Border_Reuse_t> Path_Win_Deriv_Border_Reuse;
|
||||
|
||||
PERF_TEST_P(Path_Win_Deriv_Border_Reuse, OpticalFlowPyrLK_pyr, testing::Combine(
|
||||
testing::Values<std::string>("cv/optflow/frames/720p_01.png"),
|
||||
testing::Values(7, 11),
|
||||
testing::Bool(),
|
||||
PyrBorderMode::all(),
|
||||
testing::Bool()
|
||||
)
|
||||
)
|
||||
{
|
||||
string filename = getDataPath(get<0>(GetParam()));
|
||||
Mat img = imread(filename);
|
||||
Size winSize(get<1>(GetParam()), get<1>(GetParam()));
|
||||
bool withDerivatives = get<2>(GetParam());
|
||||
int derivBorder = get<3>(GetParam());
|
||||
int pyrBorder = derivBorder;
|
||||
if(derivBorder != BORDER_TRANSPARENT)
|
||||
{
|
||||
derivBorder = BORDER_CONSTANT;
|
||||
pyrBorder = BORDER_REFLECT_101;
|
||||
}
|
||||
bool tryReuseInputImage = get<4>(GetParam());
|
||||
std::vector<Mat> pyramid;
|
||||
|
||||
img.adjustROI(winSize.height, winSize.height, winSize.width, winSize.width);
|
||||
|
||||
int maxLevel = buildOpticalFlowPyramid(img, pyramid, winSize, 1000, withDerivatives, BORDER_CONSTANT, BORDER_CONSTANT, tryReuseInputImage);
|
||||
|
||||
declare.in(img).out(pyramid);
|
||||
|
||||
|
||||
TEST_CYCLE()
|
||||
{
|
||||
buildOpticalFlowPyramid(img, pyramid, winSize, maxLevel, withDerivatives, pyrBorder, derivBorder, tryReuseInputImage);
|
||||
}
|
||||
|
||||
size_t expected_layers = ((size_t)maxLevel + 1) * (withDerivatives ? 2 : 1);
|
||||
ASSERT_EQ(expected_layers, pyramid.size());
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,17 @@
|
||||
// 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_VIDEO_PRECOMP_HPP__
|
||||
#define __OPENCV_VIDEO_PRECOMP_HPP__
|
||||
|
||||
#include "opencv2/ts.hpp"
|
||||
#include <opencv2/video.hpp>
|
||||
#include "opencv2/ts/ts_perf.hpp"
|
||||
#include "opencv2/core/utils/configuration.private.hpp"
|
||||
|
||||
namespace cvtest
|
||||
{
|
||||
using namespace perf;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,93 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
using namespace perf;
|
||||
|
||||
typedef tuple<string, int, Rect> TrackingParams_t;
|
||||
|
||||
std::vector<TrackingParams_t> getTrackingParams()
|
||||
{
|
||||
std::vector<TrackingParams_t> params {
|
||||
TrackingParams_t("david/data/david.webm", 300, Rect(163,62,47,56)),
|
||||
TrackingParams_t("dudek/data/dudek.webm", 1, Rect(123,87,132,176)),
|
||||
TrackingParams_t("faceocc2/data/faceocc2.webm", 1, Rect(118,57,82,98))
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
class Tracking : public perf::TestBaseWithParam<TrackingParams_t>
|
||||
{
|
||||
public:
|
||||
template<typename ROI_t = Rect2d, typename Tracker>
|
||||
void runTrackingTest(const Ptr<Tracker>& tracker, const TrackingParams_t& params);
|
||||
};
|
||||
|
||||
template<typename ROI_t, typename Tracker>
|
||||
void Tracking::runTrackingTest(const Ptr<Tracker>& tracker, const TrackingParams_t& params)
|
||||
{
|
||||
const int N = 10;
|
||||
string video = get<0>(params);
|
||||
int startFrame = get<1>(params);
|
||||
//int endFrame = startFrame + N;
|
||||
Rect boundingBox = get<2>(params);
|
||||
|
||||
string videoPath = findDataFile(std::string("cv/tracking/") + video);
|
||||
|
||||
VideoCapture c;
|
||||
c.open(videoPath);
|
||||
if (!c.isOpened())
|
||||
throw SkipTestException("Can't open video file");
|
||||
#if 0
|
||||
// c.set(CAP_PROP_POS_FRAMES, startFrame);
|
||||
#else
|
||||
if (startFrame)
|
||||
std::cout << "startFrame = " << startFrame << std::endl;
|
||||
for (int i = 0; i < startFrame; i++)
|
||||
{
|
||||
Mat dummy_frame;
|
||||
c >> dummy_frame;
|
||||
ASSERT_FALSE(dummy_frame.empty()) << i << ": " << videoPath;
|
||||
}
|
||||
#endif
|
||||
|
||||
// decode frames into memory (don't measure decoding performance)
|
||||
std::vector<Mat> frames;
|
||||
for (int i = 0; i < N; ++i)
|
||||
{
|
||||
Mat frame;
|
||||
c >> frame;
|
||||
ASSERT_FALSE(frame.empty()) << "i=" << i;
|
||||
frames.push_back(frame);
|
||||
}
|
||||
|
||||
std::cout << "frame size = " << frames[0].size() << std::endl;
|
||||
|
||||
PERF_SAMPLE_BEGIN();
|
||||
{
|
||||
tracker->init(frames[0], (ROI_t)boundingBox);
|
||||
for (int i = 1; i < N; ++i)
|
||||
{
|
||||
ROI_t rc;
|
||||
tracker->update(frames[i], rc);
|
||||
ASSERT_FALSE(rc.empty());
|
||||
}
|
||||
}
|
||||
PERF_SAMPLE_END();
|
||||
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
|
||||
//==================================================================================================
|
||||
|
||||
PERF_TEST_P(Tracking, MIL, testing::ValuesIn(getTrackingParams()))
|
||||
{
|
||||
auto tracker = TrackerMIL::create();
|
||||
runTrackingTest<Rect>(tracker, GetParam());
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,40 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
typedef tuple<Size, int, int> VarRefParams;
|
||||
typedef TestBaseWithParam<VarRefParams> DenseOpticalFlow_VariationalRefinement;
|
||||
|
||||
PERF_TEST_P(DenseOpticalFlow_VariationalRefinement, perf, Combine(Values(szQVGA, szVGA), Values(5, 10), Values(5, 10)))
|
||||
{
|
||||
VarRefParams params = GetParam();
|
||||
Size sz = get<0>(params);
|
||||
int sorIter = get<1>(params);
|
||||
int fixedPointIter = get<2>(params);
|
||||
|
||||
Mat frame1(sz, CV_8U);
|
||||
Mat frame2(sz, CV_8U);
|
||||
Mat flow(sz, CV_32FC2);
|
||||
|
||||
randu(frame1, 0, 255);
|
||||
randu(frame2, 0, 255);
|
||||
flow.setTo(0.0f);
|
||||
|
||||
TEST_CYCLE_N(10)
|
||||
{
|
||||
Ptr<VariationalRefinement> var = VariationalRefinement::create();
|
||||
var->setAlpha(20.0f);
|
||||
var->setGamma(10.0f);
|
||||
var->setDelta(5.0f);
|
||||
var->setSorIterations(sorIter);
|
||||
var->setFixedPointIterations(fixedPointIter);
|
||||
var->calc(frame1, frame2, flow);
|
||||
}
|
||||
|
||||
SANITY_CHECK_NOTHING();
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,913 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
//#include <math.h>
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "opencl_kernels_video.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
/*!
|
||||
The class implements the following algorithm:
|
||||
"Efficient Adaptive Density Estimation per Image Pixel for the Task of Background Subtraction"
|
||||
Z.Zivkovic, F. van der Heijden
|
||||
Pattern Recognition Letters, vol. 27, no. 7, pages 773-780, 2006
|
||||
http://www.zoranz.net/Publications/zivkovicPRL2006.pdf
|
||||
*/
|
||||
|
||||
// default parameters of gaussian background detection algorithm
|
||||
static const int defaultHistory2 = 500; // Learning rate; alpha = 1/defaultHistory2
|
||||
static const int defaultNsamples = 7; // number of samples saved in memory
|
||||
static const float defaultDist2Threshold = 20.0f*20.0f;//threshold on distance from the sample
|
||||
|
||||
// additional parameters
|
||||
static const unsigned char defaultnShadowDetection2 = (unsigned char)127; // value to use in the segmentation mask for shadows, set 0 not to do shadow detection
|
||||
static const float defaultfTau = 0.5f; // Tau - shadow threshold, see the paper for explanation
|
||||
|
||||
class BackgroundSubtractorKNNImpl CV_FINAL : public BackgroundSubtractorKNN
|
||||
{
|
||||
public:
|
||||
//! the default constructor
|
||||
BackgroundSubtractorKNNImpl()
|
||||
{
|
||||
frameSize = Size(0,0);
|
||||
frameType = 0;
|
||||
nframes = 0;
|
||||
history = defaultHistory2;
|
||||
|
||||
//set parameters
|
||||
// N - the number of samples stored in memory per model
|
||||
nN = defaultNsamples;
|
||||
|
||||
//kNN - k nearest neighbour - number on NN for detecting background - default K=[0.1*nN]
|
||||
nkNN=MAX(1,cvRound(0.1*nN*3+0.40));
|
||||
|
||||
//Tb - Threshold Tb*kernelwidth
|
||||
fTb = defaultDist2Threshold;
|
||||
|
||||
// Shadow detection
|
||||
bShadowDetection = 1;//turn on
|
||||
nShadowDetection = defaultnShadowDetection2;
|
||||
fTau = defaultfTau;// Tau - shadow threshold
|
||||
name_ = "BackgroundSubtractor.KNN";
|
||||
nLongCounter = 0;
|
||||
nMidCounter = 0;
|
||||
nShortCounter = 0;
|
||||
#ifdef HAVE_OPENCL
|
||||
opencl_ON = true;
|
||||
#endif
|
||||
}
|
||||
//! the full constructor that takes the length of the history,
|
||||
// the number of gaussian mixtures, the background ratio parameter and the noise strength
|
||||
BackgroundSubtractorKNNImpl(int _history, float _dist2Threshold, bool _bShadowDetection=true)
|
||||
{
|
||||
frameSize = Size(0,0);
|
||||
frameType = 0;
|
||||
|
||||
nframes = 0;
|
||||
history = _history > 0 ? _history : defaultHistory2;
|
||||
|
||||
//set parameters
|
||||
// N - the number of samples stored in memory per model
|
||||
nN = defaultNsamples;
|
||||
//kNN - k nearest neighbour - number on NN for detecting background - default K=[0.1*nN]
|
||||
nkNN=MAX(1,cvRound(0.1*nN*3+0.40));
|
||||
|
||||
//Tb - Threshold Tb*kernelwidth
|
||||
fTb = _dist2Threshold>0? _dist2Threshold : defaultDist2Threshold;
|
||||
|
||||
bShadowDetection = _bShadowDetection;
|
||||
nShadowDetection = defaultnShadowDetection2;
|
||||
fTau = defaultfTau;
|
||||
name_ = "BackgroundSubtractor.KNN";
|
||||
nLongCounter = 0;
|
||||
nMidCounter = 0;
|
||||
nShortCounter = 0;
|
||||
#ifdef HAVE_OPENCL
|
||||
opencl_ON = true;
|
||||
#endif
|
||||
}
|
||||
//! the destructor
|
||||
~BackgroundSubtractorKNNImpl() CV_OVERRIDE {}
|
||||
//! the update operator
|
||||
void apply(InputArray image, OutputArray fgmask, double learningRate) CV_OVERRIDE;
|
||||
|
||||
void apply(InputArray image, InputArray knownForegroundMask, OutputArray fgmask, double learningRate) CV_OVERRIDE;
|
||||
|
||||
//! computes a background image which are the mean of all background gaussians
|
||||
virtual void getBackgroundImage(OutputArray backgroundImage) const CV_OVERRIDE;
|
||||
|
||||
//! re-initialization method
|
||||
void initialize(Size _frameSize, int _frameType)
|
||||
{
|
||||
frameSize = _frameSize;
|
||||
frameType = _frameType;
|
||||
nframes = 0;
|
||||
|
||||
int nchannels = CV_MAT_CN(frameType);
|
||||
CV_Assert( nchannels <= CV_CN_MAX );
|
||||
|
||||
// Reserve memory for the model
|
||||
int size=frameSize.height*frameSize.width;
|
||||
//Reset counters
|
||||
nShortCounter = 0;
|
||||
nMidCounter = 0;
|
||||
nLongCounter = 0;
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
if (ocl::isOpenCLActivated() && opencl_ON)
|
||||
{
|
||||
create_ocl_apply_kernel();
|
||||
|
||||
kernel_getBg.create("getBackgroundImage2_kernel", ocl::video::bgfg_knn_oclsrc, format( "-D CN=%d -D NSAMPLES=%d", nchannels, nN));
|
||||
|
||||
if (kernel_apply.empty() || kernel_getBg.empty())
|
||||
opencl_ON = false;
|
||||
}
|
||||
else opencl_ON = false;
|
||||
|
||||
if (opencl_ON)
|
||||
{
|
||||
u_flag.create(frameSize.height * nN * 3, frameSize.width, CV_8UC1);
|
||||
u_flag.setTo(Scalar::all(0));
|
||||
|
||||
if (nchannels==3)
|
||||
nchannels=4;
|
||||
u_sample.create(frameSize.height * nN * 3, frameSize.width, CV_32FC(nchannels));
|
||||
u_sample.setTo(Scalar::all(0));
|
||||
|
||||
u_aModelIndexShort.create(frameSize.height, frameSize.width, CV_8UC1);
|
||||
u_aModelIndexShort.setTo(Scalar::all(0));
|
||||
u_aModelIndexMid.create(frameSize.height, frameSize.width, CV_8UC1);
|
||||
u_aModelIndexMid.setTo(Scalar::all(0));
|
||||
u_aModelIndexLong.create(frameSize.height, frameSize.width, CV_8UC1);
|
||||
u_aModelIndexLong.setTo(Scalar::all(0));
|
||||
|
||||
u_nNextShortUpdate.create(frameSize.height, frameSize.width, CV_8UC1);
|
||||
u_nNextShortUpdate.setTo(Scalar::all(0));
|
||||
u_nNextMidUpdate.create(frameSize.height, frameSize.width, CV_8UC1);
|
||||
u_nNextMidUpdate.setTo(Scalar::all(0));
|
||||
u_nNextLongUpdate.create(frameSize.height, frameSize.width, CV_8UC1);
|
||||
u_nNextLongUpdate.setTo(Scalar::all(0));
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
// for each sample of 3 speed pixel models each pixel bg model we store ...
|
||||
// values + flag (nchannels+1 values)
|
||||
bgmodel.create( 1,(nN * 3) * (nchannels+1)* size,CV_8U);
|
||||
bgmodel = Scalar::all(0);
|
||||
|
||||
//index through the three circular lists
|
||||
aModelIndexShort.create(1,size,CV_8U);
|
||||
aModelIndexMid.create(1,size,CV_8U);
|
||||
aModelIndexLong.create(1,size,CV_8U);
|
||||
//when to update next
|
||||
nNextShortUpdate.create(1,size,CV_8U);
|
||||
nNextMidUpdate.create(1,size,CV_8U);
|
||||
nNextLongUpdate.create(1,size,CV_8U);
|
||||
|
||||
aModelIndexShort = Scalar::all(0);//random? //((m_nN)*rand())/(RAND_MAX+1);//0...m_nN-1
|
||||
aModelIndexMid = Scalar::all(0);
|
||||
aModelIndexLong = Scalar::all(0);
|
||||
nNextShortUpdate = Scalar::all(0);
|
||||
nNextMidUpdate = Scalar::all(0);
|
||||
nNextLongUpdate = Scalar::all(0);
|
||||
}
|
||||
}
|
||||
|
||||
virtual String getDefaultName() const CV_OVERRIDE { return "BackgroundSubtractor_KNN"; }
|
||||
|
||||
virtual int getHistory() const CV_OVERRIDE { return history; }
|
||||
virtual void setHistory(int _nframes) CV_OVERRIDE { history = _nframes; }
|
||||
|
||||
virtual int getNSamples() const CV_OVERRIDE { return nN; }
|
||||
virtual void setNSamples(int _nN) CV_OVERRIDE { nN = _nN; }//needs reinitialization!
|
||||
|
||||
virtual int getkNNSamples() const CV_OVERRIDE { return nkNN; }
|
||||
virtual void setkNNSamples(int _nkNN) CV_OVERRIDE { nkNN = _nkNN; }
|
||||
|
||||
virtual double getDist2Threshold() const CV_OVERRIDE { return fTb; }
|
||||
virtual void setDist2Threshold(double _dist2Threshold) CV_OVERRIDE { fTb = (float)_dist2Threshold; }
|
||||
|
||||
virtual bool getDetectShadows() const CV_OVERRIDE { return bShadowDetection; }
|
||||
virtual void setDetectShadows(bool detectshadows) CV_OVERRIDE
|
||||
{
|
||||
if (bShadowDetection == detectshadows)
|
||||
return;
|
||||
bShadowDetection = detectshadows;
|
||||
#ifdef HAVE_OPENCL
|
||||
if (!kernel_apply.empty())
|
||||
{
|
||||
create_ocl_apply_kernel();
|
||||
CV_Assert( !kernel_apply.empty() );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual int getShadowValue() const CV_OVERRIDE { return nShadowDetection; }
|
||||
virtual void setShadowValue(int value) CV_OVERRIDE { nShadowDetection = (uchar)value; }
|
||||
|
||||
virtual double getShadowThreshold() const CV_OVERRIDE { return fTau; }
|
||||
virtual void setShadowThreshold(double value) CV_OVERRIDE { fTau = (float)value; }
|
||||
|
||||
virtual void write(FileStorage& fs) const CV_OVERRIDE
|
||||
{
|
||||
writeFormat(fs);
|
||||
fs << "name" << name_
|
||||
<< "history" << history
|
||||
<< "nsamples" << nN
|
||||
<< "nKNN" << nkNN
|
||||
<< "dist2Threshold" << fTb
|
||||
<< "detectShadows" << (int)bShadowDetection
|
||||
<< "shadowValue" << (int)nShadowDetection
|
||||
<< "shadowThreshold" << fTau;
|
||||
}
|
||||
|
||||
virtual void read(const FileNode& fn) CV_OVERRIDE
|
||||
{
|
||||
CV_Assert( (String)fn["name"] == name_ );
|
||||
history = (int)fn["history"];
|
||||
nN = (int)fn["nsamples"];
|
||||
nkNN = (int)fn["nKNN"];
|
||||
fTb = (float)fn["dist2Threshold"];
|
||||
bShadowDetection = (int)fn["detectShadows"] != 0;
|
||||
nShadowDetection = saturate_cast<uchar>((int)fn["shadowValue"]);
|
||||
fTau = (float)fn["shadowThreshold"];
|
||||
}
|
||||
|
||||
protected:
|
||||
Size frameSize;
|
||||
int frameType;
|
||||
int nframes;
|
||||
/////////////////////////
|
||||
//very important parameters - things you will change
|
||||
////////////////////////
|
||||
int history;
|
||||
//alpha=1/history - speed of update - if the time interval you want to average over is T
|
||||
//set alpha=1/history. It is also useful at start to make T slowly increase
|
||||
//from 1 until the desired T
|
||||
float fTb;
|
||||
//Tb - threshold on the squared distance from the sample used to decide if it is well described
|
||||
//by the background model or not. A typical value could be 2 sigma
|
||||
//and that is Tb=2*2*10*10 =400; where we take typical pixel level sigma=10
|
||||
|
||||
/////////////////////////
|
||||
//less important parameters - things you might change but be careful
|
||||
////////////////////////
|
||||
int nN;//totlal number of samples
|
||||
int nkNN;//number on NN for detecting background - default K=[0.1*nN]
|
||||
|
||||
//shadow detection parameters
|
||||
bool bShadowDetection;//default 1 - do shadow detection
|
||||
unsigned char nShadowDetection;//do shadow detection - insert this value as the detection result - 127 default value
|
||||
float fTau;
|
||||
// Tau - shadow threshold. The shadow is detected if the pixel is darker
|
||||
//version of the background. Tau is a threshold on how much darker the shadow can be.
|
||||
//Tau= 0.5 means that if pixel is more than 2 times darker then it is not shadow
|
||||
//See: Prati,Mikic,Trivedi,Cucchiara,"Detecting Moving Shadows...",IEEE PAMI,2003.
|
||||
|
||||
//model data
|
||||
int nLongCounter;//circular counter
|
||||
int nMidCounter;
|
||||
int nShortCounter;
|
||||
Mat bgmodel; // model data pixel values
|
||||
Mat aModelIndexShort;// index into the models
|
||||
Mat aModelIndexMid;
|
||||
Mat aModelIndexLong;
|
||||
Mat nNextShortUpdate;//random update points per model
|
||||
Mat nNextMidUpdate;
|
||||
Mat nNextLongUpdate;
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
mutable bool opencl_ON;
|
||||
|
||||
UMat u_flag;
|
||||
UMat u_sample;
|
||||
UMat u_aModelIndexShort;
|
||||
UMat u_aModelIndexMid;
|
||||
UMat u_aModelIndexLong;
|
||||
UMat u_nNextShortUpdate;
|
||||
UMat u_nNextMidUpdate;
|
||||
UMat u_nNextLongUpdate;
|
||||
|
||||
mutable ocl::Kernel kernel_apply;
|
||||
mutable ocl::Kernel kernel_getBg;
|
||||
#endif
|
||||
|
||||
String name_;
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
bool ocl_getBackgroundImage(OutputArray backgroundImage) const;
|
||||
bool ocl_apply(InputArray _image, OutputArray _fgmask, double learningRate=-1);
|
||||
void create_ocl_apply_kernel();
|
||||
#endif
|
||||
};
|
||||
|
||||
CV_INLINE void
|
||||
_cvUpdatePixelBackgroundNP(int x_idx, const uchar* data, int nchannels, int m_nN,
|
||||
uchar* m_aModel,
|
||||
uchar* m_nNextLongUpdate,
|
||||
uchar* m_nNextMidUpdate,
|
||||
uchar* m_nNextShortUpdate,
|
||||
uchar* m_aModelIndexLong,
|
||||
uchar* m_aModelIndexMid,
|
||||
uchar* m_aModelIndexShort,
|
||||
int m_nLongCounter,
|
||||
int m_nMidCounter,
|
||||
int m_nShortCounter,
|
||||
uchar include
|
||||
)
|
||||
{
|
||||
// hold the offset
|
||||
int ndata=1+nchannels;
|
||||
long offsetLong = ndata * (m_aModelIndexLong[x_idx] + m_nN * 2);
|
||||
long offsetMid = ndata * (m_aModelIndexMid[x_idx] + m_nN * 1);
|
||||
long offsetShort = ndata * (m_aModelIndexShort[x_idx]);
|
||||
|
||||
// Long update?
|
||||
if (m_nNextLongUpdate[x_idx] == m_nLongCounter)
|
||||
{
|
||||
// add the oldest pixel from Mid to the list of values (for each color)
|
||||
memcpy(&m_aModel[offsetLong],&m_aModel[offsetMid],ndata*sizeof(unsigned char));
|
||||
// increase the index
|
||||
m_aModelIndexLong[x_idx] = (m_aModelIndexLong[x_idx] >= (m_nN-1)) ? 0 : (m_aModelIndexLong[x_idx] + 1);
|
||||
};
|
||||
|
||||
// Mid update?
|
||||
if (m_nNextMidUpdate[x_idx] == m_nMidCounter)
|
||||
{
|
||||
// add this pixel to the list of values (for each color)
|
||||
memcpy(&m_aModel[offsetMid],&m_aModel[offsetShort],ndata*sizeof(unsigned char));
|
||||
// increase the index
|
||||
m_aModelIndexMid[x_idx] = (m_aModelIndexMid[x_idx] >= (m_nN-1)) ? 0 : (m_aModelIndexMid[x_idx] + 1);
|
||||
};
|
||||
|
||||
// Short update?
|
||||
if (m_nNextShortUpdate[x_idx] == m_nShortCounter)
|
||||
{
|
||||
// add this pixel to the list of values (for each color)
|
||||
memcpy(&m_aModel[offsetShort],data,nchannels*sizeof(unsigned char));
|
||||
//set the include flag
|
||||
m_aModel[offsetShort+nchannels]=include;
|
||||
// increase the index
|
||||
m_aModelIndexShort[x_idx] = (m_aModelIndexShort[x_idx] >= (m_nN-1)) ? 0 : (m_aModelIndexShort[x_idx] + 1);
|
||||
};
|
||||
}
|
||||
|
||||
CV_INLINE int
|
||||
_cvCheckPixelBackgroundNP(const uchar* data, int nchannels,
|
||||
int m_nN,
|
||||
uchar* m_aModel,
|
||||
float m_fTb,
|
||||
int m_nkNN,
|
||||
float tau,
|
||||
bool m_bShadowDetection,
|
||||
uchar& include)
|
||||
{
|
||||
int Pbf = 0; // the total probability that this pixel is background
|
||||
int Pb = 0; //background model probability
|
||||
float dData[CV_CN_MAX];
|
||||
|
||||
//uchar& include=data[nchannels];
|
||||
include=0;//do we include this pixel into background model?
|
||||
|
||||
int ndata=nchannels+1;
|
||||
// now increase the probability for each pixel
|
||||
for (int n = 0; n < m_nN*3; n++)
|
||||
{
|
||||
uchar* mean_m = &m_aModel[n*ndata];
|
||||
|
||||
//calculate difference and distance
|
||||
float dist2;
|
||||
|
||||
if( nchannels == 3 )
|
||||
{
|
||||
dData[0] = (float)mean_m[0] - data[0];
|
||||
dData[1] = (float)mean_m[1] - data[1];
|
||||
dData[2] = (float)mean_m[2] - data[2];
|
||||
dist2 = dData[0]*dData[0] + dData[1]*dData[1] + dData[2]*dData[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
dist2 = 0.f;
|
||||
for( int c = 0; c < nchannels; c++ )
|
||||
{
|
||||
dData[c] = (float)mean_m[c] - data[c];
|
||||
dist2 += dData[c]*dData[c];
|
||||
}
|
||||
}
|
||||
|
||||
if (dist2<m_fTb)
|
||||
{
|
||||
Pbf++;//all
|
||||
//background only
|
||||
//if(m_aModel[subPosPixel + nchannels])//indicator
|
||||
if(mean_m[nchannels])//indicator
|
||||
{
|
||||
Pb++;
|
||||
if (Pb >= m_nkNN)//Tb
|
||||
{
|
||||
include=1;//include
|
||||
return 1;//background ->exit
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
//include?
|
||||
if (Pbf>=m_nkNN)//m_nTbf)
|
||||
{
|
||||
include=1;
|
||||
}
|
||||
|
||||
int Ps = 0; // the total probability that this pixel is background shadow
|
||||
// Detected as moving object, perform shadow detection
|
||||
if (m_bShadowDetection)
|
||||
{
|
||||
for (int n = 0; n < m_nN*3; n++)
|
||||
{
|
||||
//long subPosPixel = posPixel + n*ndata;
|
||||
uchar* mean_m = &m_aModel[n*ndata];
|
||||
|
||||
if(mean_m[nchannels])//check only background
|
||||
{
|
||||
float numerator = 0.0f;
|
||||
float denominator = 0.0f;
|
||||
for( int c = 0; c < nchannels; c++ )
|
||||
{
|
||||
numerator += (float)data[c] * mean_m[c];
|
||||
denominator += (float)mean_m[c] * mean_m[c];
|
||||
}
|
||||
|
||||
// no division by zero allowed
|
||||
if( denominator == 0 )
|
||||
return 0;
|
||||
|
||||
// if tau < a < 1 then also check the color distortion
|
||||
if( numerator <= denominator && numerator >= tau*denominator )
|
||||
{
|
||||
float a = numerator / denominator;
|
||||
float dist2a = 0.0f;
|
||||
|
||||
for( int c = 0; c < nchannels; c++ )
|
||||
{
|
||||
float dD= a*mean_m[c] - data[c];
|
||||
dist2a += dD*dD;
|
||||
}
|
||||
|
||||
if (dist2a<m_fTb*a*a)
|
||||
{
|
||||
Ps++;
|
||||
if (Ps >= m_nkNN)//shadow
|
||||
return 2;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
class KNNInvoker : public ParallelLoopBody
|
||||
{
|
||||
public:
|
||||
KNNInvoker(const Mat& _src, Mat& _dst,
|
||||
uchar* _bgmodel,
|
||||
uchar* _nNextLongUpdate,
|
||||
uchar* _nNextMidUpdate,
|
||||
uchar* _nNextShortUpdate,
|
||||
uchar* _aModelIndexLong,
|
||||
uchar* _aModelIndexMid,
|
||||
uchar* _aModelIndexShort,
|
||||
int _nLongCounter,
|
||||
int _nMidCounter,
|
||||
int _nShortCounter,
|
||||
int _nN,
|
||||
float _fTb,
|
||||
int _nkNN,
|
||||
float _fTau,
|
||||
bool _bShadowDetection,
|
||||
uchar _nShadowDetection,
|
||||
const Mat& _knownForegroundMask)
|
||||
: knownForegroundMask(_knownForegroundMask)
|
||||
{
|
||||
src = &_src;
|
||||
dst = &_dst;
|
||||
m_aModel0 = _bgmodel;
|
||||
m_nNextLongUpdate0 = _nNextLongUpdate;
|
||||
m_nNextMidUpdate0 = _nNextMidUpdate;
|
||||
m_nNextShortUpdate0 = _nNextShortUpdate;
|
||||
m_aModelIndexLong0 = _aModelIndexLong;
|
||||
m_aModelIndexMid0 = _aModelIndexMid;
|
||||
m_aModelIndexShort0 = _aModelIndexShort;
|
||||
m_nLongCounter = _nLongCounter;
|
||||
m_nMidCounter = _nMidCounter;
|
||||
m_nShortCounter = _nShortCounter;
|
||||
m_nN = _nN;
|
||||
m_fTb = _fTb;
|
||||
m_fTau = _fTau;
|
||||
m_nkNN = _nkNN;
|
||||
m_bShadowDetection = _bShadowDetection;
|
||||
m_nShadowDetection = _nShadowDetection;
|
||||
}
|
||||
|
||||
void operator()(const Range& range) const CV_OVERRIDE
|
||||
{
|
||||
int y0 = range.start, y1 = range.end;
|
||||
int ncols = src->cols, nchannels = src->channels();
|
||||
int ndata=nchannels+1;
|
||||
|
||||
for ( int y = y0; y < y1; y++ )
|
||||
{
|
||||
const uchar* data = src->ptr(y);
|
||||
uchar* m_aModel = m_aModel0 + ncols*m_nN*3*ndata*y;
|
||||
uchar* m_nNextLongUpdate = m_nNextLongUpdate0 + ncols*y;
|
||||
uchar* m_nNextMidUpdate = m_nNextMidUpdate0 + ncols*y;
|
||||
uchar* m_nNextShortUpdate = m_nNextShortUpdate0 + ncols*y;
|
||||
uchar* m_aModelIndexLong = m_aModelIndexLong0 + ncols*y;
|
||||
uchar* m_aModelIndexMid = m_aModelIndexMid0 + ncols*y;
|
||||
uchar* m_aModelIndexShort = m_aModelIndexShort0 + ncols*y;
|
||||
uchar* mask = dst->ptr(y);
|
||||
|
||||
for ( int x = 0; x < ncols; x++ )
|
||||
{
|
||||
|
||||
//update model+ background subtract
|
||||
uchar include=0;
|
||||
int result= _cvCheckPixelBackgroundNP(data, nchannels,
|
||||
m_nN, m_aModel, m_fTb,m_nkNN, m_fTau,m_bShadowDetection,include);
|
||||
|
||||
_cvUpdatePixelBackgroundNP(x,data,nchannels,
|
||||
m_nN, m_aModel,
|
||||
m_nNextLongUpdate,
|
||||
m_nNextMidUpdate,
|
||||
m_nNextShortUpdate,
|
||||
m_aModelIndexLong,
|
||||
m_aModelIndexMid,
|
||||
m_aModelIndexShort,
|
||||
m_nLongCounter,
|
||||
m_nMidCounter,
|
||||
m_nShortCounter,
|
||||
include
|
||||
);
|
||||
// Check that foreground mask exists
|
||||
if (!knownForegroundMask.empty()) {
|
||||
// If input mask states pixel is foreground
|
||||
if (knownForegroundMask.at<uchar>(y, x) > 0)
|
||||
{
|
||||
mask[x] = 255; // ensure output mask marks this pixel as FG
|
||||
data += nchannels;
|
||||
m_aModel += m_nN*3*ndata;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
switch (result)
|
||||
{
|
||||
case 0:
|
||||
//foreground
|
||||
mask[x] = 255;
|
||||
break;
|
||||
case 1:
|
||||
//background
|
||||
mask[x] = 0;
|
||||
break;
|
||||
case 2:
|
||||
//shadow
|
||||
mask[x] = m_nShadowDetection;
|
||||
break;
|
||||
}
|
||||
data += nchannels;
|
||||
m_aModel += m_nN*3*ndata;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Mat* src;
|
||||
Mat* dst;
|
||||
uchar* m_aModel0;
|
||||
uchar* m_nNextLongUpdate0;
|
||||
uchar* m_nNextMidUpdate0;
|
||||
uchar* m_nNextShortUpdate0;
|
||||
uchar* m_aModelIndexLong0;
|
||||
uchar* m_aModelIndexMid0;
|
||||
uchar* m_aModelIndexShort0;
|
||||
int m_nLongCounter;
|
||||
int m_nMidCounter;
|
||||
int m_nShortCounter;
|
||||
int m_nN;
|
||||
float m_fTb;
|
||||
float m_fTau;
|
||||
int m_nkNN;
|
||||
bool m_bShadowDetection;
|
||||
uchar m_nShadowDetection;
|
||||
const Mat& knownForegroundMask;
|
||||
};
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
bool BackgroundSubtractorKNNImpl::ocl_apply(InputArray _image, OutputArray _fgmask, double learningRate)
|
||||
{
|
||||
bool needToInitialize = nframes == 0 || learningRate >= 1 || _image.size() != frameSize || _image.type() != frameType;
|
||||
|
||||
if( needToInitialize )
|
||||
initialize(_image.size(), _image.type());
|
||||
|
||||
++nframes;
|
||||
learningRate = learningRate >= 0 && nframes > 1 ? learningRate : 1./std::min( 2*nframes, history );
|
||||
CV_Assert(learningRate >= 0);
|
||||
|
||||
_fgmask.create(_image.size(), CV_8U);
|
||||
UMat fgmask = _fgmask.getUMat();
|
||||
|
||||
UMat frame = _image.getUMat();
|
||||
|
||||
//recalculate update rates - in case alpha is changed
|
||||
// calculate update parameters (using alpha)
|
||||
int Kshort,Kmid,Klong;
|
||||
//approximate exponential learning curve
|
||||
Kshort=(int)(log(0.7)/log(1-learningRate))+1;//Kshort
|
||||
Kmid=(int)(log(0.4)/log(1-learningRate))-Kshort+1;//Kmid
|
||||
Klong=(int)(log(0.1)/log(1-learningRate))-Kshort-Kmid+1;//Klong
|
||||
|
||||
//refresh rates
|
||||
int nShortUpdate = (Kshort/nN)+1;
|
||||
int nMidUpdate = (Kmid/nN)+1;
|
||||
int nLongUpdate = (Klong/nN)+1;
|
||||
|
||||
int idxArg = 0;
|
||||
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::ReadOnly(frame));
|
||||
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadOnly(u_nNextLongUpdate));
|
||||
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadOnly(u_nNextMidUpdate));
|
||||
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadOnly(u_nNextShortUpdate));
|
||||
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_aModelIndexLong));
|
||||
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_aModelIndexMid));
|
||||
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_aModelIndexShort));
|
||||
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_flag));
|
||||
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_sample));
|
||||
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::WriteOnlyNoSize(fgmask));
|
||||
|
||||
idxArg = kernel_apply.set(idxArg, nLongCounter);
|
||||
idxArg = kernel_apply.set(idxArg, nMidCounter);
|
||||
idxArg = kernel_apply.set(idxArg, nShortCounter);
|
||||
idxArg = kernel_apply.set(idxArg, fTb);
|
||||
idxArg = kernel_apply.set(idxArg, nkNN);
|
||||
idxArg = kernel_apply.set(idxArg, fTau);
|
||||
if (bShadowDetection)
|
||||
kernel_apply.set(idxArg, nShadowDetection);
|
||||
|
||||
size_t globalsize[2] = {(size_t)frame.cols, (size_t)frame.rows};
|
||||
if(!kernel_apply.run(2, globalsize, NULL, true))
|
||||
return false;
|
||||
|
||||
nShortCounter++;//0,1,...,nShortUpdate-1
|
||||
nMidCounter++;
|
||||
nLongCounter++;
|
||||
if (nShortCounter >= nShortUpdate)
|
||||
{
|
||||
nShortCounter = 0;
|
||||
randu(u_nNextShortUpdate, Scalar::all(0), Scalar::all(nShortUpdate));
|
||||
}
|
||||
if (nMidCounter >= nMidUpdate)
|
||||
{
|
||||
nMidCounter = 0;
|
||||
randu(u_nNextMidUpdate, Scalar::all(0), Scalar::all(nMidUpdate));
|
||||
}
|
||||
if (nLongCounter >= nLongUpdate)
|
||||
{
|
||||
nLongCounter = 0;
|
||||
randu(u_nNextLongUpdate, Scalar::all(0), Scalar::all(nLongUpdate));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BackgroundSubtractorKNNImpl::ocl_getBackgroundImage(OutputArray _backgroundImage) const
|
||||
{
|
||||
_backgroundImage.create(frameSize, frameType);
|
||||
UMat dst = _backgroundImage.getUMat();
|
||||
|
||||
int idxArg = 0;
|
||||
idxArg = kernel_getBg.set(idxArg, ocl::KernelArg::PtrReadOnly(u_flag));
|
||||
idxArg = kernel_getBg.set(idxArg, ocl::KernelArg::PtrReadOnly(u_sample));
|
||||
idxArg = kernel_getBg.set(idxArg, ocl::KernelArg::WriteOnly(dst));
|
||||
|
||||
size_t globalsize[2] = {(size_t)dst.cols, (size_t)dst.rows};
|
||||
|
||||
return kernel_getBg.run(2, globalsize, NULL, false);
|
||||
}
|
||||
|
||||
void BackgroundSubtractorKNNImpl::create_ocl_apply_kernel()
|
||||
{
|
||||
int nchannels = CV_MAT_CN(frameType);
|
||||
String opts = format("-D CN=%d -D NSAMPLES=%d%s", nchannels, nN, bShadowDetection ? " -D SHADOW_DETECT" : "");
|
||||
kernel_apply.create("knn_kernel", ocl::video::bgfg_knn_oclsrc, opts);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Base 3 version class
|
||||
void BackgroundSubtractorKNNImpl::apply(InputArray _image, OutputArray _fgmask, double learningRate) {
|
||||
apply(_image, noArray(), _fgmask, learningRate);
|
||||
}
|
||||
|
||||
void BackgroundSubtractorKNNImpl::apply(InputArray _image, InputArray _knownForegroundMask, OutputArray _fgmask, double learningRate)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
if (opencl_ON)
|
||||
{
|
||||
#ifndef __APPLE__
|
||||
CV_OCL_RUN(_fgmask.isUMat() && OCL_PERFORMANCE_CHECK(!ocl::Device::getDefault().isIntel() || _image.channels() == 1),
|
||||
ocl_apply(_image, _fgmask, learningRate))
|
||||
#else
|
||||
CV_OCL_RUN(_fgmask.isUMat() && OCL_PERFORMANCE_CHECK(!ocl::Device::getDefault().isIntel()),
|
||||
ocl_apply(_image, _fgmask, learningRate))
|
||||
#endif
|
||||
|
||||
opencl_ON = false;
|
||||
nframes = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool needToInitialize = nframes == 0 || learningRate >= 1 || _image.size() != frameSize || _image.type() != frameType;
|
||||
|
||||
if( needToInitialize )
|
||||
initialize(_image.size(), _image.type());
|
||||
|
||||
Mat image = _image.getMat();
|
||||
_fgmask.create( image.size(), CV_8U );
|
||||
Mat fgmask = _fgmask.getMat();
|
||||
|
||||
Mat knownForegroundMask = _knownForegroundMask.getMat();
|
||||
|
||||
if(!knownForegroundMask.empty())
|
||||
{
|
||||
CV_Assert(knownForegroundMask.type() == CV_8UC1);
|
||||
CV_Assert(knownForegroundMask.size() == image.size());
|
||||
}
|
||||
|
||||
++nframes;
|
||||
learningRate = learningRate >= 0 && nframes > 1 ? learningRate : 1./std::min( 2*nframes, history );
|
||||
CV_Assert(learningRate >= 0);
|
||||
|
||||
//recalculate update rates - in case alpha is changed
|
||||
// calculate update parameters (using alpha)
|
||||
int Kshort,Kmid,Klong;
|
||||
//approximate exponential learning curve
|
||||
Kshort=(int)(log(0.7)/log(1-learningRate))+1;//Kshort
|
||||
Kmid=(int)(log(0.4)/log(1-learningRate))-Kshort+1;//Kmid
|
||||
Klong=(int)(log(0.1)/log(1-learningRate))-Kshort-Kmid+1;//Klong
|
||||
|
||||
//refresh rates
|
||||
int nShortUpdate = (Kshort/nN)+1;
|
||||
int nMidUpdate = (Kmid/nN)+1;
|
||||
int nLongUpdate = (Klong/nN)+1;
|
||||
|
||||
parallel_for_(Range(0, image.rows),
|
||||
KNNInvoker(image, fgmask,
|
||||
bgmodel.ptr(),
|
||||
nNextLongUpdate.ptr(),
|
||||
nNextMidUpdate.ptr(),
|
||||
nNextShortUpdate.ptr(),
|
||||
aModelIndexLong.ptr(),
|
||||
aModelIndexMid.ptr(),
|
||||
aModelIndexShort.ptr(),
|
||||
nLongCounter,
|
||||
nMidCounter,
|
||||
nShortCounter,
|
||||
nN,
|
||||
fTb,
|
||||
nkNN,
|
||||
fTau,
|
||||
bShadowDetection,
|
||||
nShadowDetection,
|
||||
knownForegroundMask),
|
||||
image.total()/(double)(1 << 16));
|
||||
|
||||
nShortCounter++;//0,1,...,nShortUpdate-1
|
||||
nMidCounter++;
|
||||
nLongCounter++;
|
||||
if (nShortCounter >= nShortUpdate)
|
||||
{
|
||||
nShortCounter = 0;
|
||||
randu(nNextShortUpdate, Scalar::all(0), Scalar::all(nShortUpdate));
|
||||
}
|
||||
if (nMidCounter >= nMidUpdate)
|
||||
{
|
||||
nMidCounter = 0;
|
||||
randu(nNextMidUpdate, Scalar::all(0), Scalar::all(nMidUpdate));
|
||||
}
|
||||
if (nLongCounter >= nLongUpdate)
|
||||
{
|
||||
nLongCounter = 0;
|
||||
randu(nNextLongUpdate, Scalar::all(0), Scalar::all(nLongUpdate));
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundSubtractorKNNImpl::getBackgroundImage(OutputArray backgroundImage) const
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
if (opencl_ON)
|
||||
{
|
||||
CV_OCL_RUN(opencl_ON, ocl_getBackgroundImage(backgroundImage))
|
||||
|
||||
opencl_ON = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
int nchannels = CV_MAT_CN(frameType);
|
||||
//CV_Assert( nchannels == 3 );
|
||||
Mat meanBackground(frameSize, CV_8UC3, Scalar::all(0));
|
||||
|
||||
int ndata=nchannels+1;
|
||||
int modelstep=(ndata * nN * 3);
|
||||
|
||||
const uchar* pbgmodel=bgmodel.ptr(0);
|
||||
for(int row=0; row<meanBackground.rows; row++)
|
||||
{
|
||||
for(int col=0; col<meanBackground.cols; col++)
|
||||
{
|
||||
for (int n = 0; n < nN*3; n++)
|
||||
{
|
||||
const uchar* mean_m = &pbgmodel[n*ndata];
|
||||
if (mean_m[nchannels])
|
||||
{
|
||||
meanBackground.at<Vec3b>(row, col) = Vec3b(mean_m);
|
||||
break;
|
||||
}
|
||||
}
|
||||
pbgmodel=pbgmodel+modelstep;
|
||||
}
|
||||
}
|
||||
|
||||
switch(CV_MAT_CN(frameType))
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
std::vector<Mat> channels;
|
||||
split(meanBackground, channels);
|
||||
channels[0].copyTo(backgroundImage);
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
meanBackground.copyTo(backgroundImage);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
CV_Error(Error::StsUnsupportedFormat, "");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ptr<BackgroundSubtractorKNN> createBackgroundSubtractorKNN(int _history, double _threshold2,
|
||||
bool _bShadowDetection)
|
||||
{
|
||||
return makePtr<BackgroundSubtractorKNNImpl>(_history, (float)_threshold2, _bShadowDetection);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* End of file. */
|
||||
@@ -0,0 +1,996 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
/*//Implementation of the Gaussian mixture model background subtraction from:
|
||||
//
|
||||
//"Improved adaptive Gaussian mixture model for background subtraction"
|
||||
//Z.Zivkovic
|
||||
//International Conference Pattern Recognition, UK, August, 2004
|
||||
//http://www.zoranz.net/Publications/zivkovic2004ICPR.pdf
|
||||
//The code is very fast and performs also shadow detection.
|
||||
//Number of Gaussian components is adapted per pixel.
|
||||
//
|
||||
// and
|
||||
//
|
||||
//"Efficient Adaptive Density Estimapion per Image Pixel for the Task of Background Subtraction"
|
||||
//Z.Zivkovic, F. van der Heijden
|
||||
//Pattern Recognition Letters, vol. 27, no. 7, pages 773-780, 2006.
|
||||
//
|
||||
//The algorithm similar to the standard Stauffer&Grimson algorithm with
|
||||
//additional selection of the number of the Gaussian components based on:
|
||||
//
|
||||
//"Recursive unsupervised learning of finite mixture models "
|
||||
//Z.Zivkovic, F.van der Heijden
|
||||
//IEEE Trans. on Pattern Analysis and Machine Intelligence, vol.26, no.5, pages 651-656, 2004
|
||||
//http://www.zoranz.net/Publications/zivkovic2004PAMI.pdf
|
||||
//
|
||||
//
|
||||
//Example usage with as cpp class
|
||||
// BackgroundSubtractorMOG2 bg_model;
|
||||
//For each new image the model is updates using:
|
||||
// bg_model(img, fgmask);
|
||||
//
|
||||
//Example usage as part of the CvBGStatModel:
|
||||
// CvBGStatModel* bg_model = cvCreateGaussianBGModel2( first_frame );
|
||||
//
|
||||
// //update for each frame
|
||||
// cvUpdateBGStatModel( tmp_frame, bg_model );//segmentation result is in bg_model->foreground
|
||||
//
|
||||
// //release at the program termination
|
||||
// cvReleaseBGStatModel( &bg_model );
|
||||
//
|
||||
//Author: Z.Zivkovic, www.zoranz.net
|
||||
//Date: 7-April-2011, Version:1.0
|
||||
///////////*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "opencl_kernels_video.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
/*
|
||||
Interface of Gaussian mixture algorithm from:
|
||||
|
||||
"Improved adaptive Gaussian mixture model for background subtraction"
|
||||
Z.Zivkovic
|
||||
International Conference Pattern Recognition, UK, August, 2004
|
||||
http://www.zoranz.net/Publications/zivkovic2004ICPR.pdf
|
||||
|
||||
Advantages:
|
||||
-fast - number of Gaussian components is constantly adapted per pixel.
|
||||
-performs also shadow detection (see bgfg_segm_test.cpp example)
|
||||
|
||||
*/
|
||||
|
||||
// default parameters of gaussian background detection algorithm
|
||||
static const int defaultHistory2 = 500; // Learning rate; alpha = 1/defaultHistory2
|
||||
static const float defaultVarThreshold2 = 4.0f*4.0f;
|
||||
static const int defaultNMixtures2 = 5; // maximal number of Gaussians in mixture
|
||||
static const float defaultBackgroundRatio2 = 0.9f; // threshold sum of weights for background test
|
||||
static const float defaultVarThresholdGen2 = 3.0f*3.0f;
|
||||
static const float defaultVarInit2 = 15.0f; // initial variance for new components
|
||||
static const float defaultVarMax2 = 5*defaultVarInit2;
|
||||
static const float defaultVarMin2 = 4.0f;
|
||||
|
||||
// additional parameters
|
||||
static const float defaultfCT2 = 0.05f; // complexity reduction prior constant 0 - no reduction of number of components
|
||||
static const unsigned char defaultnShadowDetection2 = (unsigned char)127; // value to use in the segmentation mask for shadows, set 0 not to do shadow detection
|
||||
static const float defaultfTau = 0.5f; // Tau - shadow threshold, see the paper for explanation
|
||||
|
||||
|
||||
class BackgroundSubtractorMOG2Impl CV_FINAL : public BackgroundSubtractorMOG2
|
||||
{
|
||||
public:
|
||||
//! the default constructor
|
||||
BackgroundSubtractorMOG2Impl()
|
||||
{
|
||||
frameSize = Size(0,0);
|
||||
frameType = 0;
|
||||
|
||||
nframes = 0;
|
||||
history = defaultHistory2;
|
||||
varThreshold = defaultVarThreshold2;
|
||||
bShadowDetection = 1;
|
||||
|
||||
nmixtures = defaultNMixtures2;
|
||||
backgroundRatio = defaultBackgroundRatio2;
|
||||
fVarInit = defaultVarInit2;
|
||||
fVarMax = defaultVarMax2;
|
||||
fVarMin = defaultVarMin2;
|
||||
|
||||
varThresholdGen = defaultVarThresholdGen2;
|
||||
fCT = defaultfCT2;
|
||||
nShadowDetection = defaultnShadowDetection2;
|
||||
fTau = defaultfTau;
|
||||
#ifdef HAVE_OPENCL
|
||||
opencl_ON = true;
|
||||
#endif
|
||||
}
|
||||
//! the full constructor that takes the length of the history,
|
||||
// the number of gaussian mixtures, the background ratio parameter and the noise strength
|
||||
BackgroundSubtractorMOG2Impl(int _history, float _varThreshold, bool _bShadowDetection=true)
|
||||
{
|
||||
frameSize = Size(0,0);
|
||||
frameType = 0;
|
||||
|
||||
nframes = 0;
|
||||
history = _history > 0 ? _history : defaultHistory2;
|
||||
varThreshold = (_varThreshold>0)? _varThreshold : defaultVarThreshold2;
|
||||
bShadowDetection = _bShadowDetection;
|
||||
|
||||
nmixtures = defaultNMixtures2;
|
||||
backgroundRatio = defaultBackgroundRatio2;
|
||||
fVarInit = defaultVarInit2;
|
||||
fVarMax = defaultVarMax2;
|
||||
fVarMin = defaultVarMin2;
|
||||
|
||||
varThresholdGen = defaultVarThresholdGen2;
|
||||
fCT = defaultfCT2;
|
||||
nShadowDetection = defaultnShadowDetection2;
|
||||
fTau = defaultfTau;
|
||||
name_ = "BackgroundSubtractor.MOG2";
|
||||
#ifdef HAVE_OPENCL
|
||||
opencl_ON = true;
|
||||
#endif
|
||||
}
|
||||
//! the destructor
|
||||
~BackgroundSubtractorMOG2Impl() CV_OVERRIDE {}
|
||||
//! the update operator
|
||||
void apply(InputArray image, OutputArray fgmask, double learningRate) CV_OVERRIDE;
|
||||
|
||||
void apply(InputArray image, InputArray knownForegroundMask, OutputArray fgmask, double learningRate) CV_OVERRIDE;
|
||||
|
||||
//! computes a background image which are the mean of all background gaussians
|
||||
virtual void getBackgroundImage(OutputArray backgroundImage) const CV_OVERRIDE;
|
||||
|
||||
//! re-initialization method
|
||||
void initialize(Size _frameSize, int _frameType)
|
||||
{
|
||||
frameSize = _frameSize;
|
||||
frameType = _frameType;
|
||||
nframes = 0;
|
||||
|
||||
int nchannels = CV_MAT_CN(frameType);
|
||||
CV_Assert( nchannels <= CV_CN_MAX );
|
||||
CV_Assert( nmixtures <= 255);
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
if (ocl::isOpenCLActivated() && opencl_ON)
|
||||
{
|
||||
create_ocl_apply_kernel();
|
||||
|
||||
bool isFloat = CV_MAKETYPE(CV_32F,nchannels) == frameType;
|
||||
kernel_getBg.create("getBackgroundImage2_kernel", ocl::video::bgfg_mog2_oclsrc, format( "-D CN=%d -D FL=%d -D NMIXTURES=%d", nchannels, isFloat, nmixtures));
|
||||
|
||||
if (kernel_apply.empty() || kernel_getBg.empty())
|
||||
opencl_ON = false;
|
||||
}
|
||||
else opencl_ON = false;
|
||||
|
||||
if (opencl_ON)
|
||||
{
|
||||
u_weight.create(frameSize.height * nmixtures, frameSize.width, CV_32FC1);
|
||||
u_weight.setTo(Scalar::all(0));
|
||||
|
||||
u_variance.create(frameSize.height * nmixtures, frameSize.width, CV_32FC1);
|
||||
u_variance.setTo(Scalar::all(0));
|
||||
|
||||
if (nchannels==3)
|
||||
nchannels=4;
|
||||
u_mean.create(frameSize.height * nmixtures, frameSize.width, CV_32FC(nchannels)); //4 channels
|
||||
u_mean.setTo(Scalar::all(0));
|
||||
|
||||
//make the array for keeping track of the used modes per pixel - all zeros at start
|
||||
u_bgmodelUsedModes.create(frameSize, CV_8UC1);
|
||||
u_bgmodelUsedModes.setTo(cv::Scalar::all(0));
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
// for each gaussian mixture of each pixel bg model we store ...
|
||||
// the mixture weight (w),
|
||||
// the mean (nchannels values) and
|
||||
// the covariance
|
||||
bgmodel.create( 1, frameSize.height*frameSize.width*nmixtures*(2 + nchannels), CV_32F );
|
||||
//make the array for keeping track of the used modes per pixel - all zeros at start
|
||||
bgmodelUsedModes.create(frameSize,CV_8U);
|
||||
bgmodelUsedModes = Scalar::all(0);
|
||||
}
|
||||
}
|
||||
|
||||
virtual String getDefaultName() const CV_OVERRIDE { return "BackgroundSubtractor_MOG2"; }
|
||||
|
||||
virtual int getHistory() const CV_OVERRIDE { return history; }
|
||||
virtual void setHistory(int _nframes) CV_OVERRIDE { history = _nframes; }
|
||||
|
||||
virtual int getNMixtures() const CV_OVERRIDE { return nmixtures; }
|
||||
virtual void setNMixtures(int nmix) CV_OVERRIDE { nmixtures = nmix; }
|
||||
|
||||
virtual double getBackgroundRatio() const CV_OVERRIDE { return backgroundRatio; }
|
||||
virtual void setBackgroundRatio(double _backgroundRatio) CV_OVERRIDE { backgroundRatio = (float)_backgroundRatio; }
|
||||
|
||||
virtual double getVarThreshold() const CV_OVERRIDE { return varThreshold; }
|
||||
virtual void setVarThreshold(double _varThreshold) CV_OVERRIDE { varThreshold = _varThreshold; }
|
||||
|
||||
virtual double getVarThresholdGen() const CV_OVERRIDE { return varThresholdGen; }
|
||||
virtual void setVarThresholdGen(double _varThresholdGen) CV_OVERRIDE { varThresholdGen = (float)_varThresholdGen; }
|
||||
|
||||
virtual double getVarInit() const CV_OVERRIDE { return fVarInit; }
|
||||
virtual void setVarInit(double varInit) CV_OVERRIDE { fVarInit = (float)varInit; }
|
||||
|
||||
virtual double getVarMin() const CV_OVERRIDE { return fVarMin; }
|
||||
virtual void setVarMin(double varMin) CV_OVERRIDE { fVarMin = (float)varMin; }
|
||||
|
||||
virtual double getVarMax() const CV_OVERRIDE { return fVarMax; }
|
||||
virtual void setVarMax(double varMax) CV_OVERRIDE { fVarMax = (float)varMax; }
|
||||
|
||||
virtual double getComplexityReductionThreshold() const CV_OVERRIDE { return fCT; }
|
||||
virtual void setComplexityReductionThreshold(double ct) CV_OVERRIDE { fCT = (float)ct; }
|
||||
|
||||
virtual bool getDetectShadows() const CV_OVERRIDE { return bShadowDetection; }
|
||||
virtual void setDetectShadows(bool detectshadows) CV_OVERRIDE
|
||||
{
|
||||
if (bShadowDetection == detectshadows)
|
||||
return;
|
||||
bShadowDetection = detectshadows;
|
||||
#ifdef HAVE_OPENCL
|
||||
if (!kernel_apply.empty())
|
||||
{
|
||||
create_ocl_apply_kernel();
|
||||
CV_Assert( !kernel_apply.empty() );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual int getShadowValue() const CV_OVERRIDE { return nShadowDetection; }
|
||||
virtual void setShadowValue(int value) CV_OVERRIDE { nShadowDetection = (uchar)value; }
|
||||
|
||||
virtual double getShadowThreshold() const CV_OVERRIDE { return fTau; }
|
||||
virtual void setShadowThreshold(double value) CV_OVERRIDE { fTau = (float)value; }
|
||||
|
||||
virtual void write(FileStorage& fs) const CV_OVERRIDE
|
||||
{
|
||||
writeFormat(fs);
|
||||
fs << "name" << name_
|
||||
<< "history" << history
|
||||
<< "nmixtures" << nmixtures
|
||||
<< "backgroundRatio" << backgroundRatio
|
||||
<< "varThreshold" << varThreshold
|
||||
<< "varThresholdGen" << varThresholdGen
|
||||
<< "varInit" << fVarInit
|
||||
<< "varMin" << fVarMin
|
||||
<< "varMax" << fVarMax
|
||||
<< "complexityReductionThreshold" << fCT
|
||||
<< "detectShadows" << (int)bShadowDetection
|
||||
<< "shadowValue" << (int)nShadowDetection
|
||||
<< "shadowThreshold" << fTau;
|
||||
}
|
||||
|
||||
virtual void read(const FileNode& fn) CV_OVERRIDE
|
||||
{
|
||||
CV_Assert( (String)fn["name"] == name_ );
|
||||
history = (int)fn["history"];
|
||||
nmixtures = (int)fn["nmixtures"];
|
||||
backgroundRatio = (float)fn["backgroundRatio"];
|
||||
varThreshold = (double)fn["varThreshold"];
|
||||
varThresholdGen = (float)fn["varThresholdGen"];
|
||||
fVarInit = (float)fn["varInit"];
|
||||
fVarMin = (float)fn["varMin"];
|
||||
fVarMax = (float)fn["varMax"];
|
||||
fCT = (float)fn["complexityReductionThreshold"];
|
||||
bShadowDetection = (int)fn["detectShadows"] != 0;
|
||||
nShadowDetection = saturate_cast<uchar>((int)fn["shadowValue"]);
|
||||
fTau = (float)fn["shadowThreshold"];
|
||||
}
|
||||
|
||||
protected:
|
||||
Size frameSize;
|
||||
int frameType;
|
||||
Mat bgmodel;
|
||||
Mat bgmodelUsedModes;//keep track of number of modes per pixel
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
//for OCL
|
||||
|
||||
mutable bool opencl_ON;
|
||||
|
||||
UMat u_weight;
|
||||
UMat u_variance;
|
||||
UMat u_mean;
|
||||
UMat u_bgmodelUsedModes;
|
||||
|
||||
mutable ocl::Kernel kernel_apply;
|
||||
mutable ocl::Kernel kernel_getBg;
|
||||
#endif
|
||||
|
||||
int nframes;
|
||||
int history;
|
||||
int nmixtures;
|
||||
//! here it is the maximum allowed number of mixture components.
|
||||
//! Actual number is determined dynamically per pixel
|
||||
double varThreshold;
|
||||
// threshold on the squared Mahalanobis distance to decide if it is well described
|
||||
// by the background model or not. Related to Cthr from the paper.
|
||||
// This does not influence the update of the background. A typical value could be 4 sigma
|
||||
// and that is varThreshold=4*4=16; Corresponds to Tb in the paper.
|
||||
|
||||
/////////////////////////
|
||||
// less important parameters - things you might change but be careful
|
||||
////////////////////////
|
||||
float backgroundRatio;
|
||||
// corresponds to fTB=1-cf from the paper
|
||||
// TB - threshold when the component becomes significant enough to be included into
|
||||
// the background model. It is the TB=1-cf from the paper. So I use cf=0.1 => TB=0.
|
||||
// For alpha=0.001 it means that the mode should exist for approximately 105 frames before
|
||||
// it is considered foreground
|
||||
// float noiseSigma;
|
||||
float varThresholdGen;
|
||||
//correspondts to Tg - threshold on the squared Mahalan. dist. to decide
|
||||
//when a sample is close to the existing components. If it is not close
|
||||
//to any a new component will be generated. I use 3 sigma => Tg=3*3=9.
|
||||
//Smaller Tg leads to more generated components and higher Tg might make
|
||||
//lead to small number of components but they can grow too large
|
||||
float fVarInit;
|
||||
float fVarMin;
|
||||
float fVarMax;
|
||||
//initial variance for the newly generated components.
|
||||
//It will will influence the speed of adaptation. A good guess should be made.
|
||||
//A simple way is to estimate the typical standard deviation from the images.
|
||||
//I used here 10 as a reasonable value
|
||||
// min and max can be used to further control the variance
|
||||
float fCT;//CT - complexity reduction prior
|
||||
//this is related to the number of samples needed to accept that a component
|
||||
//actually exists. We use CT=0.05 of all the samples. By setting CT=0 you get
|
||||
//the standard Stauffer&Grimson algorithm (maybe not exact but very similar)
|
||||
|
||||
//shadow detection parameters
|
||||
bool bShadowDetection;//default 1 - do shadow detection
|
||||
unsigned char nShadowDetection;//do shadow detection - insert this value as the detection result - 127 default value
|
||||
float fTau;
|
||||
// Tau - shadow threshold. The shadow is detected if the pixel is darker
|
||||
//version of the background. Tau is a threshold on how much darker the shadow can be.
|
||||
//Tau= 0.5 means that if pixel is more than 2 times darker then it is not shadow
|
||||
//See: Prati,Mikic,Trivedi,Cucchiara,"Detecting Moving Shadows...",IEEE PAMI,2003.
|
||||
|
||||
String name_;
|
||||
|
||||
template <typename T, int CN>
|
||||
void getBackgroundImage_intern(OutputArray backgroundImage) const;
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
bool ocl_getBackgroundImage(OutputArray backgroundImage) const;
|
||||
bool ocl_apply(InputArray _image, OutputArray _fgmask, double learningRate=-1);
|
||||
void create_ocl_apply_kernel();
|
||||
#endif
|
||||
};
|
||||
|
||||
struct GaussBGStatModel2Params
|
||||
{
|
||||
//image info
|
||||
int nWidth;
|
||||
int nHeight;
|
||||
int nND;//number of data dimensions (image channels)
|
||||
|
||||
bool bPostFiltering;//default 1 - do postfiltering - will make shadow detection results also give value 255
|
||||
double minArea; // for postfiltering
|
||||
|
||||
bool bInit;//default 1, faster updates at start
|
||||
|
||||
/////////////////////////
|
||||
//very important parameters - things you will change
|
||||
////////////////////////
|
||||
float fAlphaT;
|
||||
//alpha - speed of update - if the time interval you want to average over is T
|
||||
//set alpha=1/T. It is also useful at start to make T slowly increase
|
||||
//from 1 until the desired T
|
||||
float fTb;
|
||||
//Tb - threshold on the squared Mahalan. dist. to decide if it is well described
|
||||
//by the background model or not. Related to Cthr from the paper.
|
||||
//This does not influence the update of the background. A typical value could be 4 sigma
|
||||
//and that is Tb=4*4=16;
|
||||
|
||||
/////////////////////////
|
||||
//less important parameters - things you might change but be careful
|
||||
////////////////////////
|
||||
float fTg;
|
||||
//Tg - threshold on the squared Mahalan. dist. to decide
|
||||
//when a sample is close to the existing components. If it is not close
|
||||
//to any a new component will be generated. I use 3 sigma => Tg=3*3=9.
|
||||
//Smaller Tg leads to more generated components and higher Tg might make
|
||||
//lead to small number of components but they can grow too large
|
||||
float fTB;//1-cf from the paper
|
||||
//TB - threshold when the component becomes significant enough to be included into
|
||||
//the background model. It is the TB=1-cf from the paper. So I use cf=0.1 => TB=0.
|
||||
//For alpha=0.001 it means that the mode should exist for approximately 105 frames before
|
||||
//it is considered foreground
|
||||
float fVarInit;
|
||||
float fVarMax;
|
||||
float fVarMin;
|
||||
//initial standard deviation for the newly generated components.
|
||||
//It will will influence the speed of adaptation. A good guess should be made.
|
||||
//A simple way is to estimate the typical standard deviation from the images.
|
||||
//I used here 10 as a reasonable value
|
||||
float fCT;//CT - complexity reduction prior
|
||||
//this is related to the number of samples needed to accept that a component
|
||||
//actually exists. We use CT=0.05 of all the samples. By setting CT=0 you get
|
||||
//the standard Stauffer&Grimson algorithm (maybe not exact but very similar)
|
||||
|
||||
//even less important parameters
|
||||
int nM;//max number of modes - const - 4 is usually enough
|
||||
|
||||
//shadow detection parameters
|
||||
bool bShadowDetection;//default 1 - do shadow detection
|
||||
unsigned char nShadowDetection;//do shadow detection - insert this value as the detection result
|
||||
float fTau;
|
||||
// Tau - shadow threshold. The shadow is detected if the pixel is darker
|
||||
//version of the background. Tau is a threshold on how much darker the shadow can be.
|
||||
//Tau= 0.5 means that if pixel is more than 2 times darker then it is not shadow
|
||||
//See: Prati,Mikic,Trivedi,Cucchiara,"Detecting Moving Shadows...",IEEE PAMI,2003.
|
||||
};
|
||||
|
||||
struct GMM
|
||||
{
|
||||
float weight;
|
||||
float variance;
|
||||
};
|
||||
|
||||
// shadow detection performed per pixel
|
||||
// should work for rgb data, could be useful for gray scale and depth data as well
|
||||
// See: Prati,Mikic,Trivedi,Cucchiara,"Detecting Moving Shadows...",IEEE PAMI,2003.
|
||||
CV_INLINE bool
|
||||
detectShadowGMM(const float* data, int nchannels, int nmodes,
|
||||
const GMM* gmm, const float* mean,
|
||||
float Tb, float TB, float tau)
|
||||
{
|
||||
float tWeight = 0;
|
||||
|
||||
// check all the components marked as background:
|
||||
for( int mode = 0; mode < nmodes; mode++, mean += nchannels )
|
||||
{
|
||||
GMM g = gmm[mode];
|
||||
|
||||
float numerator = 0.0f;
|
||||
float denominator = 0.0f;
|
||||
for( int c = 0; c < nchannels; c++ )
|
||||
{
|
||||
numerator += data[c] * mean[c];
|
||||
denominator += mean[c] * mean[c];
|
||||
}
|
||||
|
||||
// no division by zero allowed
|
||||
if( denominator == 0 )
|
||||
return false;
|
||||
|
||||
// if tau < a < 1 then also check the color distortion
|
||||
if( numerator <= denominator && numerator >= tau*denominator )
|
||||
{
|
||||
float a = numerator / denominator;
|
||||
float dist2a = 0.0f;
|
||||
|
||||
for( int c = 0; c < nchannels; c++ )
|
||||
{
|
||||
float dD= a*mean[c] - data[c];
|
||||
dist2a += dD*dD;
|
||||
}
|
||||
|
||||
if (dist2a < Tb*g.variance*a*a)
|
||||
return true;
|
||||
};
|
||||
|
||||
tWeight += g.weight;
|
||||
if( tWeight > TB )
|
||||
return false;
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
//update GMM - the base update function performed per pixel
|
||||
//
|
||||
//"Efficient Adaptive Density Estimapion per Image Pixel for the Task of Background Subtraction"
|
||||
//Z.Zivkovic, F. van der Heijden
|
||||
//Pattern Recognition Letters, vol. 27, no. 7, pages 773-780, 2006.
|
||||
//
|
||||
//The algorithm similar to the standard Stauffer&Grimson algorithm with
|
||||
//additional selection of the number of the Gaussian components based on:
|
||||
//
|
||||
//"Recursive unsupervised learning of finite mixture models "
|
||||
//Z.Zivkovic, F.van der Heijden
|
||||
//IEEE Trans. on Pattern Analysis and Machine Intelligence, vol.26, no.5, pages 651-656, 2004
|
||||
//http://www.zoranz.net/Publications/zivkovic2004PAMI.pdf
|
||||
|
||||
class MOG2Invoker : public ParallelLoopBody
|
||||
{
|
||||
public:
|
||||
MOG2Invoker(const Mat& _src, Mat& _dst,
|
||||
GMM* _gmm, float* _mean,
|
||||
uchar* _modesUsed,
|
||||
int _nmixtures, float _alphaT,
|
||||
float _Tb, float _TB, float _Tg,
|
||||
float _varInit, float _varMin, float _varMax,
|
||||
float _prune, float _tau, bool _detectShadows,
|
||||
uchar _shadowVal, const Mat& _knownForegroundMask)
|
||||
: knownForegroundMask(_knownForegroundMask)
|
||||
{
|
||||
src = &_src;
|
||||
dst = &_dst;
|
||||
gmm0 = _gmm;
|
||||
mean0 = _mean;
|
||||
modesUsed0 = _modesUsed;
|
||||
nmixtures = _nmixtures;
|
||||
alphaT = _alphaT;
|
||||
Tb = _Tb;
|
||||
TB = _TB;
|
||||
Tg = _Tg;
|
||||
varInit = _varInit;
|
||||
varMin = MIN(_varMin, _varMax);
|
||||
varMax = MAX(_varMin, _varMax);
|
||||
prune = _prune;
|
||||
tau = _tau;
|
||||
detectShadows = _detectShadows;
|
||||
shadowVal = _shadowVal;
|
||||
}
|
||||
|
||||
void operator()(const Range& range) const CV_OVERRIDE
|
||||
{
|
||||
int y0 = range.start, y1 = range.end;
|
||||
int ncols = src->cols, nchannels = src->channels();
|
||||
AutoBuffer<float> buf(src->cols*nchannels);
|
||||
float alpha1 = 1.f - alphaT;
|
||||
float dData[CV_CN_MAX];
|
||||
|
||||
for( int y = y0; y < y1; y++ )
|
||||
{
|
||||
const float* data = buf.data();
|
||||
if( src->depth() != CV_32F )
|
||||
src->row(y).convertTo(Mat(1, ncols, CV_32FC(nchannels), (void*)data), CV_32F);
|
||||
else
|
||||
data = src->ptr<float>(y);
|
||||
|
||||
float* mean = mean0 + ncols*nmixtures*nchannels*y;
|
||||
GMM* gmm = gmm0 + ncols*nmixtures*y;
|
||||
uchar* modesUsed = modesUsed0 + ncols*y;
|
||||
uchar* mask = dst->ptr(y);
|
||||
|
||||
for( int x = 0; x < ncols; x++, data += nchannels, gmm += nmixtures, mean += nmixtures*nchannels )
|
||||
{
|
||||
|
||||
// Check that foreground mask exists
|
||||
if (!knownForegroundMask.empty())
|
||||
{
|
||||
// If input mask states pixel is foreground
|
||||
if (knownForegroundMask.at<uchar>(y, x) > 0)
|
||||
{
|
||||
mask[x] = 255; // ensure output mask marks this pixel as FG
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//calculate distances to the modes (+ sort)
|
||||
//here we need to go in descending order!!!
|
||||
bool background = false;//return value -> true - the pixel classified as background
|
||||
|
||||
//internal:
|
||||
bool fitsPDF = false;//if it remains zero a new GMM mode will be added
|
||||
int nmodes = modesUsed[x];//current number of modes in GMM
|
||||
float totalWeight = 0.f;
|
||||
|
||||
float* mean_m = mean;
|
||||
|
||||
//////
|
||||
//go through all modes
|
||||
for( int mode = 0; mode < nmodes; mode++, mean_m += nchannels )
|
||||
{
|
||||
float weight = alpha1*gmm[mode].weight + prune;//need only weight if fit is found
|
||||
int swap_count = 0;
|
||||
////
|
||||
//fit not found yet
|
||||
if( !fitsPDF )
|
||||
{
|
||||
//check if it belongs to some of the remaining modes
|
||||
float var = gmm[mode].variance;
|
||||
|
||||
//calculate difference and distance
|
||||
float dist2;
|
||||
|
||||
if( nchannels == 3 )
|
||||
{
|
||||
dData[0] = mean_m[0] - data[0];
|
||||
dData[1] = mean_m[1] - data[1];
|
||||
dData[2] = mean_m[2] - data[2];
|
||||
dist2 = dData[0]*dData[0] + dData[1]*dData[1] + dData[2]*dData[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
dist2 = 0.f;
|
||||
for( int c = 0; c < nchannels; c++ )
|
||||
{
|
||||
dData[c] = mean_m[c] - data[c];
|
||||
dist2 += dData[c]*dData[c];
|
||||
}
|
||||
}
|
||||
|
||||
//background? - Tb - usually larger than Tg
|
||||
if( totalWeight < TB && dist2 < Tb*var )
|
||||
background = true;
|
||||
|
||||
//check fit
|
||||
if( dist2 < Tg*var )
|
||||
{
|
||||
/////
|
||||
//belongs to the mode
|
||||
fitsPDF = true;
|
||||
|
||||
//update distribution
|
||||
|
||||
//update weight
|
||||
weight += alphaT;
|
||||
float k = alphaT/weight;
|
||||
|
||||
//update mean
|
||||
for( int c = 0; c < nchannels; c++ )
|
||||
mean_m[c] -= k*dData[c];
|
||||
|
||||
//update variance
|
||||
float varnew = var + k*(dist2-var);
|
||||
//limit the variance
|
||||
varnew = MAX(varnew, varMin);
|
||||
varnew = MIN(varnew, varMax);
|
||||
gmm[mode].variance = varnew;
|
||||
|
||||
//sort
|
||||
//all other weights are at the same place and
|
||||
//only the matched (iModes) is higher -> just find the new place for it
|
||||
for( int i = mode; i > 0; i-- )
|
||||
{
|
||||
//check one up
|
||||
if( weight < gmm[i-1].weight )
|
||||
break;
|
||||
|
||||
swap_count++;
|
||||
//swap one up
|
||||
std::swap(gmm[i], gmm[i-1]);
|
||||
for( int c = 0; c < nchannels; c++ )
|
||||
std::swap(mean[i*nchannels + c], mean[(i-1)*nchannels + c]);
|
||||
}
|
||||
//belongs to the mode - bFitsPDF becomes 1
|
||||
/////
|
||||
}
|
||||
}//!bFitsPDF)
|
||||
|
||||
//check prune
|
||||
if( weight < -prune )
|
||||
{
|
||||
weight = 0.0;
|
||||
nmodes--;
|
||||
}
|
||||
|
||||
gmm[mode-swap_count].weight = weight;//update weight by the calculated value
|
||||
totalWeight += weight;
|
||||
}
|
||||
//go through all modes
|
||||
//////
|
||||
|
||||
// Renormalize weights. In the special case that the pixel does
|
||||
// not agree with any modes, set weights to zero (a new mode will be added below).
|
||||
float invWeight = 0.f;
|
||||
if (std::abs(totalWeight) > FLT_EPSILON) {
|
||||
invWeight = 1.f/totalWeight;
|
||||
}
|
||||
|
||||
for( int mode = 0; mode < nmodes; mode++ )
|
||||
{
|
||||
gmm[mode].weight *= invWeight;
|
||||
}
|
||||
|
||||
//make new mode if needed and exit
|
||||
if( !fitsPDF && alphaT > 0.f )
|
||||
{
|
||||
// replace the weakest or add a new one
|
||||
int mode = nmodes == nmixtures ? nmixtures-1 : nmodes++;
|
||||
|
||||
if (nmodes==1)
|
||||
gmm[mode].weight = 1.f;
|
||||
else
|
||||
{
|
||||
gmm[mode].weight = alphaT;
|
||||
|
||||
// renormalize all other weights
|
||||
for( int i = 0; i < nmodes-1; i++ )
|
||||
gmm[i].weight *= alpha1;
|
||||
}
|
||||
|
||||
// init
|
||||
for( int c = 0; c < nchannels; c++ )
|
||||
mean[mode*nchannels + c] = data[c];
|
||||
|
||||
gmm[mode].variance = varInit;
|
||||
|
||||
//sort
|
||||
//find the new place for it
|
||||
for( int i = nmodes - 1; i > 0; i-- )
|
||||
{
|
||||
// check one up
|
||||
if( alphaT < gmm[i-1].weight )
|
||||
break;
|
||||
|
||||
// swap one up
|
||||
std::swap(gmm[i], gmm[i-1]);
|
||||
for( int c = 0; c < nchannels; c++ )
|
||||
std::swap(mean[i*nchannels + c], mean[(i-1)*nchannels + c]);
|
||||
}
|
||||
}
|
||||
|
||||
//set the number of modes
|
||||
modesUsed[x] = uchar(nmodes);
|
||||
mask[x] = background ? 0 :
|
||||
detectShadows && detectShadowGMM(data, nchannels, nmodes, gmm, mean, Tb, TB, tau) ?
|
||||
shadowVal : 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Mat* src;
|
||||
Mat* dst;
|
||||
GMM* gmm0;
|
||||
float* mean0;
|
||||
uchar* modesUsed0;
|
||||
|
||||
int nmixtures;
|
||||
float alphaT, Tb, TB, Tg;
|
||||
float varInit, varMin, varMax, prune, tau;
|
||||
|
||||
bool detectShadows;
|
||||
uchar shadowVal;
|
||||
const Mat& knownForegroundMask;
|
||||
};
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
|
||||
bool BackgroundSubtractorMOG2Impl::ocl_apply(InputArray _image, OutputArray _fgmask, double learningRate)
|
||||
{
|
||||
bool needToInitialize = nframes == 0 || learningRate >= 1 || _image.size() != frameSize || _image.type() != frameType;
|
||||
|
||||
if( needToInitialize )
|
||||
initialize(_image.size(), _image.type());
|
||||
|
||||
++nframes;
|
||||
learningRate = learningRate >= 0 && nframes > 1 ? learningRate : 1./std::min( 2*nframes, history );
|
||||
CV_Assert(learningRate >= 0);
|
||||
|
||||
_fgmask.create(_image.size(), CV_8U);
|
||||
UMat fgmask = _fgmask.getUMat();
|
||||
|
||||
const double alpha1 = 1.0f - learningRate;
|
||||
|
||||
UMat frame = _image.getUMat();
|
||||
|
||||
float varMax = MAX(fVarMin, fVarMax);
|
||||
float varMin = MIN(fVarMin, fVarMax);
|
||||
|
||||
int idxArg = 0;
|
||||
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::ReadOnly(frame));
|
||||
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_bgmodelUsedModes));
|
||||
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_weight));
|
||||
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_mean));
|
||||
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::PtrReadWrite(u_variance));
|
||||
idxArg = kernel_apply.set(idxArg, ocl::KernelArg::WriteOnlyNoSize(fgmask));
|
||||
|
||||
idxArg = kernel_apply.set(idxArg, (float)learningRate); //alphaT
|
||||
idxArg = kernel_apply.set(idxArg, (float)alpha1);
|
||||
idxArg = kernel_apply.set(idxArg, (float)(-learningRate*fCT)); //prune
|
||||
|
||||
idxArg = kernel_apply.set(idxArg, (float)varThreshold); //c_Tb
|
||||
idxArg = kernel_apply.set(idxArg, backgroundRatio); //c_TB
|
||||
idxArg = kernel_apply.set(idxArg, varThresholdGen); //c_Tg
|
||||
idxArg = kernel_apply.set(idxArg, varMin);
|
||||
idxArg = kernel_apply.set(idxArg, varMax);
|
||||
idxArg = kernel_apply.set(idxArg, fVarInit);
|
||||
idxArg = kernel_apply.set(idxArg, fTau);
|
||||
if (bShadowDetection)
|
||||
kernel_apply.set(idxArg, nShadowDetection);
|
||||
|
||||
size_t globalsize[] = {(size_t)frame.cols, (size_t)frame.rows, 1};
|
||||
return kernel_apply.run(2, globalsize, NULL, true);
|
||||
}
|
||||
|
||||
bool BackgroundSubtractorMOG2Impl::ocl_getBackgroundImage(OutputArray _backgroundImage) const
|
||||
{
|
||||
_backgroundImage.create(frameSize, frameType);
|
||||
UMat dst = _backgroundImage.getUMat();
|
||||
|
||||
int idxArg = 0;
|
||||
idxArg = kernel_getBg.set(idxArg, ocl::KernelArg::PtrReadOnly(u_bgmodelUsedModes));
|
||||
idxArg = kernel_getBg.set(idxArg, ocl::KernelArg::PtrReadOnly(u_weight));
|
||||
idxArg = kernel_getBg.set(idxArg, ocl::KernelArg::PtrReadOnly(u_mean));
|
||||
idxArg = kernel_getBg.set(idxArg, ocl::KernelArg::WriteOnly(dst));
|
||||
kernel_getBg.set(idxArg, backgroundRatio);
|
||||
|
||||
size_t globalsize[2] = {(size_t)u_bgmodelUsedModes.cols, (size_t)u_bgmodelUsedModes.rows};
|
||||
|
||||
return kernel_getBg.run(2, globalsize, NULL, false);
|
||||
}
|
||||
|
||||
void BackgroundSubtractorMOG2Impl::create_ocl_apply_kernel()
|
||||
{
|
||||
int nchannels = CV_MAT_CN(frameType);
|
||||
bool isFloat = CV_MAKETYPE(CV_32F,nchannels) == frameType;
|
||||
String opts = format("-D CN=%d -D FL=%d -D NMIXTURES=%d%s", nchannels, isFloat, nmixtures, bShadowDetection ? " -D SHADOW_DETECT" : "");
|
||||
kernel_apply.create("mog2_kernel", ocl::video::bgfg_mog2_oclsrc, opts);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Base 3 version class
|
||||
void BackgroundSubtractorMOG2Impl::apply(InputArray _image, OutputArray _fgmask, double learningRate) {
|
||||
apply(_image, noArray(), _fgmask, learningRate);
|
||||
}
|
||||
|
||||
void BackgroundSubtractorMOG2Impl::apply(InputArray _image, InputArray _knownForegroundMask, OutputArray _fgmask, double learningRate)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
if (opencl_ON)
|
||||
{
|
||||
CV_OCL_RUN(_fgmask.isUMat(), ocl_apply(_image, _fgmask, learningRate))
|
||||
|
||||
opencl_ON = false;
|
||||
nframes = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool needToInitialize = nframes == 0 || learningRate >= 1 || _image.size() != frameSize || _image.type() != frameType;
|
||||
|
||||
if( needToInitialize )
|
||||
initialize(_image.size(), _image.type());
|
||||
|
||||
Mat image = _image.getMat();
|
||||
_fgmask.create( image.size(), CV_8U );
|
||||
Mat fgmask = _fgmask.getMat();
|
||||
|
||||
Mat knownForegroundMask = _knownForegroundMask.getMat();
|
||||
|
||||
if(!knownForegroundMask.empty())
|
||||
{
|
||||
CV_Assert(knownForegroundMask.type() == CV_8UC1);
|
||||
CV_Assert(knownForegroundMask.size() == image.size());
|
||||
}
|
||||
|
||||
++nframes;
|
||||
learningRate = learningRate >= 0 && nframes > 1 ? learningRate : 1./std::min( 2*nframes, history );
|
||||
CV_Assert(learningRate >= 0);
|
||||
|
||||
parallel_for_(Range(0, image.rows),
|
||||
MOG2Invoker(image, fgmask,
|
||||
bgmodel.ptr<GMM>(),
|
||||
(float*)(bgmodel.ptr() + sizeof(GMM)*nmixtures*image.rows*image.cols),
|
||||
bgmodelUsedModes.ptr(), nmixtures, (float)learningRate,
|
||||
(float)varThreshold,
|
||||
backgroundRatio, varThresholdGen,
|
||||
fVarInit, fVarMin, fVarMax, float(-learningRate*fCT), fTau,
|
||||
bShadowDetection, nShadowDetection, knownForegroundMask),
|
||||
image.total()/(double)(1 << 16));
|
||||
}
|
||||
|
||||
template <typename T, int CN>
|
||||
void BackgroundSubtractorMOG2Impl::getBackgroundImage_intern(OutputArray backgroundImage) const
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat meanBackground(frameSize, frameType, Scalar::all(0));
|
||||
int firstGaussianIdx = 0;
|
||||
const GMM* gmm = bgmodel.ptr<GMM>();
|
||||
const float* mean = reinterpret_cast<const float*>(gmm + frameSize.width*frameSize.height*nmixtures);
|
||||
Vec<float,CN> meanVal(0.f);
|
||||
for(int row=0; row<meanBackground.rows; row++)
|
||||
{
|
||||
for(int col=0; col<meanBackground.cols; col++)
|
||||
{
|
||||
int nmodes = bgmodelUsedModes.at<uchar>(row, col);
|
||||
float totalWeight = 0.f;
|
||||
for(int gaussianIdx = firstGaussianIdx; gaussianIdx < firstGaussianIdx + nmodes; gaussianIdx++)
|
||||
{
|
||||
GMM gaussian = gmm[gaussianIdx];
|
||||
size_t meanPosition = gaussianIdx*CN;
|
||||
for(int chn = 0; chn < CN; chn++)
|
||||
{
|
||||
meanVal(chn) += gaussian.weight * mean[meanPosition + chn];
|
||||
}
|
||||
totalWeight += gaussian.weight;
|
||||
|
||||
if(totalWeight > backgroundRatio)
|
||||
break;
|
||||
}
|
||||
float invWeight = 0.f;
|
||||
if (std::abs(totalWeight) > FLT_EPSILON) {
|
||||
invWeight = 1.f/totalWeight;
|
||||
}
|
||||
|
||||
meanBackground.at<Vec<T,CN> >(row, col) = Vec<T,CN>(meanVal * invWeight);
|
||||
meanVal = 0.f;
|
||||
|
||||
firstGaussianIdx += nmixtures;
|
||||
}
|
||||
}
|
||||
meanBackground.copyTo(backgroundImage);
|
||||
}
|
||||
|
||||
void BackgroundSubtractorMOG2Impl::getBackgroundImage(OutputArray backgroundImage) const
|
||||
{
|
||||
CV_Assert(frameType == CV_8UC1 || frameType == CV_8UC3 || frameType == CV_32FC1 || frameType == CV_32FC3);
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
if (opencl_ON)
|
||||
{
|
||||
CV_OCL_RUN(opencl_ON, ocl_getBackgroundImage(backgroundImage))
|
||||
|
||||
opencl_ON = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
switch(frameType)
|
||||
{
|
||||
case CV_8UC1:
|
||||
getBackgroundImage_intern<uchar,1>(backgroundImage);
|
||||
break;
|
||||
case CV_8UC3:
|
||||
getBackgroundImage_intern<uchar,3>(backgroundImage);
|
||||
break;
|
||||
case CV_32FC1:
|
||||
getBackgroundImage_intern<float,1>(backgroundImage);
|
||||
break;
|
||||
case CV_32FC3:
|
||||
getBackgroundImage_intern<float,3>(backgroundImage);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ptr<BackgroundSubtractorMOG2> createBackgroundSubtractorMOG2(int _history, double _varThreshold,
|
||||
bool _bShadowDetection)
|
||||
{
|
||||
return makePtr<BackgroundSubtractorMOG2Impl>(_history, (float)_varThreshold, _bShadowDetection);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* End of file. */
|
||||
@@ -0,0 +1,220 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of Intel Corporation may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
#include "precomp.hpp"
|
||||
|
||||
int cv::meanShift( InputArray _probImage, Rect& window, TermCriteria criteria )
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Size size;
|
||||
int cn;
|
||||
Mat mat;
|
||||
UMat umat;
|
||||
bool isUMat = _probImage.isUMat();
|
||||
|
||||
if (isUMat)
|
||||
umat = _probImage.getUMat(), cn = umat.channels(), size = umat.size();
|
||||
else
|
||||
mat = _probImage.getMat(), cn = mat.channels(), size = mat.size();
|
||||
|
||||
Rect cur_rect = window;
|
||||
|
||||
CV_Assert( cn == 1 );
|
||||
|
||||
if( window.height <= 0 || window.width <= 0 )
|
||||
CV_Error( Error::StsBadArg, "Input window has non-positive sizes" );
|
||||
|
||||
window = window & Rect(0, 0, size.width, size.height);
|
||||
|
||||
double eps = (criteria.type & TermCriteria::EPS) ? std::max(criteria.epsilon, 0.) : 1.;
|
||||
eps = cvRound(eps*eps);
|
||||
int i, niters = (criteria.type & TermCriteria::MAX_ITER) ? std::max(criteria.maxCount, 1) : 100;
|
||||
|
||||
for( i = 0; i < niters; i++ )
|
||||
{
|
||||
cur_rect = cur_rect & Rect(0, 0, size.width, size.height);
|
||||
if( cur_rect == Rect() )
|
||||
{
|
||||
cur_rect.x = size.width/2;
|
||||
cur_rect.y = size.height/2;
|
||||
}
|
||||
cur_rect.width = std::max(cur_rect.width, 1);
|
||||
cur_rect.height = std::max(cur_rect.height, 1);
|
||||
|
||||
Moments m = isUMat ? moments(umat(cur_rect)) : moments(mat(cur_rect));
|
||||
|
||||
// Calculating center of mass
|
||||
if( fabs(m.m00) < DBL_EPSILON )
|
||||
break;
|
||||
|
||||
int dx = cvRound( m.m10/m.m00 - window.width*0.5 );
|
||||
int dy = cvRound( m.m01/m.m00 - window.height*0.5 );
|
||||
|
||||
int nx = std::min(std::max(cur_rect.x + dx, 0), size.width - cur_rect.width);
|
||||
int ny = std::min(std::max(cur_rect.y + dy, 0), size.height - cur_rect.height);
|
||||
|
||||
dx = nx - cur_rect.x;
|
||||
dy = ny - cur_rect.y;
|
||||
cur_rect.x = nx;
|
||||
cur_rect.y = ny;
|
||||
|
||||
// Check for coverage centers mass & window
|
||||
if( dx*dx + dy*dy < eps )
|
||||
break;
|
||||
}
|
||||
|
||||
window = cur_rect;
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
cv::RotatedRect cv::CamShift( InputArray _probImage, Rect& window,
|
||||
TermCriteria criteria )
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
const int TOLERANCE = 10;
|
||||
Size size;
|
||||
Mat mat;
|
||||
UMat umat;
|
||||
bool isUMat = _probImage.isUMat();
|
||||
|
||||
if (isUMat)
|
||||
umat = _probImage.getUMat(), size = umat.size();
|
||||
else
|
||||
mat = _probImage.getMat(), size = mat.size();
|
||||
|
||||
meanShift( _probImage, window, criteria );
|
||||
|
||||
window.x -= TOLERANCE;
|
||||
if( window.x < 0 )
|
||||
window.x = 0;
|
||||
|
||||
window.y -= TOLERANCE;
|
||||
if( window.y < 0 )
|
||||
window.y = 0;
|
||||
|
||||
window.width += 2 * TOLERANCE;
|
||||
if( window.x + window.width > size.width )
|
||||
window.width = size.width - window.x;
|
||||
|
||||
window.height += 2 * TOLERANCE;
|
||||
if( window.y + window.height > size.height )
|
||||
window.height = size.height - window.y;
|
||||
|
||||
// Calculating moments in new center mass
|
||||
Moments m = isUMat ? moments(umat(window)) : moments(mat(window));
|
||||
|
||||
double m00 = m.m00, m10 = m.m10, m01 = m.m01;
|
||||
double mu11 = m.mu11, mu20 = m.mu20, mu02 = m.mu02;
|
||||
|
||||
if( fabs(m00) < DBL_EPSILON )
|
||||
return RotatedRect();
|
||||
|
||||
double inv_m00 = 1. / m00;
|
||||
int xc = cvRound( m10 * inv_m00 + window.x );
|
||||
int yc = cvRound( m01 * inv_m00 + window.y );
|
||||
double a = mu20 * inv_m00, b = mu11 * inv_m00, c = mu02 * inv_m00;
|
||||
|
||||
// Calculating width & height
|
||||
double square = std::sqrt( 4 * b * b + (a - c) * (a - c) );
|
||||
|
||||
// Calculating orientation
|
||||
double theta = atan2( 2 * b, a - c + square );
|
||||
|
||||
// Calculating width & length of figure
|
||||
double cs = cos( theta );
|
||||
double sn = sin( theta );
|
||||
|
||||
double rotate_a = cs * cs * mu20 + 2 * cs * sn * mu11 + sn * sn * mu02;
|
||||
double rotate_c = sn * sn * mu20 - 2 * cs * sn * mu11 + cs * cs * mu02;
|
||||
rotate_a = std::max(0.0, rotate_a); // avoid negative result due calculation numeric errors
|
||||
rotate_c = std::max(0.0, rotate_c); // avoid negative result due calculation numeric errors
|
||||
double length = std::sqrt( rotate_a * inv_m00 ) * 4;
|
||||
double width = std::sqrt( rotate_c * inv_m00 ) * 4;
|
||||
|
||||
// In case, when tetta is 0 or 1.57... the Length & Width may be exchanged
|
||||
if( length < width )
|
||||
{
|
||||
std::swap( length, width );
|
||||
std::swap( cs, sn );
|
||||
theta = CV_PI*0.5 - theta;
|
||||
}
|
||||
|
||||
// Saving results
|
||||
int _xc = cvRound( xc );
|
||||
int _yc = cvRound( yc );
|
||||
|
||||
int t0 = cvRound( fabs( length * cs ));
|
||||
int t1 = cvRound( fabs( width * sn ));
|
||||
|
||||
t0 = MAX( t0, t1 ) + 2;
|
||||
window.width = MIN( t0, (size.width - _xc) * 2 );
|
||||
|
||||
t0 = cvRound( fabs( length * sn ));
|
||||
t1 = cvRound( fabs( width * cs ));
|
||||
|
||||
t0 = MAX( t0, t1 ) + 2;
|
||||
window.height = MIN( t0, (size.height - _yc) * 2 );
|
||||
|
||||
window.x = MAX( 0, _xc - window.width / 2 );
|
||||
window.y = MAX( 0, _yc - window.height / 2 );
|
||||
|
||||
window.width = MIN( size.width - window.x, window.width );
|
||||
window.height = MIN( size.height - window.y, window.height );
|
||||
|
||||
RotatedRect box;
|
||||
box.size.height = (float)length;
|
||||
box.size.width = (float)width;
|
||||
box.angle = (float)((CV_PI*0.5+theta)*180./CV_PI);
|
||||
while(box.angle < 0)
|
||||
box.angle += 360;
|
||||
while(box.angle >= 360)
|
||||
box.angle -= 360;
|
||||
if(box.angle >= 180)
|
||||
box.angle -= 180;
|
||||
box.center = Point2f( window.x + window.width*0.5f, window.y + window.height*0.5f);
|
||||
|
||||
return box;
|
||||
}
|
||||
|
||||
/* End of file. */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,632 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of Intel Corporation may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
/****************************************************************************************\
|
||||
* Image Alignment (ECC algorithm) *
|
||||
\****************************************************************************************/
|
||||
|
||||
using namespace cv;
|
||||
|
||||
static void image_jacobian_homo_ECC(const Mat& src1, const Mat& src2, const Mat& src3, const Mat& src4, const Mat& src5,
|
||||
Mat& dst) {
|
||||
CV_Assert(src1.size() == src2.size());
|
||||
CV_Assert(src1.size() == src3.size());
|
||||
CV_Assert(src1.size() == src4.size());
|
||||
|
||||
CV_Assert(src1.rows == dst.rows);
|
||||
CV_Assert(dst.cols == (src1.cols * 8));
|
||||
CV_Assert(dst.type() == CV_MAKETYPE(CV_32F, src1.channels()));
|
||||
|
||||
CV_Assert(src5.isContinuous());
|
||||
|
||||
const float* hptr = src5.ptr<float>(0);
|
||||
|
||||
const float h0_ = hptr[0];
|
||||
const float h1_ = hptr[3];
|
||||
const float h2_ = hptr[6];
|
||||
const float h3_ = hptr[1];
|
||||
const float h4_ = hptr[4];
|
||||
const float h5_ = hptr[7];
|
||||
const float h6_ = hptr[2];
|
||||
const float h7_ = hptr[5];
|
||||
|
||||
const int w = src1.cols;
|
||||
|
||||
// create denominator for all points as a block
|
||||
Mat den_;
|
||||
addWeighted(src3, h2_, src4, h5_, 1.0, den_);
|
||||
|
||||
// create projected points
|
||||
Mat hatX_, hatY_;
|
||||
addWeighted(src3, h0_, src4, h3_, 0.0, hatX_);
|
||||
hatX_ += h6_;
|
||||
|
||||
addWeighted(src3, h1_, src4, h4_, 0.0, hatY_);
|
||||
hatY_ += h7_;
|
||||
|
||||
divide(-hatY_, den_, hatY_);
|
||||
divide(-hatX_, den_, hatX_);
|
||||
|
||||
// instead of dividing each block with den,
|
||||
// just pre-divide the block of gradients (it's more efficient)
|
||||
|
||||
Mat src1Divided_;
|
||||
Mat src2Divided_;
|
||||
|
||||
divide(src1, den_, src1Divided_);
|
||||
divide(src2, den_, src2Divided_);
|
||||
|
||||
// compute Jacobian blocks (8 blocks)
|
||||
|
||||
dst.colRange(0, w) = src1Divided_.mul(src3); // 1
|
||||
|
||||
dst.colRange(w, 2 * w) = src2Divided_.mul(src3); // 2
|
||||
|
||||
Mat temp_ = (hatX_.mul(src1Divided_) + hatY_.mul(src2Divided_));
|
||||
dst.colRange(2 * w, 3 * w) = temp_.mul(src3); // 3
|
||||
|
||||
hatX_.release();
|
||||
hatY_.release();
|
||||
|
||||
dst.colRange(3 * w, 4 * w) = src1Divided_.mul(src4); // 4
|
||||
|
||||
dst.colRange(4 * w, 5 * w) = src2Divided_.mul(src4); // 5
|
||||
|
||||
dst.colRange(5 * w, 6 * w) = temp_.mul(src4); // 6
|
||||
|
||||
src1Divided_.copyTo(dst.colRange(6 * w, 7 * w)); // 7
|
||||
|
||||
src2Divided_.copyTo(dst.colRange(7 * w, 8 * w)); // 8
|
||||
}
|
||||
|
||||
static void image_jacobian_euclidean_ECC(const Mat& src1, const Mat& src2, const Mat& src3, const Mat& src4,
|
||||
const Mat& src5, Mat& dst) {
|
||||
CV_Assert(src1.size() == src2.size());
|
||||
CV_Assert(src1.size() == src3.size());
|
||||
CV_Assert(src1.size() == src4.size());
|
||||
|
||||
CV_Assert(src1.rows == dst.rows);
|
||||
CV_Assert(dst.cols == (src1.cols * 3));
|
||||
CV_Assert(dst.type() == CV_MAKETYPE(CV_32F, src1.channels()));
|
||||
|
||||
CV_Assert(src5.isContinuous());
|
||||
|
||||
const float* hptr = src5.ptr<float>(0);
|
||||
|
||||
const float h0 = hptr[0]; // cos(theta)
|
||||
const float h1 = hptr[3]; // sin(theta)
|
||||
|
||||
const int w = src1.cols;
|
||||
|
||||
// create -sin(theta)*X -cos(theta)*Y for all points as a block -> hatX
|
||||
Mat hatX = -(src3 * h1) - (src4 * h0);
|
||||
|
||||
// create cos(theta)*X -sin(theta)*Y for all points as a block -> hatY
|
||||
Mat hatY = (src3 * h0) - (src4 * h1);
|
||||
|
||||
// compute Jacobian blocks (3 blocks)
|
||||
dst.colRange(0, w) = (src1.mul(hatX)) + (src2.mul(hatY)); // 1
|
||||
|
||||
src1.copyTo(dst.colRange(w, 2 * w)); // 2
|
||||
src2.copyTo(dst.colRange(2 * w, 3 * w)); // 3
|
||||
}
|
||||
|
||||
static void image_jacobian_affine_ECC(const Mat& src1, const Mat& src2, const Mat& src3, const Mat& src4, Mat& dst) {
|
||||
CV_Assert(src1.size() == src2.size());
|
||||
CV_Assert(src1.size() == src3.size());
|
||||
CV_Assert(src1.size() == src4.size());
|
||||
|
||||
CV_Assert(src1.rows == dst.rows);
|
||||
CV_Assert(dst.cols == (6 * src1.cols));
|
||||
|
||||
CV_Assert(dst.type() == CV_MAKETYPE(CV_32F, src1.channels()));
|
||||
|
||||
const int w = src1.cols;
|
||||
|
||||
// compute Jacobian blocks (6 blocks)
|
||||
|
||||
dst.colRange(0, w) = src1.mul(src3); // 1
|
||||
dst.colRange(w, 2 * w) = src2.mul(src3); // 2
|
||||
dst.colRange(2 * w, 3 * w) = src1.mul(src4); // 3
|
||||
dst.colRange(3 * w, 4 * w) = src2.mul(src4); // 4
|
||||
src1.copyTo(dst.colRange(4 * w, 5 * w)); // 5
|
||||
src2.copyTo(dst.colRange(5 * w, 6 * w)); // 6
|
||||
}
|
||||
|
||||
static void image_jacobian_translation_ECC(const Mat& src1, const Mat& src2, Mat& dst) {
|
||||
CV_Assert(src1.size() == src2.size());
|
||||
|
||||
CV_Assert(src1.rows == dst.rows);
|
||||
CV_Assert(dst.cols == (src1.cols * 2));
|
||||
CV_Assert(dst.type() == CV_MAKETYPE(CV_32F, src1.channels()));
|
||||
|
||||
const int w = src1.cols;
|
||||
|
||||
// compute Jacobian blocks (2 blocks)
|
||||
src1.copyTo(dst.colRange(0, w));
|
||||
src2.copyTo(dst.colRange(w, 2 * w));
|
||||
}
|
||||
|
||||
static void project_onto_jacobian_ECC(const Mat& src1, const Mat& src2, Mat& dst) {
|
||||
/* this functions is used for two types of projections. If src1.cols ==src.cols
|
||||
it does a blockwise multiplication (like in the outer product of vectors)
|
||||
of the blocks in matrices src1 and src2 and dst
|
||||
has size (number_of_blcks x number_of_blocks), otherwise dst is a vector of size
|
||||
(number_of_blocks x 1) since src2 is "multiplied"(dot) with each block of src1.
|
||||
|
||||
The number_of_blocks is equal to the number of parameters we are lloking for
|
||||
(i.e. rtanslation:2, euclidean: 3, affine: 6, homography: 8)
|
||||
|
||||
*/
|
||||
CV_Assert(src1.rows == src2.rows);
|
||||
CV_Assert((src1.cols % src2.cols) == 0);
|
||||
int w;
|
||||
|
||||
float* dstPtr = dst.ptr<float>(0);
|
||||
|
||||
if (src1.cols != src2.cols) { // dst.cols==1
|
||||
w = src2.cols;
|
||||
for (int i = 0; i < dst.rows; i++) {
|
||||
dstPtr[i] = (float)src2.dot(src1.colRange(i * w, (i + 1) * w));
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
CV_Assert(dst.cols == dst.rows); // dst is square (and symmetric)
|
||||
w = src2.cols / dst.cols;
|
||||
Mat mat;
|
||||
for (int i = 0; i < dst.rows; i++) {
|
||||
mat = Mat(src1.colRange(i * w, (i + 1) * w));
|
||||
dstPtr[i * (dst.rows + 1)] = (float)std::pow(norm(mat), 2); // diagonal elements
|
||||
|
||||
for (int j = i + 1; j < dst.cols; j++) { // j starts from i+1
|
||||
dstPtr[i * dst.cols + j] = (float)mat.dot(src2.colRange(j * w, (j + 1) * w));
|
||||
dstPtr[j * dst.cols + i] = dstPtr[i * dst.cols + j]; // due to symmetry
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void update_warping_matrix_ECC(Mat& map_matrix, const Mat& update, const int motionType) {
|
||||
CV_Assert(map_matrix.type() == CV_32FC1);
|
||||
CV_Assert(update.type() == CV_32FC1);
|
||||
|
||||
CV_Assert(motionType == MOTION_TRANSLATION || motionType == MOTION_EUCLIDEAN || motionType == MOTION_AFFINE ||
|
||||
motionType == MOTION_HOMOGRAPHY);
|
||||
|
||||
if (motionType == MOTION_HOMOGRAPHY)
|
||||
CV_Assert(map_matrix.rows == 3 && update.rows == 8);
|
||||
else if (motionType == MOTION_AFFINE)
|
||||
CV_Assert(map_matrix.rows == 2 && update.rows == 6);
|
||||
else if (motionType == MOTION_EUCLIDEAN)
|
||||
CV_Assert(map_matrix.rows == 2 && update.rows == 3);
|
||||
else
|
||||
CV_Assert(map_matrix.rows == 2 && update.rows == 2);
|
||||
|
||||
CV_Assert(update.cols == 1);
|
||||
|
||||
CV_Assert(map_matrix.isContinuous());
|
||||
CV_Assert(update.isContinuous());
|
||||
|
||||
float* mapPtr = map_matrix.ptr<float>(0);
|
||||
const float* updatePtr = update.ptr<float>(0);
|
||||
|
||||
if (motionType == MOTION_TRANSLATION) {
|
||||
mapPtr[2] += updatePtr[0];
|
||||
mapPtr[5] += updatePtr[1];
|
||||
}
|
||||
if (motionType == MOTION_AFFINE) {
|
||||
mapPtr[0] += updatePtr[0];
|
||||
mapPtr[3] += updatePtr[1];
|
||||
mapPtr[1] += updatePtr[2];
|
||||
mapPtr[4] += updatePtr[3];
|
||||
mapPtr[2] += updatePtr[4];
|
||||
mapPtr[5] += updatePtr[5];
|
||||
}
|
||||
if (motionType == MOTION_HOMOGRAPHY) {
|
||||
mapPtr[0] += updatePtr[0];
|
||||
mapPtr[3] += updatePtr[1];
|
||||
mapPtr[6] += updatePtr[2];
|
||||
mapPtr[1] += updatePtr[3];
|
||||
mapPtr[4] += updatePtr[4];
|
||||
mapPtr[7] += updatePtr[5];
|
||||
mapPtr[2] += updatePtr[6];
|
||||
mapPtr[5] += updatePtr[7];
|
||||
}
|
||||
if (motionType == MOTION_EUCLIDEAN) {
|
||||
double new_theta = updatePtr[0];
|
||||
new_theta += asin(mapPtr[3]);
|
||||
|
||||
mapPtr[2] += updatePtr[1];
|
||||
mapPtr[5] += updatePtr[2];
|
||||
mapPtr[0] = mapPtr[4] = (float)cos(new_theta);
|
||||
mapPtr[3] = (float)sin(new_theta);
|
||||
mapPtr[1] = -mapPtr[3];
|
||||
}
|
||||
}
|
||||
|
||||
/** Function that computes enhanced corelation coefficient from Georgios et.al. 2008
|
||||
* See https://github.com/opencv/opencv/issues/12432
|
||||
*/
|
||||
double cv::computeECC(InputArray templateImage, InputArray inputImage, InputArray inputMask) {
|
||||
CV_Assert(!templateImage.empty());
|
||||
CV_Assert(!inputImage.empty());
|
||||
|
||||
CV_Assert(templateImage.channels() == 1 || templateImage.channels() == 3);
|
||||
|
||||
if (!(templateImage.type() == inputImage.type()))
|
||||
CV_Error(Error::StsUnmatchedFormats, "Both input images must have the same data type");
|
||||
|
||||
Scalar meanTemplate, sdTemplate;
|
||||
|
||||
int active_pixels = inputMask.empty() ? templateImage.size().area() : countNonZero(inputMask);
|
||||
int type = templateImage.type();
|
||||
meanStdDev(templateImage, meanTemplate, sdTemplate, inputMask);
|
||||
Mat templateImage_zeromean = Mat::zeros(templateImage.size(), templateImage.type());
|
||||
Mat templateMat = templateImage.getMat();
|
||||
Mat inputMat = inputImage.getMat();
|
||||
|
||||
/*
|
||||
* For unsigned ints, when the mean is computed and subtracted, any values less than the mean
|
||||
* will be set to 0 (since there are no negatives values). This impacts the norm and dot product, which
|
||||
* ultimately results in an incorrect ECC. To circumvent this problem, if unsigned ints are provided,
|
||||
* we convert them to a signed ints with larger resolution for the subtraction step.
|
||||
*/
|
||||
if (type == CV_8U || type == CV_16U) {
|
||||
int newType = type == CV_8U ? CV_16S : CV_32S;
|
||||
Mat templateMatConverted, inputMatConverted;
|
||||
templateMat.convertTo(templateMatConverted, newType);
|
||||
cv::swap(templateMat, templateMatConverted);
|
||||
inputMat.convertTo(inputMatConverted, newType);
|
||||
cv::swap(inputMat, inputMatConverted);
|
||||
}
|
||||
subtract(templateMat, meanTemplate, templateImage_zeromean, inputMask);
|
||||
double templateImagenorm = std::sqrt(active_pixels * cv::norm(sdTemplate, NORM_L2SQR));
|
||||
|
||||
Scalar meanInput, sdInput;
|
||||
|
||||
Mat inputImage_zeromean = Mat::zeros(inputImage.size(), inputImage.type());
|
||||
meanStdDev(inputImage, meanInput, sdInput, inputMask);
|
||||
subtract(inputMat, meanInput, inputImage_zeromean, inputMask);
|
||||
double inputImagenorm = std::sqrt(active_pixels * norm(sdInput, NORM_L2SQR));
|
||||
|
||||
return templateImage_zeromean.dot(inputImage_zeromean) / (templateImagenorm * inputImagenorm);
|
||||
}
|
||||
|
||||
|
||||
double cv::findTransformECCWithMask( InputArray templateImage,
|
||||
InputArray inputImage,
|
||||
InputArray templateMask,
|
||||
InputArray inputMask,
|
||||
InputOutputArray warpMatrix,
|
||||
int motionType,
|
||||
TermCriteria criteria,
|
||||
int gaussFiltSize) {
|
||||
Mat src = templateImage.getMat(); // template image
|
||||
Mat dst = inputImage.getMat(); // input image (to be warped)
|
||||
Mat map = warpMatrix.getMat(); // warp (transformation)
|
||||
|
||||
CV_Assert(!src.empty());
|
||||
CV_Assert(!dst.empty());
|
||||
|
||||
CV_Assert(src.channels() == 1 || src.channels() == 3);
|
||||
CV_Assert(src.channels() == dst.channels());
|
||||
CV_Assert(src.depth() == dst.depth());
|
||||
CV_Assert(src.depth() == CV_8U || src.depth() == CV_16U || src.depth() == CV_32F || src.depth() == CV_64F);
|
||||
|
||||
// If the user passed an un-initialized warpMatrix, initialize to identity
|
||||
if (map.empty()) {
|
||||
int rowCount = 2;
|
||||
if (motionType == MOTION_HOMOGRAPHY)
|
||||
rowCount = 3;
|
||||
|
||||
warpMatrix.create(rowCount, 3, CV_32FC1);
|
||||
map = warpMatrix.getMat();
|
||||
map = Mat::eye(rowCount, 3, CV_32F);
|
||||
}
|
||||
|
||||
if (!(src.type() == dst.type()))
|
||||
CV_Error(Error::StsUnmatchedFormats, "Both input images must have the same data type");
|
||||
|
||||
if (map.type() != CV_32FC1)
|
||||
CV_Error(Error::StsUnsupportedFormat, "warpMatrix must be single-channel floating-point matrix");
|
||||
|
||||
CV_Assert(map.cols == 3);
|
||||
CV_Assert(map.rows == 2 || map.rows == 3);
|
||||
|
||||
CV_Assert(motionType == MOTION_AFFINE || motionType == MOTION_HOMOGRAPHY || motionType == MOTION_EUCLIDEAN ||
|
||||
motionType == MOTION_TRANSLATION);
|
||||
|
||||
if (motionType == MOTION_HOMOGRAPHY) {
|
||||
CV_Assert(map.rows == 3);
|
||||
}
|
||||
|
||||
CV_Assert(criteria.type & TermCriteria::COUNT || criteria.type & TermCriteria::EPS);
|
||||
const int numberOfIterations = (criteria.type & TermCriteria::COUNT) ? criteria.maxCount : 200;
|
||||
const double termination_eps = (criteria.type & TermCriteria::EPS) ? criteria.epsilon : -1;
|
||||
|
||||
int paramTemp = 6; // default: affine
|
||||
switch (motionType) {
|
||||
case MOTION_TRANSLATION:
|
||||
paramTemp = 2;
|
||||
break;
|
||||
case MOTION_EUCLIDEAN:
|
||||
paramTemp = 3;
|
||||
break;
|
||||
case MOTION_HOMOGRAPHY:
|
||||
paramTemp = 8;
|
||||
break;
|
||||
}
|
||||
|
||||
const int numberOfParameters = paramTemp;
|
||||
|
||||
const int ws = src.cols;
|
||||
const int hs = src.rows;
|
||||
const int wd = dst.cols;
|
||||
const int hd = dst.rows;
|
||||
|
||||
Mat Xcoord = Mat(1, ws, CV_32F);
|
||||
Mat Ycoord = Mat(hs, 1, CV_32F);
|
||||
Mat Xgrid = Mat(hs, ws, CV_32F);
|
||||
Mat Ygrid = Mat(hs, ws, CV_32F);
|
||||
|
||||
float* XcoPtr = Xcoord.ptr<float>(0);
|
||||
float* YcoPtr = Ycoord.ptr<float>(0);
|
||||
int j;
|
||||
for (j = 0; j < ws; j++) XcoPtr[j] = (float)j;
|
||||
for (j = 0; j < hs; j++) YcoPtr[j] = (float)j;
|
||||
|
||||
repeat(Xcoord, hs, 1, Xgrid);
|
||||
repeat(Ycoord, 1, ws, Ygrid);
|
||||
|
||||
Xcoord.release();
|
||||
Ycoord.release();
|
||||
|
||||
const int channels = src.channels();
|
||||
int type = CV_MAKETYPE(CV_32F, channels);
|
||||
|
||||
std::vector<cv::Mat> XgridCh(channels, Xgrid);
|
||||
cv::merge(XgridCh, Xgrid);
|
||||
|
||||
std::vector<cv::Mat> YgridCh(channels, Ygrid);
|
||||
cv::merge(YgridCh, Ygrid);
|
||||
|
||||
Mat templateZM = Mat(hs, ws, type); // to store the (smoothed)zero-mean version of template
|
||||
Mat templateFloat = Mat(hs, ws, type); // to store the (smoothed) template
|
||||
Mat imageFloat = Mat(hd, wd, type); // to store the (smoothed) input image
|
||||
Mat imageWarped = Mat(hs, ws, type); // to store the warped zero-mean input image
|
||||
Mat imageMask = Mat(hs, ws, CV_8U); // to store the final mask
|
||||
|
||||
// Gaussian filtering is optional
|
||||
src.convertTo(templateFloat, templateFloat.type());
|
||||
GaussianBlur(templateFloat, templateFloat, Size(gaussFiltSize, gaussFiltSize), 0, 0);
|
||||
|
||||
dst.convertTo(imageFloat, imageFloat.type());
|
||||
GaussianBlur(imageFloat, imageFloat, Size(gaussFiltSize, gaussFiltSize), 0, 0);
|
||||
|
||||
// needed matrices for gradients and warped gradients
|
||||
Mat gradientX = Mat::zeros(hd, wd, type);
|
||||
Mat gradientY = Mat::zeros(hd, wd, type);
|
||||
Mat gradientXWarped = Mat(hs, ws, type);
|
||||
Mat gradientYWarped = Mat(hs, ws, type);
|
||||
|
||||
// calculate first order image derivatives
|
||||
Matx13f dx(-0.5f, 0.0f, 0.5f);
|
||||
|
||||
filter2D(imageFloat, gradientX, -1, dx);
|
||||
filter2D(imageFloat, gradientY, -1, dx.t());
|
||||
|
||||
// To use in mask warping
|
||||
Mat templtMask;
|
||||
if(templateMask.empty())
|
||||
{
|
||||
templtMask = Mat::ones(hs, ws, CV_8U);
|
||||
}
|
||||
else
|
||||
{
|
||||
threshold(templateMask, templtMask, 0, 1, THRESH_BINARY);
|
||||
templtMask.convertTo(templtMask, CV_32F);
|
||||
GaussianBlur(templtMask, templtMask, Size(gaussFiltSize, gaussFiltSize), 0, 0);
|
||||
templtMask *= (0.5/0.95);
|
||||
templtMask.convertTo(templtMask, CV_8U);
|
||||
}
|
||||
|
||||
//to use it for mask warping
|
||||
Mat preMask;
|
||||
if(inputMask.empty())
|
||||
{
|
||||
preMask = Mat::ones(hd, wd, CV_8U);
|
||||
}
|
||||
else
|
||||
{
|
||||
Mat preMaskFloat;
|
||||
threshold(inputMask, preMask, 0, 1, THRESH_BINARY);
|
||||
|
||||
preMask.convertTo(preMaskFloat, CV_32F);
|
||||
GaussianBlur(preMaskFloat, preMaskFloat, Size(gaussFiltSize, gaussFiltSize), 0, 0);
|
||||
// Change threshold.
|
||||
preMaskFloat *= (0.5/0.95);
|
||||
// Rounding conversion.
|
||||
preMaskFloat.convertTo(preMask, CV_8U);
|
||||
|
||||
// If there's no template mask, we can apply image masks to gradients only once.
|
||||
// Otherwise, we'll need to combine the template and image masks at each iteration.
|
||||
if (templateMask.empty())
|
||||
{
|
||||
cv::Mat zeroMask = (preMask == 0);
|
||||
gradientX.setTo(0, zeroMask);
|
||||
gradientY.setTo(0, zeroMask);
|
||||
}
|
||||
}
|
||||
|
||||
// matrices needed for solving linear equation system for maximizing ECC
|
||||
Mat jacobian = Mat(hs, ws * numberOfParameters, type);
|
||||
Mat hessian = Mat(numberOfParameters, numberOfParameters, CV_32F);
|
||||
Mat hessianInv = Mat(numberOfParameters, numberOfParameters, CV_32F);
|
||||
Mat imageProjection = Mat(numberOfParameters, 1, CV_32F);
|
||||
Mat templateProjection = Mat(numberOfParameters, 1, CV_32F);
|
||||
Mat imageProjectionHessian = Mat(numberOfParameters, 1, CV_32F);
|
||||
Mat errorProjection = Mat(numberOfParameters, 1, CV_32F);
|
||||
|
||||
Mat deltaP = Mat(numberOfParameters, 1, CV_32F); // transformation parameter correction
|
||||
Mat error = Mat(hs, ws, CV_32F); // error as 2D matrix
|
||||
|
||||
const int imageFlags = INTER_LINEAR + WARP_INVERSE_MAP;
|
||||
const int maskFlags = INTER_NEAREST + WARP_INVERSE_MAP;
|
||||
|
||||
// iteratively update map_matrix
|
||||
double rho = -1;
|
||||
double last_rho = -termination_eps;
|
||||
for (int i = 1; (i <= numberOfIterations) && (fabs(rho - last_rho) >= termination_eps); i++) {
|
||||
// warp-back portion of the inputImage and gradients to the coordinate space of the templateImage
|
||||
if (motionType != MOTION_HOMOGRAPHY) {
|
||||
warpAffine(imageFloat, imageWarped, map, imageWarped.size(), imageFlags);
|
||||
warpAffine(gradientX, gradientXWarped, map, gradientXWarped.size(), imageFlags);
|
||||
warpAffine(gradientY, gradientYWarped, map, gradientYWarped.size(), imageFlags);
|
||||
warpAffine(preMask, imageMask, map, imageMask.size(), maskFlags);
|
||||
} else {
|
||||
warpPerspective(imageFloat, imageWarped, map, imageWarped.size(), imageFlags);
|
||||
warpPerspective(gradientX, gradientXWarped, map, gradientXWarped.size(), imageFlags);
|
||||
warpPerspective(gradientY, gradientYWarped, map, gradientYWarped.size(), imageFlags);
|
||||
warpPerspective(preMask, imageMask, map, imageMask.size(), maskFlags);
|
||||
}
|
||||
|
||||
if (!templateMask.empty())
|
||||
{
|
||||
cv::bitwise_and(imageMask, templtMask, imageMask);
|
||||
|
||||
cv::Mat zeroMask = (imageMask == 0);
|
||||
gradientXWarped.setTo(0, zeroMask);
|
||||
gradientYWarped.setTo(0, zeroMask);
|
||||
}
|
||||
|
||||
Scalar imgMean, imgStd, tmpMean, tmpStd;
|
||||
meanStdDev(imageWarped, imgMean, imgStd, imageMask);
|
||||
meanStdDev(templateFloat, tmpMean, tmpStd, imageMask);
|
||||
|
||||
subtract(imageWarped, imgMean, imageWarped, imageMask); // zero-mean input
|
||||
templateZM = Mat::zeros(templateZM.rows, templateZM.cols, templateZM.type());
|
||||
subtract(templateFloat, tmpMean, templateZM, imageMask); // zero-mean template
|
||||
|
||||
int validPixels = countNonZero(imageMask);
|
||||
double tmpNorm = std::sqrt(validPixels * cv::norm(tmpStd, cv::NORM_L2SQR));
|
||||
double imgNorm = std::sqrt(validPixels * cv::norm(imgStd, cv::NORM_L2SQR));
|
||||
|
||||
// calculate jacobian of image wrt parameters
|
||||
switch (motionType) {
|
||||
case MOTION_AFFINE:
|
||||
image_jacobian_affine_ECC(gradientXWarped, gradientYWarped, Xgrid, Ygrid, jacobian);
|
||||
break;
|
||||
case MOTION_HOMOGRAPHY:
|
||||
image_jacobian_homo_ECC(gradientXWarped, gradientYWarped, Xgrid, Ygrid, map, jacobian);
|
||||
break;
|
||||
case MOTION_TRANSLATION:
|
||||
image_jacobian_translation_ECC(gradientXWarped, gradientYWarped, jacobian);
|
||||
break;
|
||||
case MOTION_EUCLIDEAN:
|
||||
image_jacobian_euclidean_ECC(gradientXWarped, gradientYWarped, Xgrid, Ygrid, map, jacobian);
|
||||
break;
|
||||
}
|
||||
|
||||
// calculate Hessian and its inverse
|
||||
project_onto_jacobian_ECC(jacobian, jacobian, hessian);
|
||||
|
||||
hessianInv = hessian.inv();
|
||||
|
||||
const double correlation = templateZM.dot(imageWarped);
|
||||
|
||||
// calculate enhanced correlation coefficient (ECC)->rho
|
||||
last_rho = rho;
|
||||
rho = correlation / (imgNorm * tmpNorm);
|
||||
if (cvIsNaN(rho)) {
|
||||
CV_Error(Error::StsNoConv, "NaN encountered.");
|
||||
}
|
||||
|
||||
// project images into jacobian
|
||||
project_onto_jacobian_ECC(jacobian, imageWarped, imageProjection);
|
||||
project_onto_jacobian_ECC(jacobian, templateZM, templateProjection);
|
||||
|
||||
// calculate the parameter lambda to account for illumination variation
|
||||
imageProjectionHessian = hessianInv * imageProjection;
|
||||
const double lambda_n = (imgNorm * imgNorm) - imageProjection.dot(imageProjectionHessian);
|
||||
const double lambda_d = correlation - templateProjection.dot(imageProjectionHessian);
|
||||
if (lambda_d <= 0.0) {
|
||||
rho = -1;
|
||||
CV_Error(Error::StsNoConv,
|
||||
"The algorithm stopped before its convergence. The correlation is going to be minimized. Images "
|
||||
"may be uncorrelated or non-overlapped");
|
||||
}
|
||||
const double lambda = (lambda_n / lambda_d);
|
||||
|
||||
// estimate the update step delta_p
|
||||
error = lambda * templateZM - imageWarped;
|
||||
project_onto_jacobian_ECC(jacobian, error, errorProjection);
|
||||
deltaP = hessianInv * errorProjection;
|
||||
|
||||
// update warping matrix
|
||||
update_warping_matrix_ECC(map, deltaP, motionType);
|
||||
}
|
||||
|
||||
// return final correlation coefficient
|
||||
return rho;
|
||||
}
|
||||
|
||||
double cv::findTransformECC(InputArray templateImage,
|
||||
InputArray inputImage,
|
||||
InputOutputArray warpMatrix,
|
||||
int motionType,
|
||||
TermCriteria criteria,
|
||||
InputArray inputMask,
|
||||
int gaussFiltSize
|
||||
) {
|
||||
return findTransformECCWithMask(templateImage, inputImage, noArray(), inputMask,
|
||||
warpMatrix, motionType, criteria, gaussFiltSize);
|
||||
}
|
||||
|
||||
double cv::findTransformECC(InputArray templateImage, InputArray inputImage, InputOutputArray warpMatrix,
|
||||
int motionType, TermCriteria criteria, InputArray inputMask) {
|
||||
// Use default value of 5 for gaussFiltSize to maintain backward compatibility.
|
||||
return findTransformECC(templateImage, inputImage, warpMatrix, motionType, criteria, inputMask, 5);
|
||||
}
|
||||
|
||||
/* End of file. */
|
||||
@@ -0,0 +1,888 @@
|
||||
// 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"
|
||||
|
||||
/****************************************************************************************\
|
||||
* Image Alignment (ECC algorithm, pyramidal version) *
|
||||
\****************************************************************************************/
|
||||
|
||||
namespace cv {
|
||||
ECCParameters::ECCParameters() : criteria(TermCriteria::COUNT + TermCriteria::EPS, 50, 1e-6)
|
||||
{}
|
||||
|
||||
typedef std::vector<cv::Mat> MatPyramid;
|
||||
|
||||
template<int motionType> struct MotionTraits {};
|
||||
|
||||
template<> struct MotionTraits<MOTION_TRANSLATION> {
|
||||
enum { paramAmount = 2 };
|
||||
static inline void tailHandlerGetCoord(float& sx,
|
||||
float& sy,
|
||||
float& denominator,
|
||||
int col,
|
||||
float numeratorX0,
|
||||
float numeratorY0,
|
||||
float /*denominator0*/,
|
||||
float /*a00*/,
|
||||
float /*a10*/,
|
||||
float /*a20*/)
|
||||
{
|
||||
denominator = 0;
|
||||
sx = (numeratorX0 + col);
|
||||
sy = numeratorY0;
|
||||
}
|
||||
template<typename elemtype>
|
||||
static constexpr std::array<float, paramAmount> fillJacobian(int /*col*/, int /*row*/, float/*sx*/, float/*sy*/, float fVal,
|
||||
elemtype gx, elemtype gy, float /*a00*/, float /*a10*/,
|
||||
float/*denominator*/) {
|
||||
#define GX (fVal * gx)
|
||||
#define GY (fVal * gy)
|
||||
return std::array<float, paramAmount>{GX, GY};
|
||||
#undef GX
|
||||
#undef GY
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct MotionTraits<MOTION_EUCLIDEAN> {
|
||||
enum { paramAmount = 3 };
|
||||
static inline void tailHandlerGetCoord(float& sx,
|
||||
float& sy,
|
||||
float& denominator,
|
||||
int col,
|
||||
float numeratorX0,
|
||||
float numeratorY0,
|
||||
float /*denominator0*/,
|
||||
float a00,
|
||||
float a10,
|
||||
float /*a20*/)
|
||||
{
|
||||
denominator = 0;
|
||||
sx = (numeratorX0 + a00 * col);
|
||||
sy = (numeratorY0 + a10 * col);
|
||||
}
|
||||
|
||||
template<typename elemtype>
|
||||
static constexpr std::array<float, paramAmount> fillJacobian(int col, int row, float/*sx*/, float/*sy*/, float fVal,
|
||||
elemtype gx, elemtype gy, float a00, float a10,
|
||||
float/*denominator*/) {
|
||||
#define GX (fVal * gx)
|
||||
#define GY (fVal * gy)
|
||||
#define HATX (-col * a10 - row * a00)
|
||||
#define HATY (col * a00 - row * a10)
|
||||
#define GZ (GX * HATX + GY * HATY)
|
||||
return std::array<float, paramAmount>{GZ, GX, GY};
|
||||
#undef GX
|
||||
#undef GY
|
||||
#undef HATX
|
||||
#undef HATY
|
||||
#undef GZ
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct MotionTraits<MOTION_AFFINE> {
|
||||
enum { paramAmount = 6};
|
||||
static inline void tailHandlerGetCoord(float& sx,
|
||||
float& sy,
|
||||
float& denominator,
|
||||
int col,
|
||||
float numeratorX0,
|
||||
float numeratorY0,
|
||||
float /*denominator0*/,
|
||||
float a00,
|
||||
float a10,
|
||||
float /*a20*/)
|
||||
{
|
||||
denominator = 0;
|
||||
sx = (numeratorX0 + a00 * col);
|
||||
sy = (numeratorY0 + a10 * col);
|
||||
}
|
||||
|
||||
template<typename elemtype>
|
||||
static constexpr std::array<float, paramAmount> fillJacobian(int col, int row, float/*sx*/, float/*sy*/, float fVal,
|
||||
elemtype gx, elemtype gy, float /*a00*/, float /*a10*/,
|
||||
float/*denominator*/) {
|
||||
#define GX (fVal * gx)
|
||||
#define GY (fVal * gy)
|
||||
return std::array<float, paramAmount>{GX * col, GY * col, GX * row, GY * row, GX, GY};
|
||||
#undef GX
|
||||
#undef GY
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct MotionTraits<MOTION_HOMOGRAPHY> {
|
||||
enum { paramAmount = 8};
|
||||
static inline void tailHandlerGetCoord(float& sx,
|
||||
float& sy,
|
||||
float& denominator,
|
||||
int col,
|
||||
float numeratorX0,
|
||||
float numeratorY0,
|
||||
float denominator0,
|
||||
float a00,
|
||||
float a10,
|
||||
float a20)
|
||||
{
|
||||
denominator = 1.f / (col * a20 + denominator0);
|
||||
sx = (numeratorX0 + a00 * col) * denominator;
|
||||
sy = (numeratorY0 + a10 * col) * denominator;
|
||||
}
|
||||
|
||||
template<typename elemtype>
|
||||
static constexpr std::array<float, paramAmount> fillJacobian(int col, int row, float sx, float sy, float fVal,
|
||||
elemtype gx, elemtype gy, float/*a00*/, float/*a10*/,
|
||||
float denominator) {
|
||||
#define GX (fVal * float(gx) * denominator)
|
||||
#define GY (fVal * float(gy) * denominator)
|
||||
#define GZ (-(GX * sx + GY * sy))
|
||||
return std::array<float, paramAmount>{GX * col, GY * col, GZ * col, GX * row, GY * row, GZ * row, GX, GY};
|
||||
#undef GX
|
||||
#undef GY
|
||||
#undef GZ
|
||||
}
|
||||
};
|
||||
|
||||
inline void reinterpret(Mat& mat, int newdepth) {
|
||||
mat.flags = (mat.flags & ~CV_MAT_DEPTH_MASK) | newdepth;
|
||||
}
|
||||
|
||||
template<int N, class F>
|
||||
class constexprForClass
|
||||
{
|
||||
public:
|
||||
static inline void execute(F&& fVal) {
|
||||
constexprForClass<N-1, F>::execute(std::forward<F>(fVal));
|
||||
fVal(N-1);
|
||||
}
|
||||
};
|
||||
|
||||
template<class F>
|
||||
class constexprForClass<0, F>
|
||||
{
|
||||
public:
|
||||
static inline void execute(F&&) {}
|
||||
};
|
||||
|
||||
template<int N, class F>
|
||||
void constexprFor(F&& fVal) {
|
||||
constexprForClass<N, F>::execute(std::forward<F>(fVal));
|
||||
}
|
||||
template<int R, int C, class F>
|
||||
class constexprForUpperTriangleClassOneRow
|
||||
{
|
||||
public:
|
||||
static inline void execute(F&& fVal) {
|
||||
constexprForUpperTriangleClassOneRow<R, C-1, F>::execute(std::forward<F>(fVal));
|
||||
fVal(R, R + C - 1);
|
||||
}
|
||||
};
|
||||
|
||||
template<int R, class F>
|
||||
class constexprForUpperTriangleClassOneRow<R, 0, F>
|
||||
{
|
||||
public:
|
||||
static inline void execute(F&&) {}
|
||||
};
|
||||
|
||||
template<int R, int D, class F>
|
||||
class constexprForUpperTriangleClass
|
||||
{
|
||||
public:
|
||||
static inline void execute(F&& fVal) {
|
||||
constexprForUpperTriangleClass<R-1, D, F>::execute(std::forward<F>(fVal));
|
||||
constexprForUpperTriangleClassOneRow<R-1, D-R+1, F>::execute(std::forward<F>(fVal));
|
||||
}
|
||||
};
|
||||
|
||||
template<int D, class F>
|
||||
class constexprForUpperTriangleClass<0, D, F>
|
||||
{
|
||||
public:
|
||||
static inline void execute(F&&) {}
|
||||
};
|
||||
|
||||
template<int M, class F>
|
||||
void constexprForUpperTriangle(F&& fVal) {
|
||||
constexprForUpperTriangleClass<M,M,F>::execute(std::forward<F>(fVal));
|
||||
}
|
||||
|
||||
template<int MotionType>
|
||||
constexpr int hessianRowStart(int row) {
|
||||
return row == 0 ? 0 : (MotionTraits<MotionType>::paramAmount - row + 1 + hessianRowStart<MotionType>(row - 1));
|
||||
}
|
||||
|
||||
template<int motionType, typename elemtype>
|
||||
static double imageHessianProjECC(const Mat& map,
|
||||
const Mat& sampleWithGrad,
|
||||
const Mat& ref,
|
||||
double& sampSum,
|
||||
double& sampSqSum,
|
||||
double& refSum,
|
||||
double& refSqSum,
|
||||
int& nz,
|
||||
Mat& hessian,
|
||||
Mat& sampleProj,
|
||||
Mat& refProj,
|
||||
int deltaY,
|
||||
int interpolation) {
|
||||
static_assert(std::is_same<float, elemtype>::value, "imageHessianProjECC: f16 is not supported yet");
|
||||
#define HESSIAN_PARAMS (MotionTraits<motionType>::paramAmount)
|
||||
|
||||
CV_Assert(map.type() == CV_64F);
|
||||
CV_Assert(interpolation == INTER_NEAREST || interpolation == INTER_LINEAR);
|
||||
CV_Assert(hessian.type() == CV_64F && sampleProj.type() == CV_64F && refProj.type() == CV_64F);
|
||||
if (sampleProj.size() != Size(1, HESSIAN_PARAMS) || refProj.size() != Size(1, HESSIAN_PARAMS)) {
|
||||
CV_Error(Error::BadImageSize, format("imageHessianProjECC: Wrong sample projection/reference projection size. 1x%d expected", HESSIAN_PARAMS));
|
||||
}
|
||||
if (hessian.size() != Size(HESSIAN_PARAMS, HESSIAN_PARAMS)) {
|
||||
CV_Error(Error::BadImageSize, format("imageHessianProjECC: Wrong hessian size. %dx%d expected", HESSIAN_PARAMS, HESSIAN_PARAMS));
|
||||
}
|
||||
if (!map.isContinuous()) {
|
||||
CV_Error(Error::BadStep, "imageHessianProjECC: Map should be continuous");
|
||||
}
|
||||
if (std::is_same<float, elemtype>::value) {
|
||||
CV_Assert(sampleWithGrad.type() == CV_32FC4 && ref.type() == CV_32FC2);
|
||||
}
|
||||
|
||||
int hr = ref.rows;
|
||||
int wr = ref.cols;
|
||||
int hs = sampleWithGrad.rows;
|
||||
int ws = sampleWithGrad.cols;
|
||||
unsigned int ycond = hs - (INTER_LINEAR ? 1 : 0);
|
||||
unsigned int xcond = ws - (INTER_LINEAR ? 1 : 0);
|
||||
|
||||
hessian = Mat::zeros(hessian.size(), hessian.type());
|
||||
sampleProj = Mat::zeros(sampleProj.size(), sampleProj.type());
|
||||
refProj = Mat::zeros(refProj.size(), refProj.type());
|
||||
|
||||
const int MAX_STRIPES = 128;
|
||||
int stripesAmount = std::min(MAX_STRIPES, hr / deltaY);
|
||||
std::vector<Matx<double, MotionTraits<motionType>::paramAmount, MotionTraits<motionType>::paramAmount> > hessPs(stripesAmount);
|
||||
std::vector<Vec<double, MotionTraits<motionType>::paramAmount> > iprojs(stripesAmount);
|
||||
std::vector<Vec<double, MotionTraits<motionType>::paramAmount> > tprojs(stripesAmount);
|
||||
std::vector<Vec<double, MotionTraits<motionType>::paramAmount> > projSubs(stripesAmount);
|
||||
std::vector<double> correlations(stripesAmount, 0.);
|
||||
|
||||
std::vector<double> sampSums(stripesAmount, 0);
|
||||
std::vector<double> sampSqSums(stripesAmount, 0);
|
||||
std::vector<double> refSums(stripesAmount, 0);
|
||||
std::vector<double> refSqSums(stripesAmount, 0);
|
||||
std::vector<int> nzs(stripesAmount, 0);
|
||||
std::vector<double> sampMaskedSums(stripesAmount, 0);
|
||||
std::vector<double> refMaskedSums(stripesAmount, 0);
|
||||
|
||||
double a00 = map.at<double>(0, 0);
|
||||
double a01 = map.at<double>(0, 1);
|
||||
double a02 = map.at<double>(0, 2);
|
||||
double a10 = map.at<double>(1, 0);
|
||||
double a11 = map.at<double>(1, 1);
|
||||
double a12 = map.at<double>(1, 2);
|
||||
double a20 = 0;
|
||||
double a21 = 0;
|
||||
double a22 = 0;
|
||||
if (motionType == MOTION_HOMOGRAPHY) {
|
||||
a20 = map.at<double>(2, 0);
|
||||
a21 = map.at<double>(2, 1);
|
||||
a22 = map.at<double>(2, 2);
|
||||
}
|
||||
|
||||
const elemtype* samplePtr0 = sampleWithGrad.ptr<elemtype>(0);
|
||||
|
||||
parallel_for_(Range(0, stripesAmount), [&](const Range& range) {
|
||||
int stripeIdx = range.start;
|
||||
int ystart = (hr * stripeIdx) / stripesAmount;
|
||||
ystart = roundUp(ystart, deltaY);
|
||||
int yend = (hr * (range.end)) / stripesAmount;
|
||||
// we don't store intermediate jacobian; instead, we iteratively update Hessian, sampleProj and refProj
|
||||
for (int y = ystart; y < yend; y += deltaY) {
|
||||
const elemtype* refPtr = ref.ptr<elemtype>(y);
|
||||
|
||||
std::array<float, (HESSIAN_PARAMS * HESSIAN_PARAMS + HESSIAN_PARAMS) / 2> hessPcache{};
|
||||
std::array<float, HESSIAN_PARAMS> iprojCache{};
|
||||
std::array<float, HESSIAN_PARAMS> tprojCache{};
|
||||
std::array<float, HESSIAN_PARAMS> projSubCache{};
|
||||
|
||||
const float numeratorX0 = y * (float)a01 + (float)a02;
|
||||
const float numeratorY0 = y * (float)a11 + (float)a12;
|
||||
const float denominator0 = y * (float)a21 + (float)a22;
|
||||
int x = 0;
|
||||
for (; x < wr; x++) { //Tail handler
|
||||
float sx, sy, denominator;
|
||||
MotionTraits<motionType>::tailHandlerGetCoord(sx, sy, denominator, x, numeratorX0, numeratorY0,
|
||||
denominator0, (float)a00, (float)a10, (float)a20);
|
||||
const unsigned int x0 = (interpolation == INTER_LINEAR) ? static_cast<int>(std::floor(sx)) : saturate_cast<unsigned>(sx);
|
||||
const unsigned int y0 = (interpolation == INTER_LINEAR) ? static_cast<int>(std::floor(sy)) : saturate_cast<unsigned>(sy);
|
||||
if(interpolation == INTER_LINEAR && (static_cast<int>(x0 < xcond) & static_cast<int>(y0 < ycond)) == 0)
|
||||
continue;
|
||||
if (interpolation == INTER_NEAREST && (static_cast<int>(x0 < xcond) & static_cast<int>(y0 < ycond)) == 0)
|
||||
continue;
|
||||
float sampleVal = 0;
|
||||
float gx = 0;
|
||||
float gy = 0;
|
||||
float fVal = 0;
|
||||
if(interpolation == INTER_LINEAR) {
|
||||
const int x1 = x0 + 1;
|
||||
const int y1 = y0 + 1;
|
||||
|
||||
const float dx = sx - x0;
|
||||
const float dy = sy - y0;
|
||||
|
||||
const float p00_val = samplePtr0[4 * y0 * ws + 4 * x0];
|
||||
const float p01_val = samplePtr0[4 * y0 * ws + 4 * x1];
|
||||
const float p10_val = samplePtr0[4 * y1 * ws + 4 * x0];
|
||||
const float p11_val = samplePtr0[4 * y1 * ws + 4 * x1];
|
||||
const float p0_val = p00_val * (1.0f - dx) + p01_val * dx;
|
||||
const float p1_val = p10_val * (1.0f - dx) + p11_val * dx;
|
||||
|
||||
const float p00_gx = samplePtr0[4 * y0 * ws + 4 * x0 + 1];
|
||||
const float p01_gx = samplePtr0[4 * y0 * ws + 4 * x1 + 1];
|
||||
const float p10_gx = samplePtr0[4 * y1 * ws + 4 * x0 + 1];
|
||||
const float p11_gx = samplePtr0[4 * y1 * ws + 4 * x1 + 1];
|
||||
const float p0_gx = p00_gx * (1.0f - dx) + p01_gx * dx;
|
||||
const float p1_gx = p10_gx * (1.0f - dx) + p11_gx * dx;
|
||||
|
||||
const float p00_gy = samplePtr0[4 * y0 * ws + 4 * x0 + 2];
|
||||
const float p01_gy = samplePtr0[4 * y0 * ws + 4 * x1 + 2];
|
||||
const float p10_gy = samplePtr0[4 * y1 * ws + 4 * x0 + 2];
|
||||
const float p11_gy = samplePtr0[4 * y1 * ws + 4 * x1 + 2];
|
||||
const float p0_gy = p00_gy * (1.0f - dx) + p01_gy * dx;
|
||||
const float p1_gy = p10_gy * (1.0f - dx) + p11_gy * dx;
|
||||
|
||||
const float p00_mask = samplePtr0[4 * y0 * ws + 4 * x0 + 2] == 0.f ? 0.f : 1.f;
|
||||
const float p01_mask = samplePtr0[4 * y0 * ws + 4 * x1 + 2] == 0.f ? 0.f : 1.f;
|
||||
const float p10_mask = samplePtr0[4 * y1 * ws + 4 * x0 + 2] == 0.f ? 0.f : 1.f;
|
||||
const float p11_mask = samplePtr0[4 * y1 * ws + 4 * x1 + 2] == 0.f ? 0.f : 1.f;
|
||||
|
||||
sampleVal = p0_val * (1.0f - dy) + p1_val * dy;
|
||||
gx = p0_gx * (1.0f - dy) + p1_gx * dy;
|
||||
gy = p0_gy * (1.0f - dy) + p1_gy * dy;
|
||||
fVal = p00_mask * p01_mask * p10_mask * p11_mask;
|
||||
}
|
||||
else { // if(interpolation == INTER_NEAREST)
|
||||
const elemtype* samplePtr = samplePtr0 + y0 * (ws * 4) + x0 * 4;
|
||||
sampleVal = samplePtr[0];
|
||||
gx = samplePtr[1];
|
||||
gy = samplePtr[2];
|
||||
fVal = float(samplePtr[3]) == 0.f ? 0.f : 1.f;
|
||||
}
|
||||
|
||||
float refVal = refPtr[2 * x];
|
||||
fVal *= float(refPtr[2 * x + 1]) == 0.f ? 0.f : 1.f;
|
||||
sampleVal *= fVal;
|
||||
refVal *= fVal;
|
||||
sampSums[stripeIdx] += sampleVal;
|
||||
sampSqSums[stripeIdx] += sampleVal * sampleVal;
|
||||
refSums[stripeIdx] += refVal;
|
||||
refSqSums[stripeIdx] += refVal * refVal;
|
||||
nzs[stripeIdx] += (int)fVal;
|
||||
sampMaskedSums[stripeIdx] += sampleVal;
|
||||
refMaskedSums[stripeIdx] += refVal;
|
||||
std::array<float, HESSIAN_PARAMS> jac = MotionTraits<motionType>::fillJacobian(x, y, sx, sy,
|
||||
fVal, gx,
|
||||
gy, (float)a00,
|
||||
(float)a10, denominator);
|
||||
constexprForUpperTriangle<HESSIAN_PARAMS>([&](int row_i, int col_i) {
|
||||
hessPcache[hessianRowStart<motionType>(row_i) + (col_i - row_i)] += jac[row_i] * jac[col_i];
|
||||
});
|
||||
constexprFor<HESSIAN_PARAMS>([&](int elem) {
|
||||
iprojCache[elem] += jac[elem] * sampleVal;
|
||||
tprojCache[elem] += jac[elem] * refVal;
|
||||
projSubCache[elem] += jac[elem] * fVal;
|
||||
});
|
||||
correlations[stripeIdx] += sampleVal * refVal;
|
||||
}
|
||||
|
||||
constexprForUpperTriangle<HESSIAN_PARAMS>([&](int row, int col) {
|
||||
hessPs[stripeIdx](row, col) += hessPcache[hessianRowStart<motionType>(row) + (col - row)];
|
||||
});
|
||||
constexprFor<HESSIAN_PARAMS>([&](int elem) {
|
||||
iprojs[stripeIdx][elem] += iprojCache[elem];
|
||||
tprojs[stripeIdx][elem] += tprojCache[elem];
|
||||
projSubs[stripeIdx][elem] += projSubCache[elem];
|
||||
});
|
||||
}
|
||||
});
|
||||
double sampMaskedSum = 0;
|
||||
double refMaskedSum = 0;
|
||||
double correlation = 0;
|
||||
sampSum = sampSqSum = refSum = refSqSum = nz = 0;
|
||||
|
||||
for (int stripeIdx = 0; stripeIdx < stripesAmount; stripeIdx++) {
|
||||
correlation += correlations[stripeIdx];
|
||||
sampSum += sampSums[stripeIdx];
|
||||
sampSqSum += sampSqSums[stripeIdx];
|
||||
refSum += refSums[stripeIdx];
|
||||
refSqSum += refSqSums[stripeIdx];
|
||||
sampMaskedSum += sampMaskedSums[stripeIdx];
|
||||
refMaskedSum += refMaskedSums[stripeIdx];
|
||||
nz += nzs[stripeIdx];
|
||||
}
|
||||
double scale = nz == 0 ? 0. : 1. / nz;
|
||||
double sampMean = sampSum * scale;
|
||||
double refMean = refSum * scale;
|
||||
correlation += nz * sampMean * refMean - sampMaskedSum * refMean - refMaskedSum * sampMean;
|
||||
double* hessPtr = hessian.ptr<double>(0);
|
||||
double* sampleProjPtr = sampleProj.ptr<double>(0);
|
||||
double* refProjPtr = refProj.ptr<double>(0);
|
||||
for (int stripeIdx = 0; stripeIdx < stripesAmount; stripeIdx++) {
|
||||
for (int hessNum = 0; hessNum < HESSIAN_PARAMS * HESSIAN_PARAMS; hessNum++) {
|
||||
hessPtr[hessNum] += hessPs[stripeIdx].val[hessNum];
|
||||
}
|
||||
for (int projNum = 0; projNum < HESSIAN_PARAMS; projNum++) {
|
||||
sampleProjPtr[projNum] += iprojs[stripeIdx][projNum] - projSubs[stripeIdx][projNum] * sampMean;
|
||||
refProjPtr[projNum] += tprojs[stripeIdx][projNum] - projSubs[stripeIdx][projNum] * refMean;
|
||||
}
|
||||
}
|
||||
constexprForUpperTriangle<HESSIAN_PARAMS>([&](int row, int col) {
|
||||
hessPtr[col * HESSIAN_PARAMS + row] = hessPtr[row * HESSIAN_PARAMS + col];
|
||||
});
|
||||
return correlation;
|
||||
#undef HESSIAN_PARAMS
|
||||
}
|
||||
|
||||
static void updateWarpingMatrixECC(Mat& map_matrix, const Mat& update, const int motionType) {
|
||||
CV_Assert(map_matrix.type() == CV_64FC1);
|
||||
CV_Assert(update.type() == CV_64FC1);
|
||||
|
||||
CV_Assert(motionType == MOTION_TRANSLATION || motionType == MOTION_EUCLIDEAN || motionType == MOTION_AFFINE ||
|
||||
motionType == MOTION_HOMOGRAPHY);
|
||||
|
||||
if (motionType == MOTION_HOMOGRAPHY)
|
||||
CV_Assert(map_matrix.rows == 3 && update.rows == 8);
|
||||
else if (motionType == MOTION_AFFINE)
|
||||
CV_Assert(map_matrix.rows == 2 && update.rows == 6);
|
||||
else if (motionType == MOTION_EUCLIDEAN)
|
||||
CV_Assert(map_matrix.rows == 2 && update.rows == 3);
|
||||
else
|
||||
CV_Assert(map_matrix.rows == 2 && update.rows == 2);
|
||||
|
||||
CV_Assert(update.cols == 1);
|
||||
|
||||
CV_Assert(map_matrix.isContinuous());
|
||||
CV_Assert(update.isContinuous());
|
||||
|
||||
double* mapPtr = map_matrix.ptr<double>(0);
|
||||
const double* updatePtr = update.ptr<double>(0);
|
||||
|
||||
if (motionType == MOTION_TRANSLATION) {
|
||||
mapPtr[2] += updatePtr[0];
|
||||
mapPtr[5] += updatePtr[1];
|
||||
}
|
||||
if (motionType == MOTION_AFFINE) {
|
||||
mapPtr[0] += updatePtr[0];
|
||||
mapPtr[3] += updatePtr[1];
|
||||
mapPtr[1] += updatePtr[2];
|
||||
mapPtr[4] += updatePtr[3];
|
||||
mapPtr[2] += updatePtr[4];
|
||||
mapPtr[5] += updatePtr[5];
|
||||
}
|
||||
if (motionType == MOTION_HOMOGRAPHY) {
|
||||
mapPtr[0] += updatePtr[0];
|
||||
mapPtr[3] += updatePtr[1];
|
||||
mapPtr[6] += updatePtr[2];
|
||||
mapPtr[1] += updatePtr[3];
|
||||
mapPtr[4] += updatePtr[4];
|
||||
mapPtr[7] += updatePtr[5];
|
||||
mapPtr[2] += updatePtr[6];
|
||||
mapPtr[5] += updatePtr[7];
|
||||
}
|
||||
if (motionType == MOTION_EUCLIDEAN) {
|
||||
double new_theta = updatePtr[0];
|
||||
new_theta += asin(mapPtr[3]);
|
||||
|
||||
mapPtr[2] += updatePtr[1];
|
||||
mapPtr[5] += updatePtr[2];
|
||||
mapPtr[0] = mapPtr[4] = cos(new_theta);
|
||||
mapPtr[3] = sin(new_theta);
|
||||
mapPtr[1] = -mapPtr[3];
|
||||
}
|
||||
}
|
||||
|
||||
static void optimizeECC(Mat& sampleWithGrad,
|
||||
const Mat& reference,
|
||||
Mat& map,
|
||||
int motionType,
|
||||
double* rho,
|
||||
double* lastRho,
|
||||
int deltaY,
|
||||
int nparams,
|
||||
int interpolation) {
|
||||
CV_Assert(interpolation == INTER_NEAREST || interpolation == INTER_LINEAR);
|
||||
|
||||
// warp-back portion of the inputImage and gradients to the coordinate space of the referenceFloat
|
||||
double correlation = 0;
|
||||
|
||||
// matrices needed for solving linear equation system for maximizing ECC
|
||||
Mat hessian = Mat(nparams, nparams, CV_64F);
|
||||
Mat hessianInv = Mat(nparams, nparams, CV_64F);
|
||||
Mat sampleProjection = Mat(nparams, 1, CV_64F);
|
||||
Mat referenceProjection = Mat(nparams, 1, CV_64F);
|
||||
Mat sampleProjectionHessian = Mat(nparams, 1, CV_64F);
|
||||
Mat errorProjection = Mat(nparams, 1, CV_64F);
|
||||
Mat deltaP = Mat(nparams, 1, CV_64F);
|
||||
|
||||
double sampSum;
|
||||
double sampSqSum;
|
||||
double referenceSum;
|
||||
double referenceSqSum;
|
||||
int nz;
|
||||
|
||||
{ // if(imageWithGrad.type() == CV_32FC4)
|
||||
if (motionType == MOTION_TRANSLATION) {
|
||||
correlation = imageHessianProjECC<MOTION_TRANSLATION, float>(map,
|
||||
sampleWithGrad,
|
||||
reference,
|
||||
sampSum,
|
||||
sampSqSum,
|
||||
referenceSum,
|
||||
referenceSqSum,
|
||||
nz,
|
||||
hessian,
|
||||
sampleProjection,
|
||||
referenceProjection,
|
||||
deltaY,
|
||||
interpolation);
|
||||
} else if (motionType == MOTION_EUCLIDEAN) {
|
||||
correlation = imageHessianProjECC<MOTION_EUCLIDEAN, float>(map,
|
||||
sampleWithGrad,
|
||||
reference,
|
||||
sampSum,
|
||||
sampSqSum,
|
||||
referenceSum,
|
||||
referenceSqSum,
|
||||
nz,
|
||||
hessian,
|
||||
sampleProjection,
|
||||
referenceProjection,
|
||||
deltaY,
|
||||
interpolation);
|
||||
} else if (motionType == MOTION_AFFINE) {
|
||||
correlation = imageHessianProjECC<MOTION_AFFINE, float>(map,
|
||||
sampleWithGrad,
|
||||
reference,
|
||||
sampSum,
|
||||
sampSqSum,
|
||||
referenceSum,
|
||||
referenceSqSum,
|
||||
nz,
|
||||
hessian,
|
||||
sampleProjection,
|
||||
referenceProjection,
|
||||
deltaY,
|
||||
interpolation);
|
||||
} else {
|
||||
correlation = imageHessianProjECC<MOTION_HOMOGRAPHY, float>(map,
|
||||
sampleWithGrad,
|
||||
reference,
|
||||
sampSum,
|
||||
sampSqSum,
|
||||
referenceSum,
|
||||
referenceSqSum,
|
||||
nz,
|
||||
hessian,
|
||||
sampleProjection,
|
||||
referenceProjection,
|
||||
deltaY,
|
||||
interpolation);
|
||||
}
|
||||
}
|
||||
double scale = nz == 0 ? 0. : 1. / nz;
|
||||
double sampMean = sampSum * scale;
|
||||
double refMean = referenceSum * scale;
|
||||
double sampStd = std::sqrt(std::max(sampSqSum * scale - sampMean * sampMean, 0.));
|
||||
double refStd = std::sqrt(std::max(referenceSqSum * scale - refMean * refMean, 0.));
|
||||
|
||||
// inverse of Hessian
|
||||
hessianInv = hessian.inv();
|
||||
// calculate enhanced correlation coefficient (ECC)->rho
|
||||
*lastRho = *rho;
|
||||
double refNorm = std::sqrt(nz * refStd * refStd);
|
||||
double sampNorm = std::sqrt(nz * sampStd * sampStd);
|
||||
|
||||
*rho = correlation / (sampNorm * refNorm);
|
||||
if ((bool)cvIsNaN(*rho)) {
|
||||
CV_Error(Error::StsNoConv, "NaN encountered.");
|
||||
}
|
||||
|
||||
// calculate the parameter lambda to account for illumination variation
|
||||
sampleProjectionHessian = hessianInv * sampleProjection;
|
||||
const double lambdaN = (sampNorm * sampNorm) - sampleProjection.dot(sampleProjectionHessian);
|
||||
const double lambdaD = correlation - referenceProjection.dot(sampleProjectionHessian);
|
||||
|
||||
if (lambdaD <= 0.0) {
|
||||
CV_Error(Error::StsNoConv, "The algorithm stopped before its convergence. The correlation is going to be minimized. "
|
||||
"Images may be uncorrelated or non-overlapped");
|
||||
}
|
||||
const double lambda = (lambdaN / lambdaD);
|
||||
|
||||
// estimate the update step delta_p
|
||||
errorProjection = lambda * referenceProjection - sampleProjection;
|
||||
gemm(hessianInv, errorProjection, 1., noArray(), 0., deltaP);
|
||||
|
||||
// update warping matrix
|
||||
updateWarpingMatrixECC(map, deltaP, motionType);
|
||||
}
|
||||
|
||||
static Mat prepareGradients(const Mat& sample) {
|
||||
CV_Assert(sample.type() == CV_32FC2 || sample.type() == CV_16FC2);
|
||||
|
||||
const int ws = sample.cols;
|
||||
const int hs = sample.rows;
|
||||
|
||||
Mat sampleWithGrad;
|
||||
int ntasks = std::min(4, hs);
|
||||
|
||||
{
|
||||
sampleWithGrad = Mat(hs, ws, CV_32FC4);
|
||||
float* dstPtr = sampleWithGrad.ptr<float>();
|
||||
parallel_for_(Range(0, ntasks), [&](const Range& range) {
|
||||
int rowstart = range.start * hs / ntasks;
|
||||
int rowend = range.end * hs / ntasks;
|
||||
for (int row = rowstart; row < rowend; row++) {
|
||||
const float* sampleCurLine = sample.ptr<float>(row);
|
||||
const float* samplePrevLine = sample.ptr<float>(std::max(row - 1, 0));
|
||||
const float* sampleNextLine = sample.ptr<float>(std::min(row + 1, hs - 1));
|
||||
float gradDivY = (row > 0 && row + 1 < hs) ? 0.5f : 0.25f;
|
||||
int col = 0;
|
||||
for (; col < ws; col++) {
|
||||
int prevCol = std::max(col - 1, 0);
|
||||
int nextCol = std::min(col + 1, ws - 1);
|
||||
float gradDivX = (col > 0 && col + 1 < ws) ? 0.5f : 0.25f;
|
||||
dstPtr[row * ws * 4 + col * 4] = sampleCurLine[2 * col];
|
||||
dstPtr[row * ws * 4 + col * 4 + 1] =
|
||||
gradDivX * (sampleCurLine[2 * nextCol] - sampleCurLine[2 * prevCol]);
|
||||
dstPtr[row * ws * 4 + col * 4 + 2] = gradDivY * (sampleNextLine[2 * col] - samplePrevLine[2 * col]);
|
||||
dstPtr[row * ws * 4 + col * 4 + 3] = sampleCurLine[2 * col + 1];
|
||||
}
|
||||
}
|
||||
}, ntasks);
|
||||
}
|
||||
return sampleWithGrad;
|
||||
}
|
||||
|
||||
static void buildPyramidECC(InputArray inputImage,
|
||||
MatPyramid& imgPyramid,
|
||||
InputArray& mask,
|
||||
MatPyramid& maskPyramid,
|
||||
int nlevels) {
|
||||
imgPyramid.resize(nlevels);
|
||||
inputImage.getMat().convertTo(imgPyramid[0], CV_8UC1);
|
||||
maskPyramid.resize(nlevels);
|
||||
if (!mask.empty()) {
|
||||
mask.getMat().convertTo(maskPyramid[0], CV_8UC1);
|
||||
}
|
||||
for (int pyrLevel = 0; pyrLevel < nlevels - 1; ++pyrLevel) {
|
||||
Size size = Size((imgPyramid[pyrLevel].cols + 1) / 2, (imgPyramid[pyrLevel].rows + 1) / 2);
|
||||
pyrDown(imgPyramid[pyrLevel], imgPyramid[pyrLevel + 1], size);
|
||||
if (!mask.empty()) {
|
||||
pyrDown(maskPyramid[pyrLevel], maskPyramid[pyrLevel + 1], size);
|
||||
threshold(maskPyramid[pyrLevel + 1], maskPyramid[pyrLevel + 1], 254, 0xff, THRESH_BINARY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Mat spliceWithMask(const Mat& image, const Mat& mask) {
|
||||
CV_Assert(image.type() == CV_32F && (mask.empty() || mask.type() == CV_8U));
|
||||
if (!mask.empty() && image.size() != mask.size()) {
|
||||
CV_Error(Error::BadImageSize, "spliceWithMask: Mask and image have to be of same size.");
|
||||
}
|
||||
const int hs = image.rows;
|
||||
const int ws = image.cols;
|
||||
|
||||
Mat result;
|
||||
int ntasks = std::min(4, hs);
|
||||
{
|
||||
union conv_ {
|
||||
uint32_t valU;
|
||||
float val;
|
||||
conv_() : valU(0xffffffff) {}
|
||||
} conv;
|
||||
result = Mat(hs, ws, CV_32FC2);
|
||||
parallel_for_(Range(0, ntasks), [&](const Range& range) {
|
||||
int rowstart = range.start * hs / ntasks;
|
||||
int rowend = range.end * hs / ntasks;
|
||||
for (int row = rowstart; row < rowend; row++) {
|
||||
float* dstPtr = result.ptr<float>(row);
|
||||
const float* srcPtr = image.ptr<float>(row);
|
||||
const uint8_t* maskPtr = !mask.empty() ? mask.ptr<uint8_t>(row) : nullptr;
|
||||
int col = 0;
|
||||
for (; col < ws; col++) {
|
||||
dstPtr[col * 2] = srcPtr[col];
|
||||
dstPtr[col * 2 + 1] = (!maskPtr || maskPtr[col]) ? conv.val : 0;
|
||||
}
|
||||
}
|
||||
}, ntasks);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void scaleWarpMatrix(Mat& warpMatrix, float scale) {
|
||||
if (warpMatrix.rows == 3) {
|
||||
Mat invertScaleMat = Mat(3, 3, CV_64F, 0.f);
|
||||
invertScaleMat.at<double>(0, 0) = 1.f / scale;
|
||||
invertScaleMat.at<double>(1, 1) = 1.f / scale;
|
||||
invertScaleMat.at<double>(2, 2) = 1.f;
|
||||
Mat scaleMatrix = invertScaleMat.clone();
|
||||
scaleMatrix.at<double>(0, 0) = scale;
|
||||
scaleMatrix.at<double>(1, 1) = scale;
|
||||
gemm(warpMatrix, invertScaleMat, 1., noArray(), 0., warpMatrix);
|
||||
gemm(scaleMatrix, warpMatrix, 1., noArray(), 0., warpMatrix);
|
||||
// Normalization, internal algorithms assumes, that a22 = 1.0f
|
||||
for (int mel = 0; mel < 8; mel++) {
|
||||
(reinterpret_cast<double*>(warpMatrix.data))[mel] /=
|
||||
(reinterpret_cast<double*>(warpMatrix.data))[8];
|
||||
}
|
||||
(reinterpret_cast<double*>(warpMatrix.data))[8] = 1.f;
|
||||
} else {
|
||||
warpMatrix.at<double>(0, 2) *= scale;
|
||||
warpMatrix.at<double>(1, 2) *= scale;
|
||||
}
|
||||
}
|
||||
|
||||
static void checkParams(const MatPyramid& referencePyramid,
|
||||
const MatPyramid& samplePyramid,
|
||||
Mat& map,
|
||||
std::vector<int>& itersPerLevel,
|
||||
const ECCParameters& eccParams) {
|
||||
if (itersPerLevel.empty()) {
|
||||
itersPerLevel.resize(eccParams.nlevels, eccParams.criteria.maxCount);
|
||||
}
|
||||
CV_Assert(eccParams.interpolation == INTER_NEAREST || eccParams.interpolation == INTER_LINEAR);
|
||||
CV_Assert(static_cast<int>(itersPerLevel.size()) == eccParams.nlevels);
|
||||
for (const auto& lvl : referencePyramid) {
|
||||
CV_Assert(!lvl.empty() && lvl.type() == referencePyramid[0].type());
|
||||
}
|
||||
CV_Assert(!samplePyramid.empty());
|
||||
for (const auto& lvl : samplePyramid) {
|
||||
CV_Assert(!lvl.empty() && lvl.type() == samplePyramid[0].type());
|
||||
}
|
||||
CV_Assert(samplePyramid.size() == referencePyramid.size() && samplePyramid.size() == itersPerLevel.size());
|
||||
CV_Assert(referencePyramid.back().rows > 1 && referencePyramid.back().cols > 1 &&
|
||||
samplePyramid.back().rows > 1 && samplePyramid.back().cols > 1);
|
||||
// If the user passed an un-initialized warpMatrix, initialize to identity
|
||||
if (referencePyramid[0].type() != CV_32FC2 && referencePyramid[0].type() != CV_16FC2) {
|
||||
CV_Error(Error::StsError, "Reference pyramid have to be prepared via prepareReferencePyramid function");
|
||||
}
|
||||
// accept only 1-channel images
|
||||
CV_Assert(samplePyramid[0].type() == CV_32FC2 || samplePyramid[0].type() != CV_16FC2);
|
||||
CV_Assert(map.type() == CV_64FC1);
|
||||
if (map.cols != 3 || (map.rows != 2 && map.rows != 3)) {
|
||||
CV_Error(Error::BadImageSize, "warpMatrix has incorrect size");
|
||||
}
|
||||
|
||||
if (eccParams.motionType != MOTION_TRANSLATION && eccParams.motionType != MOTION_EUCLIDEAN &&
|
||||
eccParams.motionType != MOTION_AFFINE && eccParams.motionType != MOTION_HOMOGRAPHY) {
|
||||
CV_Error(Error::StsError, "Incorrect motion type");
|
||||
}
|
||||
|
||||
if (eccParams.motionType == MOTION_HOMOGRAPHY && map.rows != 3) {
|
||||
CV_Error(Error::BadImageSize, "warpMatrix has incorrect size");
|
||||
}
|
||||
|
||||
if (!((bool)(eccParams.criteria.type & TermCriteria::COUNT) || (bool)(eccParams.criteria.type & TermCriteria::EPS))) {
|
||||
CV_Error(Error::StsError, "Incorrect stop eccParams.criteria");
|
||||
}
|
||||
}
|
||||
|
||||
static MatPyramid prepareECCPyramid(InputArray image,
|
||||
InputArray imageMask, // Can be empty
|
||||
int gaussFiltSize,
|
||||
int nlevels) {
|
||||
MatPyramid imagePyramid, maskPyramid;
|
||||
buildPyramidECC(image, imagePyramid, imageMask, maskPyramid, nlevels);
|
||||
for (int lvl = 0; lvl < nlevels; lvl++) {
|
||||
Mat imgFloat;
|
||||
imagePyramid[lvl].convertTo(imgFloat, CV_32F, 1. / 255.);
|
||||
if (gaussFiltSize != 0) {
|
||||
GaussianBlur(imgFloat, imgFloat, Size(gaussFiltSize, gaussFiltSize), 0, 0);
|
||||
}
|
||||
imagePyramid[lvl] = spliceWithMask(
|
||||
imgFloat,
|
||||
(static_cast<int>(maskPyramid.size()) > lvl && !maskPyramid[lvl].empty()) ? maskPyramid[lvl] : Mat());
|
||||
}
|
||||
return imagePyramid;
|
||||
}
|
||||
|
||||
double findTransformECCMultiScale(InputArray reference,
|
||||
InputArray sample,
|
||||
InputOutputArray warpMatrixA,
|
||||
const ECCParameters& eccParams,
|
||||
InputArray referenceMask,
|
||||
InputArray sampleMask) {
|
||||
MatPyramid referencePyramid = prepareECCPyramid(reference, referenceMask, eccParams.gaussFiltSize, eccParams.nlevels);
|
||||
MatPyramid samplePyramid = prepareECCPyramid(sample, sampleMask, eccParams.gaussFiltSize, eccParams.nlevels);
|
||||
Mat& warpMatrix = warpMatrixA.getMatRef();
|
||||
std::vector<int> itersPerLevelCopy = eccParams.itersPerLevel;
|
||||
// If the user passed an un-initialized warpMatrix, initialize to identity
|
||||
if (warpMatrix.empty())
|
||||
{
|
||||
int rowCount = eccParams.motionType == MOTION_HOMOGRAPHY ? 3 : 2;
|
||||
warpMatrix = Mat::eye(rowCount, 3, CV_64FC1);
|
||||
}
|
||||
int warpMatrixType = warpMatrix.type();
|
||||
if (warpMatrixType != CV_64FC1)
|
||||
{
|
||||
warpMatrix.convertTo(warpMatrix, CV_64FC1);
|
||||
}
|
||||
|
||||
checkParams(referencePyramid,
|
||||
samplePyramid,
|
||||
warpMatrix,
|
||||
itersPerLevelCopy,
|
||||
eccParams);
|
||||
|
||||
int nparams = 0;
|
||||
switch (eccParams.motionType) {
|
||||
case MOTION_TRANSLATION:
|
||||
nparams = MotionTraits<MOTION_TRANSLATION>::paramAmount;
|
||||
break;
|
||||
case MOTION_EUCLIDEAN:
|
||||
nparams = MotionTraits<MOTION_EUCLIDEAN>::paramAmount;
|
||||
break;
|
||||
case MOTION_AFFINE:
|
||||
nparams = MotionTraits<MOTION_AFFINE>::paramAmount;
|
||||
break;
|
||||
case MOTION_HOMOGRAPHY:
|
||||
nparams = MotionTraits<MOTION_HOMOGRAPHY>::paramAmount;
|
||||
break;
|
||||
default:
|
||||
CV_Error(Error::StsBadArg, "Incorrect motion type");
|
||||
}
|
||||
|
||||
const std::vector<int> numberOfIterations = ((eccParams.criteria.type & TermCriteria::COUNT) != 0)
|
||||
? itersPerLevelCopy
|
||||
: std::vector<int>(eccParams.nlevels, 200);
|
||||
const double terminationEPS = (bool)(eccParams.criteria.type & TermCriteria::EPS) ? eccParams.criteria.epsilon : -1;
|
||||
|
||||
// Scale warp matrix multiple times to lower pyramid level
|
||||
for (int pyrLevel = 0; pyrLevel < eccParams.nlevels - 1; pyrLevel++) {
|
||||
scaleWarpMatrix(warpMatrix, 0.5);
|
||||
}
|
||||
double rho = -1;
|
||||
for (int pyrLevel = eccParams.nlevels - 1; pyrLevel >= 0; --pyrLevel) {
|
||||
const int hr = referencePyramid[pyrLevel].rows;
|
||||
|
||||
Mat sampleWithGrad = prepareGradients(samplePyramid[pyrLevel]);
|
||||
|
||||
const int LOW_SIZE = 200;
|
||||
int deltaY = hr < LOW_SIZE ? 1 : 2;
|
||||
|
||||
// iteratively update mapMatrix
|
||||
double lastRho = -terminationEPS;
|
||||
for (int i = 1; (i <= numberOfIterations[pyrLevel]) && (fabs(rho - lastRho) >= terminationEPS); i++) {
|
||||
optimizeECC(sampleWithGrad, referencePyramid[pyrLevel], warpMatrix, eccParams.motionType, &rho, &lastRho, deltaY, nparams, eccParams.interpolation);
|
||||
}
|
||||
if (pyrLevel > 0) {
|
||||
scaleWarpMatrix(warpMatrix, 2);
|
||||
}
|
||||
}
|
||||
if(warpMatrixType != CV_64FC1)
|
||||
{
|
||||
warpMatrix.convertTo(warpMatrix, warpMatrixType);
|
||||
}
|
||||
// return final correlation coefficient
|
||||
return rho;
|
||||
}
|
||||
};
|
||||
/* End of file. */
|
||||
@@ -0,0 +1,126 @@
|
||||
// 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_VIDEO_HAL_REPLACEMENT_HPP
|
||||
#define OPENCV_VIDEO_HAL_REPLACEMENT_HPP
|
||||
|
||||
#include "opencv2/core/hal/interface.h"
|
||||
|
||||
#if defined(__clang__) // clang or MSVC clang
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-parameter"
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4100)
|
||||
#elif defined(__GNUC__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#endif
|
||||
|
||||
//! @addtogroup video_hal_interface
|
||||
//! @note Define your functions to override default implementations:
|
||||
//! @code
|
||||
//! #undef cv_hal_LK_optical_flow_level
|
||||
//! #define cv_hal_LK_optical_flow_level my_hal_LK_optical_flow_level
|
||||
//! @endcode
|
||||
//! @{
|
||||
|
||||
/**
|
||||
@brief Lucas-Kanade optical flow for single pyramid layer. See calcOpticalFlowPyrLK.
|
||||
@note OpenCV builds pyramid levels with `win_size` padding. Out-of-bound access to source
|
||||
image data is legal within `+-win_size` range.
|
||||
@param prev_data previous frame image data
|
||||
@param prev_data_step previous frame image data step
|
||||
@param prev_deriv_data previous frame Schaar derivatives
|
||||
@param prev_deriv_step previous frame Schaar derivatives step
|
||||
@param next_data next frame image data
|
||||
@param next_step next frame image step
|
||||
@param width input images width
|
||||
@param height input images height
|
||||
@param cn source image channels
|
||||
@param prev_points 2d points coordinates (x,y) on the previous frame
|
||||
@param next_points points coordinates (x,y) on the next frame
|
||||
@param point_count - amount of input points
|
||||
@param status optical flow status for each point. Optional output, expected if not nullptr is provided
|
||||
@param err optical flow estimation error for each point. Optional output, expected if not nullptr is provided
|
||||
@param win_width optical flow window width
|
||||
@param win_height optical flow window heigh
|
||||
@param termination_count maximum algorithm iterations. 0 means unlimited
|
||||
@param termination_epsilon maximal allowed algorithm error
|
||||
@param get_min_eigen_vals return minimal egen values as point errors in err buffer
|
||||
@param min_eigen_vals_threshold eigen values threshold
|
||||
**/
|
||||
inline int hal_ni_LKOpticalFlowLevel(const uchar *prev_data, size_t prev_data_step,
|
||||
const short* prev_deriv_data, size_t prev_deriv_step,
|
||||
const uchar* next_data, size_t next_step,
|
||||
int width, int height, int cn,
|
||||
const float *prev_points, float *next_points, size_t point_count,
|
||||
uchar *status, float *err,
|
||||
const int win_width, const int win_height,
|
||||
int termination_count, double termination_epsilon,
|
||||
bool get_min_eigen_vals,
|
||||
float min_eigen_vals_threshold)
|
||||
{
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
//! @cond IGNORED
|
||||
#define cv_hal_LKOpticalFlowLevel hal_ni_LKOpticalFlowLevel
|
||||
//! @endcond
|
||||
|
||||
/**
|
||||
@brief Computes Schaar derivatives with inteleaved layout xyxy...
|
||||
@note OpenCV builds pyramid levels with `win_size` padding. Out-of-bound access to source
|
||||
image data is legal within `+-win_size` range.
|
||||
@param src_data source image data
|
||||
@param src_step source image step
|
||||
@param dst_data destination buffer data
|
||||
@param dst_step destination buffer step
|
||||
@param width image width
|
||||
@param height image height
|
||||
@param cn source image channels
|
||||
**/
|
||||
inline int hal_ni_ScharrDeriv(const uchar* src_data, size_t src_step,
|
||||
short* dst_data, size_t dst_step,
|
||||
int width, int height, int cn)
|
||||
{
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
//! @cond IGNORED
|
||||
#define cv_hal_ScharrDeriv hal_ni_ScharrDeriv
|
||||
//! @endcond
|
||||
|
||||
//! @}
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#elif defined(__GNUC__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#include "custom_hal.hpp"
|
||||
|
||||
//! @cond IGNORED
|
||||
#define CALL_HAL_RET(name, fun, retval, ...) \
|
||||
int res = __CV_EXPAND(fun(__VA_ARGS__, &retval)); \
|
||||
if (res == CV_HAL_ERROR_OK) \
|
||||
return retval; \
|
||||
else if (res != CV_HAL_ERROR_NOT_IMPLEMENTED) \
|
||||
CV_Error_(cv::Error::StsInternal, \
|
||||
("HAL implementation " CVAUX_STR(name) " ==> " CVAUX_STR(fun) " returned %d (0x%08x)", res, res));
|
||||
|
||||
|
||||
#define CALL_HAL(name, fun, ...) \
|
||||
int res = __CV_EXPAND(fun(__VA_ARGS__)); \
|
||||
if (res == CV_HAL_ERROR_OK) \
|
||||
return; \
|
||||
else if (res != CV_HAL_ERROR_NOT_IMPLEMENTED) \
|
||||
CV_Error_(cv::Error::StsInternal, \
|
||||
("HAL implementation " CVAUX_STR(name) " ==> " CVAUX_STR(fun) " returned %d (0x%08x)", res, res));
|
||||
//! @endcond
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,134 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of Intel Corporation may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
#include "precomp.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
KalmanFilter::KalmanFilter() {}
|
||||
KalmanFilter::KalmanFilter(int dynamParams, int measureParams, int controlParams, int type)
|
||||
{
|
||||
init(dynamParams, measureParams, controlParams, type);
|
||||
}
|
||||
|
||||
void KalmanFilter::init(int DP, int MP, int CP, int type)
|
||||
{
|
||||
CV_Assert( DP > 0 && MP > 0 );
|
||||
CV_Assert( type == CV_32F || type == CV_64F );
|
||||
CP = std::max(CP, 0);
|
||||
|
||||
statePre = Mat::zeros(DP, 1, type);
|
||||
statePost = Mat::zeros(DP, 1, type);
|
||||
transitionMatrix = Mat::eye(DP, DP, type);
|
||||
|
||||
processNoiseCov = Mat::eye(DP, DP, type);
|
||||
measurementMatrix = Mat::zeros(MP, DP, type);
|
||||
measurementNoiseCov = Mat::eye(MP, MP, type);
|
||||
|
||||
errorCovPre = Mat::zeros(DP, DP, type);
|
||||
errorCovPost = Mat::zeros(DP, DP, type);
|
||||
gain = Mat::zeros(DP, MP, type);
|
||||
|
||||
if( CP > 0 )
|
||||
controlMatrix = Mat::zeros(DP, CP, type);
|
||||
else
|
||||
controlMatrix.release();
|
||||
|
||||
temp1.create(DP, DP, type);
|
||||
temp2.create(MP, DP, type);
|
||||
temp3.create(MP, MP, type);
|
||||
temp4.create(MP, DP, type);
|
||||
temp5.create(MP, 1, type);
|
||||
}
|
||||
|
||||
const Mat& KalmanFilter::predict(const Mat& control)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
// update the state: x'(k) = A*x(k)
|
||||
statePre = transitionMatrix*statePost;
|
||||
|
||||
if( !control.empty() )
|
||||
// x'(k) = x'(k) + B*u(k)
|
||||
statePre += controlMatrix*control;
|
||||
|
||||
// update error covariance matrices: temp1 = A*P(k)
|
||||
temp1 = transitionMatrix*errorCovPost;
|
||||
|
||||
// P'(k) = temp1*At + Q
|
||||
gemm(temp1, transitionMatrix, 1, processNoiseCov, 1, errorCovPre, GEMM_2_T);
|
||||
|
||||
// handle the case when there will be no measurement before the next predict.
|
||||
statePre.copyTo(statePost);
|
||||
errorCovPre.copyTo(errorCovPost);
|
||||
|
||||
return statePre;
|
||||
}
|
||||
|
||||
const Mat& KalmanFilter::correct(const Mat& measurement)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
// temp2 = H*P'(k)
|
||||
temp2 = measurementMatrix * errorCovPre;
|
||||
|
||||
// temp3 = temp2*Ht + R
|
||||
gemm(temp2, measurementMatrix, 1, measurementNoiseCov, 1, temp3, GEMM_2_T);
|
||||
|
||||
// temp4 = inv(temp3)*temp2 = Kt(k)
|
||||
solve(temp3, temp2, temp4, DECOMP_SVD);
|
||||
|
||||
// K(k)
|
||||
gain = temp4.t();
|
||||
|
||||
// temp5 = z(k) - H*x'(k)
|
||||
temp5 = measurement - measurementMatrix*statePre;
|
||||
|
||||
// x(k) = x'(k) + K(k)*temp5
|
||||
statePost = statePre + gain*temp5;
|
||||
|
||||
// P(k) = P'(k) - K(k)*temp2
|
||||
errorCovPost = errorCovPre - gain*temp2;
|
||||
|
||||
return statePost;
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,22 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "lkpyramid.hpp"
|
||||
#include "opencv2/core/hal/intrin.hpp"
|
||||
|
||||
#include "lkpyramid.simd.hpp"
|
||||
#include "lkpyramid.simd_declarations.hpp" // defines CV_CPU_DISPATCH_MODES_ALL based on CMakeLists.txt
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
|
||||
void ScharrDerivInvoker_impl(const Mat& src, Mat& dst, const Range& range)
|
||||
{
|
||||
CV_CPU_DISPATCH(ScharrDerivInvoker_SIMD, (src, dst, range), CV_CPU_DISPATCH_MODES_ALL);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace cv
|
||||
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
typedef short deriv_type;
|
||||
|
||||
void ScharrDerivInvoker_impl(const Mat& src, Mat& dst, const Range& range);
|
||||
|
||||
struct ScharrDerivInvoker : ParallelLoopBody
|
||||
{
|
||||
ScharrDerivInvoker(const Mat& _src, const Mat& _dst)
|
||||
: src(_src), dst(_dst)
|
||||
{ }
|
||||
|
||||
void operator()(const Range& range) const CV_OVERRIDE;
|
||||
|
||||
const Mat& src;
|
||||
const Mat& dst;
|
||||
};
|
||||
|
||||
struct LKTrackerInvoker : ParallelLoopBody
|
||||
{
|
||||
LKTrackerInvoker( const Mat& _prevImg, const Mat& _prevDeriv, const Mat& _nextImg,
|
||||
const Point2f* _prevPts, Point2f* _nextPts,
|
||||
uchar* _status, float* _err,
|
||||
Size _winSize, TermCriteria _criteria,
|
||||
int _level, int _maxLevel, int _flags, float _minEigThreshold );
|
||||
|
||||
void operator()(const Range& range) const CV_OVERRIDE;
|
||||
|
||||
const Mat* prevImg;
|
||||
const Mat* nextImg;
|
||||
const Mat* prevDeriv;
|
||||
const Point2f* prevPts;
|
||||
Point2f* nextPts;
|
||||
uchar* status;
|
||||
float* err;
|
||||
Size winSize;
|
||||
TermCriteria criteria;
|
||||
int level;
|
||||
int maxLevel;
|
||||
int flags;
|
||||
float minEigThreshold;
|
||||
};
|
||||
|
||||
}// namespace detail
|
||||
}// namespace cv
|
||||
@@ -0,0 +1,118 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
|
||||
#include "precomp.hpp"
|
||||
#include "lkpyramid.hpp"
|
||||
#include "opencv2/core/hal/intrin.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
|
||||
CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN
|
||||
|
||||
// SIMD-optimized implementation
|
||||
void ScharrDerivInvoker_SIMD(const Mat& src, Mat& dst, const Range& range);
|
||||
|
||||
CV_CPU_OPTIMIZATION_NAMESPACE_END
|
||||
|
||||
} // namespace detail
|
||||
} // namespace cv
|
||||
|
||||
#ifndef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
|
||||
CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN
|
||||
|
||||
void ScharrDerivInvoker_SIMD(const Mat& src, Mat& dst, const Range& range)
|
||||
{
|
||||
typedef short deriv_type;
|
||||
int rows = src.rows, cols = src.cols, cn = src.channels(), colsn = cols*cn;
|
||||
|
||||
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_SIMD)
|
||||
const int vlanes = VTraits<v_int16>::vlanes();
|
||||
v_int16 c3 = vx_setall_s16(3), c10 = vx_setall_s16(10);
|
||||
#endif
|
||||
|
||||
for( y = range.start; y < range.end; 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 = (deriv_type *)dst.ptr<deriv_type>(y);
|
||||
|
||||
// do vertical convolution
|
||||
x = 0;
|
||||
#if (CV_SIMD)
|
||||
{
|
||||
for( ; x <= colsn - vlanes; x += vlanes )
|
||||
{
|
||||
v_int16 s0 = v_reinterpret_as_s16(vx_load_expand(srow0 + x));
|
||||
v_int16 s1 = v_reinterpret_as_s16(vx_load_expand(srow1 + x));
|
||||
v_int16 s2 = v_reinterpret_as_s16(vx_load_expand(srow2 + x));
|
||||
|
||||
v_int16 t1 = v_sub(s2, s0);
|
||||
v_int16 t0 = v_add(v_mul_wrap(v_add(s0, s2), c3), v_mul_wrap(s1, c10));
|
||||
|
||||
v_store(trow0 + x, t0);
|
||||
v_store(trow1 + x, t1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
for( ; x < colsn; x++ )
|
||||
{
|
||||
int t0 = (srow0[x] + srow2[x])*3 + srow1[x]*10;
|
||||
int t1 = srow2[x] - srow0[x];
|
||||
trow0[x] = (deriv_type)t0;
|
||||
trow1[x] = (deriv_type)t1;
|
||||
}
|
||||
|
||||
// make border
|
||||
int x0 = (cols > 1 ? 1 : 0)*cn, x1 = (cols > 1 ? cols-2 : 0)*cn;
|
||||
for( int k = 0; k < cn; k++ )
|
||||
{
|
||||
trow0[-cn + k] = trow0[x0 + k]; trow0[colsn + k] = trow0[x1 + k];
|
||||
trow1[-cn + k] = trow1[x0 + k]; trow1[colsn + k] = trow1[x1 + k];
|
||||
}
|
||||
|
||||
// do horizontal convolution, interleave the results and store them to dst
|
||||
x = 0;
|
||||
#if (CV_SIMD)
|
||||
{
|
||||
for( ; x <= colsn - vlanes; x += vlanes )
|
||||
{
|
||||
v_int16 s0 = vx_load(trow0 + x - cn);
|
||||
v_int16 s1 = vx_load(trow0 + x + cn);
|
||||
v_int16 s2 = vx_load(trow1 + x - cn);
|
||||
v_int16 s3 = vx_load(trow1 + x);
|
||||
v_int16 s4 = vx_load(trow1 + x + cn);
|
||||
|
||||
v_int16 t0 = v_sub(s1, s0);
|
||||
v_int16 t1 = v_add(v_mul_wrap(v_add(s2, s4), c3), v_mul_wrap(s3, c10));
|
||||
|
||||
v_store_interleave((drow + x*2), t0, t1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
for( ; x < colsn; x++ )
|
||||
{
|
||||
deriv_type t0 = (deriv_type)(trow0[x+cn] - trow0[x-cn]);
|
||||
deriv_type t1 = (deriv_type)((trow1[x+cn] + trow1[x-cn])*3 + trow1[x]*10);
|
||||
drow[x*2] = t0; drow[x*2+1] = t1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CV_CPU_OPTIMIZATION_NAMESPACE_END
|
||||
|
||||
} // namespace detail
|
||||
} // namespace cv
|
||||
|
||||
#endif // CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
|
||||
@@ -0,0 +1,248 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2018 Ya-Chiu Wu, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// @Authors
|
||||
// Ya-Chiu Wu, yacwu@cs.nctu.edu.tw
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#if CN==1
|
||||
|
||||
#define T_MEAN float
|
||||
#define F_ZERO (0.0f)
|
||||
|
||||
#define frameToMean(a, b) (b) = *(a);
|
||||
#define meanToFrame(a, b) *b = convert_uchar_sat(a);
|
||||
|
||||
#else
|
||||
|
||||
#define T_MEAN float4
|
||||
#define F_ZERO (0.0f, 0.0f, 0.0f, 0.0f)
|
||||
|
||||
#define meanToFrame(a, b)\
|
||||
b[0] = convert_uchar_sat(a.x); \
|
||||
b[1] = convert_uchar_sat(a.y); \
|
||||
b[2] = convert_uchar_sat(a.z);
|
||||
|
||||
#define frameToMean(a, b)\
|
||||
b.x = a[0]; \
|
||||
b.y = a[1]; \
|
||||
b.z = a[2]; \
|
||||
b.w = 0.0f;
|
||||
|
||||
#endif
|
||||
|
||||
__kernel void knn_kernel(__global const uchar* frame, int frame_step, int frame_offset, int frame_row, int frame_col,
|
||||
__global const uchar* nNextLongUpdate,
|
||||
__global const uchar* nNextMidUpdate,
|
||||
__global const uchar* nNextShortUpdate,
|
||||
__global uchar* aModelIndexLong,
|
||||
__global uchar* aModelIndexMid,
|
||||
__global uchar* aModelIndexShort,
|
||||
__global uchar* flag,
|
||||
__global uchar* sample,
|
||||
__global uchar* fgmask, int fgmask_step, int fgmask_offset,
|
||||
int nLongCounter, int nMidCounter, int nShortCounter,
|
||||
float c_Tb, int c_nkNN, float c_tau
|
||||
#ifdef SHADOW_DETECT
|
||||
, uchar c_shadowVal
|
||||
#endif
|
||||
)
|
||||
{
|
||||
int x = get_global_id(0);
|
||||
int y = get_global_id(1);
|
||||
|
||||
if( x < frame_col && y < frame_row)
|
||||
{
|
||||
__global const uchar* _frame = (frame + mad24(y, frame_step, mad24(x, CN, frame_offset)));
|
||||
T_MEAN pix;
|
||||
frameToMean(_frame, pix);
|
||||
|
||||
uchar foreground = 255; // 0 - the pixel classified as background
|
||||
|
||||
int Pbf = 0;
|
||||
int Pb = 0;
|
||||
uchar include = 0;
|
||||
|
||||
int pt_idx = mad24(y, frame_col, x);
|
||||
int idx_step = frame_row * frame_col;
|
||||
|
||||
__global T_MEAN* _sample = (__global T_MEAN*)(sample);
|
||||
|
||||
for (uchar n = 0; n < (NSAMPLES) * 3 ; ++n)
|
||||
{
|
||||
int n_idx = mad24(n, idx_step, pt_idx);
|
||||
|
||||
T_MEAN c_mean = _sample[n_idx];
|
||||
|
||||
uchar c_flag = flag[n_idx];
|
||||
|
||||
T_MEAN diff = c_mean - pix;
|
||||
float dist2 = dot(diff, diff);
|
||||
|
||||
if (dist2 < c_Tb)
|
||||
{
|
||||
Pbf++;
|
||||
if (c_flag)
|
||||
{
|
||||
Pb++;
|
||||
if (Pb >= c_nkNN)
|
||||
{
|
||||
include = 1;
|
||||
foreground = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Pbf >= c_nkNN)
|
||||
{
|
||||
include = 1;
|
||||
}
|
||||
|
||||
#ifdef SHADOW_DETECT
|
||||
if (foreground)
|
||||
{
|
||||
int Ps = 0;
|
||||
for (uchar n = 0; n < (NSAMPLES) * 3 ; ++n)
|
||||
{
|
||||
int n_idx = mad24(n, idx_step, pt_idx);
|
||||
uchar c_flag = flag[n_idx];
|
||||
|
||||
if (c_flag)
|
||||
{
|
||||
T_MEAN c_mean = _sample[n_idx];
|
||||
|
||||
float numerator = dot(pix, c_mean);
|
||||
float denominator = dot(c_mean, c_mean);
|
||||
|
||||
if (denominator == 0)
|
||||
break;
|
||||
|
||||
if (numerator <= denominator && numerator >= c_tau * denominator)
|
||||
{
|
||||
float a = numerator / denominator;
|
||||
|
||||
T_MEAN dD = mad(a, c_mean, -pix);
|
||||
|
||||
if (dot(dD, dD) < c_Tb * a * a)
|
||||
{
|
||||
Ps++;
|
||||
if (Ps >= c_nkNN)
|
||||
{
|
||||
foreground = c_shadowVal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
__global uchar* _fgmask = fgmask + mad24(y, fgmask_step, x + fgmask_offset);
|
||||
*_fgmask = (uchar)foreground;
|
||||
|
||||
__global const uchar* _nNextLongUpdate = nNextLongUpdate + pt_idx;
|
||||
__global const uchar* _nNextMidUpdate = nNextMidUpdate + pt_idx;
|
||||
__global const uchar* _nNextShortUpdate = nNextShortUpdate + pt_idx;
|
||||
__global uchar* _aModelIndexLong = aModelIndexLong + pt_idx;
|
||||
__global uchar* _aModelIndexMid = aModelIndexMid + pt_idx;
|
||||
__global uchar* _aModelIndexShort = aModelIndexShort + pt_idx;
|
||||
|
||||
uchar nextLongUpdate = _nNextLongUpdate[0];
|
||||
uchar nextMidUpdate = _nNextMidUpdate[0];
|
||||
uchar nextShortUpdate = _nNextShortUpdate[0];
|
||||
uchar modelIndexLong = _aModelIndexLong[0];
|
||||
uchar modelIndexMid = _aModelIndexMid[0];
|
||||
uchar modelIndexShort = _aModelIndexShort[0];
|
||||
int offsetLong = mad24(mad24(2, (NSAMPLES), modelIndexLong), idx_step, pt_idx);
|
||||
int offsetMid = mad24((NSAMPLES)+modelIndexMid, idx_step, pt_idx);
|
||||
int offsetShort = mad24(modelIndexShort, idx_step, pt_idx);
|
||||
if (nextLongUpdate == nLongCounter)
|
||||
{
|
||||
_sample[offsetLong] = _sample[offsetMid];
|
||||
flag[offsetLong] = flag[offsetMid];
|
||||
_aModelIndexLong[0] = (modelIndexLong >= ((NSAMPLES)-1)) ? 0 : (modelIndexLong + 1);
|
||||
}
|
||||
|
||||
if (nextMidUpdate == nMidCounter)
|
||||
{
|
||||
_sample[offsetMid] = _sample[offsetShort];
|
||||
flag[offsetMid] = flag[offsetShort];
|
||||
_aModelIndexMid[0] = (modelIndexMid >= ((NSAMPLES)-1)) ? 0 : (modelIndexMid + 1);
|
||||
}
|
||||
|
||||
if (nextShortUpdate == nShortCounter)
|
||||
{
|
||||
_sample[offsetShort] = pix;
|
||||
flag[offsetShort] = include;
|
||||
_aModelIndexShort[0] = (modelIndexShort >= ((NSAMPLES)-1)) ? 0 : (modelIndexShort + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__kernel void getBackgroundImage2_kernel(__global const uchar* flag,
|
||||
__global const uchar* sample,
|
||||
__global uchar* dst, int dst_step, int dst_offset, int dst_row, int dst_col)
|
||||
{
|
||||
int x = get_global_id(0);
|
||||
int y = get_global_id(1);
|
||||
|
||||
if(x < dst_col && y < dst_row)
|
||||
{
|
||||
int pt_idx = mad24(y, dst_col, x);
|
||||
|
||||
T_MEAN meanVal = (T_MEAN)F_ZERO;
|
||||
|
||||
__global T_MEAN* _sample = (__global T_MEAN*)(sample);
|
||||
int idx_step = dst_row * dst_col;
|
||||
for (uchar n = 0; n < (NSAMPLES) * 3 ; ++n)
|
||||
{
|
||||
int n_idx = mad24(n, idx_step, pt_idx);
|
||||
uchar c_flag = flag[n_idx];
|
||||
if(c_flag)
|
||||
{
|
||||
meanVal = _sample[n_idx];
|
||||
break;
|
||||
}
|
||||
}
|
||||
__global uchar* _dst = dst + mad24(y, dst_step, mad24(x, CN, dst_offset));
|
||||
meanToFrame(meanVal, _dst);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
#if CN==1
|
||||
|
||||
#define T_MEAN float
|
||||
#define F_ZERO (0.0f)
|
||||
#define cnMode 1
|
||||
|
||||
#define frameToMean(a, b) (b) = *(a);
|
||||
#if FL==0
|
||||
#define meanToFrame(a, b) *b = convert_uchar_sat(a);
|
||||
#else
|
||||
#define meanToFrame(a, b) *b = (float)a;
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#define T_MEAN float4
|
||||
#define F_ZERO (0.0f, 0.0f, 0.0f, 0.0f)
|
||||
#define cnMode 4
|
||||
|
||||
#if FL == 0
|
||||
#define meanToFrame(a, b)\
|
||||
b[0] = convert_uchar_sat(a.x); \
|
||||
b[1] = convert_uchar_sat(a.y); \
|
||||
b[2] = convert_uchar_sat(a.z);
|
||||
#else
|
||||
#define meanToFrame(a, b)\
|
||||
b[0] = a.x; \
|
||||
b[1] = a.y; \
|
||||
b[2] = a.z;
|
||||
#endif
|
||||
|
||||
#define frameToMean(a, b)\
|
||||
b.x = a[0]; \
|
||||
b.y = a[1]; \
|
||||
b.z = a[2]; \
|
||||
b.w = 0.0f;
|
||||
|
||||
#endif
|
||||
|
||||
__kernel void mog2_kernel(__global const uchar* frame, int frame_step, int frame_offset, int frame_row, int frame_col, //uchar || uchar3
|
||||
__global uchar* modesUsed, //uchar
|
||||
__global uchar* weight, //float
|
||||
__global uchar* mean, //T_MEAN=float || float4
|
||||
__global uchar* variance, //float
|
||||
__global uchar* fgmask, int fgmask_step, int fgmask_offset, //uchar
|
||||
float alphaT, float alpha1, float prune,
|
||||
float c_Tb, float c_TB, float c_Tg, float c_varMin, //constants
|
||||
float c_varMax, float c_varInit, float c_tau
|
||||
#ifdef SHADOW_DETECT
|
||||
, uchar c_shadowVal
|
||||
#endif
|
||||
)
|
||||
{
|
||||
int x = get_global_id(0);
|
||||
int y = get_global_id(1);
|
||||
|
||||
if( x < frame_col && y < frame_row)
|
||||
{
|
||||
#if FL==0
|
||||
__global const uchar* _frame = (frame + mad24(y, frame_step, mad24(x, CN, frame_offset)));
|
||||
#else
|
||||
__global const float* _frame = ((__global const float*)( frame + mad24(y, frame_step, frame_offset)) + mad24(x, CN, 0));
|
||||
#endif
|
||||
T_MEAN pix;
|
||||
frameToMean(_frame, pix);
|
||||
|
||||
uchar foreground = 255; // 0 - the pixel classified as background
|
||||
|
||||
bool fitsPDF = false; //if it remains zero a new GMM mode will be added
|
||||
|
||||
int pt_idx = mad24(y, frame_col, x);
|
||||
int idx_step = frame_row * frame_col;
|
||||
|
||||
__global uchar* _modesUsed = modesUsed + pt_idx;
|
||||
uchar nmodes = _modesUsed[0];
|
||||
|
||||
float totalWeight = 0.0f;
|
||||
|
||||
__global float* _weight = (__global float*)(weight);
|
||||
__global float* _variance = (__global float*)(variance);
|
||||
__global T_MEAN* _mean = (__global T_MEAN*)(mean);
|
||||
|
||||
uchar mode = 0;
|
||||
for (; mode < nmodes; ++mode)
|
||||
{
|
||||
int mode_idx = mad24(mode, idx_step, pt_idx);
|
||||
float c_weight = mad(alpha1, _weight[mode_idx], prune);
|
||||
|
||||
float c_var = _variance[mode_idx];
|
||||
|
||||
T_MEAN c_mean = _mean[mode_idx];
|
||||
|
||||
T_MEAN diff = c_mean - pix;
|
||||
float dist2 = dot(diff, diff);
|
||||
|
||||
if (totalWeight < c_TB && dist2 < c_Tb * c_var)
|
||||
foreground = 0;
|
||||
|
||||
if (dist2 < c_Tg * c_var)
|
||||
{
|
||||
fitsPDF = true;
|
||||
c_weight += alphaT;
|
||||
|
||||
float k = alphaT / c_weight;
|
||||
T_MEAN mean_new = mad((T_MEAN)-k, diff, c_mean);
|
||||
float variance_new = clamp(mad(k, (dist2 - c_var), c_var), c_varMin, c_varMax);
|
||||
|
||||
for (int i = mode; i > 0; --i)
|
||||
{
|
||||
int prev_idx = mode_idx - idx_step;
|
||||
if (c_weight < _weight[prev_idx])
|
||||
break;
|
||||
|
||||
_weight[mode_idx] = _weight[prev_idx];
|
||||
_variance[mode_idx] = _variance[prev_idx];
|
||||
_mean[mode_idx] = _mean[prev_idx];
|
||||
|
||||
mode_idx = prev_idx;
|
||||
}
|
||||
|
||||
_mean[mode_idx] = mean_new;
|
||||
_variance[mode_idx] = variance_new;
|
||||
_weight[mode_idx] = c_weight; //update weight by the calculated value
|
||||
|
||||
totalWeight += c_weight;
|
||||
|
||||
mode ++;
|
||||
|
||||
break;
|
||||
}
|
||||
if (c_weight < -prune)
|
||||
c_weight = 0.0f;
|
||||
|
||||
_weight[mode_idx] = c_weight; //update weight by the calculated value
|
||||
totalWeight += c_weight;
|
||||
}
|
||||
|
||||
for (; mode < nmodes; ++mode)
|
||||
{
|
||||
int mode_idx = mad24(mode, idx_step, pt_idx);
|
||||
float c_weight = mad(alpha1, _weight[mode_idx], prune);
|
||||
|
||||
if (c_weight < -prune)
|
||||
{
|
||||
c_weight = 0.0f;
|
||||
nmodes = mode;
|
||||
break;
|
||||
}
|
||||
_weight[mode_idx] = c_weight; //update weight by the calculated value
|
||||
totalWeight += c_weight;
|
||||
}
|
||||
|
||||
if (0.f < totalWeight)
|
||||
{
|
||||
totalWeight = 1.f / totalWeight;
|
||||
for (int mode = 0; mode < nmodes; ++mode)
|
||||
_weight[mad24(mode, idx_step, pt_idx)] *= totalWeight;
|
||||
}
|
||||
|
||||
if (!fitsPDF)
|
||||
{
|
||||
uchar mode = nmodes == (NMIXTURES) ? (NMIXTURES) - 1 : nmodes++;
|
||||
int mode_idx = mad24(mode, idx_step, pt_idx);
|
||||
|
||||
if (nmodes == 1)
|
||||
_weight[mode_idx] = 1.f;
|
||||
else
|
||||
{
|
||||
_weight[mode_idx] = alphaT;
|
||||
|
||||
for (int i = pt_idx; i < mode_idx; i += idx_step)
|
||||
_weight[i] *= alpha1;
|
||||
}
|
||||
|
||||
for (int i = nmodes - 1; i > 0; --i)
|
||||
{
|
||||
int prev_idx = mode_idx - idx_step;
|
||||
if (alphaT < _weight[prev_idx])
|
||||
break;
|
||||
|
||||
_weight[mode_idx] = _weight[prev_idx];
|
||||
_variance[mode_idx] = _variance[prev_idx];
|
||||
_mean[mode_idx] = _mean[prev_idx];
|
||||
|
||||
mode_idx = prev_idx;
|
||||
}
|
||||
|
||||
_mean[mode_idx] = pix;
|
||||
_variance[mode_idx] = c_varInit;
|
||||
}
|
||||
|
||||
_modesUsed[0] = nmodes;
|
||||
#ifdef SHADOW_DETECT
|
||||
if (foreground)
|
||||
{
|
||||
float tWeight = 0.0f;
|
||||
|
||||
for (uchar mode = 0; mode < nmodes; ++mode)
|
||||
{
|
||||
int mode_idx = mad24(mode, idx_step, pt_idx);
|
||||
T_MEAN c_mean = _mean[mode_idx];
|
||||
|
||||
float numerator = dot(pix, c_mean);
|
||||
float denominator = dot(c_mean, c_mean);
|
||||
|
||||
if (denominator == 0)
|
||||
break;
|
||||
|
||||
if (numerator <= denominator && numerator >= c_tau * denominator)
|
||||
{
|
||||
float a = numerator / denominator;
|
||||
|
||||
T_MEAN dD = mad(a, c_mean, -pix);
|
||||
|
||||
if (dot(dD, dD) < c_Tb * _variance[mode_idx] * a * a)
|
||||
{
|
||||
foreground = c_shadowVal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tWeight += _weight[mode_idx];
|
||||
if (tWeight > c_TB)
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
__global uchar* _fgmask = fgmask + mad24(y, fgmask_step, x + fgmask_offset);
|
||||
*_fgmask = (uchar)foreground;
|
||||
}
|
||||
}
|
||||
|
||||
__kernel void getBackgroundImage2_kernel(__global const uchar* modesUsed,
|
||||
__global const uchar* weight,
|
||||
__global const uchar* mean,
|
||||
__global uchar* dst, int dst_step, int dst_offset, int dst_row, int dst_col,
|
||||
float c_TB)
|
||||
{
|
||||
int x = get_global_id(0);
|
||||
int y = get_global_id(1);
|
||||
|
||||
if(x < dst_col && y < dst_row)
|
||||
{
|
||||
int pt_idx = mad24(y, dst_col, x);
|
||||
|
||||
__global const uchar* _modesUsed = modesUsed + pt_idx;
|
||||
uchar nmodes = _modesUsed[0];
|
||||
|
||||
T_MEAN meanVal = (T_MEAN)F_ZERO;
|
||||
|
||||
float totalWeight = 0.0f;
|
||||
__global const float* _weight = (__global const float*)weight;
|
||||
__global const T_MEAN* _mean = (__global const T_MEAN*)(mean);
|
||||
int idx_step = dst_row * dst_col;
|
||||
for (uchar mode = 0; mode < nmodes; ++mode)
|
||||
{
|
||||
int mode_idx = mad24(mode, idx_step, pt_idx);
|
||||
float c_weight = _weight[mode_idx];
|
||||
T_MEAN c_mean = _mean[mode_idx];
|
||||
|
||||
meanVal = mad(c_weight, c_mean, meanVal);
|
||||
|
||||
totalWeight += c_weight;
|
||||
|
||||
if (totalWeight > c_TB)
|
||||
break;
|
||||
}
|
||||
|
||||
if (0.f < totalWeight)
|
||||
meanVal = meanVal / totalWeight;
|
||||
else
|
||||
meanVal = (T_MEAN)(0.f);
|
||||
|
||||
#if FL==0
|
||||
__global uchar* _dst = dst + mad24(y, dst_step, mad24(x, CN, dst_offset));
|
||||
meanToFrame(meanVal, _dst);
|
||||
#else
|
||||
__global float* _dst = ((__global float*)( dst + mad24(y, dst_step, dst_offset)) + mad24(x, CN, 0));
|
||||
meanToFrame(meanVal, _dst);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,540 @@
|
||||
// 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.
|
||||
|
||||
//#define CV_USE_SUBGROUPS
|
||||
|
||||
#define EPS 0.001f
|
||||
#define INF 1E+10F
|
||||
|
||||
//#define DIS_BORDER_SIZE xxx
|
||||
//#define DIS_PATCH_SIZE xxx
|
||||
//#define DIS_PATCH_STRIDE xxx
|
||||
|
||||
#define DIS_PATCH_SIZE_HALF (DIS_PATCH_SIZE / 2)
|
||||
|
||||
#ifndef DIS_BORDER_SIZE
|
||||
|
||||
__kernel void dis_precomputeStructureTensor_hor(__global const short *I0x,
|
||||
__global const short *I0y,
|
||||
int w, int h, int ws,
|
||||
__global float *I0xx_aux_ptr,
|
||||
__global float *I0yy_aux_ptr,
|
||||
__global float *I0xy_aux_ptr,
|
||||
__global float *I0x_aux_ptr,
|
||||
__global float *I0y_aux_ptr)
|
||||
{
|
||||
|
||||
int i = get_global_id(0);
|
||||
|
||||
if (i >= h) return;
|
||||
|
||||
const __global short *x_row = I0x + i * w;
|
||||
const __global short *y_row = I0y + i * w;
|
||||
|
||||
float sum_xx = 0.0f, sum_yy = 0.0f, sum_xy = 0.0f, sum_x = 0.0f, sum_y = 0.0f;
|
||||
float8 x_vec = convert_float8(vload8(0, x_row));
|
||||
float8 y_vec = convert_float8(vload8(0, y_row));
|
||||
sum_xx = dot(x_vec.lo, x_vec.lo) + dot(x_vec.hi, x_vec.hi);
|
||||
sum_yy = dot(y_vec.lo, y_vec.lo) + dot(y_vec.hi, y_vec.hi);
|
||||
sum_xy = dot(x_vec.lo, y_vec.lo) + dot(x_vec.hi, y_vec.hi);
|
||||
sum_x = dot(x_vec.lo, 1.0f) + dot(x_vec.hi, 1.0f);
|
||||
sum_y = dot(y_vec.lo, 1.0f) + dot(y_vec.hi, 1.0f);
|
||||
|
||||
I0xx_aux_ptr[i * ws] = sum_xx;
|
||||
I0yy_aux_ptr[i * ws] = sum_yy;
|
||||
I0xy_aux_ptr[i * ws] = sum_xy;
|
||||
I0x_aux_ptr[i * ws] = sum_x;
|
||||
I0y_aux_ptr[i * ws] = sum_y;
|
||||
|
||||
int js = 1;
|
||||
for (int j = DIS_PATCH_SIZE; j < w; j++)
|
||||
{
|
||||
short x_val1 = x_row[j];
|
||||
short x_val2 = x_row[j - DIS_PATCH_SIZE];
|
||||
short y_val1 = y_row[j];
|
||||
short y_val2 = y_row[j - DIS_PATCH_SIZE];
|
||||
sum_xx += (x_val1 * x_val1 - x_val2 * x_val2);
|
||||
sum_yy += (y_val1 * y_val1 - y_val2 * y_val2);
|
||||
sum_xy += (x_val1 * y_val1 - x_val2 * y_val2);
|
||||
sum_x += (x_val1 - x_val2);
|
||||
sum_y += (y_val1 - y_val2);
|
||||
if ((j - DIS_PATCH_SIZE + 1) % DIS_PATCH_STRIDE == 0)
|
||||
{
|
||||
int index = i * ws + js;
|
||||
I0xx_aux_ptr[index] = sum_xx;
|
||||
I0yy_aux_ptr[index] = sum_yy;
|
||||
I0xy_aux_ptr[index] = sum_xy;
|
||||
I0x_aux_ptr[index] = sum_x;
|
||||
I0y_aux_ptr[index] = sum_y;
|
||||
js++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__kernel void dis_precomputeStructureTensor_ver(__global const float *I0xx_aux_ptr,
|
||||
__global const float *I0yy_aux_ptr,
|
||||
__global const float *I0xy_aux_ptr,
|
||||
__global const float *I0x_aux_ptr,
|
||||
__global const float *I0y_aux_ptr,
|
||||
int w, int h, int ws,
|
||||
__global float *I0xx_ptr,
|
||||
__global float *I0yy_ptr,
|
||||
__global float *I0xy_ptr,
|
||||
__global float *I0x_ptr,
|
||||
__global float *I0y_ptr)
|
||||
{
|
||||
int j = get_global_id(0);
|
||||
|
||||
if (j >= ws) return;
|
||||
|
||||
float sum_xx, sum_yy, sum_xy, sum_x, sum_y;
|
||||
sum_xx = sum_yy = sum_xy = sum_x = sum_y = 0.0f;
|
||||
|
||||
for (int i = 0; i < DIS_PATCH_SIZE; i++)
|
||||
{
|
||||
sum_xx += I0xx_aux_ptr[i * ws + j];
|
||||
sum_yy += I0yy_aux_ptr[i * ws + j];
|
||||
sum_xy += I0xy_aux_ptr[i * ws + j];
|
||||
sum_x += I0x_aux_ptr[i * ws + j];
|
||||
sum_y += I0y_aux_ptr[i * ws + j];
|
||||
}
|
||||
I0xx_ptr[j] = sum_xx;
|
||||
I0yy_ptr[j] = sum_yy;
|
||||
I0xy_ptr[j] = sum_xy;
|
||||
I0x_ptr[j] = sum_x;
|
||||
I0y_ptr[j] = sum_y;
|
||||
|
||||
int is = 1;
|
||||
for (int i = DIS_PATCH_SIZE; i < h; i++)
|
||||
{
|
||||
sum_xx += (I0xx_aux_ptr[i * ws + j] - I0xx_aux_ptr[(i - DIS_PATCH_SIZE) * ws + j]);
|
||||
sum_yy += (I0yy_aux_ptr[i * ws + j] - I0yy_aux_ptr[(i - DIS_PATCH_SIZE) * ws + j]);
|
||||
sum_xy += (I0xy_aux_ptr[i * ws + j] - I0xy_aux_ptr[(i - DIS_PATCH_SIZE) * ws + j]);
|
||||
sum_x += (I0x_aux_ptr[i * ws + j] - I0x_aux_ptr[(i - DIS_PATCH_SIZE) * ws + j]);
|
||||
sum_y += (I0y_aux_ptr[i * ws + j] - I0y_aux_ptr[(i - DIS_PATCH_SIZE) * ws + j]);
|
||||
|
||||
if ((i - DIS_PATCH_SIZE + 1) % DIS_PATCH_STRIDE == 0)
|
||||
{
|
||||
I0xx_ptr[is * ws + j] = sum_xx;
|
||||
I0yy_ptr[is * ws + j] = sum_yy;
|
||||
I0xy_ptr[is * ws + j] = sum_xy;
|
||||
I0x_ptr[is * ws + j] = sum_x;
|
||||
I0y_ptr[is * ws + j] = sum_y;
|
||||
is++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__kernel void dis_densification(__global const float2 *S_ptr,
|
||||
__global const uchar *i0, __global const uchar *i1,
|
||||
int w, int h, int ws,
|
||||
__global float2 *U_ptr)
|
||||
{
|
||||
int x = get_global_id(0);
|
||||
int y = get_global_id(1);
|
||||
int i, j;
|
||||
|
||||
if (x >= w || y >= h) return;
|
||||
|
||||
int start_is, end_is;
|
||||
int start_js, end_js;
|
||||
|
||||
end_is = min(y / DIS_PATCH_STRIDE, (h - DIS_PATCH_SIZE) / DIS_PATCH_STRIDE);
|
||||
start_is = max(0, y - DIS_PATCH_SIZE + DIS_PATCH_STRIDE) / DIS_PATCH_STRIDE;
|
||||
start_is = min(start_is, end_is);
|
||||
|
||||
end_js = min(x / DIS_PATCH_STRIDE, (w - DIS_PATCH_SIZE) / DIS_PATCH_STRIDE);
|
||||
start_js = max(0, x - DIS_PATCH_SIZE + DIS_PATCH_STRIDE) / DIS_PATCH_STRIDE;
|
||||
start_js = min(start_js, end_js);
|
||||
|
||||
float sum_coef = 0.0f;
|
||||
float2 sum_U = (float2)(0.0f, 0.0f);
|
||||
|
||||
int i_l, i_u;
|
||||
int j_l, j_u;
|
||||
float i_m, j_m, diff;
|
||||
|
||||
i = y;
|
||||
j = x;
|
||||
|
||||
/* Iterate through all the patches that overlap the current location (i,j) */
|
||||
for (int is = start_is; is <= end_is; is++)
|
||||
for (int js = start_js; js <= end_js; js++)
|
||||
{
|
||||
float2 s_val = S_ptr[is * ws + js];
|
||||
uchar2 i1_vec1, i1_vec2;
|
||||
|
||||
j_m = min(max(j + s_val.x, 0.0f), w - 1.0f - EPS);
|
||||
i_m = min(max(i + s_val.y, 0.0f), h - 1.0f - EPS);
|
||||
j_l = (int)j_m;
|
||||
j_u = j_l + 1;
|
||||
i_l = (int)i_m;
|
||||
i_u = i_l + 1;
|
||||
i1_vec1 = vload2(0, i1 + i_u * w + j_l);
|
||||
i1_vec2 = vload2(0, i1 + i_l * w + j_l);
|
||||
diff = (j_m - j_l) * (i_m - i_l) * i1_vec1.y +
|
||||
(j_u - j_m) * (i_m - i_l) * i1_vec1.x +
|
||||
(j_m - j_l) * (i_u - i_m) * i1_vec2.y +
|
||||
(j_u - j_m) * (i_u - i_m) * i1_vec2.x - i0[i * w + j];
|
||||
float coef = 1.0f / max(1.0f, fabs(diff));
|
||||
sum_U += coef * s_val;
|
||||
sum_coef += coef;
|
||||
}
|
||||
|
||||
float inv_sum_coef = 1.0 / sum_coef;
|
||||
U_ptr[i * w + j] = sum_U * inv_sum_coef;
|
||||
}
|
||||
|
||||
#else // DIS_BORDER_SIZE
|
||||
|
||||
#define INIT_BILINEAR_WEIGHTS(Ux, Uy) \
|
||||
i_I1 = clamp(i + Uy + DIS_BORDER_SIZE, i_lower_limit, i_upper_limit); \
|
||||
j_I1 = clamp(j + Ux + DIS_BORDER_SIZE, j_lower_limit, j_upper_limit); \
|
||||
{ \
|
||||
float di = i_I1 - floor(i_I1); \
|
||||
float dj = j_I1 - floor(j_I1); \
|
||||
w11 = di * dj; \
|
||||
w10 = di * (1 - dj); \
|
||||
w01 = (1 - di) * dj; \
|
||||
w00 = (1 - di) * (1 - dj); \
|
||||
}
|
||||
|
||||
float computeSSDMeanNorm(const __global uchar *I0_ptr, const __global uchar *I1_ptr,
|
||||
int I0_stride, int I1_stride,
|
||||
float w00, float w01, float w10, float w11, int i
|
||||
#ifndef CV_USE_SUBGROUPS
|
||||
, __local float2 *smem /*[8]*/
|
||||
#endif
|
||||
)
|
||||
{
|
||||
float sum_diff = 0.0f, sum_diff_sq = 0.0f;
|
||||
int n = DIS_PATCH_SIZE * DIS_PATCH_SIZE;
|
||||
|
||||
uchar8 I1_vec1, I1_vec2, I0_vec;
|
||||
uchar I1_val1, I1_val2;
|
||||
|
||||
I0_vec = vload8(0, I0_ptr + i * I0_stride);
|
||||
I1_vec1 = vload8(0, I1_ptr + i * I1_stride);
|
||||
I1_vec2 = vload8(0, I1_ptr + (i + 1) * I1_stride);
|
||||
I1_val1 = I1_ptr[i * I1_stride + 8];
|
||||
I1_val2 = I1_ptr[(i + 1) * I1_stride + 8];
|
||||
|
||||
float8 vec = w00 * convert_float8(I1_vec1) + w01 * convert_float8((uchar8)(I1_vec1.s123, I1_vec1.s4567, I1_val1)) +
|
||||
w10 * convert_float8(I1_vec2) + w11 * convert_float8((uchar8)(I1_vec2.s123, I1_vec2.s4567, I1_val2)) -
|
||||
convert_float8(I0_vec);
|
||||
|
||||
sum_diff = (dot(vec.lo, 1.0) + dot(vec.hi, 1.0));
|
||||
sum_diff_sq = (dot(vec.lo, vec.lo) + dot(vec.hi, vec.hi));
|
||||
|
||||
#ifdef CV_USE_SUBGROUPS
|
||||
sum_diff = sub_group_reduce_add(sum_diff);
|
||||
sum_diff_sq = sub_group_reduce_add(sum_diff_sq);
|
||||
#else
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
smem[i] = (float2)(sum_diff, sum_diff_sq);
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (i < 4)
|
||||
smem[i] += smem[i + 4];
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (i < 2)
|
||||
smem[i] += smem[i + 2];
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (i == 0)
|
||||
smem[0] += smem[1];
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
float2 reduce_add_result = smem[0];
|
||||
sum_diff = reduce_add_result.x;
|
||||
sum_diff_sq = reduce_add_result.y;
|
||||
#endif
|
||||
|
||||
return sum_diff_sq - sum_diff * sum_diff / n;
|
||||
}
|
||||
|
||||
__attribute__((reqd_work_group_size(8, 1, 1)))
|
||||
__kernel void dis_patch_inverse_search_fwd_1(__global const float2 *U_ptr,
|
||||
__global const uchar *I0_ptr, __global const uchar *I1_ptr,
|
||||
int w, int h, int ws, int hs,
|
||||
__global float2 *S_ptr)
|
||||
{
|
||||
int id = get_global_id(0);
|
||||
int is = get_group_id(0);
|
||||
|
||||
int i = is * DIS_PATCH_STRIDE;
|
||||
int j = 0;
|
||||
int w_ext = w + 2 * DIS_BORDER_SIZE;
|
||||
|
||||
float i_lower_limit = DIS_BORDER_SIZE - DIS_PATCH_SIZE + 1.0f;
|
||||
float i_upper_limit = DIS_BORDER_SIZE + h - 1.0f;
|
||||
float j_lower_limit = DIS_BORDER_SIZE - DIS_PATCH_SIZE + 1.0f;
|
||||
float j_upper_limit = DIS_BORDER_SIZE + w - 1.0f;
|
||||
|
||||
float2 prev_U = U_ptr[(i + DIS_PATCH_SIZE_HALF) * w + j + DIS_PATCH_SIZE_HALF];
|
||||
S_ptr[is * ws] = prev_U;
|
||||
j += DIS_PATCH_STRIDE;
|
||||
|
||||
#ifdef CV_USE_SUBGROUPS
|
||||
int sid = get_sub_group_local_id();
|
||||
#define EXTRA_ARGS_computeSSDMeanNorm sid
|
||||
#else
|
||||
__local float2 smem[8];
|
||||
int sid = get_local_id(0);
|
||||
#define EXTRA_ARGS_computeSSDMeanNorm sid, smem
|
||||
#endif
|
||||
for (int js = 1; js < ws; js++, j += DIS_PATCH_STRIDE)
|
||||
{
|
||||
float2 U = U_ptr[(i + DIS_PATCH_SIZE_HALF) * w + j + DIS_PATCH_SIZE_HALF];
|
||||
|
||||
float i_I1, j_I1, w00, w01, w10, w11;
|
||||
|
||||
INIT_BILINEAR_WEIGHTS(U.x, U.y);
|
||||
float min_SSD = computeSSDMeanNorm(
|
||||
I0_ptr + i * w + j, I1_ptr + (int)i_I1 * w_ext + (int)j_I1,
|
||||
w, w_ext, w00, w01, w10, w11, EXTRA_ARGS_computeSSDMeanNorm);
|
||||
|
||||
INIT_BILINEAR_WEIGHTS(prev_U.x, prev_U.y);
|
||||
float cur_SSD = computeSSDMeanNorm(
|
||||
I0_ptr + i * w + j, I1_ptr + (int)i_I1 * w_ext + (int)j_I1,
|
||||
w, w_ext, w00, w01, w10, w11, EXTRA_ARGS_computeSSDMeanNorm);
|
||||
|
||||
prev_U = (cur_SSD < min_SSD) ? prev_U : U;
|
||||
S_ptr[is * ws + js] = prev_U;
|
||||
}
|
||||
|
||||
#undef EXTRA_ARGS_computeSSDMeanNorm
|
||||
}
|
||||
#endif // DIS_BORDER_SIZE
|
||||
|
||||
float4 processPatchMeanNorm(const __global uchar *I0_ptr, const __global uchar *I1_ptr,
|
||||
const __global short *I0x_ptr, const __global short *I0y_ptr,
|
||||
int I0_stride, int I1_stride, float w00, float w01, float w10,
|
||||
float w11, float x_grad_sum, float y_grad_sum)
|
||||
{
|
||||
const float inv_n = 1.0f / (float)(DIS_PATCH_SIZE * DIS_PATCH_SIZE);
|
||||
|
||||
float sum_diff = 0.0, sum_diff_sq = 0.0;
|
||||
float sum_I0x_mul = 0.0, sum_I0y_mul = 0.0;
|
||||
|
||||
uchar8 I1_vec1;
|
||||
uchar8 I1_vec2 = vload8(0, I1_ptr);
|
||||
uchar I1_val1;
|
||||
uchar I1_val2 = I1_ptr[DIS_PATCH_SIZE];
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
uchar8 I0_vec = vload8(0, I0_ptr + i * I0_stride);
|
||||
|
||||
I1_vec1 = I1_vec2;
|
||||
I1_vec2 = vload8(0, I1_ptr + (i + 1) * I1_stride);
|
||||
I1_val1 = I1_val2;
|
||||
I1_val2 = I1_ptr[(i + 1) * I1_stride + DIS_PATCH_SIZE];
|
||||
|
||||
float8 vec = w00 * convert_float8(I1_vec1) + w01 * convert_float8((uchar8)(I1_vec1.s123, I1_vec1.s4567, I1_val1)) +
|
||||
w10 * convert_float8(I1_vec2) + w11 * convert_float8((uchar8)(I1_vec2.s123, I1_vec2.s4567, I1_val2)) -
|
||||
convert_float8(I0_vec);
|
||||
|
||||
sum_diff += (dot(vec.lo, 1.0) + dot(vec.hi, 1.0));
|
||||
sum_diff_sq += (dot(vec.lo, vec.lo) + dot(vec.hi, vec.hi));
|
||||
|
||||
short8 I0x_vec = vload8(0, I0x_ptr + i * I0_stride);
|
||||
short8 I0y_vec = vload8(0, I0y_ptr + i * I0_stride);
|
||||
|
||||
sum_I0x_mul += dot(vec.lo, convert_float4(I0x_vec.lo));
|
||||
sum_I0x_mul += dot(vec.hi, convert_float4(I0x_vec.hi));
|
||||
sum_I0y_mul += dot(vec.lo, convert_float4(I0y_vec.lo));
|
||||
sum_I0y_mul += dot(vec.hi, convert_float4(I0y_vec.hi));
|
||||
}
|
||||
|
||||
float dst_dUx = sum_I0x_mul - sum_diff * x_grad_sum * inv_n;
|
||||
float dst_dUy = sum_I0y_mul - sum_diff * y_grad_sum * inv_n;
|
||||
float SSD = sum_diff_sq - sum_diff * sum_diff * inv_n;
|
||||
|
||||
return (float4)(SSD, dst_dUx, dst_dUy, 0);
|
||||
}
|
||||
|
||||
#ifdef DIS_BORDER_SIZE
|
||||
__kernel void dis_patch_inverse_search_fwd_2(__global const float2 *U_ptr,
|
||||
__global const uchar *I0_ptr, __global const uchar *I1_ptr,
|
||||
__global const short *I0x_ptr, __global const short *I0y_ptr,
|
||||
__global const float *xx_ptr, __global const float *yy_ptr,
|
||||
__global const float *xy_ptr,
|
||||
__global const float *x_ptr, __global const float *y_ptr,
|
||||
int w, int h, int ws, int hs, int num_inner_iter,
|
||||
__global float2 *S_ptr)
|
||||
{
|
||||
int js = get_global_id(0);
|
||||
int is = get_global_id(1);
|
||||
int i = is * DIS_PATCH_STRIDE;
|
||||
int j = js * DIS_PATCH_STRIDE;
|
||||
const int psz = DIS_PATCH_SIZE;
|
||||
int w_ext = w + 2 * DIS_BORDER_SIZE;
|
||||
int index = is * ws + js;
|
||||
|
||||
if (js >= ws || is >= hs) return;
|
||||
|
||||
float2 U0 = S_ptr[index];
|
||||
float2 cur_U = U0;
|
||||
float cur_xx = xx_ptr[index];
|
||||
float cur_yy = yy_ptr[index];
|
||||
float cur_xy = xy_ptr[index];
|
||||
float detH = cur_xx * cur_yy - cur_xy * cur_xy;
|
||||
|
||||
float inv_detH = (fabs(detH) < EPS) ? 1.0 / EPS : 1.0 / detH;
|
||||
float invH11 = cur_yy * inv_detH;
|
||||
float invH12 = -cur_xy * inv_detH;
|
||||
float invH22 = cur_xx * inv_detH;
|
||||
|
||||
float prev_SSD = INF;
|
||||
float x_grad_sum = x_ptr[index];
|
||||
float y_grad_sum = y_ptr[index];
|
||||
|
||||
const float i_lower_limit = DIS_BORDER_SIZE - DIS_PATCH_SIZE + 1.0f;
|
||||
const float i_upper_limit = DIS_BORDER_SIZE + h - 1.0f;
|
||||
const float j_lower_limit = DIS_BORDER_SIZE - DIS_PATCH_SIZE + 1.0f;
|
||||
const float j_upper_limit = DIS_BORDER_SIZE + w - 1.0f;
|
||||
|
||||
for (int t = 0; t < num_inner_iter; t++)
|
||||
{
|
||||
float i_I1, j_I1, w00, w01, w10, w11;
|
||||
INIT_BILINEAR_WEIGHTS(cur_U.x, cur_U.y);
|
||||
float4 res = processPatchMeanNorm(
|
||||
I0_ptr + i * w + j, I1_ptr + (int)i_I1 * w_ext + (int)j_I1,
|
||||
I0x_ptr + i * w + j, I0y_ptr + i * w + j,
|
||||
w, w_ext, w00, w01, w10, w11,
|
||||
x_grad_sum, y_grad_sum);
|
||||
|
||||
float SSD = res.x;
|
||||
float dUx = res.y;
|
||||
float dUy = res.z;
|
||||
float dx = invH11 * dUx + invH12 * dUy;
|
||||
float dy = invH12 * dUx + invH22 * dUy;
|
||||
|
||||
cur_U -= (float2)(dx, dy);
|
||||
|
||||
if (SSD >= prev_SSD)
|
||||
break;
|
||||
prev_SSD = SSD;
|
||||
}
|
||||
|
||||
float2 vec = cur_U - U0;
|
||||
S_ptr[index] = (dot(vec, vec) <= (float)(DIS_PATCH_SIZE * DIS_PATCH_SIZE)) ? cur_U : U0;
|
||||
}
|
||||
|
||||
__attribute__((reqd_work_group_size(8, 1, 1)))
|
||||
__kernel void dis_patch_inverse_search_bwd_1(__global const uchar *I0_ptr, __global const uchar *I1_ptr,
|
||||
int w, int h, int ws, int hs,
|
||||
__global float2 *S_ptr)
|
||||
{
|
||||
int id = get_global_id(0);
|
||||
int is = get_group_id(0);
|
||||
|
||||
is = (hs - 1 - is);
|
||||
int i = is * DIS_PATCH_STRIDE;
|
||||
int j = (ws - 2) * DIS_PATCH_STRIDE;
|
||||
const int w_ext = w + 2 * DIS_BORDER_SIZE;
|
||||
|
||||
const float i_lower_limit = DIS_BORDER_SIZE - DIS_PATCH_SIZE + 1.0f;
|
||||
const float i_upper_limit = DIS_BORDER_SIZE + h - 1.0f;
|
||||
const float j_lower_limit = DIS_BORDER_SIZE - DIS_PATCH_SIZE + 1.0f;
|
||||
const float j_upper_limit = DIS_BORDER_SIZE + w - 1.0f;
|
||||
|
||||
#ifdef CV_USE_SUBGROUPS
|
||||
int sid = get_sub_group_local_id();
|
||||
#define EXTRA_ARGS_computeSSDMeanNorm sid
|
||||
#else
|
||||
__local float2 smem[8];
|
||||
int sid = get_local_id(0);
|
||||
#define EXTRA_ARGS_computeSSDMeanNorm sid, smem
|
||||
#endif
|
||||
|
||||
for (int js = (ws - 2); js > -1; js--, j -= DIS_PATCH_STRIDE)
|
||||
{
|
||||
float2 U0 = S_ptr[is * ws + js];
|
||||
float2 U1 = S_ptr[is * ws + js + 1];
|
||||
|
||||
float i_I1, j_I1, w00, w01, w10, w11;
|
||||
|
||||
INIT_BILINEAR_WEIGHTS(U0.x, U0.y);
|
||||
float min_SSD = computeSSDMeanNorm(
|
||||
I0_ptr + i * w + j, I1_ptr + (int)i_I1 * w_ext + (int)j_I1,
|
||||
w, w_ext, w00, w01, w10, w11, EXTRA_ARGS_computeSSDMeanNorm);
|
||||
|
||||
INIT_BILINEAR_WEIGHTS(U1.x, U1.y);
|
||||
float cur_SSD = computeSSDMeanNorm(
|
||||
I0_ptr + i * w + j, I1_ptr + (int)i_I1 * w_ext + (int)j_I1,
|
||||
w, w_ext, w00, w01, w10, w11, EXTRA_ARGS_computeSSDMeanNorm);
|
||||
|
||||
S_ptr[is * ws + js] = (cur_SSD < min_SSD) ? U1 : U0;
|
||||
}
|
||||
|
||||
#undef EXTRA_ARGS_computeSSDMeanNorm
|
||||
}
|
||||
|
||||
__kernel void dis_patch_inverse_search_bwd_2(__global const uchar *I0_ptr, __global const uchar *I1_ptr,
|
||||
__global const short *I0x_ptr, __global const short *I0y_ptr,
|
||||
__global const float *xx_ptr, __global const float *yy_ptr,
|
||||
__global const float *xy_ptr,
|
||||
__global const float *x_ptr, __global const float *y_ptr,
|
||||
int w, int h, int ws, int hs, int num_inner_iter,
|
||||
__global float2 *S_ptr)
|
||||
{
|
||||
int js = get_global_id(0);
|
||||
int is = get_global_id(1);
|
||||
if (js >= ws || is >= hs) return;
|
||||
|
||||
js = (ws - 1 - js);
|
||||
is = (hs - 1 - is);
|
||||
|
||||
int j = js * DIS_PATCH_STRIDE;
|
||||
int i = is * DIS_PATCH_STRIDE;
|
||||
int w_ext = w + 2 * DIS_BORDER_SIZE;
|
||||
int index = is * ws + js;
|
||||
|
||||
float2 U0 = S_ptr[index];
|
||||
float2 cur_U = U0;
|
||||
float cur_xx = xx_ptr[index];
|
||||
float cur_yy = yy_ptr[index];
|
||||
float cur_xy = xy_ptr[index];
|
||||
float detH = cur_xx * cur_yy - cur_xy * cur_xy;
|
||||
|
||||
float inv_detH = (fabs(detH) < EPS) ? 1.0 / EPS : 1.0 / detH;
|
||||
float invH11 = cur_yy * inv_detH;
|
||||
float invH12 = -cur_xy * inv_detH;
|
||||
float invH22 = cur_xx * inv_detH;
|
||||
|
||||
float prev_SSD = INF;
|
||||
float x_grad_sum = x_ptr[index];
|
||||
float y_grad_sum = y_ptr[index];
|
||||
|
||||
const float i_lower_limit = DIS_BORDER_SIZE - DIS_PATCH_SIZE + 1.0f;
|
||||
const float i_upper_limit = DIS_BORDER_SIZE + h - 1.0f;
|
||||
const float j_lower_limit = DIS_BORDER_SIZE - DIS_PATCH_SIZE + 1.0f;
|
||||
const float j_upper_limit = DIS_BORDER_SIZE + w - 1.0f;
|
||||
|
||||
for (int t = 0; t < num_inner_iter; t++)
|
||||
{
|
||||
float i_I1, j_I1, w00, w01, w10, w11;
|
||||
INIT_BILINEAR_WEIGHTS(cur_U.x, cur_U.y);
|
||||
float4 res = processPatchMeanNorm(
|
||||
I0_ptr + i * w + j, I1_ptr + (int)i_I1 * w_ext + (int)j_I1,
|
||||
I0x_ptr + i * w + j, I0y_ptr + i * w + j,
|
||||
w, w_ext, w00, w01, w10, w11,
|
||||
x_grad_sum, y_grad_sum);
|
||||
|
||||
float SSD = res.x;
|
||||
float dUx = res.y;
|
||||
float dUy = res.z;
|
||||
float dx = invH11 * dUx + invH12 * dUy;
|
||||
float dy = invH12 * dUx + invH22 * dUy;
|
||||
|
||||
cur_U -= (float2)(dx, dy);
|
||||
|
||||
if (SSD >= prev_SSD)
|
||||
break;
|
||||
prev_SSD = SSD;
|
||||
}
|
||||
|
||||
float2 vec = cur_U - U0;
|
||||
S_ptr[index] = ((dot(vec, vec)) <= (float)(DIS_PATCH_SIZE * DIS_PATCH_SIZE)) ? cur_U : U0;
|
||||
}
|
||||
#endif // DIS_BORDER_SIZE
|
||||
@@ -0,0 +1,429 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2010-2012, Multicoreware, Inc., all rights reserved.
|
||||
// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// @Authors
|
||||
// Sen Liu, swjtuls1987@126.com
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors as is and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
|
||||
#define tx (int)get_local_id(0)
|
||||
#define ty get_local_id(1)
|
||||
#define bx get_group_id(0)
|
||||
#define bdx (int)get_local_size(0)
|
||||
|
||||
#define BORDER_SIZE 5
|
||||
#define MAX_KSIZE_HALF 100
|
||||
|
||||
#ifndef polyN
|
||||
#define polyN 5
|
||||
#endif
|
||||
|
||||
#if USE_DOUBLE
|
||||
#ifdef cl_amd_fp64
|
||||
#pragma OPENCL EXTENSION cl_amd_fp64:enable
|
||||
#elif defined (cl_khr_fp64)
|
||||
#pragma OPENCL EXTENSION cl_khr_fp64:enable
|
||||
#endif
|
||||
#define TYPE double
|
||||
#define VECTYPE double4
|
||||
#else
|
||||
#define TYPE float
|
||||
#define VECTYPE float4
|
||||
#endif
|
||||
|
||||
__kernel void polynomialExpansion(__global __const float * src, int srcStep,
|
||||
__global float * dst, int dstStep,
|
||||
const int rows, const int cols,
|
||||
__global __const float * c_g,
|
||||
__global __const float * c_xg,
|
||||
__global __const float * c_xxg,
|
||||
__local float * smem,
|
||||
const VECTYPE ig)
|
||||
{
|
||||
const int y = get_global_id(1);
|
||||
const int x = bx * (bdx - 2*polyN) + tx - polyN;
|
||||
|
||||
int xWarped;
|
||||
__local float *row = smem + tx;
|
||||
|
||||
if (y < rows && y >= 0)
|
||||
{
|
||||
xWarped = min(max(x, 0), cols - 1);
|
||||
|
||||
row[0] = src[mad24(y, srcStep, xWarped)] * c_g[0];
|
||||
row[bdx] = 0.f;
|
||||
row[2*bdx] = 0.f;
|
||||
|
||||
#pragma unroll
|
||||
for (int k = 1; k <= polyN; ++k)
|
||||
{
|
||||
float t0 = src[mad24(max(y - k, 0), srcStep, xWarped)];
|
||||
float t1 = src[mad24(min(y + k, rows - 1), srcStep, xWarped)];
|
||||
|
||||
row[0] += c_g[k] * (t0 + t1);
|
||||
row[bdx] += c_xg[k] * (t1 - t0);
|
||||
row[2*bdx] += c_xxg[k] * (t0 + t1);
|
||||
}
|
||||
}
|
||||
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
if (y < rows && y >= 0 && tx >= polyN && tx + polyN < bdx && x < cols)
|
||||
{
|
||||
TYPE b1 = c_g[0] * row[0];
|
||||
TYPE b3 = c_g[0] * row[bdx];
|
||||
TYPE b5 = c_g[0] * row[2*bdx];
|
||||
TYPE b2 = 0, b4 = 0, b6 = 0;
|
||||
|
||||
#pragma unroll
|
||||
for (int k = 1; k <= polyN; ++k)
|
||||
{
|
||||
b1 += (row[k] + row[-k]) * c_g[k];
|
||||
b4 += (row[k] + row[-k]) * c_xxg[k];
|
||||
b2 += (row[k] - row[-k]) * c_xg[k];
|
||||
b3 += (row[k + bdx] + row[-k + bdx]) * c_g[k];
|
||||
b6 += (row[k + bdx] - row[-k + bdx]) * c_xg[k];
|
||||
b5 += (row[k + 2*bdx] + row[-k + 2*bdx]) * c_g[k];
|
||||
}
|
||||
|
||||
dst[mad24(y, dstStep, xWarped)] = (float)(b3*ig.s0);
|
||||
dst[mad24(rows + y, dstStep, xWarped)] = (float)(b2*ig.s0);
|
||||
dst[mad24(2*rows + y, dstStep, xWarped)] = (float)(b1*ig.s1 + b5*ig.s2);
|
||||
dst[mad24(3*rows + y, dstStep, xWarped)] = (float)(b1*ig.s1 + b4*ig.s2);
|
||||
dst[mad24(4*rows + y, dstStep, xWarped)] = (float)(b6*ig.s3);
|
||||
}
|
||||
}
|
||||
|
||||
inline int idx_row_low(const int y, const int last_row)
|
||||
{
|
||||
return abs(y) % (last_row + 1);
|
||||
}
|
||||
|
||||
inline int idx_row_high(const int y, const int last_row)
|
||||
{
|
||||
return abs(last_row - abs(last_row - y)) % (last_row + 1);
|
||||
}
|
||||
|
||||
inline int idx_col_low(const int x, const int last_col)
|
||||
{
|
||||
return abs(x) % (last_col + 1);
|
||||
}
|
||||
|
||||
inline int idx_col_high(const int x, const int last_col)
|
||||
{
|
||||
return abs(last_col - abs(last_col - x)) % (last_col + 1);
|
||||
}
|
||||
|
||||
inline int idx_col(const int x, const int last_col)
|
||||
{
|
||||
return idx_col_low(idx_col_high(x, last_col), last_col);
|
||||
}
|
||||
|
||||
__kernel void gaussianBlur(__global const float * src, int srcStep,
|
||||
__global float * dst, int dstStep, const int rows, const int cols,
|
||||
__global const float * c_gKer, const int ksizeHalf,
|
||||
__local float * smem)
|
||||
{
|
||||
const int y = get_global_id(1);
|
||||
const int x = get_global_id(0);
|
||||
|
||||
__local float *row = smem + ty * (bdx + 2*ksizeHalf);
|
||||
|
||||
if (y < rows)
|
||||
{
|
||||
// Vertical pass
|
||||
for (int i = tx; i < bdx + 2*ksizeHalf; i += bdx)
|
||||
{
|
||||
int xExt = (int)(bx * bdx) + i - ksizeHalf;
|
||||
xExt = idx_col(xExt, cols - 1);
|
||||
row[i] = src[mad24(y, srcStep, xExt)] * c_gKer[0];
|
||||
for (int j = 1; j <= ksizeHalf; ++j)
|
||||
row[i] += (src[mad24(idx_row_low(y - j, rows - 1), srcStep, xExt)]
|
||||
+ src[mad24(idx_row_high(y + j, rows - 1), srcStep, xExt)]) * c_gKer[j];
|
||||
}
|
||||
}
|
||||
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
if (y < rows && y >= 0 && x < cols && x >= 0)
|
||||
{
|
||||
// Horizontal pass
|
||||
row += tx + ksizeHalf;
|
||||
float res = row[0] * c_gKer[0];
|
||||
for (int i = 1; i <= ksizeHalf; ++i)
|
||||
res += (row[-i] + row[i]) * c_gKer[i];
|
||||
|
||||
dst[mad24(y, dstStep, x)] = res;
|
||||
}
|
||||
}
|
||||
|
||||
__kernel void gaussianBlur5(__global const float * src, int srcStep,
|
||||
__global float * dst, int dstStep,
|
||||
const int rows, const int cols,
|
||||
__global const float * c_gKer, const int ksizeHalf,
|
||||
__local float * smem)
|
||||
{
|
||||
const int y = get_global_id(1);
|
||||
const int x = get_global_id(0);
|
||||
|
||||
const int smw = bdx + 2*ksizeHalf; // shared memory "cols"
|
||||
__local volatile float *row = smem + 5 * ty * smw;
|
||||
|
||||
if (y < rows)
|
||||
{
|
||||
// Vertical pass
|
||||
for (int i = tx; i < bdx + 2*ksizeHalf; i += bdx)
|
||||
{
|
||||
int xExt = (int)(bx * bdx) + i - ksizeHalf;
|
||||
xExt = idx_col(xExt, cols - 1);
|
||||
|
||||
#pragma unroll
|
||||
for (int k = 0; k < 5; ++k)
|
||||
row[k*smw + i] = src[mad24(k*rows + y, srcStep, xExt)] * c_gKer[0];
|
||||
|
||||
for (int j = 1; j <= ksizeHalf; ++j)
|
||||
#pragma unroll
|
||||
for (int k = 0; k < 5; ++k)
|
||||
row[k*smw + i] +=
|
||||
(src[mad24(k*rows + idx_row_low(y - j, rows - 1), srcStep, xExt)] +
|
||||
src[mad24(k*rows + idx_row_high(y + j, rows - 1), srcStep, xExt)]) * c_gKer[j];
|
||||
}
|
||||
}
|
||||
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
if (y < rows && y >= 0 && x < cols && x >= 0)
|
||||
{
|
||||
// Horizontal pass
|
||||
|
||||
row += tx + ksizeHalf;
|
||||
float res[5];
|
||||
|
||||
#pragma unroll
|
||||
for (int k = 0; k < 5; ++k)
|
||||
res[k] = row[k*smw] * c_gKer[0];
|
||||
|
||||
for (int i = 1; i <= ksizeHalf; ++i)
|
||||
#pragma unroll
|
||||
for (int k = 0; k < 5; ++k)
|
||||
res[k] += (row[k*smw - i] + row[k*smw + i]) * c_gKer[i];
|
||||
|
||||
#pragma unroll
|
||||
for (int k = 0; k < 5; ++k)
|
||||
dst[mad24(k*rows + y, dstStep, x)] = res[k];
|
||||
}
|
||||
}
|
||||
__constant float c_border[BORDER_SIZE + 1] = { 0.14f, 0.14f, 0.4472f, 0.4472f, 0.4472f, 1.f };
|
||||
|
||||
__kernel void updateMatrices(__global const float * flowx, int xStep,
|
||||
__global const float * flowy, int yStep,
|
||||
const int rows, const int cols,
|
||||
__global const float * R0, int R0Step,
|
||||
__global const float * R1, int R1Step,
|
||||
__global float * M, int mStep)
|
||||
{
|
||||
const int y = get_global_id(1);
|
||||
const int x = get_global_id(0);
|
||||
|
||||
if (y < rows && y >= 0 && x < cols && x >= 0)
|
||||
{
|
||||
float dx = flowx[mad24(y, xStep, x)];
|
||||
float dy = flowy[mad24(y, yStep, x)];
|
||||
float fx = x + dx;
|
||||
float fy = y + dy;
|
||||
|
||||
int x1 = convert_int(floor(fx));
|
||||
int y1 = convert_int(floor(fy));
|
||||
fx -= x1;
|
||||
fy -= y1;
|
||||
|
||||
float r2, r3, r4, r5, r6;
|
||||
|
||||
if (x1 >= 0 && y1 >= 0 && x1 < cols - 1 && y1 < rows - 1)
|
||||
{
|
||||
float a00 = (1.f - fx) * (1.f - fy);
|
||||
float a01 = fx * (1.f - fy);
|
||||
float a10 = (1.f - fx) * fy;
|
||||
float a11 = fx * fy;
|
||||
|
||||
r2 = a00 * R1[mad24(y1, R1Step, x1)] +
|
||||
a01 * R1[mad24(y1, R1Step, x1 + 1)] +
|
||||
a10 * R1[mad24(y1 + 1, R1Step, x1)] +
|
||||
a11 * R1[mad24(y1 + 1, R1Step, x1 + 1)];
|
||||
|
||||
r3 = a00 * R1[mad24(rows + y1, R1Step, x1)] +
|
||||
a01 * R1[mad24(rows + y1, R1Step, x1 + 1)] +
|
||||
a10 * R1[mad24(rows + y1 + 1, R1Step, x1)] +
|
||||
a11 * R1[mad24(rows + y1 + 1, R1Step, x1 + 1)];
|
||||
|
||||
r4 = a00 * R1[mad24(2*rows + y1, R1Step, x1)] +
|
||||
a01 * R1[mad24(2*rows + y1, R1Step, x1 + 1)] +
|
||||
a10 * R1[mad24(2*rows + y1 + 1, R1Step, x1)] +
|
||||
a11 * R1[mad24(2*rows + y1 + 1, R1Step, x1 + 1)];
|
||||
|
||||
r5 = a00 * R1[mad24(3*rows + y1, R1Step, x1)] +
|
||||
a01 * R1[mad24(3*rows + y1, R1Step, x1 + 1)] +
|
||||
a10 * R1[mad24(3*rows + y1 + 1, R1Step, x1)] +
|
||||
a11 * R1[mad24(3*rows + y1 + 1, R1Step, x1 + 1)];
|
||||
|
||||
r6 = a00 * R1[mad24(4*rows + y1, R1Step, x1)] +
|
||||
a01 * R1[mad24(4*rows + y1, R1Step, x1 + 1)] +
|
||||
a10 * R1[mad24(4*rows + y1 + 1, R1Step, x1)] +
|
||||
a11 * R1[mad24(4*rows + y1 + 1, R1Step, x1 + 1)];
|
||||
|
||||
r4 = (R0[mad24(2*rows + y, R0Step, x)] + r4) * 0.5f;
|
||||
r5 = (R0[mad24(3*rows + y, R0Step, x)] + r5) * 0.5f;
|
||||
r6 = (R0[mad24(4*rows + y, R0Step, x)] + r6) * 0.25f;
|
||||
}
|
||||
else
|
||||
{
|
||||
r2 = r3 = 0.f;
|
||||
r4 = R0[mad24(2*rows + y, R0Step, x)];
|
||||
r5 = R0[mad24(3*rows + y, R0Step, x)];
|
||||
r6 = R0[mad24(4*rows + y, R0Step, x)] * 0.5f;
|
||||
}
|
||||
|
||||
r2 = (R0[mad24(y, R0Step, x)] - r2) * 0.5f;
|
||||
r3 = (R0[mad24(rows + y, R0Step, x)] - r3) * 0.5f;
|
||||
|
||||
r2 += r4*dy + r6*dx;
|
||||
r3 += r6*dy + r5*dx;
|
||||
|
||||
float scale =
|
||||
c_border[min(x, BORDER_SIZE)] *
|
||||
c_border[min(y, BORDER_SIZE)] *
|
||||
c_border[min(cols - x - 1, BORDER_SIZE)] *
|
||||
c_border[min(rows - y - 1, BORDER_SIZE)];
|
||||
|
||||
r2 *= scale;
|
||||
r3 *= scale;
|
||||
r4 *= scale;
|
||||
r5 *= scale;
|
||||
r6 *= scale;
|
||||
|
||||
M[mad24(y, mStep, x)] = r4*r4 + r6*r6;
|
||||
M[mad24(rows + y, mStep, x)] = (r4 + r5)*r6;
|
||||
M[mad24(2*rows + y, mStep, x)] = r5*r5 + r6*r6;
|
||||
M[mad24(3*rows + y, mStep, x)] = r4*r2 + r6*r3;
|
||||
M[mad24(4*rows + y, mStep, x)] = r6*r2 + r5*r3;
|
||||
}
|
||||
}
|
||||
|
||||
__kernel void boxFilter5(__global const float * src, int srcStep,
|
||||
__global float * dst, int dstStep,
|
||||
const int rows, const int cols,
|
||||
const int ksizeHalf,
|
||||
__local float * smem)
|
||||
{
|
||||
const int y = get_global_id(1);
|
||||
const int x = get_global_id(0);
|
||||
|
||||
const float boxAreaInv = 1.f / ((1 + 2*ksizeHalf) * (1 + 2*ksizeHalf));
|
||||
const int smw = bdx + 2*ksizeHalf; // shared memory "width"
|
||||
__local float *row = smem + 5 * ty * smw;
|
||||
|
||||
if (y < rows)
|
||||
{
|
||||
// Vertical pass
|
||||
for (int i = tx; i < bdx + 2*ksizeHalf; i += bdx)
|
||||
{
|
||||
int xExt = (int)(bx * bdx) + i - ksizeHalf;
|
||||
xExt = min(max(xExt, 0), cols - 1);
|
||||
|
||||
#pragma unroll
|
||||
for (int k = 0; k < 5; ++k)
|
||||
row[k*smw + i] = src[mad24(k*rows + y, srcStep, xExt)];
|
||||
|
||||
for (int j = 1; j <= ksizeHalf; ++j)
|
||||
#pragma unroll
|
||||
for (int k = 0; k < 5; ++k)
|
||||
row[k*smw + i] +=
|
||||
src[mad24(k*rows + max(y - j, 0), srcStep, xExt)] +
|
||||
src[mad24(k*rows + min(y + j, rows - 1), srcStep, xExt)];
|
||||
}
|
||||
}
|
||||
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
if (y < rows && y >= 0 && x < cols && x >= 0)
|
||||
{
|
||||
// Horizontal pass
|
||||
|
||||
row += tx + ksizeHalf;
|
||||
float res[5];
|
||||
|
||||
#pragma unroll
|
||||
for (int k = 0; k < 5; ++k)
|
||||
res[k] = row[k*smw];
|
||||
|
||||
for (int i = 1; i <= ksizeHalf; ++i)
|
||||
#pragma unroll
|
||||
for (int k = 0; k < 5; ++k)
|
||||
res[k] += row[k*smw - i] + row[k*smw + i];
|
||||
|
||||
#pragma unroll
|
||||
for (int k = 0; k < 5; ++k)
|
||||
dst[mad24(k*rows + y, dstStep, x)] = res[k] * boxAreaInv;
|
||||
}
|
||||
}
|
||||
|
||||
__kernel void updateFlow(__global const float * M, int mStep,
|
||||
__global float * flowx, int xStep,
|
||||
__global float * flowy, int yStep,
|
||||
const int rows, const int cols)
|
||||
{
|
||||
const int y = get_global_id(1);
|
||||
const int x = get_global_id(0);
|
||||
|
||||
if (y < rows && y >= 0 && x < cols && x >= 0)
|
||||
{
|
||||
float g11 = M[mad24(y, mStep, x)];
|
||||
float g12 = M[mad24(rows + y, mStep, x)];
|
||||
float g22 = M[mad24(2*rows + y, mStep, x)];
|
||||
float h1 = M[mad24(3*rows + y, mStep, x)];
|
||||
float h2 = M[mad24(4*rows + y, mStep, x)];
|
||||
|
||||
float detInv = 1.f / (g11*g22 - g12*g12 + 1e-3f);
|
||||
|
||||
flowx[mad24(y, xStep, x)] = (g11*h2 - g12*h1) * detInv;
|
||||
flowy[mad24(y, yStep, x)] = (g22*h1 - g12*h2) * detInv;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,558 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2010-2012, Multicoreware, Inc., all rights reserved.
|
||||
// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// @Authors
|
||||
// Dachuan Zhao, dachuan@multicorewareinc.com
|
||||
// Yao Wang, bitwangyaoyao@gmail.com
|
||||
// Xiaopeng Fu, fuxiaopeng2222@163.com
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors as is and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#define GRIDSIZE 3
|
||||
#define LSx 8
|
||||
#define LSy 8
|
||||
// define local memory sizes
|
||||
#define LM_W (LSx*GRIDSIZE+2)
|
||||
#define LM_H (LSy*GRIDSIZE+2)
|
||||
#define BUFFER (LSx*LSy)
|
||||
#define BUFFER2 BUFFER>>1
|
||||
|
||||
#ifdef CPU
|
||||
|
||||
inline void reduce3(float val1, float val2, float val3, __local float* smem1, __local float* smem2, __local float* smem3, int tid)
|
||||
{
|
||||
smem1[tid] = val1;
|
||||
smem2[tid] = val2;
|
||||
smem3[tid] = val3;
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
for(int i = BUFFER2; i > 0; i >>= 1)
|
||||
{
|
||||
if(tid < i)
|
||||
{
|
||||
smem1[tid] += smem1[tid + i];
|
||||
smem2[tid] += smem2[tid + i];
|
||||
smem3[tid] += smem3[tid + i];
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
}
|
||||
}
|
||||
|
||||
inline void reduce2(float val1, float val2, __local float* smem1, __local float* smem2, int tid)
|
||||
{
|
||||
smem1[tid] = val1;
|
||||
smem2[tid] = val2;
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
for(int i = BUFFER2; i > 0; i >>= 1)
|
||||
{
|
||||
if(tid < i)
|
||||
{
|
||||
smem1[tid] += smem1[tid + i];
|
||||
smem2[tid] += smem2[tid + i];
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
}
|
||||
}
|
||||
|
||||
inline void reduce1(float val1, __local float* smem1, int tid)
|
||||
{
|
||||
smem1[tid] = val1;
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
for(int i = BUFFER2; i > 0; i >>= 1)
|
||||
{
|
||||
if(tid < i)
|
||||
{
|
||||
smem1[tid] += smem1[tid + i];
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
}
|
||||
}
|
||||
#else
|
||||
inline void reduce3(float val1, float val2, float val3,
|
||||
__local float* smem1, __local float* smem2, __local float* smem3, int tid)
|
||||
{
|
||||
smem1[tid] = val1;
|
||||
smem2[tid] = val2;
|
||||
smem3[tid] = val3;
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
if (tid < 32)
|
||||
{
|
||||
smem1[tid] += smem1[tid + 32];
|
||||
smem2[tid] += smem2[tid + 32];
|
||||
smem3[tid] += smem3[tid + 32];
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (tid < 16)
|
||||
{
|
||||
smem1[tid] += smem1[tid + 16];
|
||||
smem2[tid] += smem2[tid + 16];
|
||||
smem3[tid] += smem3[tid + 16];
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (tid < 8)
|
||||
{
|
||||
smem1[tid] += smem1[tid + 8];
|
||||
smem2[tid] += smem2[tid + 8];
|
||||
smem3[tid] += smem3[tid + 8];
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (tid < 4)
|
||||
{
|
||||
smem1[tid] += smem1[tid + 4];
|
||||
smem2[tid] += smem2[tid + 4];
|
||||
smem3[tid] += smem3[tid + 4];
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (tid == 0)
|
||||
{
|
||||
smem1[0] = (smem1[0] + smem1[1]) + (smem1[2] + smem1[3]);
|
||||
smem2[0] = (smem2[0] + smem2[1]) + (smem2[2] + smem2[3]);
|
||||
smem3[0] = (smem3[0] + smem3[1]) + (smem3[2] + smem3[3]);
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
}
|
||||
|
||||
inline void reduce2(float val1, float val2, __local float* smem1, __local float* smem2, int tid)
|
||||
{
|
||||
smem1[tid] = val1;
|
||||
smem2[tid] = val2;
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
if (tid < 32)
|
||||
{
|
||||
smem1[tid] += smem1[tid + 32];
|
||||
smem2[tid] += smem2[tid + 32];
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (tid < 16)
|
||||
{
|
||||
smem1[tid] += smem1[tid + 16];
|
||||
smem2[tid] += smem2[tid + 16];
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (tid < 8)
|
||||
{
|
||||
smem1[tid] += smem1[tid + 8];
|
||||
smem2[tid] += smem2[tid + 8];
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (tid < 4)
|
||||
{
|
||||
smem1[tid] += smem1[tid + 4];
|
||||
smem2[tid] += smem2[tid + 4];
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (tid == 0)
|
||||
{
|
||||
smem1[0] = (smem1[0] + smem1[1]) + (smem1[2] + smem1[3]);
|
||||
smem2[0] = (smem2[0] + smem2[1]) + (smem2[2] + smem2[3]);
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
}
|
||||
|
||||
inline void reduce1(float val1, __local float* smem1, int tid)
|
||||
{
|
||||
smem1[tid] = val1;
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
if (tid < 32)
|
||||
{
|
||||
smem1[tid] += smem1[tid + 32];
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (tid < 16)
|
||||
{
|
||||
smem1[tid] += smem1[tid + 16];
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (tid < 8)
|
||||
{
|
||||
smem1[tid] += smem1[tid + 8];
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (tid < 4)
|
||||
{
|
||||
smem1[tid] += smem1[tid + 4];
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
if (tid == 0)
|
||||
{
|
||||
smem1[0] = (smem1[0] + smem1[1]) + (smem1[2] + smem1[3]);
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define SCALE (1.0f / (1 << 20))
|
||||
#define THRESHOLD 0.01f
|
||||
|
||||
// Image read mode
|
||||
__constant sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_LINEAR;
|
||||
|
||||
// macro to get pixel value from local memory
|
||||
|
||||
#define VAL(_y,_x,_yy,_xx) (IPatchLocal[mad24(((_y) + (_yy)), LM_W, ((_x) + (_xx)))])
|
||||
inline void SetPatch(local float* IPatchLocal, int TileY, int TileX,
|
||||
float* Pch, float* Dx, float* Dy,
|
||||
float* A11, float* A12, float* A22, float w)
|
||||
{
|
||||
int xid=get_local_id(0);
|
||||
int yid=get_local_id(1);
|
||||
int xBase = mad24(TileX, LSx, (xid + 1));
|
||||
int yBase = mad24(TileY, LSy, (yid + 1));
|
||||
|
||||
*Pch = VAL(yBase,xBase,0,0);
|
||||
|
||||
*Dx = mad((VAL(yBase,xBase,-1,1) + VAL(yBase,xBase,+1,1) - VAL(yBase,xBase,-1,-1) - VAL(yBase,xBase,+1,-1)), 3.0f, (VAL(yBase,xBase,0,1) - VAL(yBase,xBase,0,-1)) * 10.0f) * w;
|
||||
*Dy = mad((VAL(yBase,xBase,1,-1) + VAL(yBase,xBase,1,+1) - VAL(yBase,xBase,-1,-1) - VAL(yBase,xBase,-1,+1)), 3.0f, (VAL(yBase,xBase,1,0) - VAL(yBase,xBase,-1,0)) * 10.0f) * w;
|
||||
|
||||
*A11 = mad(*Dx, *Dx, *A11);
|
||||
*A12 = mad(*Dx, *Dy, *A12);
|
||||
*A22 = mad(*Dy, *Dy, *A22);
|
||||
}
|
||||
#undef VAL
|
||||
|
||||
inline void GetPatch(image2d_t J, float x, float y,
|
||||
float* Pch, float* Dx, float* Dy,
|
||||
float* b1, float* b2)
|
||||
{
|
||||
float diff = read_imagef(J, sampler, (float2)(x,y)).x-*Pch;
|
||||
*b1 = mad(diff, *Dx, *b1);
|
||||
*b2 = mad(diff, *Dy, *b2);
|
||||
}
|
||||
|
||||
inline void GetError(image2d_t J, const float x, const float y, const float* Pch, float* errval, float w)
|
||||
{
|
||||
float diff = ((((read_imagef(J, sampler, (float2)(x,y)).x * 16384) + 256) / 512) - (((*Pch * 16384) + 256) /512)) * w;
|
||||
*errval += fabs(diff);
|
||||
}
|
||||
|
||||
|
||||
//macro to read pixel value into local memory.
|
||||
#define READI(_y,_x) IPatchLocal[mad24(mad24((_y), LSy, yid), LM_W, mad24((_x), LSx, xid))] = read_imagef(I, sampler, (float2)(mad((float)(_x), (float)LSx, Point.x + xid - 0.5f), mad((float)(_y), (float)LSy, Point.y + yid - 0.5f))).x;
|
||||
void ReadPatchIToLocalMem(image2d_t I, float2 Point, local float* IPatchLocal)
|
||||
{
|
||||
int xid=get_local_id(0);
|
||||
int yid=get_local_id(1);
|
||||
//read (3*LSx)*(3*LSy) window. each macro call read LSx*LSy pixels block
|
||||
READI(0,0);READI(0,1);READI(0,2);
|
||||
READI(1,0);READI(1,1);READI(1,2);
|
||||
READI(2,0);READI(2,1);READI(2,2);
|
||||
if(xid<2)
|
||||
{// read last 2 columns border. each macro call reads 2*LSy pixels block
|
||||
READI(0,3);
|
||||
READI(1,3);
|
||||
READI(2,3);
|
||||
}
|
||||
|
||||
if(yid<2)
|
||||
{// read last 2 row. each macro call reads LSx*2 pixels block
|
||||
READI(3,0);READI(3,1);READI(3,2);
|
||||
}
|
||||
|
||||
if(yid<2 && xid<2)
|
||||
{// read right bottom 2x2 corner. one macro call reads 2*2 pixels block
|
||||
READI(3,3);
|
||||
}
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
}
|
||||
#undef READI
|
||||
|
||||
__attribute__((reqd_work_group_size(LSx, LSy, 1)))
|
||||
__kernel void lkSparse(image2d_t I, image2d_t J,
|
||||
__global const float2* prevPts, __global float2* nextPts, __global uchar* status, __global float* err,
|
||||
const int level, const int rows, const int cols, int PATCH_X, int PATCH_Y, int c_winSize_x, int c_winSize_y, int c_iters, char calcErr)
|
||||
{
|
||||
__local float smem1[BUFFER];
|
||||
__local float smem2[BUFFER];
|
||||
__local float smem3[BUFFER];
|
||||
|
||||
int xid=get_local_id(0);
|
||||
int yid=get_local_id(1);
|
||||
int gid=get_group_id(0);
|
||||
int xsize=get_local_size(0);
|
||||
int ysize=get_local_size(1);
|
||||
int k;
|
||||
|
||||
#ifdef CPU
|
||||
float wx0 = 1.0f;
|
||||
float wy0 = 1.0f;
|
||||
int xBase = mad24(xsize, 2, xid);
|
||||
int yBase = mad24(ysize, 2, yid);
|
||||
float wx1 = (xBase < c_winSize_x) ? 1 : 0;
|
||||
float wy1 = (yBase < c_winSize_y) ? 1 : 0;
|
||||
#else
|
||||
#if WSX == 1
|
||||
float wx0 = 1.0f;
|
||||
int xBase = mad24(xsize, 2, xid);
|
||||
float wx1 = (xBase < c_winSize_x) ? 1 : 0;
|
||||
#else
|
||||
int xBase = mad24(xsize, 1, xid);
|
||||
float wx0 = (xBase < c_winSize_x) ? 1 : 0;
|
||||
float wx1 = 0.0f;
|
||||
#endif
|
||||
#if WSY == 1
|
||||
float wy0 = 1.0f;
|
||||
int yBase = mad24(ysize, 2, yid);
|
||||
float wy1 = (yBase < c_winSize_y) ? 1 : 0;
|
||||
#else
|
||||
int yBase = mad24(ysize, 1, yid);
|
||||
float wy0 = (yBase < c_winSize_y) ? 1 : 0;
|
||||
float wy1 = 0.0f;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
float2 c_halfWin = (float2)((c_winSize_x - 1)>>1, (c_winSize_y - 1)>>1);
|
||||
|
||||
const int tid = mad24(yid, xsize, xid);
|
||||
|
||||
float2 prevPt = prevPts[gid] / (float2)(1 << level);
|
||||
|
||||
if (prevPt.x < 0 || prevPt.x >= cols || prevPt.y < 0 || prevPt.y >= rows)
|
||||
{
|
||||
if (tid == 0 && level == 0)
|
||||
{
|
||||
status[gid] = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
prevPt -= c_halfWin;
|
||||
|
||||
// extract the patch from the first image, compute covariation matrix of derivatives
|
||||
|
||||
float A11 = 0;
|
||||
float A12 = 0;
|
||||
float A22 = 0;
|
||||
|
||||
float I_patch[GRIDSIZE][GRIDSIZE];
|
||||
float dIdx_patch[GRIDSIZE][GRIDSIZE];
|
||||
float dIdy_patch[GRIDSIZE][GRIDSIZE];
|
||||
|
||||
// local memory to read image with border to calc sobels
|
||||
local float IPatchLocal[LM_W*LM_H];
|
||||
ReadPatchIToLocalMem(I,prevPt,IPatchLocal);
|
||||
|
||||
{
|
||||
SetPatch(IPatchLocal, 0, 0,
|
||||
&I_patch[0][0], &dIdx_patch[0][0], &dIdy_patch[0][0],
|
||||
&A11, &A12, &A22,1);
|
||||
|
||||
|
||||
SetPatch(IPatchLocal, 0, 1,
|
||||
&I_patch[0][1], &dIdx_patch[0][1], &dIdy_patch[0][1],
|
||||
&A11, &A12, &A22,wx0);
|
||||
|
||||
SetPatch(IPatchLocal, 0, 2,
|
||||
&I_patch[0][2], &dIdx_patch[0][2], &dIdy_patch[0][2],
|
||||
&A11, &A12, &A22,wx1);
|
||||
}
|
||||
{
|
||||
SetPatch(IPatchLocal, 1, 0,
|
||||
&I_patch[1][0], &dIdx_patch[1][0], &dIdy_patch[1][0],
|
||||
&A11, &A12, &A22,wy0);
|
||||
|
||||
|
||||
SetPatch(IPatchLocal, 1,1,
|
||||
&I_patch[1][1], &dIdx_patch[1][1], &dIdy_patch[1][1],
|
||||
&A11, &A12, &A22,wx0*wy0);
|
||||
|
||||
SetPatch(IPatchLocal, 1,2,
|
||||
&I_patch[1][2], &dIdx_patch[1][2], &dIdy_patch[1][2],
|
||||
&A11, &A12, &A22,wx1*wy0);
|
||||
}
|
||||
{
|
||||
SetPatch(IPatchLocal, 2,0,
|
||||
&I_patch[2][0], &dIdx_patch[2][0], &dIdy_patch[2][0],
|
||||
&A11, &A12, &A22,wy1);
|
||||
|
||||
|
||||
SetPatch(IPatchLocal, 2,1,
|
||||
&I_patch[2][1], &dIdx_patch[2][1], &dIdy_patch[2][1],
|
||||
&A11, &A12, &A22,wx0*wy1);
|
||||
|
||||
SetPatch(IPatchLocal, 2,2,
|
||||
&I_patch[2][2], &dIdx_patch[2][2], &dIdy_patch[2][2],
|
||||
&A11, &A12, &A22,wx1*wy1);
|
||||
}
|
||||
|
||||
|
||||
reduce3(A11, A12, A22, smem1, smem2, smem3, tid);
|
||||
|
||||
A11 = smem1[0];
|
||||
A12 = smem2[0];
|
||||
A22 = smem3[0];
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
float D = mad(A11, A22, - A12 * A12);
|
||||
|
||||
if (D < 1.192092896e-07f)
|
||||
{
|
||||
if (tid == 0 && level == 0)
|
||||
status[gid] = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
A11 /= D;
|
||||
A12 /= D;
|
||||
A22 /= D;
|
||||
|
||||
prevPt = mad(nextPts[gid], 2.0f, - c_halfWin);
|
||||
|
||||
float2 offset0 = (float2)(xid + 0.5f, yid + 0.5f);
|
||||
float2 offset1 = (float2)(xsize, ysize);
|
||||
float2 loc0 = prevPt + offset0;
|
||||
float2 loc1 = loc0 + offset1;
|
||||
float2 loc2 = loc1 + offset1;
|
||||
|
||||
for (k = 0; k < c_iters; ++k)
|
||||
{
|
||||
if (prevPt.x < -c_halfWin.x || prevPt.x >= cols || prevPt.y < -c_halfWin.y || prevPt.y >= rows)
|
||||
{
|
||||
if (tid == 0 && level == 0)
|
||||
status[gid] = 0;
|
||||
break;
|
||||
}
|
||||
float b1 = 0;
|
||||
float b2 = 0;
|
||||
|
||||
{
|
||||
GetPatch(J, loc0.x, loc0.y,
|
||||
&I_patch[0][0], &dIdx_patch[0][0], &dIdy_patch[0][0],
|
||||
&b1, &b2);
|
||||
|
||||
|
||||
GetPatch(J, loc1.x, loc0.y,
|
||||
&I_patch[0][1], &dIdx_patch[0][1], &dIdy_patch[0][1],
|
||||
&b1, &b2);
|
||||
|
||||
GetPatch(J, loc2.x, loc0.y,
|
||||
&I_patch[0][2], &dIdx_patch[0][2], &dIdy_patch[0][2],
|
||||
&b1, &b2);
|
||||
}
|
||||
{
|
||||
GetPatch(J, loc0.x, loc1.y,
|
||||
&I_patch[1][0], &dIdx_patch[1][0], &dIdy_patch[1][0],
|
||||
&b1, &b2);
|
||||
|
||||
|
||||
GetPatch(J, loc1.x, loc1.y,
|
||||
&I_patch[1][1], &dIdx_patch[1][1], &dIdy_patch[1][1],
|
||||
&b1, &b2);
|
||||
|
||||
GetPatch(J, loc2.x, loc1.y,
|
||||
&I_patch[1][2], &dIdx_patch[1][2], &dIdy_patch[1][2],
|
||||
&b1, &b2);
|
||||
}
|
||||
{
|
||||
GetPatch(J, loc0.x, loc2.y,
|
||||
&I_patch[2][0], &dIdx_patch[2][0], &dIdy_patch[2][0],
|
||||
&b1, &b2);
|
||||
|
||||
|
||||
GetPatch(J, loc1.x, loc2.y,
|
||||
&I_patch[2][1], &dIdx_patch[2][1], &dIdy_patch[2][1],
|
||||
&b1, &b2);
|
||||
|
||||
GetPatch(J, loc2.x, loc2.y,
|
||||
&I_patch[2][2], &dIdx_patch[2][2], &dIdy_patch[2][2],
|
||||
&b1, &b2);
|
||||
}
|
||||
|
||||
reduce2(b1, b2, smem1, smem2, tid);
|
||||
|
||||
b1 = smem1[0];
|
||||
b2 = smem2[0];
|
||||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
float2 delta;
|
||||
delta.x = mad(A12, b2, - A22 * b1) * 32.0f;
|
||||
delta.y = mad(A12, b1, - A11 * b2) * 32.0f;
|
||||
|
||||
prevPt += delta;
|
||||
loc0 += delta;
|
||||
loc1 += delta;
|
||||
loc2 += delta;
|
||||
|
||||
if (fabs(delta.x) < THRESHOLD && fabs(delta.y) < THRESHOLD)
|
||||
break;
|
||||
}
|
||||
|
||||
D = 0.0f;
|
||||
if (calcErr)
|
||||
{
|
||||
{
|
||||
GetError(J, loc0.x, loc0.y, &I_patch[0][0], &D, 1);
|
||||
GetError(J, loc1.x, loc0.y, &I_patch[0][1], &D, wx0);
|
||||
}
|
||||
{
|
||||
GetError(J, loc0.x, loc1.y, &I_patch[1][0], &D, wy0);
|
||||
GetError(J, loc1.x, loc1.y, &I_patch[1][1], &D, wx0*wy0);
|
||||
}
|
||||
if(xBase < c_winSize_x)
|
||||
{
|
||||
GetError(J, loc2.x, loc0.y, &I_patch[0][2], &D, wx1);
|
||||
GetError(J, loc2.x, loc1.y, &I_patch[1][2], &D, wx1*wy0);
|
||||
}
|
||||
if(yBase < c_winSize_y)
|
||||
{
|
||||
GetError(J, loc0.x, loc2.y, &I_patch[2][0], &D, wy1);
|
||||
GetError(J, loc1.x, loc2.y, &I_patch[2][1], &D, wx0*wy1);
|
||||
if(xBase < c_winSize_x)
|
||||
GetError(J, loc2.x, loc2.y, &I_patch[2][2], &D, wx1*wy1);
|
||||
}
|
||||
|
||||
reduce1(D, smem1, tid);
|
||||
}
|
||||
|
||||
if (tid == 0)
|
||||
{
|
||||
prevPt += c_halfWin;
|
||||
|
||||
nextPts[gid] = prevPt;
|
||||
|
||||
if (calcErr)
|
||||
err[gid] = smem1[0] / (float)(32 * c_winSize_x * c_winSize_y);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,129 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
#include "precomp.hpp"
|
||||
#include<iostream>
|
||||
#include<fstream>
|
||||
|
||||
namespace cv {
|
||||
|
||||
const float FLOW_TAG_FLOAT = 202021.25f;
|
||||
const char *FLOW_TAG_STRING = "PIEH";
|
||||
CV_EXPORTS_W Mat readOpticalFlow( const String& path )
|
||||
{
|
||||
Mat_<Point2f> flow;
|
||||
std::ifstream file(path.c_str(), std::ios_base::binary);
|
||||
if ( !file.good() )
|
||||
return Mat(); // no file - return empty matrix
|
||||
|
||||
float tag;
|
||||
file.read((char*) &tag, sizeof(float));
|
||||
if ( tag != FLOW_TAG_FLOAT )
|
||||
return Mat();
|
||||
|
||||
int width, height;
|
||||
|
||||
file.read((char*) &width, 4);
|
||||
file.read((char*) &height, 4);
|
||||
|
||||
flow.create(height, width);
|
||||
|
||||
for ( int i = 0; i < flow.rows; ++i )
|
||||
{
|
||||
for ( int j = 0; j < flow.cols; ++j )
|
||||
{
|
||||
Point2f u;
|
||||
file.read((char*) &u.x, sizeof(float));
|
||||
file.read((char*) &u.y, sizeof(float));
|
||||
if ( !file.good() )
|
||||
{
|
||||
flow.release();
|
||||
return Mat();
|
||||
}
|
||||
|
||||
flow(i, j) = u;
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
return Mat(flow);
|
||||
}
|
||||
|
||||
CV_EXPORTS_W bool writeOpticalFlow( const String& path, InputArray flow )
|
||||
{
|
||||
const int nChannels = 2;
|
||||
|
||||
Mat input = flow.getMat();
|
||||
if ( input.channels() != nChannels || input.depth() != CV_32F || path.length() == 0 )
|
||||
return false;
|
||||
|
||||
std::ofstream file(path.c_str(), std::ofstream::binary);
|
||||
if ( !file.good() )
|
||||
return false;
|
||||
|
||||
int nRows, nCols;
|
||||
|
||||
nRows = (int) input.size().height;
|
||||
nCols = (int) input.size().width;
|
||||
|
||||
const int headerSize = 12;
|
||||
char header[headerSize];
|
||||
memcpy(header, FLOW_TAG_STRING, 4);
|
||||
// size of ints is known - has been asserted in the current function
|
||||
memcpy(header + 4, reinterpret_cast<const char*>(&nCols), sizeof(nCols));
|
||||
memcpy(header + 8, reinterpret_cast<const char*>(&nRows), sizeof(nRows));
|
||||
file.write(header, headerSize);
|
||||
if ( !file.good() )
|
||||
return false;
|
||||
|
||||
int row;
|
||||
char* p;
|
||||
for ( row = 0; row < nRows; row++ )
|
||||
{
|
||||
p = input.ptr<char>(row);
|
||||
file.write(p, nCols * nChannels * sizeof(float));
|
||||
if ( !file.good() )
|
||||
return false;
|
||||
}
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef __OPENCV_PRECOMP_H__
|
||||
#define __OPENCV_PRECOMP_H__
|
||||
|
||||
#include "opencv2/video.hpp"
|
||||
#include "opencv2/core/utility.hpp"
|
||||
#include "opencv2/core/private.hpp"
|
||||
#include "opencv2/core/ocl.hpp"
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/geometry.hpp"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,25 @@
|
||||
// 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/video/detail/tracking.detail.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
TrackerFeature::~TrackerFeature()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
void TrackerFeature::compute(const std::vector<Mat>& images, Mat& response)
|
||||
{
|
||||
if (images.empty())
|
||||
return;
|
||||
|
||||
computeImpl(images, response);
|
||||
}
|
||||
|
||||
}}} // namespace cv::detail::tracking
|
||||
@@ -0,0 +1,121 @@
|
||||
// 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/video/detail/tracking.detail.hpp"
|
||||
#include "tracking_feature.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
inline namespace internal {
|
||||
|
||||
class TrackerFeatureHAAR : public TrackerFeature
|
||||
{
|
||||
public:
|
||||
struct Params
|
||||
{
|
||||
Params();
|
||||
int numFeatures; //!< # of rects
|
||||
Size rectSize; //!< rect size
|
||||
bool isIntegral; //!< true if input images are integral, false otherwise
|
||||
};
|
||||
|
||||
TrackerFeatureHAAR(const TrackerFeatureHAAR::Params& parameters = TrackerFeatureHAAR::Params());
|
||||
|
||||
virtual ~TrackerFeatureHAAR() CV_OVERRIDE {}
|
||||
|
||||
protected:
|
||||
bool computeImpl(const std::vector<Mat>& images, Mat& response) CV_OVERRIDE;
|
||||
|
||||
private:
|
||||
Params params;
|
||||
Ptr<CvHaarEvaluator> featureEvaluator;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parameters
|
||||
*/
|
||||
|
||||
TrackerFeatureHAAR::Params::Params()
|
||||
{
|
||||
numFeatures = 250;
|
||||
rectSize = Size(100, 100);
|
||||
isIntegral = false;
|
||||
}
|
||||
|
||||
TrackerFeatureHAAR::TrackerFeatureHAAR(const TrackerFeatureHAAR::Params& parameters)
|
||||
: params(parameters)
|
||||
{
|
||||
CvHaarFeatureParams haarParams;
|
||||
haarParams.numFeatures = params.numFeatures;
|
||||
haarParams.isIntegral = params.isIntegral;
|
||||
featureEvaluator = makePtr<CvHaarEvaluator>();
|
||||
featureEvaluator->init(&haarParams, 1, params.rectSize);
|
||||
}
|
||||
|
||||
class Parallel_compute : public cv::ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
Ptr<CvHaarEvaluator> featureEvaluator;
|
||||
std::vector<Mat> images;
|
||||
Mat response;
|
||||
//std::vector<CvHaarEvaluator::FeatureHaar> features;
|
||||
public:
|
||||
Parallel_compute(Ptr<CvHaarEvaluator>& fe, const std::vector<Mat>& img, Mat& resp)
|
||||
: featureEvaluator(fe)
|
||||
, images(img)
|
||||
, response(resp)
|
||||
{
|
||||
|
||||
//features = featureEvaluator->getFeatures();
|
||||
}
|
||||
|
||||
virtual void operator()(const cv::Range& r) const CV_OVERRIDE
|
||||
{
|
||||
for (int jf = r.start; jf != r.end; ++jf)
|
||||
{
|
||||
int cols = images[jf].cols;
|
||||
int rows = images[jf].rows;
|
||||
for (int j = 0; j < featureEvaluator->getNumFeatures(); j++)
|
||||
{
|
||||
float res = 0;
|
||||
featureEvaluator->getFeatures()[j].eval(images[jf], Rect(0, 0, cols, rows), &res);
|
||||
(Mat_<float>(response))(j, jf) = res;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool TrackerFeatureHAAR::computeImpl(const std::vector<Mat>& images, Mat& response)
|
||||
{
|
||||
if (images.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int numFeatures = featureEvaluator->getNumFeatures();
|
||||
|
||||
response = Mat_<float>(Size((int)images.size(), numFeatures));
|
||||
|
||||
std::vector<CvHaarEvaluator::FeatureHaar> f = featureEvaluator->getFeatures();
|
||||
//for each sample compute #n_feature -> put each feature (n Rect) in response
|
||||
parallel_for_(Range(0, (int)images.size()), Parallel_compute(featureEvaluator, images, response));
|
||||
|
||||
/*for ( size_t i = 0; i < images.size(); i++ )
|
||||
{
|
||||
int c = images[i].cols;
|
||||
int r = images[i].rows;
|
||||
for ( int j = 0; j < numFeatures; j++ )
|
||||
{
|
||||
float res = 0;
|
||||
featureEvaluator->getFeatures( j ).eval( images[i], Rect( 0, 0, c, r ), &res );
|
||||
( Mat_<float>( response ) )( j, i ) = res;
|
||||
}
|
||||
}*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}}}} // namespace cv::detail::tracking::internal
|
||||
@@ -0,0 +1,60 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "../../precomp.hpp"
|
||||
#include "opencv2/video/detail/tracking.detail.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
TrackerFeatureSet::TrackerFeatureSet()
|
||||
{
|
||||
blockAddTrackerFeature = false;
|
||||
}
|
||||
|
||||
TrackerFeatureSet::~TrackerFeatureSet()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
void TrackerFeatureSet::extraction(const std::vector<Mat>& images)
|
||||
{
|
||||
blockAddTrackerFeature = true;
|
||||
|
||||
clearResponses();
|
||||
responses.resize(features.size());
|
||||
|
||||
for (size_t i = 0; i < features.size(); i++)
|
||||
{
|
||||
CV_DbgAssert(features[i]);
|
||||
features[i]->compute(images, responses[i]);
|
||||
}
|
||||
}
|
||||
|
||||
bool TrackerFeatureSet::addTrackerFeature(const Ptr<TrackerFeature>& feature)
|
||||
{
|
||||
CV_Assert(!blockAddTrackerFeature);
|
||||
CV_Assert(feature);
|
||||
|
||||
features.push_back(feature);
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<Ptr<TrackerFeature>>& TrackerFeatureSet::getTrackerFeatures() const
|
||||
{
|
||||
return features;
|
||||
}
|
||||
|
||||
const std::vector<Mat>& TrackerFeatureSet::getResponses() const
|
||||
{
|
||||
return responses;
|
||||
}
|
||||
|
||||
void TrackerFeatureSet::clearResponses()
|
||||
{
|
||||
responses.clear();
|
||||
}
|
||||
|
||||
}}} // namespace cv::detail::tracking
|
||||
@@ -0,0 +1,85 @@
|
||||
// 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 "tracker_mil_model.hpp"
|
||||
|
||||
/**
|
||||
* TrackerMILModel
|
||||
*/
|
||||
|
||||
namespace cv {
|
||||
inline namespace tracking {
|
||||
namespace impl {
|
||||
|
||||
TrackerMILModel::TrackerMILModel(const Rect& boundingBox)
|
||||
{
|
||||
currentSample.clear();
|
||||
mode = MODE_POSITIVE;
|
||||
width = boundingBox.width;
|
||||
height = boundingBox.height;
|
||||
|
||||
Ptr<TrackerStateEstimatorMILBoosting::TrackerMILTargetState> initState = Ptr<TrackerStateEstimatorMILBoosting::TrackerMILTargetState>(
|
||||
new TrackerStateEstimatorMILBoosting::TrackerMILTargetState(Point2f((float)boundingBox.x, (float)boundingBox.y), boundingBox.width, boundingBox.height,
|
||||
true, Mat()));
|
||||
trajectory.push_back(initState);
|
||||
}
|
||||
|
||||
void TrackerMILModel::responseToConfidenceMap(const std::vector<Mat>& responses, ConfidenceMap& confidenceMap)
|
||||
{
|
||||
if (currentSample.empty())
|
||||
{
|
||||
CV_Error(cv::Error::StsError, "The samples in Model estimation are empty");
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < responses.size(); i++)
|
||||
{
|
||||
//for each column (one sample) there are #num_feature
|
||||
//get informations from currentSample
|
||||
for (int j = 0; j < responses.at(i).cols; j++)
|
||||
{
|
||||
|
||||
Size currentSize;
|
||||
Point currentOfs;
|
||||
currentSample.at(j).locateROI(currentSize, currentOfs);
|
||||
bool foreground = false;
|
||||
if (mode == MODE_POSITIVE || mode == MODE_ESTIMATON)
|
||||
{
|
||||
foreground = true;
|
||||
}
|
||||
else if (mode == MODE_NEGATIVE)
|
||||
{
|
||||
foreground = false;
|
||||
}
|
||||
|
||||
//get the column of the HAAR responses
|
||||
Mat singleResponse = responses.at(i).col(j);
|
||||
|
||||
//create the state
|
||||
Ptr<TrackerStateEstimatorMILBoosting::TrackerMILTargetState> currentState = Ptr<TrackerStateEstimatorMILBoosting::TrackerMILTargetState>(
|
||||
new TrackerStateEstimatorMILBoosting::TrackerMILTargetState(currentOfs, width, height, foreground, singleResponse));
|
||||
|
||||
confidenceMap.push_back(std::make_pair(currentState, 0.0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TrackerMILModel::modelEstimationImpl(const std::vector<Mat>& responses)
|
||||
{
|
||||
responseToConfidenceMap(responses, currentConfidenceMap);
|
||||
}
|
||||
|
||||
void TrackerMILModel::modelUpdateImpl()
|
||||
{
|
||||
}
|
||||
|
||||
void TrackerMILModel::setMode(int trainingMode, const std::vector<Mat>& samples)
|
||||
{
|
||||
currentSample.clear();
|
||||
currentSample = samples;
|
||||
|
||||
mode = trainingMode;
|
||||
}
|
||||
|
||||
}}} // namespace cv::tracking::impl
|
||||
@@ -0,0 +1,67 @@
|
||||
// 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_TRACKER_MIL_MODEL_HPP__
|
||||
#define __OPENCV_TRACKER_MIL_MODEL_HPP__
|
||||
|
||||
#include "opencv2/video/detail/tracking.detail.hpp"
|
||||
#include "tracker_mil_state.hpp"
|
||||
|
||||
namespace cv {
|
||||
inline namespace tracking {
|
||||
namespace impl {
|
||||
|
||||
using namespace cv::detail::tracking;
|
||||
|
||||
/**
|
||||
* \brief Implementation of TrackerModel for MIL algorithm
|
||||
*/
|
||||
class TrackerMILModel : public detail::TrackerModel
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
MODE_POSITIVE = 1, // mode for positive features
|
||||
MODE_NEGATIVE = 2, // mode for negative features
|
||||
MODE_ESTIMATON = 3 // mode for estimation step
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Constructor
|
||||
* \param boundingBox The first boundingBox
|
||||
*/
|
||||
TrackerMILModel(const Rect& boundingBox);
|
||||
|
||||
/**
|
||||
* \brief Destructor
|
||||
*/
|
||||
~TrackerMILModel() {}
|
||||
|
||||
/**
|
||||
* \brief Set the mode
|
||||
*/
|
||||
void setMode(int trainingMode, const std::vector<Mat>& samples);
|
||||
|
||||
/**
|
||||
* \brief Create the ConfidenceMap from a list of responses
|
||||
* \param responses The list of the responses
|
||||
* \param confidenceMap The output
|
||||
*/
|
||||
void responseToConfidenceMap(const std::vector<Mat>& responses, ConfidenceMap& confidenceMap);
|
||||
|
||||
protected:
|
||||
void modelEstimationImpl(const std::vector<Mat>& responses) CV_OVERRIDE;
|
||||
void modelUpdateImpl() CV_OVERRIDE;
|
||||
|
||||
private:
|
||||
int mode;
|
||||
std::vector<Mat> currentSample;
|
||||
|
||||
int width; //initial width of the boundingBox
|
||||
int height; //initial height of the boundingBox
|
||||
};
|
||||
|
||||
}}} // namespace cv::tracking::impl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,159 @@
|
||||
// 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/video/detail/tracking.detail.hpp"
|
||||
#include "tracker_mil_state.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
/**
|
||||
* TrackerStateEstimatorMILBoosting::TrackerMILTargetState
|
||||
*/
|
||||
TrackerStateEstimatorMILBoosting::TrackerMILTargetState::TrackerMILTargetState(const Point2f& position, int width, int height, bool foreground,
|
||||
const Mat& features)
|
||||
{
|
||||
setTargetPosition(position);
|
||||
setTargetWidth(width);
|
||||
setTargetHeight(height);
|
||||
setTargetFg(foreground);
|
||||
setFeatures(features);
|
||||
}
|
||||
|
||||
void TrackerStateEstimatorMILBoosting::TrackerMILTargetState::setTargetFg(bool foreground)
|
||||
{
|
||||
isTarget = foreground;
|
||||
}
|
||||
|
||||
void TrackerStateEstimatorMILBoosting::TrackerMILTargetState::setFeatures(const Mat& features)
|
||||
{
|
||||
targetFeatures = features;
|
||||
}
|
||||
|
||||
bool TrackerStateEstimatorMILBoosting::TrackerMILTargetState::isTargetFg() const
|
||||
{
|
||||
return isTarget;
|
||||
}
|
||||
|
||||
Mat TrackerStateEstimatorMILBoosting::TrackerMILTargetState::getFeatures() const
|
||||
{
|
||||
return targetFeatures;
|
||||
}
|
||||
|
||||
TrackerStateEstimatorMILBoosting::TrackerStateEstimatorMILBoosting(int nFeatures)
|
||||
{
|
||||
className = "BOOSTING";
|
||||
trained = false;
|
||||
numFeatures = nFeatures;
|
||||
}
|
||||
|
||||
TrackerStateEstimatorMILBoosting::~TrackerStateEstimatorMILBoosting()
|
||||
{
|
||||
}
|
||||
|
||||
void TrackerStateEstimatorMILBoosting::setCurrentConfidenceMap(ConfidenceMap& confidenceMap)
|
||||
{
|
||||
currentConfidenceMap.clear();
|
||||
currentConfidenceMap = confidenceMap;
|
||||
}
|
||||
|
||||
uint TrackerStateEstimatorMILBoosting::max_idx(const std::vector<float>& v)
|
||||
{
|
||||
const float* findPtr = &(*std::max_element(v.begin(), v.end()));
|
||||
const float* beginPtr = &(*v.begin());
|
||||
return (uint)(findPtr - beginPtr);
|
||||
}
|
||||
|
||||
Ptr<TrackerTargetState> TrackerStateEstimatorMILBoosting::estimateImpl(const std::vector<ConfidenceMap>& /*confidenceMaps*/)
|
||||
{
|
||||
//run ClfMilBoost classify in order to compute next location
|
||||
if (currentConfidenceMap.empty())
|
||||
return Ptr<TrackerTargetState>();
|
||||
|
||||
Mat positiveStates;
|
||||
Mat negativeStates;
|
||||
|
||||
prepareData(currentConfidenceMap, positiveStates, negativeStates);
|
||||
|
||||
std::vector<float> prob = boostMILModel.classify(positiveStates);
|
||||
|
||||
int bestind = max_idx(prob);
|
||||
//float resp = prob[bestind];
|
||||
|
||||
return currentConfidenceMap.at(bestind).first;
|
||||
}
|
||||
|
||||
void TrackerStateEstimatorMILBoosting::prepareData(const ConfidenceMap& confidenceMap, Mat& positive, Mat& negative)
|
||||
{
|
||||
|
||||
int posCounter = 0;
|
||||
int negCounter = 0;
|
||||
|
||||
for (size_t i = 0; i < confidenceMap.size(); i++)
|
||||
{
|
||||
Ptr<TrackerMILTargetState> currentTargetState = confidenceMap.at(i).first.staticCast<TrackerMILTargetState>();
|
||||
CV_DbgAssert(currentTargetState);
|
||||
if (currentTargetState->isTargetFg())
|
||||
posCounter++;
|
||||
else
|
||||
negCounter++;
|
||||
}
|
||||
|
||||
positive.create(posCounter, numFeatures, CV_32FC1);
|
||||
negative.create(negCounter, numFeatures, CV_32FC1);
|
||||
|
||||
//TODO change with mat fast access
|
||||
//initialize trainData (positive and negative)
|
||||
|
||||
int pc = 0;
|
||||
int nc = 0;
|
||||
for (size_t i = 0; i < confidenceMap.size(); i++)
|
||||
{
|
||||
Ptr<TrackerMILTargetState> currentTargetState = confidenceMap.at(i).first.staticCast<TrackerMILTargetState>();
|
||||
Mat stateFeatures = currentTargetState->getFeatures();
|
||||
|
||||
if (currentTargetState->isTargetFg())
|
||||
{
|
||||
for (int j = 0; j < stateFeatures.rows; j++)
|
||||
{
|
||||
//fill the positive trainData with the value of the feature j for sample i
|
||||
positive.at<float>(pc, j) = stateFeatures.at<float>(j, 0);
|
||||
}
|
||||
pc++;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int j = 0; j < stateFeatures.rows; j++)
|
||||
{
|
||||
//fill the negative trainData with the value of the feature j for sample i
|
||||
negative.at<float>(nc, j) = stateFeatures.at<float>(j, 0);
|
||||
}
|
||||
nc++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TrackerStateEstimatorMILBoosting::updateImpl(std::vector<ConfidenceMap>& confidenceMaps)
|
||||
{
|
||||
|
||||
if (!trained)
|
||||
{
|
||||
//this is the first time that the classifier is built
|
||||
//init MIL
|
||||
boostMILModel.init();
|
||||
trained = true;
|
||||
}
|
||||
|
||||
ConfidenceMap lastConfidenceMap = confidenceMaps.back();
|
||||
Mat positiveStates;
|
||||
Mat negativeStates;
|
||||
|
||||
prepareData(lastConfidenceMap, positiveStates, negativeStates);
|
||||
//update MIL
|
||||
boostMILModel.update(positiveStates, negativeStates);
|
||||
}
|
||||
|
||||
}}} // namespace cv::detail::tracking
|
||||
@@ -0,0 +1,87 @@
|
||||
// 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_VIDEO_DETAIL_TRACKING_MIL_STATE_HPP
|
||||
#define OPENCV_VIDEO_DETAIL_TRACKING_MIL_STATE_HPP
|
||||
|
||||
#include "opencv2/video/detail/tracking.detail.hpp"
|
||||
#include "tracking_online_mil.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
/** @brief TrackerStateEstimator based on Boosting
|
||||
*/
|
||||
class CV_EXPORTS TrackerStateEstimatorMILBoosting : public TrackerStateEstimator
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Implementation of the target state for TrackerStateEstimatorMILBoosting
|
||||
*/
|
||||
class TrackerMILTargetState : public TrackerTargetState
|
||||
{
|
||||
|
||||
public:
|
||||
/**
|
||||
* \brief Constructor
|
||||
* \param position Top left corner of the bounding box
|
||||
* \param width Width of the bounding box
|
||||
* \param height Height of the bounding box
|
||||
* \param foreground label for target or background
|
||||
* \param features features extracted
|
||||
*/
|
||||
TrackerMILTargetState(const Point2f& position, int width, int height, bool foreground, const Mat& features);
|
||||
|
||||
~TrackerMILTargetState() {}
|
||||
|
||||
/** @brief Set label: true for target foreground, false for background
|
||||
@param foreground Label for background/foreground
|
||||
*/
|
||||
void setTargetFg(bool foreground);
|
||||
/** @brief Set the features extracted from TrackerFeatureSet
|
||||
@param features The features extracted
|
||||
*/
|
||||
void setFeatures(const Mat& features);
|
||||
/** @brief Get the label. Return true for target foreground, false for background
|
||||
*/
|
||||
bool isTargetFg() const;
|
||||
/** @brief Get the features extracted
|
||||
*/
|
||||
Mat getFeatures() const;
|
||||
|
||||
private:
|
||||
bool isTarget;
|
||||
Mat targetFeatures;
|
||||
};
|
||||
|
||||
/** @brief Constructor
|
||||
@param nFeatures Number of features for each sample
|
||||
*/
|
||||
TrackerStateEstimatorMILBoosting(int nFeatures = 250);
|
||||
~TrackerStateEstimatorMILBoosting();
|
||||
|
||||
/** @brief Set the current confidenceMap
|
||||
@param confidenceMap The current :cConfidenceMap
|
||||
*/
|
||||
void setCurrentConfidenceMap(ConfidenceMap& confidenceMap);
|
||||
|
||||
protected:
|
||||
Ptr<TrackerTargetState> estimateImpl(const std::vector<ConfidenceMap>& confidenceMaps) CV_OVERRIDE;
|
||||
void updateImpl(std::vector<ConfidenceMap>& confidenceMaps) CV_OVERRIDE;
|
||||
|
||||
private:
|
||||
uint max_idx(const std::vector<float>& v);
|
||||
void prepareData(const ConfidenceMap& confidenceMap, Mat& positive, Mat& negative);
|
||||
|
||||
ClfMilBoost boostMILModel;
|
||||
bool trained;
|
||||
int numFeatures;
|
||||
|
||||
ConfidenceMap currentConfidenceMap;
|
||||
};
|
||||
|
||||
}}} // namespace cv::detail::tracking
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,132 @@
|
||||
// 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/video/detail/tracking.detail.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
TrackerModel::TrackerModel()
|
||||
{
|
||||
stateEstimator = Ptr<TrackerStateEstimator>();
|
||||
maxCMLength = 10;
|
||||
}
|
||||
|
||||
TrackerModel::~TrackerModel()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
bool TrackerModel::setTrackerStateEstimator(Ptr<TrackerStateEstimator> trackerStateEstimator)
|
||||
{
|
||||
if (stateEstimator.get())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
stateEstimator = trackerStateEstimator;
|
||||
return true;
|
||||
}
|
||||
|
||||
Ptr<TrackerStateEstimator> TrackerModel::getTrackerStateEstimator() const
|
||||
{
|
||||
return stateEstimator;
|
||||
}
|
||||
|
||||
void TrackerModel::modelEstimation(const std::vector<Mat>& responses)
|
||||
{
|
||||
modelEstimationImpl(responses);
|
||||
}
|
||||
|
||||
void TrackerModel::clearCurrentConfidenceMap()
|
||||
{
|
||||
currentConfidenceMap.clear();
|
||||
}
|
||||
|
||||
void TrackerModel::modelUpdate()
|
||||
{
|
||||
modelUpdateImpl();
|
||||
|
||||
if (maxCMLength != -1 && (int)confidenceMaps.size() >= maxCMLength - 1)
|
||||
{
|
||||
int l = maxCMLength / 2;
|
||||
confidenceMaps.erase(confidenceMaps.begin(), confidenceMaps.begin() + l);
|
||||
}
|
||||
if (maxCMLength != -1 && (int)trajectory.size() >= maxCMLength - 1)
|
||||
{
|
||||
int l = maxCMLength / 2;
|
||||
trajectory.erase(trajectory.begin(), trajectory.begin() + l);
|
||||
}
|
||||
confidenceMaps.push_back(currentConfidenceMap);
|
||||
stateEstimator->update(confidenceMaps);
|
||||
|
||||
clearCurrentConfidenceMap();
|
||||
}
|
||||
|
||||
bool TrackerModel::runStateEstimator()
|
||||
{
|
||||
if (!stateEstimator)
|
||||
{
|
||||
CV_Error(cv::Error::StsError, "Tracker state estimator is not setted");
|
||||
}
|
||||
Ptr<TrackerTargetState> targetState = stateEstimator->estimate(confidenceMaps);
|
||||
if (!targetState)
|
||||
return false;
|
||||
|
||||
setLastTargetState(targetState);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TrackerModel::setLastTargetState(const Ptr<TrackerTargetState>& lastTargetState)
|
||||
{
|
||||
trajectory.push_back(lastTargetState);
|
||||
}
|
||||
|
||||
Ptr<TrackerTargetState> TrackerModel::getLastTargetState() const
|
||||
{
|
||||
return trajectory.back();
|
||||
}
|
||||
|
||||
const std::vector<ConfidenceMap>& TrackerModel::getConfidenceMaps() const
|
||||
{
|
||||
return confidenceMaps;
|
||||
}
|
||||
|
||||
const ConfidenceMap& TrackerModel::getLastConfidenceMap() const
|
||||
{
|
||||
return confidenceMaps.back();
|
||||
}
|
||||
|
||||
Point2f TrackerTargetState::getTargetPosition() const
|
||||
{
|
||||
return targetPosition;
|
||||
}
|
||||
|
||||
void TrackerTargetState::setTargetPosition(const Point2f& position)
|
||||
{
|
||||
targetPosition = position;
|
||||
}
|
||||
|
||||
int TrackerTargetState::getTargetWidth() const
|
||||
{
|
||||
return targetWidth;
|
||||
}
|
||||
|
||||
void TrackerTargetState::setTargetWidth(int width)
|
||||
{
|
||||
targetWidth = width;
|
||||
}
|
||||
int TrackerTargetState::getTargetHeight() const
|
||||
{
|
||||
return targetHeight;
|
||||
}
|
||||
|
||||
void TrackerTargetState::setTargetHeight(int height)
|
||||
{
|
||||
targetHeight = height;
|
||||
}
|
||||
|
||||
}}} // namespace cv::detail::tracking
|
||||
@@ -0,0 +1,68 @@
|
||||
// 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/video/detail/tracking.detail.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
TrackerSampler::TrackerSampler()
|
||||
{
|
||||
blockAddTrackerSampler = false;
|
||||
}
|
||||
|
||||
TrackerSampler::~TrackerSampler()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
void TrackerSampler::sampling(const Mat& image, Rect boundingBox)
|
||||
{
|
||||
clearSamples();
|
||||
|
||||
for (size_t i = 0; i < samplers.size(); i++)
|
||||
{
|
||||
CV_DbgAssert(samplers[i]);
|
||||
std::vector<Mat> current_samples;
|
||||
samplers[i]->sampling(image, boundingBox, current_samples);
|
||||
|
||||
//push in samples all current_samples
|
||||
for (size_t j = 0; j < current_samples.size(); j++)
|
||||
{
|
||||
std::vector<Mat>::iterator it = samples.end();
|
||||
samples.insert(it, current_samples.at(j));
|
||||
}
|
||||
}
|
||||
|
||||
blockAddTrackerSampler = true;
|
||||
}
|
||||
|
||||
bool TrackerSampler::addTrackerSamplerAlgorithm(const Ptr<TrackerSamplerAlgorithm>& sampler)
|
||||
{
|
||||
CV_Assert(!blockAddTrackerSampler);
|
||||
CV_Assert(sampler);
|
||||
|
||||
samplers.push_back(sampler);
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<Ptr<TrackerSamplerAlgorithm>>& TrackerSampler::getSamplers() const
|
||||
{
|
||||
return samplers;
|
||||
}
|
||||
|
||||
const std::vector<Mat>& TrackerSampler::getSamples() const
|
||||
{
|
||||
return samples;
|
||||
}
|
||||
|
||||
void TrackerSampler::clearSamples()
|
||||
{
|
||||
samples.clear();
|
||||
}
|
||||
|
||||
}}} // namespace cv::detail::tracking
|
||||
@@ -0,0 +1,124 @@
|
||||
// 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/video/detail/tracking.detail.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
TrackerSamplerAlgorithm::~TrackerSamplerAlgorithm()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
TrackerSamplerCSC::Params::Params()
|
||||
{
|
||||
initInRad = 3;
|
||||
initMaxNegNum = 65;
|
||||
searchWinSize = 25;
|
||||
trackInPosRad = 4;
|
||||
trackMaxNegNum = 65;
|
||||
trackMaxPosNum = 100000;
|
||||
}
|
||||
|
||||
TrackerSamplerCSC::TrackerSamplerCSC(const TrackerSamplerCSC::Params& parameters)
|
||||
: params(parameters)
|
||||
{
|
||||
mode = MODE_INIT_POS;
|
||||
rng = theRNG();
|
||||
}
|
||||
|
||||
TrackerSamplerCSC::~TrackerSamplerCSC()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
bool TrackerSamplerCSC::sampling(const Mat& image, const Rect& boundingBox, std::vector<Mat>& sample)
|
||||
{
|
||||
CV_Assert(!image.empty());
|
||||
|
||||
float inrad = 0;
|
||||
float outrad = 0;
|
||||
int maxnum = 0;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case MODE_INIT_POS:
|
||||
inrad = params.initInRad;
|
||||
sample = sampleImage(image, boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height, inrad);
|
||||
break;
|
||||
case MODE_INIT_NEG:
|
||||
inrad = 2.0f * params.searchWinSize;
|
||||
outrad = 1.5f * params.initInRad;
|
||||
maxnum = params.initMaxNegNum;
|
||||
sample = sampleImage(image, boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height, inrad, outrad, maxnum);
|
||||
break;
|
||||
case MODE_TRACK_POS:
|
||||
inrad = params.trackInPosRad;
|
||||
outrad = 0;
|
||||
maxnum = params.trackMaxPosNum;
|
||||
sample = sampleImage(image, boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height, inrad, outrad, maxnum);
|
||||
break;
|
||||
case MODE_TRACK_NEG:
|
||||
inrad = 1.5f * params.searchWinSize;
|
||||
outrad = params.trackInPosRad + 5;
|
||||
maxnum = params.trackMaxNegNum;
|
||||
sample = sampleImage(image, boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height, inrad, outrad, maxnum);
|
||||
break;
|
||||
case MODE_DETECT:
|
||||
inrad = params.searchWinSize;
|
||||
sample = sampleImage(image, boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height, inrad);
|
||||
break;
|
||||
default:
|
||||
inrad = params.initInRad;
|
||||
sample = sampleImage(image, boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height, inrad);
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TrackerSamplerCSC::setMode(int samplingMode)
|
||||
{
|
||||
mode = samplingMode;
|
||||
}
|
||||
|
||||
std::vector<Mat> TrackerSamplerCSC::sampleImage(const Mat& img, int x, int y, int w, int h, float inrad, float outrad, int maxnum)
|
||||
{
|
||||
int rowsz = img.rows - h - 1;
|
||||
int colsz = img.cols - w - 1;
|
||||
float inradsq = inrad * inrad;
|
||||
float outradsq = outrad * outrad;
|
||||
int dist;
|
||||
|
||||
uint minrow = max(0, (int)y - (int)inrad);
|
||||
uint maxrow = min((int)rowsz - 1, (int)y + (int)inrad);
|
||||
uint mincol = max(0, (int)x - (int)inrad);
|
||||
uint maxcol = min((int)colsz - 1, (int)x + (int)inrad);
|
||||
|
||||
//fprintf(stderr,"inrad=%f minrow=%d maxrow=%d mincol=%d maxcol=%d\n",inrad,minrow,maxrow,mincol,maxcol);
|
||||
|
||||
std::vector<Mat> samples;
|
||||
samples.resize((maxrow - minrow + 1) * (maxcol - mincol + 1));
|
||||
int i = 0;
|
||||
|
||||
float prob = ((float)(maxnum)) / samples.size();
|
||||
|
||||
for (int r = minrow; r <= int(maxrow); r++)
|
||||
for (int c = mincol; c <= int(maxcol); c++)
|
||||
{
|
||||
dist = (y - r) * (y - r) + (x - c) * (x - c);
|
||||
if (float(rng.uniform(0.f, 1.f)) < prob && dist < inradsq && dist >= outradsq)
|
||||
{
|
||||
samples[i] = img(Rect(c, r, w, h));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
samples.resize(min(i, maxnum));
|
||||
return samples;
|
||||
}
|
||||
|
||||
}}} // namespace cv::detail::tracking
|
||||
@@ -0,0 +1,37 @@
|
||||
// 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/video/detail/tracking.detail.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
TrackerStateEstimator::~TrackerStateEstimator()
|
||||
{
|
||||
}
|
||||
|
||||
Ptr<TrackerTargetState> TrackerStateEstimator::estimate(const std::vector<ConfidenceMap>& confidenceMaps)
|
||||
{
|
||||
if (confidenceMaps.empty())
|
||||
return Ptr<TrackerTargetState>();
|
||||
|
||||
return estimateImpl(confidenceMaps);
|
||||
}
|
||||
|
||||
void TrackerStateEstimator::update(std::vector<ConfidenceMap>& confidenceMaps)
|
||||
{
|
||||
if (confidenceMaps.empty())
|
||||
return;
|
||||
|
||||
return updateImpl(confidenceMaps);
|
||||
}
|
||||
|
||||
String TrackerStateEstimator::getClassName() const
|
||||
{
|
||||
return className;
|
||||
}
|
||||
|
||||
}}} // namespace cv::detail::tracking
|
||||
@@ -0,0 +1,582 @@
|
||||
// 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/video/detail/tracking.detail.hpp"
|
||||
#include "tracking_feature.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
/*
|
||||
* TODO This implementation is based on apps/traincascade/
|
||||
* TODO Changed CvHaarEvaluator based on ADABOOSTING implementation (Grabner et al.)
|
||||
*/
|
||||
|
||||
CvParams::CvParams()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
//---------------------------- FeatureParams --------------------------------------
|
||||
|
||||
CvFeatureParams::CvFeatureParams()
|
||||
: maxCatCount(0)
|
||||
, featSize(1)
|
||||
, numFeatures(1)
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
//------------------------------------- FeatureEvaluator ---------------------------------------
|
||||
|
||||
void CvFeatureEvaluator::init(const CvFeatureParams* _featureParams, int _maxSampleCount, Size _winSize)
|
||||
{
|
||||
CV_Assert(_featureParams);
|
||||
CV_Assert(_maxSampleCount > 0);
|
||||
featureParams = (CvFeatureParams*)_featureParams;
|
||||
winSize = _winSize;
|
||||
numFeatures = _featureParams->numFeatures;
|
||||
cls.create((int)_maxSampleCount, 1, CV_32FC1);
|
||||
generateFeatures();
|
||||
}
|
||||
|
||||
void CvFeatureEvaluator::setImage(const Mat& img, uchar clsLabel, int idx)
|
||||
{
|
||||
winSize.width = img.cols;
|
||||
winSize.height = img.rows;
|
||||
//CV_Assert( img.cols == winSize.width );
|
||||
//CV_Assert( img.rows == winSize.height );
|
||||
CV_Assert(idx < cls.rows);
|
||||
cls.ptr<float>(idx)[0] = clsLabel;
|
||||
}
|
||||
|
||||
CvHaarFeatureParams::CvHaarFeatureParams()
|
||||
{
|
||||
isIntegral = false;
|
||||
}
|
||||
|
||||
//--------------------- HaarFeatureEvaluator ----------------
|
||||
|
||||
void CvHaarEvaluator::init(const CvFeatureParams* _featureParams, int /*_maxSampleCount*/, Size _winSize)
|
||||
{
|
||||
CV_Assert(_featureParams);
|
||||
int cols = (_winSize.width + 1) * (_winSize.height + 1);
|
||||
sum.create((int)1, cols, CV_32SC1);
|
||||
isIntegral = ((CvHaarFeatureParams*)_featureParams)->isIntegral;
|
||||
CvFeatureEvaluator::init(_featureParams, 1, _winSize);
|
||||
}
|
||||
|
||||
void CvHaarEvaluator::setImage(const Mat& img, uchar /*clsLabel*/, int /*idx*/)
|
||||
{
|
||||
CV_DbgAssert(!sum.empty());
|
||||
|
||||
winSize.width = img.cols;
|
||||
winSize.height = img.rows;
|
||||
|
||||
CvFeatureEvaluator::setImage(img, 1, 0);
|
||||
if (!isIntegral)
|
||||
{
|
||||
std::vector<Mat_<float>> ii_imgs;
|
||||
compute_integral(img, ii_imgs);
|
||||
_ii_img = ii_imgs[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
_ii_img = img;
|
||||
}
|
||||
}
|
||||
|
||||
void CvHaarEvaluator::generateFeatures()
|
||||
{
|
||||
generateFeatures(featureParams->numFeatures);
|
||||
}
|
||||
|
||||
void CvHaarEvaluator::generateFeatures(int nFeatures)
|
||||
{
|
||||
for (int i = 0; i < nFeatures; i++)
|
||||
{
|
||||
CvHaarEvaluator::FeatureHaar feature(Size(winSize.width, winSize.height));
|
||||
features.push_back(feature);
|
||||
}
|
||||
}
|
||||
|
||||
#define INITSIGMA(numAreas) (static_cast<float>(sqrt(256.0f * 256.0f / 12.0f * (numAreas))));
|
||||
|
||||
CvHaarEvaluator::FeatureHaar::FeatureHaar(Size patchSize)
|
||||
{
|
||||
try
|
||||
{
|
||||
generateRandomFeature(patchSize);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// FIXIT
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void CvHaarEvaluator::FeatureHaar::generateRandomFeature(Size patchSize)
|
||||
{
|
||||
cv::Point2i position;
|
||||
Size baseDim;
|
||||
Size sizeFactor;
|
||||
int area;
|
||||
|
||||
CV_Assert(!patchSize.empty());
|
||||
|
||||
//Size minSize = Size( 3, 3 );
|
||||
int minArea = 9;
|
||||
|
||||
bool valid = false;
|
||||
while (!valid)
|
||||
{
|
||||
//choose position and scale
|
||||
position.y = rand() % (patchSize.height);
|
||||
position.x = rand() % (patchSize.width);
|
||||
|
||||
baseDim.width = (int)((1 - sqrt(1 - (float)rand() * (float)(1.0 / RAND_MAX))) * patchSize.width);
|
||||
baseDim.height = (int)((1 - sqrt(1 - (float)rand() * (float)(1.0 / RAND_MAX))) * patchSize.height);
|
||||
|
||||
//select types
|
||||
//float probType[11] = {0.0909f, 0.0909f, 0.0909f, 0.0909f, 0.0909f, 0.0909f, 0.0909f, 0.0909f, 0.0909f, 0.0909f, 0.0950f};
|
||||
float probType[11] = { 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
float prob = (float)rand() * (float)(1.0 / RAND_MAX);
|
||||
|
||||
if (prob < probType[0])
|
||||
{
|
||||
//check if feature is valid
|
||||
sizeFactor.height = 2;
|
||||
sizeFactor.width = 1;
|
||||
if (position.y + baseDim.height * sizeFactor.height >= patchSize.height || position.x + baseDim.width * sizeFactor.width >= patchSize.width)
|
||||
continue;
|
||||
area = baseDim.height * sizeFactor.height * baseDim.width * sizeFactor.width;
|
||||
if (area < minArea)
|
||||
continue;
|
||||
|
||||
m_type = 1;
|
||||
m_numAreas = 2;
|
||||
m_weights.resize(m_numAreas);
|
||||
m_weights[0] = 1;
|
||||
m_weights[1] = -1;
|
||||
m_areas.resize(m_numAreas);
|
||||
m_areas[0].x = position.x;
|
||||
m_areas[0].y = position.y;
|
||||
m_areas[0].height = baseDim.height;
|
||||
m_areas[0].width = baseDim.width;
|
||||
m_areas[1].x = position.x;
|
||||
m_areas[1].y = position.y + baseDim.height;
|
||||
m_areas[1].height = baseDim.height;
|
||||
m_areas[1].width = baseDim.width;
|
||||
m_initMean = 0;
|
||||
m_initSigma = INITSIGMA(m_numAreas);
|
||||
|
||||
valid = true;
|
||||
}
|
||||
else if (prob < probType[0] + probType[1])
|
||||
{
|
||||
//check if feature is valid
|
||||
sizeFactor.height = 1;
|
||||
sizeFactor.width = 2;
|
||||
if (position.y + baseDim.height * sizeFactor.height >= patchSize.height || position.x + baseDim.width * sizeFactor.width >= patchSize.width)
|
||||
continue;
|
||||
area = baseDim.height * sizeFactor.height * baseDim.width * sizeFactor.width;
|
||||
if (area < minArea)
|
||||
continue;
|
||||
|
||||
m_type = 2;
|
||||
m_numAreas = 2;
|
||||
m_weights.resize(m_numAreas);
|
||||
m_weights[0] = 1;
|
||||
m_weights[1] = -1;
|
||||
m_areas.resize(m_numAreas);
|
||||
m_areas[0].x = position.x;
|
||||
m_areas[0].y = position.y;
|
||||
m_areas[0].height = baseDim.height;
|
||||
m_areas[0].width = baseDim.width;
|
||||
m_areas[1].x = position.x + baseDim.width;
|
||||
m_areas[1].y = position.y;
|
||||
m_areas[1].height = baseDim.height;
|
||||
m_areas[1].width = baseDim.width;
|
||||
m_initMean = 0;
|
||||
m_initSigma = INITSIGMA(m_numAreas);
|
||||
valid = true;
|
||||
}
|
||||
else if (prob < probType[0] + probType[1] + probType[2])
|
||||
{
|
||||
//check if feature is valid
|
||||
sizeFactor.height = 4;
|
||||
sizeFactor.width = 1;
|
||||
if (position.y + baseDim.height * sizeFactor.height >= patchSize.height || position.x + baseDim.width * sizeFactor.width >= patchSize.width)
|
||||
continue;
|
||||
area = baseDim.height * sizeFactor.height * baseDim.width * sizeFactor.width;
|
||||
if (area < minArea)
|
||||
continue;
|
||||
|
||||
m_type = 3;
|
||||
m_numAreas = 3;
|
||||
m_weights.resize(m_numAreas);
|
||||
m_weights[0] = 1;
|
||||
m_weights[1] = -2;
|
||||
m_weights[2] = 1;
|
||||
m_areas.resize(m_numAreas);
|
||||
m_areas[0].x = position.x;
|
||||
m_areas[0].y = position.y;
|
||||
m_areas[0].height = baseDim.height;
|
||||
m_areas[0].width = baseDim.width;
|
||||
m_areas[1].x = position.x;
|
||||
m_areas[1].y = position.y + baseDim.height;
|
||||
m_areas[1].height = 2 * baseDim.height;
|
||||
m_areas[1].width = baseDim.width;
|
||||
m_areas[2].y = position.y + 3 * baseDim.height;
|
||||
m_areas[2].x = position.x;
|
||||
m_areas[2].height = baseDim.height;
|
||||
m_areas[2].width = baseDim.width;
|
||||
m_initMean = 0;
|
||||
m_initSigma = INITSIGMA(m_numAreas);
|
||||
valid = true;
|
||||
}
|
||||
else if (prob < probType[0] + probType[1] + probType[2] + probType[3])
|
||||
{
|
||||
//check if feature is valid
|
||||
sizeFactor.height = 1;
|
||||
sizeFactor.width = 4;
|
||||
if (position.y + baseDim.height * sizeFactor.height >= patchSize.height || position.x + baseDim.width * sizeFactor.width >= patchSize.width)
|
||||
continue;
|
||||
area = baseDim.height * sizeFactor.height * baseDim.width * sizeFactor.width;
|
||||
if (area < minArea)
|
||||
continue;
|
||||
|
||||
m_type = 3;
|
||||
m_numAreas = 3;
|
||||
m_weights.resize(m_numAreas);
|
||||
m_weights[0] = 1;
|
||||
m_weights[1] = -2;
|
||||
m_weights[2] = 1;
|
||||
m_areas.resize(m_numAreas);
|
||||
m_areas[0].x = position.x;
|
||||
m_areas[0].y = position.y;
|
||||
m_areas[0].height = baseDim.height;
|
||||
m_areas[0].width = baseDim.width;
|
||||
m_areas[1].x = position.x + baseDim.width;
|
||||
m_areas[1].y = position.y;
|
||||
m_areas[1].height = baseDim.height;
|
||||
m_areas[1].width = 2 * baseDim.width;
|
||||
m_areas[2].y = position.y;
|
||||
m_areas[2].x = position.x + 3 * baseDim.width;
|
||||
m_areas[2].height = baseDim.height;
|
||||
m_areas[2].width = baseDim.width;
|
||||
m_initMean = 0;
|
||||
m_initSigma = INITSIGMA(m_numAreas);
|
||||
valid = true;
|
||||
}
|
||||
else if (prob < probType[0] + probType[1] + probType[2] + probType[3] + probType[4])
|
||||
{
|
||||
//check if feature is valid
|
||||
sizeFactor.height = 2;
|
||||
sizeFactor.width = 2;
|
||||
if (position.y + baseDim.height * sizeFactor.height >= patchSize.height || position.x + baseDim.width * sizeFactor.width >= patchSize.width)
|
||||
continue;
|
||||
area = baseDim.height * sizeFactor.height * baseDim.width * sizeFactor.width;
|
||||
if (area < minArea)
|
||||
continue;
|
||||
|
||||
m_type = 5;
|
||||
m_numAreas = 4;
|
||||
m_weights.resize(m_numAreas);
|
||||
m_weights[0] = 1;
|
||||
m_weights[1] = -1;
|
||||
m_weights[2] = -1;
|
||||
m_weights[3] = 1;
|
||||
m_areas.resize(m_numAreas);
|
||||
m_areas[0].x = position.x;
|
||||
m_areas[0].y = position.y;
|
||||
m_areas[0].height = baseDim.height;
|
||||
m_areas[0].width = baseDim.width;
|
||||
m_areas[1].x = position.x + baseDim.width;
|
||||
m_areas[1].y = position.y;
|
||||
m_areas[1].height = baseDim.height;
|
||||
m_areas[1].width = baseDim.width;
|
||||
m_areas[2].y = position.y + baseDim.height;
|
||||
m_areas[2].x = position.x;
|
||||
m_areas[2].height = baseDim.height;
|
||||
m_areas[2].width = baseDim.width;
|
||||
m_areas[3].y = position.y + baseDim.height;
|
||||
m_areas[3].x = position.x + baseDim.width;
|
||||
m_areas[3].height = baseDim.height;
|
||||
m_areas[3].width = baseDim.width;
|
||||
m_initMean = 0;
|
||||
m_initSigma = INITSIGMA(m_numAreas);
|
||||
valid = true;
|
||||
}
|
||||
else if (prob < probType[0] + probType[1] + probType[2] + probType[3] + probType[4] + probType[5])
|
||||
{
|
||||
//check if feature is valid
|
||||
sizeFactor.height = 3;
|
||||
sizeFactor.width = 3;
|
||||
if (position.y + baseDim.height * sizeFactor.height >= patchSize.height || position.x + baseDim.width * sizeFactor.width >= patchSize.width)
|
||||
continue;
|
||||
area = baseDim.height * sizeFactor.height * baseDim.width * sizeFactor.width;
|
||||
if (area < minArea)
|
||||
continue;
|
||||
|
||||
m_type = 6;
|
||||
m_numAreas = 2;
|
||||
m_weights.resize(m_numAreas);
|
||||
m_weights[0] = 1;
|
||||
m_weights[1] = -9;
|
||||
m_areas.resize(m_numAreas);
|
||||
m_areas[0].x = position.x;
|
||||
m_areas[0].y = position.y;
|
||||
m_areas[0].height = 3 * baseDim.height;
|
||||
m_areas[0].width = 3 * baseDim.width;
|
||||
m_areas[1].x = position.x + baseDim.width;
|
||||
m_areas[1].y = position.y + baseDim.height;
|
||||
m_areas[1].height = baseDim.height;
|
||||
m_areas[1].width = baseDim.width;
|
||||
m_initMean = -8 * 128;
|
||||
m_initSigma = INITSIGMA(m_numAreas);
|
||||
valid = true;
|
||||
}
|
||||
else if (prob < probType[0] + probType[1] + probType[2] + probType[3] + probType[4] + probType[5] + probType[6])
|
||||
{
|
||||
//check if feature is valid
|
||||
sizeFactor.height = 3;
|
||||
sizeFactor.width = 1;
|
||||
if (position.y + baseDim.height * sizeFactor.height >= patchSize.height || position.x + baseDim.width * sizeFactor.width >= patchSize.width)
|
||||
continue;
|
||||
area = baseDim.height * sizeFactor.height * baseDim.width * sizeFactor.width;
|
||||
if (area < minArea)
|
||||
continue;
|
||||
|
||||
m_type = 7;
|
||||
m_numAreas = 3;
|
||||
m_weights.resize(m_numAreas);
|
||||
m_weights[0] = 1;
|
||||
m_weights[1] = -2;
|
||||
m_weights[2] = 1;
|
||||
m_areas.resize(m_numAreas);
|
||||
m_areas[0].x = position.x;
|
||||
m_areas[0].y = position.y;
|
||||
m_areas[0].height = baseDim.height;
|
||||
m_areas[0].width = baseDim.width;
|
||||
m_areas[1].x = position.x;
|
||||
m_areas[1].y = position.y + baseDim.height;
|
||||
m_areas[1].height = baseDim.height;
|
||||
m_areas[1].width = baseDim.width;
|
||||
m_areas[2].y = position.y + baseDim.height * 2;
|
||||
m_areas[2].x = position.x;
|
||||
m_areas[2].height = baseDim.height;
|
||||
m_areas[2].width = baseDim.width;
|
||||
m_initMean = 0;
|
||||
m_initSigma = INITSIGMA(m_numAreas);
|
||||
valid = true;
|
||||
}
|
||||
else if (prob < probType[0] + probType[1] + probType[2] + probType[3] + probType[4] + probType[5] + probType[6] + probType[7])
|
||||
{
|
||||
//check if feature is valid
|
||||
sizeFactor.height = 1;
|
||||
sizeFactor.width = 3;
|
||||
if (position.y + baseDim.height * sizeFactor.height >= patchSize.height || position.x + baseDim.width * sizeFactor.width >= patchSize.width)
|
||||
continue;
|
||||
|
||||
area = baseDim.height * sizeFactor.height * baseDim.width * sizeFactor.width;
|
||||
|
||||
if (area < minArea)
|
||||
continue;
|
||||
|
||||
m_type = 8;
|
||||
m_numAreas = 3;
|
||||
m_weights.resize(m_numAreas);
|
||||
m_weights[0] = 1;
|
||||
m_weights[1] = -2;
|
||||
m_weights[2] = 1;
|
||||
m_areas.resize(m_numAreas);
|
||||
m_areas[0].x = position.x;
|
||||
m_areas[0].y = position.y;
|
||||
m_areas[0].height = baseDim.height;
|
||||
m_areas[0].width = baseDim.width;
|
||||
m_areas[1].x = position.x + baseDim.width;
|
||||
m_areas[1].y = position.y;
|
||||
m_areas[1].height = baseDim.height;
|
||||
m_areas[1].width = baseDim.width;
|
||||
m_areas[2].y = position.y;
|
||||
m_areas[2].x = position.x + 2 * baseDim.width;
|
||||
m_areas[2].height = baseDim.height;
|
||||
m_areas[2].width = baseDim.width;
|
||||
m_initMean = 0;
|
||||
m_initSigma = INITSIGMA(m_numAreas);
|
||||
valid = true;
|
||||
}
|
||||
else if (prob < probType[0] + probType[1] + probType[2] + probType[3] + probType[4] + probType[5] + probType[6] + probType[7] + probType[8])
|
||||
{
|
||||
//check if feature is valid
|
||||
sizeFactor.height = 3;
|
||||
sizeFactor.width = 3;
|
||||
if (position.y + baseDim.height * sizeFactor.height >= patchSize.height || position.x + baseDim.width * sizeFactor.width >= patchSize.width)
|
||||
continue;
|
||||
area = baseDim.height * sizeFactor.height * baseDim.width * sizeFactor.width;
|
||||
if (area < minArea)
|
||||
continue;
|
||||
|
||||
m_type = 9;
|
||||
m_numAreas = 2;
|
||||
m_weights.resize(m_numAreas);
|
||||
m_weights[0] = 1;
|
||||
m_weights[1] = -2;
|
||||
m_areas.resize(m_numAreas);
|
||||
m_areas[0].x = position.x;
|
||||
m_areas[0].y = position.y;
|
||||
m_areas[0].height = 3 * baseDim.height;
|
||||
m_areas[0].width = 3 * baseDim.width;
|
||||
m_areas[1].x = position.x + baseDim.width;
|
||||
m_areas[1].y = position.y + baseDim.height;
|
||||
m_areas[1].height = baseDim.height;
|
||||
m_areas[1].width = baseDim.width;
|
||||
m_initMean = 0;
|
||||
m_initSigma = INITSIGMA(m_numAreas);
|
||||
valid = true;
|
||||
}
|
||||
else if (prob
|
||||
< probType[0] + probType[1] + probType[2] + probType[3] + probType[4] + probType[5] + probType[6] + probType[7] + probType[8] + probType[9])
|
||||
{
|
||||
//check if feature is valid
|
||||
sizeFactor.height = 3;
|
||||
sizeFactor.width = 1;
|
||||
if (position.y + baseDim.height * sizeFactor.height >= patchSize.height || position.x + baseDim.width * sizeFactor.width >= patchSize.width)
|
||||
continue;
|
||||
area = baseDim.height * sizeFactor.height * baseDim.width * sizeFactor.width;
|
||||
if (area < minArea)
|
||||
continue;
|
||||
|
||||
m_type = 10;
|
||||
m_numAreas = 3;
|
||||
m_weights.resize(m_numAreas);
|
||||
m_weights[0] = 1;
|
||||
m_weights[1] = -1;
|
||||
m_weights[2] = 1;
|
||||
m_areas.resize(m_numAreas);
|
||||
m_areas[0].x = position.x;
|
||||
m_areas[0].y = position.y;
|
||||
m_areas[0].height = baseDim.height;
|
||||
m_areas[0].width = baseDim.width;
|
||||
m_areas[1].x = position.x;
|
||||
m_areas[1].y = position.y + baseDim.height;
|
||||
m_areas[1].height = baseDim.height;
|
||||
m_areas[1].width = baseDim.width;
|
||||
m_areas[2].y = position.y + baseDim.height * 2;
|
||||
m_areas[2].x = position.x;
|
||||
m_areas[2].height = baseDim.height;
|
||||
m_areas[2].width = baseDim.width;
|
||||
m_initMean = 128;
|
||||
m_initSigma = INITSIGMA(m_numAreas);
|
||||
valid = true;
|
||||
}
|
||||
else if (prob
|
||||
< probType[0] + probType[1] + probType[2] + probType[3] + probType[4] + probType[5] + probType[6] + probType[7] + probType[8] + probType[9]
|
||||
+ probType[10])
|
||||
{
|
||||
//check if feature is valid
|
||||
sizeFactor.height = 1;
|
||||
sizeFactor.width = 3;
|
||||
if (position.y + baseDim.height * sizeFactor.height >= patchSize.height || position.x + baseDim.width * sizeFactor.width >= patchSize.width)
|
||||
continue;
|
||||
area = baseDim.height * sizeFactor.height * baseDim.width * sizeFactor.width;
|
||||
if (area < minArea)
|
||||
continue;
|
||||
|
||||
m_type = 11;
|
||||
m_numAreas = 3;
|
||||
m_weights.resize(m_numAreas);
|
||||
m_weights[0] = 1;
|
||||
m_weights[1] = -1;
|
||||
m_weights[2] = 1;
|
||||
m_areas.resize(m_numAreas);
|
||||
m_areas[0].x = position.x;
|
||||
m_areas[0].y = position.y;
|
||||
m_areas[0].height = baseDim.height;
|
||||
m_areas[0].width = baseDim.width;
|
||||
m_areas[1].x = position.x + baseDim.width;
|
||||
m_areas[1].y = position.y;
|
||||
m_areas[1].height = baseDim.height;
|
||||
m_areas[1].width = baseDim.width;
|
||||
m_areas[2].y = position.y;
|
||||
m_areas[2].x = position.x + 2 * baseDim.width;
|
||||
m_areas[2].height = baseDim.height;
|
||||
m_areas[2].width = baseDim.width;
|
||||
m_initMean = 128;
|
||||
m_initSigma = INITSIGMA(m_numAreas);
|
||||
valid = true;
|
||||
}
|
||||
else
|
||||
CV_Error(Error::StsAssert, "");
|
||||
}
|
||||
|
||||
m_initSize = patchSize;
|
||||
m_curSize = m_initSize;
|
||||
m_scaleFactorWidth = m_scaleFactorHeight = 1.0f;
|
||||
m_scaleAreas.resize(m_numAreas);
|
||||
m_scaleWeights.resize(m_numAreas);
|
||||
for (int curArea = 0; curArea < m_numAreas; curArea++)
|
||||
{
|
||||
m_scaleAreas[curArea] = m_areas[curArea];
|
||||
m_scaleWeights[curArea] = (float)m_weights[curArea] / (float)(m_areas[curArea].width * m_areas[curArea].height);
|
||||
}
|
||||
}
|
||||
|
||||
bool CvHaarEvaluator::FeatureHaar::eval(const Mat& image, Rect /*ROI*/, float* result) const
|
||||
{
|
||||
|
||||
*result = 0.0f;
|
||||
|
||||
for (int curArea = 0; curArea < m_numAreas; curArea++)
|
||||
{
|
||||
*result += (float)getSum(image, Rect(m_areas[curArea].x, m_areas[curArea].y, m_areas[curArea].width, m_areas[curArea].height))
|
||||
* m_scaleWeights[curArea];
|
||||
}
|
||||
|
||||
/*
|
||||
if( image->getUseVariance() )
|
||||
{
|
||||
float variance = (float) image->getVariance( ROI );
|
||||
*result /= variance;
|
||||
}
|
||||
*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
float CvHaarEvaluator::FeatureHaar::getSum(const Mat& image, Rect imageROI) const
|
||||
{
|
||||
// left upper Origin
|
||||
int OriginX = imageROI.x;
|
||||
int OriginY = imageROI.y;
|
||||
|
||||
// Check and fix width and height
|
||||
int Width = imageROI.width;
|
||||
int Height = imageROI.height;
|
||||
|
||||
if (OriginX + Width >= image.cols - 1)
|
||||
Width = (image.cols - 1) - OriginX;
|
||||
if (OriginY + Height >= image.rows - 1)
|
||||
Height = (image.rows - 1) - OriginY;
|
||||
|
||||
float value = 0;
|
||||
int depth = image.depth();
|
||||
|
||||
if (depth == CV_8U || depth == CV_32S)
|
||||
value = static_cast<float>(image.at<int>(OriginY + Height, OriginX + Width) + image.at<int>(OriginY, OriginX) - image.at<int>(OriginY, OriginX + Width)
|
||||
- image.at<int>(OriginY + Height, OriginX));
|
||||
else if (depth == CV_64F)
|
||||
value = static_cast<float>(image.at<double>(OriginY + Height, OriginX + Width) + image.at<double>(OriginY, OriginX)
|
||||
- image.at<double>(OriginY, OriginX + Width) - image.at<double>(OriginY + Height, OriginX));
|
||||
else if (depth == CV_32F)
|
||||
value = static_cast<float>(image.at<float>(OriginY + Height, OriginX + Width) + image.at<float>(OriginY, OriginX) - image.at<float>(OriginY, OriginX + Width)
|
||||
- image.at<float>(OriginY + Height, OriginX));
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
}}} // namespace cv::detail::tracking
|
||||
@@ -0,0 +1,168 @@
|
||||
// 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_VIDEO_DETAIL_TRACKING_FEATURE_HPP
|
||||
#define OPENCV_VIDEO_DETAIL_TRACKING_FEATURE_HPP
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
|
||||
/*
|
||||
* TODO This implementation is based on apps/traincascade/
|
||||
* TODO Changed CvHaarEvaluator based on ADABOOSTING implementation (Grabner et al.)
|
||||
*/
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
//! @addtogroup tracking_detail
|
||||
//! @{
|
||||
|
||||
inline namespace feature {
|
||||
|
||||
class CvParams
|
||||
{
|
||||
public:
|
||||
CvParams();
|
||||
virtual ~CvParams()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class CvFeatureParams : public CvParams
|
||||
{
|
||||
public:
|
||||
enum FeatureType
|
||||
{
|
||||
HAAR = 0,
|
||||
LBP = 1,
|
||||
HOG = 2
|
||||
};
|
||||
|
||||
CvFeatureParams();
|
||||
static Ptr<CvFeatureParams> create(CvFeatureParams::FeatureType featureType);
|
||||
int maxCatCount; // 0 in case of numerical features
|
||||
int featSize; // 1 in case of simple features (HAAR, LBP) and N_BINS(9)*N_CELLS(4) in case of Dalal's HOG features
|
||||
int numFeatures;
|
||||
};
|
||||
|
||||
class CvFeatureEvaluator
|
||||
{
|
||||
public:
|
||||
virtual ~CvFeatureEvaluator()
|
||||
{
|
||||
}
|
||||
virtual void init(const CvFeatureParams* _featureParams, int _maxSampleCount, Size _winSize);
|
||||
virtual void setImage(const Mat& img, uchar clsLabel, int idx);
|
||||
static Ptr<CvFeatureEvaluator> create(CvFeatureParams::FeatureType type);
|
||||
|
||||
int getNumFeatures() const
|
||||
{
|
||||
return numFeatures;
|
||||
}
|
||||
int getMaxCatCount() const
|
||||
{
|
||||
return featureParams->maxCatCount;
|
||||
}
|
||||
int getFeatureSize() const
|
||||
{
|
||||
return featureParams->featSize;
|
||||
}
|
||||
const Mat& getCls() const
|
||||
{
|
||||
return cls;
|
||||
}
|
||||
float getCls(int si) const
|
||||
{
|
||||
return cls.at<float>(si, 0);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void generateFeatures() = 0;
|
||||
|
||||
int npos, nneg;
|
||||
int numFeatures;
|
||||
Size winSize;
|
||||
CvFeatureParams* featureParams;
|
||||
Mat cls;
|
||||
};
|
||||
|
||||
class CvHaarFeatureParams : public CvFeatureParams
|
||||
{
|
||||
public:
|
||||
CvHaarFeatureParams();
|
||||
bool isIntegral;
|
||||
};
|
||||
|
||||
class CvHaarEvaluator : public CvFeatureEvaluator
|
||||
{
|
||||
public:
|
||||
class FeatureHaar
|
||||
{
|
||||
|
||||
public:
|
||||
FeatureHaar(Size patchSize);
|
||||
bool eval(const Mat& image, Rect ROI, float* result) const;
|
||||
inline int getNumAreas() const { return m_numAreas; }
|
||||
inline const std::vector<float>& getWeights() const { return m_weights; }
|
||||
inline const std::vector<Rect>& getAreas() const { return m_areas; }
|
||||
|
||||
private:
|
||||
int m_type;
|
||||
int m_numAreas;
|
||||
std::vector<float> m_weights;
|
||||
float m_initMean;
|
||||
float m_initSigma;
|
||||
void generateRandomFeature(Size imageSize);
|
||||
float getSum(const Mat& image, Rect imgROI) const;
|
||||
std::vector<Rect> m_areas; // areas within the patch over which to compute the feature
|
||||
cv::Size m_initSize; // size of the patch used during training
|
||||
cv::Size m_curSize; // size of the patches currently under investigation
|
||||
float m_scaleFactorHeight; // scaling factor in vertical direction
|
||||
float m_scaleFactorWidth; // scaling factor in horizontal direction
|
||||
std::vector<Rect> m_scaleAreas; // areas after scaling
|
||||
std::vector<float> m_scaleWeights; // weights after scaling
|
||||
};
|
||||
|
||||
virtual void init(const CvFeatureParams* _featureParams, int _maxSampleCount, Size _winSize) CV_OVERRIDE;
|
||||
virtual void setImage(const Mat& img, uchar clsLabel = 0, int idx = 1) CV_OVERRIDE;
|
||||
inline const std::vector<CvHaarEvaluator::FeatureHaar>& getFeatures() const { return features; }
|
||||
inline CvHaarEvaluator::FeatureHaar& getFeatures(int idx)
|
||||
{
|
||||
return features[idx];
|
||||
}
|
||||
inline void setWinSize(Size patchSize) { winSize = patchSize; }
|
||||
inline Size getWinSize() const { return winSize; }
|
||||
virtual void generateFeatures() CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* \brief Overload the original generateFeatures in order to limit the number of the features
|
||||
* @param numFeatures Number of the features
|
||||
*/
|
||||
virtual void generateFeatures(int numFeatures);
|
||||
|
||||
protected:
|
||||
bool isIntegral;
|
||||
|
||||
/* TODO Added from MIL implementation */
|
||||
Mat _ii_img;
|
||||
void compute_integral(const cv::Mat& img, std::vector<cv::Mat_<float>>& ii_imgs)
|
||||
{
|
||||
Mat ii_img;
|
||||
integral(img, ii_img, CV_32F);
|
||||
split(ii_img, ii_imgs);
|
||||
}
|
||||
|
||||
std::vector<FeatureHaar> features;
|
||||
Mat sum; /* sum images (each row represents image) */
|
||||
};
|
||||
|
||||
} // namespace feature
|
||||
|
||||
//! @}
|
||||
|
||||
}}} // namespace cv::detail::tracking
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,356 @@
|
||||
// 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 "tracking_online_mil.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
#define sign(s) ((s > 0) ? 1 : ((s < 0) ? -1 : 0))
|
||||
|
||||
template <class T>
|
||||
class SortableElementRev
|
||||
{
|
||||
public:
|
||||
T _val;
|
||||
int _ind;
|
||||
SortableElementRev()
|
||||
: _val(), _ind(0)
|
||||
{
|
||||
}
|
||||
SortableElementRev(T val, int ind)
|
||||
{
|
||||
_val = val;
|
||||
_ind = ind;
|
||||
}
|
||||
bool operator<(SortableElementRev<T>& b)
|
||||
{
|
||||
return (_val < b._val);
|
||||
}
|
||||
};
|
||||
|
||||
static bool CompareSortableElementRev(const SortableElementRev<float>& i, const SortableElementRev<float>& j)
|
||||
{
|
||||
return i._val < j._val;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void sort_order_des(std::vector<T>& v, std::vector<int>& order)
|
||||
{
|
||||
uint n = (uint)v.size();
|
||||
std::vector<SortableElementRev<T>> v2;
|
||||
v2.resize(n);
|
||||
order.clear();
|
||||
order.resize(n);
|
||||
for (uint i = 0; i < n; i++)
|
||||
{
|
||||
v2[i]._ind = i;
|
||||
v2[i]._val = v[i];
|
||||
}
|
||||
//std::sort( v2.begin(), v2.end() );
|
||||
std::sort(v2.begin(), v2.end(), CompareSortableElementRev);
|
||||
for (uint i = 0; i < n; i++)
|
||||
{
|
||||
order[i] = v2[i]._ind;
|
||||
v[i] = v2[i]._val;
|
||||
}
|
||||
}
|
||||
|
||||
//implementations for strong classifier
|
||||
|
||||
ClfMilBoost::Params::Params()
|
||||
{
|
||||
_numSel = 50;
|
||||
_numFeat = 250;
|
||||
_lRate = 0.85f;
|
||||
}
|
||||
|
||||
ClfMilBoost::ClfMilBoost()
|
||||
: _numsamples(0)
|
||||
, _counter(0)
|
||||
{
|
||||
_myParams = ClfMilBoost::Params();
|
||||
_numsamples = 0;
|
||||
}
|
||||
|
||||
ClfMilBoost::~ClfMilBoost()
|
||||
{
|
||||
_selectors.clear();
|
||||
for (size_t i = 0; i < _weakclf.size(); i++)
|
||||
delete _weakclf.at(i);
|
||||
}
|
||||
|
||||
void ClfMilBoost::init(const ClfMilBoost::Params& parameters)
|
||||
{
|
||||
_myParams = parameters;
|
||||
_numsamples = 0;
|
||||
|
||||
//_ftrs = Ftr::generate( _myParams->_ftrParams, _myParams->_numFeat );
|
||||
// if( params->_storeFtrHistory )
|
||||
// Ftr::toViz( _ftrs, "haarftrs" );
|
||||
_weakclf.resize(_myParams._numFeat);
|
||||
for (int k = 0; k < _myParams._numFeat; k++)
|
||||
{
|
||||
_weakclf[k] = new ClfOnlineStump(k);
|
||||
_weakclf[k]->_lRate = _myParams._lRate;
|
||||
}
|
||||
_counter = 0;
|
||||
}
|
||||
|
||||
void ClfMilBoost::update(const Mat& posx, const Mat& negx)
|
||||
{
|
||||
int numneg = negx.rows;
|
||||
int numpos = posx.rows;
|
||||
|
||||
// compute ftrs
|
||||
//if( !posx.ftrsComputed() )
|
||||
// Ftr::compute( posx, _ftrs );
|
||||
//if( !negx.ftrsComputed() )
|
||||
// Ftr::compute( negx, _ftrs );
|
||||
|
||||
// initialize H
|
||||
static std::vector<float> Hpos, Hneg;
|
||||
Hpos.clear();
|
||||
Hneg.clear();
|
||||
Hpos.resize(posx.rows, 0.0f), Hneg.resize(negx.rows, 0.0f);
|
||||
|
||||
_selectors.clear();
|
||||
std::vector<float> posw(posx.rows), negw(negx.rows);
|
||||
std::vector<std::vector<float>> pospred(_weakclf.size()), negpred(_weakclf.size());
|
||||
|
||||
// train all weak classifiers without weights
|
||||
#ifdef _OPENMP
|
||||
#pragma omp parallel for
|
||||
#endif
|
||||
for (int m = 0; m < _myParams._numFeat; m++)
|
||||
{
|
||||
_weakclf[m]->update(posx, negx);
|
||||
pospred[m] = _weakclf[m]->classifySetF(posx);
|
||||
negpred[m] = _weakclf[m]->classifySetF(negx);
|
||||
}
|
||||
|
||||
// pick the best features
|
||||
for (int s = 0; s < _myParams._numSel; s++)
|
||||
{
|
||||
|
||||
// compute errors/likl for all weak clfs
|
||||
std::vector<float> poslikl(_weakclf.size(), 1.0f), neglikl(_weakclf.size()), likl(_weakclf.size());
|
||||
#ifdef _OPENMP
|
||||
#pragma omp parallel for
|
||||
#endif
|
||||
for (int w = 0; w < (int)_weakclf.size(); w++)
|
||||
{
|
||||
float lll = 1.0f;
|
||||
for (int j = 0; j < numpos; j++)
|
||||
lll *= (1 - sigmoid(Hpos[j] + pospred[w][j]));
|
||||
poslikl[w] = (float)-log(1 - lll + 1e-5);
|
||||
|
||||
lll = 0.0f;
|
||||
for (int j = 0; j < numneg; j++)
|
||||
lll += (float)-log(1e-5f + 1 - sigmoid(Hneg[j] + negpred[w][j]));
|
||||
neglikl[w] = lll;
|
||||
|
||||
likl[w] = poslikl[w] / numpos + neglikl[w] / numneg;
|
||||
}
|
||||
|
||||
// pick best weak clf
|
||||
std::vector<int> order;
|
||||
sort_order_des(likl, order);
|
||||
|
||||
// find best weakclf that isn't already included
|
||||
for (uint k = 0; k < order.size(); k++)
|
||||
if (std::count(_selectors.begin(), _selectors.end(), order[k]) == 0)
|
||||
{
|
||||
_selectors.push_back(order[k]);
|
||||
break;
|
||||
}
|
||||
|
||||
// update H = H + h_m
|
||||
#ifdef _OPENMP
|
||||
#pragma omp parallel for
|
||||
#endif
|
||||
for (int k = 0; k < posx.rows; k++)
|
||||
Hpos[k] += pospred[_selectors[s]][k];
|
||||
#ifdef _OPENMP
|
||||
#pragma omp parallel for
|
||||
#endif
|
||||
for (int k = 0; k < negx.rows; k++)
|
||||
Hneg[k] += negpred[_selectors[s]][k];
|
||||
}
|
||||
|
||||
//if( _myParams->_storeFtrHistory )
|
||||
//for ( uint j = 0; j < _selectors.size(); j++ )
|
||||
// _ftrHist( _selectors[j], _counter ) = 1.0f / ( j + 1 );
|
||||
|
||||
_counter++;
|
||||
/* */
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<float> ClfMilBoost::classify(const Mat& x, bool logR)
|
||||
{
|
||||
int numsamples = x.rows;
|
||||
std::vector<float> res(numsamples);
|
||||
std::vector<float> tr;
|
||||
|
||||
for (uint w = 0; w < _selectors.size(); w++)
|
||||
{
|
||||
tr = _weakclf[_selectors[w]]->classifySetF(x);
|
||||
#ifdef _OPENMP
|
||||
#pragma omp parallel for
|
||||
#endif
|
||||
for (int j = 0; j < numsamples; j++)
|
||||
{
|
||||
res[j] += tr[j];
|
||||
}
|
||||
}
|
||||
|
||||
// return probabilities or log odds ratio
|
||||
if (!logR)
|
||||
{
|
||||
#ifdef _OPENMP
|
||||
#pragma omp parallel for
|
||||
#endif
|
||||
for (int j = 0; j < (int)res.size(); j++)
|
||||
{
|
||||
res[j] = sigmoid(res[j]);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
//implementations for weak classifier
|
||||
|
||||
ClfOnlineStump::ClfOnlineStump()
|
||||
: _mu0(0), _mu1(0), _sig0(0), _sig1(0)
|
||||
, _q(0)
|
||||
, _s(0)
|
||||
, _log_n1(0), _log_n0(0)
|
||||
, _e1(0), _e0(0)
|
||||
, _lRate(0)
|
||||
{
|
||||
_trained = false;
|
||||
_ind = -1;
|
||||
init();
|
||||
}
|
||||
|
||||
ClfOnlineStump::ClfOnlineStump(int ind)
|
||||
: _mu0(0), _mu1(0), _sig0(0), _sig1(0)
|
||||
, _q(0)
|
||||
, _s(0)
|
||||
, _log_n1(0), _log_n0(0)
|
||||
, _e1(0), _e0(0)
|
||||
, _lRate(0)
|
||||
{
|
||||
_trained = false;
|
||||
_ind = ind;
|
||||
init();
|
||||
}
|
||||
void ClfOnlineStump::init()
|
||||
{
|
||||
_mu0 = 0;
|
||||
_mu1 = 0;
|
||||
_sig0 = 1;
|
||||
_sig1 = 1;
|
||||
_lRate = 0.85f;
|
||||
_trained = false;
|
||||
}
|
||||
|
||||
void ClfOnlineStump::update(const Mat& posx, const Mat& negx, const Mat_<float>& /*posw*/, const Mat_<float>& /*negw*/)
|
||||
{
|
||||
//std::cout << " ClfOnlineStump::update" << _ind << std::endl;
|
||||
float posmu = 0.0, negmu = 0.0;
|
||||
if (posx.cols > 0)
|
||||
posmu = float(mean(posx.col(_ind))[0]);
|
||||
if (negx.cols > 0)
|
||||
negmu = float(mean(negx.col(_ind))[0]);
|
||||
|
||||
if (_trained)
|
||||
{
|
||||
if (posx.cols > 0)
|
||||
{
|
||||
_mu1 = (_lRate * _mu1 + (1 - _lRate) * posmu);
|
||||
cv::Mat diff = posx.col(_ind) - _mu1;
|
||||
_sig1 = _lRate * _sig1 + (1 - _lRate) * float(mean(diff.mul(diff))[0]);
|
||||
}
|
||||
if (negx.cols > 0)
|
||||
{
|
||||
_mu0 = (_lRate * _mu0 + (1 - _lRate) * negmu);
|
||||
cv::Mat diff = negx.col(_ind) - _mu0;
|
||||
_sig0 = _lRate * _sig0 + (1 - _lRate) * float(mean(diff.mul(diff))[0]);
|
||||
}
|
||||
|
||||
_q = (_mu1 - _mu0) / 2;
|
||||
_s = sign(_mu1 - _mu0);
|
||||
_log_n0 = std::log(float(1.0f / std::pow(_sig0, 0.5f)));
|
||||
_log_n1 = std::log(float(1.0f / std::pow(_sig1, 0.5f)));
|
||||
//_e1 = -1.0f/(2.0f*_sig1+1e-99f);
|
||||
//_e0 = -1.0f/(2.0f*_sig0+1e-99f);
|
||||
_e1 = -1.0f / (2.0f * _sig1 + std::numeric_limits<float>::min());
|
||||
_e0 = -1.0f / (2.0f * _sig0 + std::numeric_limits<float>::min());
|
||||
}
|
||||
else
|
||||
{
|
||||
_trained = true;
|
||||
if (posx.cols > 0)
|
||||
{
|
||||
_mu1 = posmu;
|
||||
cv::Scalar scal_mean, scal_std_dev;
|
||||
cv::meanStdDev(posx.col(_ind), scal_mean, scal_std_dev);
|
||||
_sig1 = float(scal_std_dev[0]) * float(scal_std_dev[0]) + 1e-9f;
|
||||
}
|
||||
|
||||
if (negx.cols > 0)
|
||||
{
|
||||
_mu0 = negmu;
|
||||
cv::Scalar scal_mean, scal_std_dev;
|
||||
cv::meanStdDev(negx.col(_ind), scal_mean, scal_std_dev);
|
||||
_sig0 = float(scal_std_dev[0]) * float(scal_std_dev[0]) + 1e-9f;
|
||||
}
|
||||
|
||||
_q = (_mu1 - _mu0) / 2;
|
||||
_s = sign(_mu1 - _mu0);
|
||||
_log_n0 = std::log(float(1.0f / std::pow(_sig0, 0.5f)));
|
||||
_log_n1 = std::log(float(1.0f / std::pow(_sig1, 0.5f)));
|
||||
//_e1 = -1.0f/(2.0f*_sig1+1e-99f);
|
||||
//_e0 = -1.0f/(2.0f*_sig0+1e-99f);
|
||||
_e1 = -1.0f / (2.0f * _sig1 + std::numeric_limits<float>::min());
|
||||
_e0 = -1.0f / (2.0f * _sig0 + std::numeric_limits<float>::min());
|
||||
}
|
||||
}
|
||||
|
||||
bool ClfOnlineStump::classify(const Mat& x, int i)
|
||||
{
|
||||
float xx = x.at<float>(i, _ind);
|
||||
double log_p0 = (xx - _mu0) * (xx - _mu0) * _e0 + _log_n0;
|
||||
double log_p1 = (xx - _mu1) * (xx - _mu1) * _e1 + _log_n1;
|
||||
return log_p1 > log_p0;
|
||||
}
|
||||
|
||||
float ClfOnlineStump::classifyF(const Mat& x, int i)
|
||||
{
|
||||
float xx = x.at<float>(i, _ind);
|
||||
double log_p0 = (xx - _mu0) * (xx - _mu0) * _e0 + _log_n0;
|
||||
double log_p1 = (xx - _mu1) * (xx - _mu1) * _e1 + _log_n1;
|
||||
return float(log_p1 - log_p0);
|
||||
}
|
||||
|
||||
inline std::vector<float> ClfOnlineStump::classifySetF(const Mat& x)
|
||||
{
|
||||
std::vector<float> res(x.rows);
|
||||
|
||||
#ifdef _OPENMP
|
||||
#pragma omp parallel for
|
||||
#endif
|
||||
for (int k = 0; k < (int)res.size(); k++)
|
||||
{
|
||||
res[k] = classifyF(x, k);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
}}} // namespace cv::detail::tracking
|
||||
@@ -0,0 +1,79 @@
|
||||
// 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_VIDEO_DETAIL_TRACKING_ONLINE_MIL_HPP
|
||||
#define OPENCV_VIDEO_DETAIL_TRACKING_ONLINE_MIL_HPP
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
//! @addtogroup tracking_detail
|
||||
//! @{
|
||||
|
||||
//TODO based on the original implementation
|
||||
//http://vision.ucsd.edu/~bbabenko/project_miltrack.shtml
|
||||
|
||||
class ClfOnlineStump;
|
||||
|
||||
class CV_EXPORTS ClfMilBoost
|
||||
{
|
||||
public:
|
||||
struct CV_EXPORTS Params
|
||||
{
|
||||
Params();
|
||||
int _numSel;
|
||||
int _numFeat;
|
||||
float _lRate;
|
||||
};
|
||||
|
||||
ClfMilBoost();
|
||||
~ClfMilBoost();
|
||||
void init(const ClfMilBoost::Params& parameters = ClfMilBoost::Params());
|
||||
void update(const Mat& posx, const Mat& negx);
|
||||
std::vector<float> classify(const Mat& x, bool logR = true);
|
||||
|
||||
inline float sigmoid(float x)
|
||||
{
|
||||
return 1.0f / (1.0f + exp(-x));
|
||||
}
|
||||
|
||||
private:
|
||||
uint _numsamples;
|
||||
ClfMilBoost::Params _myParams;
|
||||
std::vector<int> _selectors;
|
||||
std::vector<ClfOnlineStump*> _weakclf;
|
||||
uint _counter;
|
||||
};
|
||||
|
||||
class ClfOnlineStump
|
||||
{
|
||||
public:
|
||||
float _mu0, _mu1, _sig0, _sig1;
|
||||
float _q;
|
||||
int _s;
|
||||
float _log_n1, _log_n0;
|
||||
float _e1, _e0;
|
||||
float _lRate;
|
||||
|
||||
ClfOnlineStump();
|
||||
ClfOnlineStump(int ind);
|
||||
void init();
|
||||
void update(const Mat& posx, const Mat& negx, const cv::Mat_<float>& posw = cv::Mat_<float>(), const cv::Mat_<float>& negw = cv::Mat_<float>());
|
||||
bool classify(const Mat& x, int i);
|
||||
float classifyF(const Mat& x, int i);
|
||||
std::vector<float> classifySetF(const Mat& x);
|
||||
|
||||
private:
|
||||
bool _trained;
|
||||
int _ind;
|
||||
};
|
||||
|
||||
//! @}
|
||||
|
||||
}}} // namespace cv::detail::tracking
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
// 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"
|
||||
|
||||
namespace cv {
|
||||
|
||||
Tracker::Tracker()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
Tracker::~Tracker()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
} // namespace cv
|
||||
@@ -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.
|
||||
|
||||
#include "../precomp.hpp"
|
||||
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
#include "opencv2/dnn.hpp"
|
||||
#endif
|
||||
|
||||
namespace cv {
|
||||
|
||||
TrackerDaSiamRPN::TrackerDaSiamRPN()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
TrackerDaSiamRPN::~TrackerDaSiamRPN()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
TrackerDaSiamRPN::Params::Params()
|
||||
{
|
||||
model = "dasiamrpn_model.onnx";
|
||||
kernel_cls1 = "dasiamrpn_kernel_cls1.onnx";
|
||||
kernel_r1 = "dasiamrpn_kernel_r1.onnx";
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
backend = dnn::DNN_BACKEND_DEFAULT;
|
||||
target = dnn::DNN_TARGET_CPU;
|
||||
#else
|
||||
backend = -1; // invalid value
|
||||
target = -1; // invalid value
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
|
||||
template <typename T> static
|
||||
T sizeCal(const T& w, const T& h)
|
||||
{
|
||||
T pad = (w + h) * T(0.5);
|
||||
T sz2 = (w + pad) * (h + pad);
|
||||
return sqrt(sz2);
|
||||
}
|
||||
|
||||
template <>
|
||||
Mat sizeCal(const Mat& w, const Mat& h)
|
||||
{
|
||||
Mat pad = (w + h) * 0.5;
|
||||
Mat sz2 = (w + pad).mul((h + pad));
|
||||
|
||||
cv::sqrt(sz2, sz2);
|
||||
return sz2;
|
||||
}
|
||||
|
||||
class TrackerDaSiamRPNImpl : public TrackerDaSiamRPN
|
||||
{
|
||||
public:
|
||||
TrackerDaSiamRPNImpl(const TrackerDaSiamRPN::Params& parameters)
|
||||
{
|
||||
siamRPN = dnn::readNet(parameters.model);
|
||||
siamKernelCL1 = dnn::readNet(parameters.kernel_cls1);
|
||||
siamKernelR1 = dnn::readNet(parameters.kernel_r1);
|
||||
|
||||
CV_Assert(!siamRPN.empty());
|
||||
CV_Assert(!siamKernelCL1.empty());
|
||||
CV_Assert(!siamKernelR1.empty());
|
||||
|
||||
siamRPN.setPreferableBackend(parameters.backend);
|
||||
siamRPN.setPreferableTarget(parameters.target);
|
||||
siamKernelR1.setPreferableBackend(parameters.backend);
|
||||
siamKernelR1.setPreferableTarget(parameters.target);
|
||||
siamKernelCL1.setPreferableBackend(parameters.backend);
|
||||
siamKernelCL1.setPreferableTarget(parameters.target);
|
||||
}
|
||||
|
||||
TrackerDaSiamRPNImpl(const dnn::Net& siam_rpn, const dnn::Net& kernel_cls1, const dnn::Net& kernel_r1)
|
||||
{
|
||||
CV_Assert(!siam_rpn.empty());
|
||||
CV_Assert(!kernel_cls1.empty());
|
||||
CV_Assert(!kernel_r1.empty());
|
||||
|
||||
siamRPN = siam_rpn;
|
||||
siamKernelCL1 = kernel_cls1;
|
||||
siamKernelR1 = kernel_r1;
|
||||
}
|
||||
|
||||
void init(InputArray image, const Rect& boundingBox) CV_OVERRIDE;
|
||||
bool update(InputArray image, Rect& boundingBox) CV_OVERRIDE;
|
||||
float getTrackingScore() CV_OVERRIDE;
|
||||
|
||||
protected:
|
||||
dnn::Net siamRPN, siamKernelR1, siamKernelCL1;
|
||||
Rect boundingBox_;
|
||||
Mat image_;
|
||||
struct trackerConfig
|
||||
{
|
||||
float windowInfluence = 0.43f;
|
||||
float lr = 0.4f;
|
||||
int scale = 8;
|
||||
bool swapRB = false;
|
||||
int totalStride = 8;
|
||||
float penaltyK = 0.055f;
|
||||
int exemplarSize = 127;
|
||||
int instanceSize = 271;
|
||||
float contextAmount = 0.5f;
|
||||
std::vector<float> ratios = { 0.33f, 0.5f, 1.0f, 2.0f, 3.0f };
|
||||
int anchorNum = int(ratios.size());
|
||||
Mat anchors;
|
||||
Mat windows;
|
||||
Scalar avgChans;
|
||||
Size imgSize = { 0, 0 };
|
||||
Rect2f targetBox = { 0, 0, 0, 0 };
|
||||
int scoreSize = (instanceSize - exemplarSize) / totalStride + 1;
|
||||
float tracking_score;
|
||||
|
||||
void update_scoreSize()
|
||||
{
|
||||
scoreSize = int((instanceSize - exemplarSize) / totalStride + 1);
|
||||
}
|
||||
};
|
||||
trackerConfig trackState;
|
||||
|
||||
void softmax(const Mat& src, Mat& dst);
|
||||
void elementMax(Mat& src);
|
||||
Mat generateHanningWindow();
|
||||
Mat generateAnchors();
|
||||
Mat getSubwindow(Mat& img, const Rect2f& targetBox, float originalSize, Scalar avgChans);
|
||||
void trackerInit(Mat img);
|
||||
void trackerEval(Mat img);
|
||||
};
|
||||
|
||||
void TrackerDaSiamRPNImpl::init(InputArray image, const Rect& boundingBox)
|
||||
{
|
||||
image_ = image.getMat().clone();
|
||||
|
||||
trackState.update_scoreSize();
|
||||
trackState.targetBox = Rect2f(
|
||||
float(boundingBox.x) + float(boundingBox.width) * 0.5f, // FIXIT don't use center in Rect structures, it is confusing
|
||||
float(boundingBox.y) + float(boundingBox.height) * 0.5f,
|
||||
float(boundingBox.width),
|
||||
float(boundingBox.height)
|
||||
);
|
||||
trackerInit(image_);
|
||||
}
|
||||
|
||||
void TrackerDaSiamRPNImpl::trackerInit(Mat img)
|
||||
{
|
||||
Rect2f targetBox = trackState.targetBox;
|
||||
Mat anchors = generateAnchors();
|
||||
trackState.anchors = anchors;
|
||||
|
||||
Mat windows = generateHanningWindow();
|
||||
|
||||
trackState.windows = windows;
|
||||
trackState.imgSize = img.size();
|
||||
|
||||
trackState.avgChans = mean(img);
|
||||
float wc = targetBox.width + trackState.contextAmount * (targetBox.width + targetBox.height);
|
||||
float hc = targetBox.height + trackState.contextAmount * (targetBox.width + targetBox.height);
|
||||
float sz = (float)cvRound(sqrt(wc * hc));
|
||||
|
||||
Mat zCrop = getSubwindow(img, targetBox, sz, trackState.avgChans);
|
||||
Mat blob;
|
||||
|
||||
dnn::blobFromImage(zCrop, blob, 1.0, Size(trackState.exemplarSize, trackState.exemplarSize), Scalar(), trackState.swapRB, false, CV_32F);
|
||||
siamRPN.setInput(blob);
|
||||
Mat out1;
|
||||
siamRPN.forward(out1, "onnx_node_output_0!63");
|
||||
|
||||
siamKernelCL1.setInput(out1);
|
||||
siamKernelR1.setInput(out1);
|
||||
|
||||
Mat cls1 = siamKernelCL1.forward();
|
||||
Mat r1 = siamKernelR1.forward();
|
||||
std::vector<int> r1_shape = { 20, 256, 4, 4 }, cls1_shape = { 10, 256, 4, 4 };
|
||||
|
||||
siamRPN.setParam("onnx_node_output_0!65", 0, r1.reshape(0, r1_shape));
|
||||
siamRPN.setParam("onnx_node_output_0!68", 0, cls1.reshape(0, cls1_shape));
|
||||
}
|
||||
|
||||
bool TrackerDaSiamRPNImpl::update(InputArray image, Rect& boundingBox)
|
||||
{
|
||||
image_ = image.getMat().clone();
|
||||
trackerEval(image_);
|
||||
boundingBox = {
|
||||
int(trackState.targetBox.x - int(trackState.targetBox.width / 2)),
|
||||
int(trackState.targetBox.y - int(trackState.targetBox.height / 2)),
|
||||
int(trackState.targetBox.width),
|
||||
int(trackState.targetBox.height)
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
void TrackerDaSiamRPNImpl::trackerEval(Mat img)
|
||||
{
|
||||
Rect2f targetBox = trackState.targetBox;
|
||||
|
||||
float wc = targetBox.height + trackState.contextAmount * (targetBox.width + targetBox.height);
|
||||
float hc = targetBox.width + trackState.contextAmount * (targetBox.width + targetBox.height);
|
||||
|
||||
float sz = sqrt(wc * hc);
|
||||
float scaleZ = trackState.exemplarSize / sz;
|
||||
|
||||
float searchSize = float((trackState.instanceSize - trackState.exemplarSize) / 2);
|
||||
float pad = searchSize / scaleZ;
|
||||
float sx = sz + 2 * pad;
|
||||
|
||||
Mat xCrop = getSubwindow(img, targetBox, (float)cvRound(sx), trackState.avgChans);
|
||||
|
||||
Mat blob;
|
||||
std::vector<Mat> outs;
|
||||
std::vector<String> outNames;
|
||||
Mat delta, score;
|
||||
Mat sc, rc, penalty, pscore;
|
||||
|
||||
dnn::blobFromImage(xCrop, blob, 1.0, Size(trackState.instanceSize, trackState.instanceSize), Scalar(), trackState.swapRB, false, CV_32F);
|
||||
|
||||
siamRPN.setInput(blob);
|
||||
|
||||
outNames = siamRPN.getUnconnectedOutLayersNames();
|
||||
siamRPN.forward(outs, outNames);
|
||||
|
||||
delta = outs[0];
|
||||
score = outs[1];
|
||||
|
||||
score = score.reshape(0, { 2, trackState.anchorNum, trackState.scoreSize, trackState.scoreSize });
|
||||
delta = delta.reshape(0, { 4, trackState.anchorNum, trackState.scoreSize, trackState.scoreSize });
|
||||
|
||||
softmax(score, score);
|
||||
|
||||
targetBox.width *= scaleZ;
|
||||
targetBox.height *= scaleZ;
|
||||
|
||||
score = score.row(1);
|
||||
score = score.reshape(0, { 5, 19, 19 });
|
||||
|
||||
// Post processing
|
||||
delta.row(0) = delta.row(0).mul(trackState.anchors.row(2)) + trackState.anchors.row(0);
|
||||
delta.row(1) = delta.row(1).mul(trackState.anchors.row(3)) + trackState.anchors.row(1);
|
||||
exp(delta.row(2), delta.row(2));
|
||||
delta.row(2) = delta.row(2).mul(trackState.anchors.row(2));
|
||||
exp(delta.row(3), delta.row(3));
|
||||
delta.row(3) = delta.row(3).mul(trackState.anchors.row(3));
|
||||
|
||||
sc = sizeCal(delta.row(2), delta.row(3)) / sizeCal(targetBox.width, targetBox.height);
|
||||
elementMax(sc);
|
||||
|
||||
rc = delta.row(2).mul(1 / delta.row(3));
|
||||
rc = (targetBox.width / targetBox.height) / rc;
|
||||
elementMax(rc);
|
||||
|
||||
// Calculating the penalty
|
||||
exp(((rc.mul(sc) - 1.) * trackState.penaltyK * (-1.0)), penalty);
|
||||
penalty = penalty.reshape(0, { trackState.anchorNum, trackState.scoreSize, trackState.scoreSize });
|
||||
|
||||
pscore = penalty.mul(score);
|
||||
pscore = pscore * (1.0 - trackState.windowInfluence) + trackState.windows * trackState.windowInfluence;
|
||||
|
||||
int bestID[2] = { 0, 0 };
|
||||
// Find the index of best score.
|
||||
minMaxIdx(pscore.reshape(0, { trackState.anchorNum * trackState.scoreSize * trackState.scoreSize, 1 }), 0, 0, 0, bestID);
|
||||
delta = delta.reshape(0, { 4, trackState.anchorNum * trackState.scoreSize * trackState.scoreSize });
|
||||
penalty = penalty.reshape(0, { trackState.anchorNum * trackState.scoreSize * trackState.scoreSize, 1 });
|
||||
score = score.reshape(0, { trackState.anchorNum * trackState.scoreSize * trackState.scoreSize, 1 });
|
||||
|
||||
int index[2] = { 0, bestID[0] };
|
||||
Rect2f resBox = { 0, 0, 0, 0 };
|
||||
|
||||
resBox.x = delta.at<float>(index) / scaleZ;
|
||||
index[0] = 1;
|
||||
resBox.y = delta.at<float>(index) / scaleZ;
|
||||
index[0] = 2;
|
||||
resBox.width = delta.at<float>(index) / scaleZ;
|
||||
index[0] = 3;
|
||||
resBox.height = delta.at<float>(index) / scaleZ;
|
||||
|
||||
float lr = penalty.at<float>(bestID) * score.at<float>(bestID) * trackState.lr;
|
||||
|
||||
resBox.x = resBox.x + targetBox.x;
|
||||
resBox.y = resBox.y + targetBox.y;
|
||||
targetBox.width /= scaleZ;
|
||||
targetBox.height /= scaleZ;
|
||||
|
||||
resBox.width = targetBox.width * (1 - lr) + resBox.width * lr;
|
||||
resBox.height = targetBox.height * (1 - lr) + resBox.height * lr;
|
||||
|
||||
resBox.x = float(fmax(0., fmin(float(trackState.imgSize.width), resBox.x)));
|
||||
resBox.y = float(fmax(0., fmin(float(trackState.imgSize.height), resBox.y)));
|
||||
resBox.width = float(fmax(10., fmin(float(trackState.imgSize.width), resBox.width)));
|
||||
resBox.height = float(fmax(10., fmin(float(trackState.imgSize.height), resBox.height)));
|
||||
|
||||
trackState.targetBox = resBox;
|
||||
trackState.tracking_score = score.at<float>(bestID);
|
||||
}
|
||||
|
||||
float TrackerDaSiamRPNImpl::getTrackingScore()
|
||||
{
|
||||
return trackState.tracking_score;
|
||||
}
|
||||
|
||||
void TrackerDaSiamRPNImpl::softmax(const Mat& src, Mat& dst)
|
||||
{
|
||||
Mat maxVal;
|
||||
cv::max(src.row(1), src.row(0), maxVal);
|
||||
|
||||
src.row(1) -= maxVal;
|
||||
src.row(0) -= maxVal;
|
||||
|
||||
exp(src, dst);
|
||||
|
||||
Mat sumVal = dst.row(0) + dst.row(1);
|
||||
dst.row(0) = dst.row(0) / sumVal;
|
||||
dst.row(1) = dst.row(1) / sumVal;
|
||||
}
|
||||
|
||||
void TrackerDaSiamRPNImpl::elementMax(Mat& src)
|
||||
{
|
||||
int* p = src.size.p;
|
||||
int index[4] = { 0, 0, 0, 0 };
|
||||
for (int n = 0; n < *p; n++)
|
||||
{
|
||||
for (int k = 0; k < *(p + 1); k++)
|
||||
{
|
||||
for (int i = 0; i < *(p + 2); i++)
|
||||
{
|
||||
for (int j = 0; j < *(p + 3); j++)
|
||||
{
|
||||
index[0] = n, index[1] = k, index[2] = i, index[3] = j;
|
||||
float& v = src.at<float>(index);
|
||||
v = fmax(v, 1.0f / v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Mat TrackerDaSiamRPNImpl::generateHanningWindow()
|
||||
{
|
||||
Mat baseWindows, HanningWindows;
|
||||
|
||||
createHanningWindow(baseWindows, Size(trackState.scoreSize, trackState.scoreSize), CV_32F);
|
||||
baseWindows = baseWindows.reshape(0, { 1, trackState.scoreSize, trackState.scoreSize });
|
||||
HanningWindows = baseWindows.clone();
|
||||
for (int i = 1; i < trackState.anchorNum; i++)
|
||||
{
|
||||
HanningWindows.push_back(baseWindows);
|
||||
}
|
||||
|
||||
return HanningWindows;
|
||||
}
|
||||
|
||||
Mat TrackerDaSiamRPNImpl::generateAnchors()
|
||||
{
|
||||
int totalStride = trackState.totalStride, scales = trackState.scale, scoreSize = trackState.scoreSize;
|
||||
std::vector<float> ratios = trackState.ratios;
|
||||
std::vector<Rect2f> baseAnchors;
|
||||
int anchorNum = int(ratios.size());
|
||||
int size = totalStride * totalStride;
|
||||
|
||||
float ori = -(float(scoreSize / 2)) * float(totalStride);
|
||||
|
||||
for (auto i = 0; i < anchorNum; i++)
|
||||
{
|
||||
int ws = int(sqrt(size / ratios[i]));
|
||||
int hs = int(ws * ratios[i]);
|
||||
|
||||
float wws = float(ws) * scales;
|
||||
float hhs = float(hs) * scales;
|
||||
Rect2f anchor = { 0, 0, wws, hhs };
|
||||
baseAnchors.push_back(anchor);
|
||||
}
|
||||
|
||||
int anchorIndex[4] = { 0, 0, 0, 0 };
|
||||
const int sizes[4] = { 4, (int)ratios.size(), scoreSize, scoreSize };
|
||||
Mat anchors(4, sizes, CV_32F);
|
||||
|
||||
for (auto i = 0; i < scoreSize; i++)
|
||||
{
|
||||
for (auto j = 0; j < scoreSize; j++)
|
||||
{
|
||||
for (auto k = 0; k < anchorNum; k++)
|
||||
{
|
||||
anchorIndex[0] = 1, anchorIndex[1] = k, anchorIndex[2] = i, anchorIndex[3] = j;
|
||||
anchors.at<float>(anchorIndex) = ori + totalStride * i;
|
||||
|
||||
anchorIndex[0] = 0;
|
||||
anchors.at<float>(anchorIndex) = ori + totalStride * j;
|
||||
|
||||
anchorIndex[0] = 2;
|
||||
anchors.at<float>(anchorIndex) = baseAnchors[k].width;
|
||||
|
||||
anchorIndex[0] = 3;
|
||||
anchors.at<float>(anchorIndex) = baseAnchors[k].height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return anchors;
|
||||
}
|
||||
|
||||
Mat TrackerDaSiamRPNImpl::getSubwindow(Mat& img, const Rect2f& targetBox, float originalSize, Scalar avgChans)
|
||||
{
|
||||
Mat zCrop, dst;
|
||||
Size imgSize = img.size();
|
||||
float c = (originalSize + 1) / 2;
|
||||
float xMin = (float)cvRound(targetBox.x - c);
|
||||
float xMax = xMin + originalSize - 1;
|
||||
float yMin = (float)cvRound(targetBox.y - c);
|
||||
float yMax = yMin + originalSize - 1;
|
||||
|
||||
int leftPad = (int)(fmax(0., -xMin));
|
||||
int topPad = (int)(fmax(0., -yMin));
|
||||
int rightPad = (int)(fmax(0., xMax - imgSize.width + 1));
|
||||
int bottomPad = (int)(fmax(0., yMax - imgSize.height + 1));
|
||||
|
||||
xMin = xMin + leftPad;
|
||||
xMax = xMax + leftPad;
|
||||
yMax = yMax + topPad;
|
||||
yMin = yMin + topPad;
|
||||
|
||||
if (topPad == 0 && bottomPad == 0 && leftPad == 0 && rightPad == 0)
|
||||
{
|
||||
img(Rect(int(xMin), int(yMin), int(xMax - xMin + 1), int(yMax - yMin + 1))).copyTo(zCrop);
|
||||
}
|
||||
else
|
||||
{
|
||||
copyMakeBorder(img, dst, topPad, bottomPad, leftPad, rightPad, BORDER_CONSTANT, avgChans);
|
||||
dst(Rect(int(xMin), int(yMin), int(xMax - xMin + 1), int(yMax - yMin + 1))).copyTo(zCrop);
|
||||
}
|
||||
|
||||
return zCrop;
|
||||
}
|
||||
|
||||
Ptr<TrackerDaSiamRPN> TrackerDaSiamRPN::create(const TrackerDaSiamRPN::Params& parameters)
|
||||
{
|
||||
return makePtr<TrackerDaSiamRPNImpl>(parameters);
|
||||
}
|
||||
|
||||
Ptr<TrackerDaSiamRPN> TrackerDaSiamRPN::create(const dnn::Net& siam_rpn, const dnn::Net& kernel_cls1, const dnn::Net& kernel_r1)
|
||||
{
|
||||
return makePtr<TrackerDaSiamRPNImpl>(siam_rpn, kernel_cls1, kernel_r1);
|
||||
}
|
||||
|
||||
#else // OPENCV_HAVE_DNN
|
||||
Ptr<TrackerDaSiamRPN> TrackerDaSiamRPN::create(const TrackerDaSiamRPN::Params& parameters)
|
||||
{
|
||||
(void)(parameters);
|
||||
CV_Error(cv::Error::StsNotImplemented, "to use DaSiamRPN, the tracking module needs to be built with opencv_dnn !");
|
||||
}
|
||||
#endif // OPENCV_HAVE_DNN
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
// 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 "detail/tracker_mil_model.hpp"
|
||||
|
||||
#include "detail/tracker_feature_haar.impl.hpp"
|
||||
|
||||
namespace cv {
|
||||
inline namespace tracking {
|
||||
namespace impl {
|
||||
|
||||
using cv::detail::tracking::internal::TrackerFeatureHAAR;
|
||||
|
||||
|
||||
class TrackerMILImpl CV_FINAL : public TrackerMIL
|
||||
{
|
||||
public:
|
||||
TrackerMILImpl(const TrackerMIL::Params& parameters);
|
||||
|
||||
virtual void init(InputArray image, const Rect& boundingBox) CV_OVERRIDE;
|
||||
virtual bool update(InputArray image, Rect& boundingBox) CV_OVERRIDE;
|
||||
|
||||
void compute_integral(const Mat& img, Mat& ii_img);
|
||||
|
||||
TrackerMIL::Params params;
|
||||
|
||||
Ptr<TrackerMILModel> model;
|
||||
Ptr<TrackerSampler> sampler;
|
||||
Ptr<TrackerFeatureSet> featureSet;
|
||||
};
|
||||
|
||||
TrackerMILImpl::TrackerMILImpl(const TrackerMIL::Params& parameters)
|
||||
: params(parameters)
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
void TrackerMILImpl::compute_integral(const Mat& img, Mat& ii_img)
|
||||
{
|
||||
Mat ii;
|
||||
std::vector<Mat> ii_imgs;
|
||||
integral(img, ii, CV_32F); // FIXIT split first
|
||||
split(ii, ii_imgs);
|
||||
ii_img = ii_imgs[0];
|
||||
}
|
||||
|
||||
void TrackerMILImpl::init(InputArray image, const Rect& boundingBox)
|
||||
{
|
||||
sampler = makePtr<TrackerSampler>();
|
||||
featureSet = makePtr<TrackerFeatureSet>();
|
||||
|
||||
Mat intImage;
|
||||
compute_integral(image.getMat(), intImage);
|
||||
TrackerSamplerCSC::Params CSCparameters;
|
||||
CSCparameters.initInRad = params.samplerInitInRadius;
|
||||
CSCparameters.searchWinSize = params.samplerSearchWinSize;
|
||||
CSCparameters.initMaxNegNum = params.samplerInitMaxNegNum;
|
||||
CSCparameters.trackInPosRad = params.samplerTrackInRadius;
|
||||
CSCparameters.trackMaxPosNum = params.samplerTrackMaxPosNum;
|
||||
CSCparameters.trackMaxNegNum = params.samplerTrackMaxNegNum;
|
||||
|
||||
Ptr<TrackerSamplerAlgorithm> CSCSampler = makePtr<TrackerSamplerCSC>(CSCparameters);
|
||||
CV_Assert(sampler->addTrackerSamplerAlgorithm(CSCSampler));
|
||||
|
||||
//or add CSC sampler with default parameters
|
||||
//sampler->addTrackerSamplerAlgorithm( "CSC" );
|
||||
|
||||
//Positive sampling
|
||||
CSCSampler.staticCast<TrackerSamplerCSC>()->setMode(TrackerSamplerCSC::MODE_INIT_POS);
|
||||
sampler->sampling(intImage, boundingBox);
|
||||
std::vector<Mat> posSamples = sampler->getSamples();
|
||||
|
||||
//Negative sampling
|
||||
CSCSampler.staticCast<TrackerSamplerCSC>()->setMode(TrackerSamplerCSC::MODE_INIT_NEG);
|
||||
sampler->sampling(intImage, boundingBox);
|
||||
std::vector<Mat> negSamples = sampler->getSamples();
|
||||
|
||||
CV_Assert(!posSamples.empty());
|
||||
CV_Assert(!negSamples.empty());
|
||||
|
||||
//compute HAAR features
|
||||
TrackerFeatureHAAR::Params HAARparameters;
|
||||
HAARparameters.numFeatures = params.featureSetNumFeatures;
|
||||
HAARparameters.rectSize = Size((int)boundingBox.width, (int)boundingBox.height);
|
||||
HAARparameters.isIntegral = true;
|
||||
Ptr<TrackerFeature> trackerFeature = makePtr<TrackerFeatureHAAR>(HAARparameters);
|
||||
featureSet->addTrackerFeature(trackerFeature);
|
||||
|
||||
featureSet->extraction(posSamples);
|
||||
const std::vector<Mat> posResponse = featureSet->getResponses();
|
||||
|
||||
featureSet->extraction(negSamples);
|
||||
const std::vector<Mat> negResponse = featureSet->getResponses();
|
||||
|
||||
model = makePtr<TrackerMILModel>(boundingBox);
|
||||
Ptr<TrackerStateEstimatorMILBoosting> stateEstimator = makePtr<TrackerStateEstimatorMILBoosting>(params.featureSetNumFeatures);
|
||||
model->setTrackerStateEstimator(stateEstimator);
|
||||
|
||||
//Run model estimation and update
|
||||
model.staticCast<TrackerMILModel>()->setMode(TrackerMILModel::MODE_POSITIVE, posSamples);
|
||||
model->modelEstimation(posResponse);
|
||||
model.staticCast<TrackerMILModel>()->setMode(TrackerMILModel::MODE_NEGATIVE, negSamples);
|
||||
model->modelEstimation(negResponse);
|
||||
model->modelUpdate();
|
||||
}
|
||||
|
||||
bool TrackerMILImpl::update(InputArray image, Rect& boundingBox)
|
||||
{
|
||||
Mat intImage;
|
||||
compute_integral(image.getMat(), intImage);
|
||||
|
||||
//get the last location [AAM] X(k-1)
|
||||
Ptr<TrackerTargetState> lastLocation = model->getLastTargetState();
|
||||
Rect lastBoundingBox((int)lastLocation->getTargetPosition().x, (int)lastLocation->getTargetPosition().y, lastLocation->getTargetWidth(),
|
||||
lastLocation->getTargetHeight());
|
||||
|
||||
//sampling new frame based on last location
|
||||
auto& samplers = sampler->getSamplers();
|
||||
CV_Assert(!samplers.empty());
|
||||
CV_Assert(samplers[0]);
|
||||
samplers[0].staticCast<TrackerSamplerCSC>()->setMode(TrackerSamplerCSC::MODE_DETECT);
|
||||
sampler->sampling(intImage, lastBoundingBox);
|
||||
std::vector<Mat> detectSamples = sampler->getSamples();
|
||||
if (detectSamples.empty())
|
||||
return false;
|
||||
|
||||
/*//TODO debug samples
|
||||
Mat f;
|
||||
image.copyTo(f);
|
||||
|
||||
for( size_t i = 0; i < detectSamples.size(); i=i+10 )
|
||||
{
|
||||
Size sz;
|
||||
Point off;
|
||||
detectSamples.at(i).locateROI(sz, off);
|
||||
rectangle(f, Rect(off.x,off.y,detectSamples.at(i).cols,detectSamples.at(i).rows), Scalar(255,0,0), 1);
|
||||
}*/
|
||||
|
||||
//extract features from new samples
|
||||
featureSet->extraction(detectSamples);
|
||||
std::vector<Mat> response = featureSet->getResponses();
|
||||
|
||||
//predict new location
|
||||
ConfidenceMap cmap;
|
||||
model.staticCast<TrackerMILModel>()->setMode(TrackerMILModel::MODE_ESTIMATON, detectSamples);
|
||||
model.staticCast<TrackerMILModel>()->responseToConfidenceMap(response, cmap);
|
||||
model->getTrackerStateEstimator().staticCast<TrackerStateEstimatorMILBoosting>()->setCurrentConfidenceMap(cmap);
|
||||
|
||||
if (!model->runStateEstimator())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Ptr<TrackerTargetState> currentState = model->getLastTargetState();
|
||||
boundingBox = Rect((int)currentState->getTargetPosition().x, (int)currentState->getTargetPosition().y, currentState->getTargetWidth(),
|
||||
currentState->getTargetHeight());
|
||||
|
||||
/*//TODO debug
|
||||
rectangle(f, lastBoundingBox, Scalar(0,255,0), 1);
|
||||
rectangle(f, boundingBox, Scalar(0,0,255), 1);
|
||||
imshow("f", f);
|
||||
//waitKey( 0 );*/
|
||||
|
||||
//sampling new frame based on new location
|
||||
//Positive sampling
|
||||
samplers[0].staticCast<TrackerSamplerCSC>()->setMode(TrackerSamplerCSC::MODE_INIT_POS);
|
||||
sampler->sampling(intImage, boundingBox);
|
||||
std::vector<Mat> posSamples = sampler->getSamples();
|
||||
|
||||
//Negative sampling
|
||||
samplers[0].staticCast<TrackerSamplerCSC>()->setMode(TrackerSamplerCSC::MODE_INIT_NEG);
|
||||
sampler->sampling(intImage, boundingBox);
|
||||
std::vector<Mat> negSamples = sampler->getSamples();
|
||||
|
||||
if (posSamples.empty() || negSamples.empty())
|
||||
return false;
|
||||
|
||||
//extract features
|
||||
featureSet->extraction(posSamples);
|
||||
std::vector<Mat> posResponse = featureSet->getResponses();
|
||||
|
||||
featureSet->extraction(negSamples);
|
||||
std::vector<Mat> negResponse = featureSet->getResponses();
|
||||
|
||||
//model estimate
|
||||
model.staticCast<TrackerMILModel>()->setMode(TrackerMILModel::MODE_POSITIVE, posSamples);
|
||||
model->modelEstimation(posResponse);
|
||||
model.staticCast<TrackerMILModel>()->setMode(TrackerMILModel::MODE_NEGATIVE, negSamples);
|
||||
model->modelEstimation(negResponse);
|
||||
|
||||
//model update
|
||||
model->modelUpdate();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}} // namespace tracking::impl
|
||||
|
||||
TrackerMIL::Params::Params()
|
||||
{
|
||||
samplerInitInRadius = 3;
|
||||
samplerSearchWinSize = 25;
|
||||
samplerInitMaxNegNum = 65;
|
||||
samplerTrackInRadius = 4;
|
||||
samplerTrackMaxPosNum = 100000;
|
||||
samplerTrackMaxNegNum = 65;
|
||||
featureSetNumFeatures = 250;
|
||||
}
|
||||
|
||||
TrackerMIL::TrackerMIL()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
TrackerMIL::~TrackerMIL()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
Ptr<TrackerMIL> TrackerMIL::create(const TrackerMIL::Params& parameters)
|
||||
{
|
||||
return makePtr<tracking::impl::TrackerMILImpl>(parameters);
|
||||
}
|
||||
|
||||
} // namespace cv
|
||||
@@ -0,0 +1,374 @@
|
||||
// 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 file is modified from the https://github.com/HonglinChu/NanoTrack/blob/master/ncnn_macos_nanotrack/nanotrack.cpp
|
||||
// Author, HongLinChu, 1628464345@qq.com
|
||||
// Adapt to OpenCV, ZihaoMu: zihaomu@outlook.com
|
||||
|
||||
// Link to original inference code: https://github.com/HonglinChu/NanoTrack
|
||||
// Link to original training repo: https://github.com/HonglinChu/SiamTrackers/tree/master/NanoTrack
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
#include "opencv2/dnn.hpp"
|
||||
#endif
|
||||
|
||||
namespace cv {
|
||||
|
||||
TrackerNano::TrackerNano()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
TrackerNano::~TrackerNano()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
TrackerNano::Params::Params()
|
||||
{
|
||||
backbone = "backbone.onnx";
|
||||
neckhead = "neckhead.onnx";
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
backend = dnn::DNN_BACKEND_DEFAULT;
|
||||
target = dnn::DNN_TARGET_CPU;
|
||||
#else
|
||||
backend = -1; // invalid value
|
||||
target = -1; // invalid value
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
static void softmax(const Mat& src, Mat& dst)
|
||||
{
|
||||
Mat maxVal;
|
||||
cv::max(src.row(1), src.row(0), maxVal);
|
||||
|
||||
src.row(1) -= maxVal;
|
||||
src.row(0) -= maxVal;
|
||||
|
||||
exp(src, dst);
|
||||
|
||||
Mat sumVal = dst.row(0) + dst.row(1);
|
||||
dst.row(0) = dst.row(0) / sumVal;
|
||||
dst.row(1) = dst.row(1) / sumVal;
|
||||
}
|
||||
|
||||
static float sizeCal(float w, float h)
|
||||
{
|
||||
float pad = (w + h) * 0.5f;
|
||||
float sz2 = (w + pad) * (h + pad);
|
||||
return sqrt(sz2);
|
||||
}
|
||||
|
||||
static Mat sizeCal(const Mat& w, const Mat& h)
|
||||
{
|
||||
Mat pad = (w + h) * 0.5;
|
||||
Mat sz2 = (w + pad).mul((h + pad));
|
||||
|
||||
cv::sqrt(sz2, sz2);
|
||||
return sz2;
|
||||
}
|
||||
|
||||
// Similar python code: r = np.maximum(r, 1. / r) # r is matrix
|
||||
static void elementReciprocalMax(Mat& srcDst)
|
||||
{
|
||||
size_t totalV = srcDst.total();
|
||||
float* ptr = srcDst.ptr<float>(0);
|
||||
for (size_t i = 0; i < totalV; i++)
|
||||
{
|
||||
float val = *(ptr + i);
|
||||
*(ptr + i) = std::max(val, 1.0f/val);
|
||||
}
|
||||
}
|
||||
|
||||
class TrackerNanoImpl : public TrackerNano
|
||||
{
|
||||
public:
|
||||
TrackerNanoImpl(const TrackerNano::Params& parameters)
|
||||
{
|
||||
dnn::EngineType engine = dnn::ENGINE_AUTO;
|
||||
if (parameters.backend != 0 || parameters.target != 0){
|
||||
engine = dnn::ENGINE_CLASSIC;
|
||||
}
|
||||
backbone = dnn::readNet(parameters.backbone, "", "", engine);
|
||||
neckhead = dnn::readNet(parameters.neckhead, "", "", engine);
|
||||
|
||||
CV_Assert(!backbone.empty());
|
||||
CV_Assert(!neckhead.empty());
|
||||
|
||||
backbone.setPreferableBackend(parameters.backend);
|
||||
backbone.setPreferableTarget(parameters.target);
|
||||
neckhead.setPreferableBackend(parameters.backend);
|
||||
neckhead.setPreferableTarget(parameters.target);
|
||||
}
|
||||
|
||||
TrackerNanoImpl(const dnn::Net& _backbone, const dnn::Net& _neckhead)
|
||||
{
|
||||
CV_Assert(!_backbone.empty());
|
||||
CV_Assert(!_neckhead.empty());
|
||||
|
||||
backbone = _backbone;
|
||||
neckhead = _neckhead;
|
||||
}
|
||||
|
||||
void init(InputArray image, const Rect& boundingBox) CV_OVERRIDE;
|
||||
bool update(InputArray image, Rect& boundingBox) CV_OVERRIDE;
|
||||
float getTrackingScore() CV_OVERRIDE;
|
||||
|
||||
// Save the target bounding box for each frame.
|
||||
std::vector<float> targetSz = {0, 0}; // H and W of bounding box
|
||||
std::vector<float> targetPos = {0, 0}; // center point of bounding box (x, y)
|
||||
float tracking_score;
|
||||
|
||||
struct trackerConfig
|
||||
{
|
||||
float windowInfluence = 0.455f;
|
||||
float lr = 0.37f;
|
||||
float contextAmount = 0.5;
|
||||
bool swapRB = true;
|
||||
int totalStride = 16;
|
||||
float penaltyK = 0.055f;
|
||||
};
|
||||
|
||||
protected:
|
||||
const int exemplarSize = 127;
|
||||
const int instanceSize = 255;
|
||||
|
||||
trackerConfig trackState;
|
||||
int scoreSize;
|
||||
Size imgSize = {0, 0};
|
||||
Mat hanningWindow;
|
||||
Mat grid2searchX, grid2searchY;
|
||||
|
||||
dnn::Net backbone, neckhead;
|
||||
Mat image;
|
||||
|
||||
void getSubwindow(Mat& dstCrop, Mat& srcImg, int originalSz, int resizeSz);
|
||||
void generateGrids();
|
||||
};
|
||||
|
||||
void TrackerNanoImpl::generateGrids()
|
||||
{
|
||||
int sz = scoreSize;
|
||||
const int sz2 = sz / 2;
|
||||
|
||||
std::vector<float> x1Vec(sz, 0);
|
||||
|
||||
for (int i = 0; i < sz; i++)
|
||||
{
|
||||
x1Vec[i] = (float)(i - sz2);
|
||||
}
|
||||
|
||||
Mat x1M(1, sz, CV_32FC1, x1Vec.data());
|
||||
|
||||
cv::repeat(x1M, sz, 1, grid2searchX);
|
||||
cv::repeat(x1M.t(), 1, sz, grid2searchY);
|
||||
|
||||
grid2searchX *= trackState.totalStride;
|
||||
grid2searchY *= trackState.totalStride;
|
||||
|
||||
grid2searchX += instanceSize/2;
|
||||
grid2searchY += instanceSize/2;
|
||||
}
|
||||
|
||||
void TrackerNanoImpl::init(InputArray image_, const Rect &boundingBox_)
|
||||
{
|
||||
scoreSize = (instanceSize - exemplarSize) / trackState.totalStride + 8;
|
||||
trackState = trackerConfig();
|
||||
image = image_.getMat().clone();
|
||||
|
||||
// convert Rect2d from left-up to center.
|
||||
targetPos[0] = float(boundingBox_.x) + float(boundingBox_.width) * 0.5f;
|
||||
targetPos[1] = float(boundingBox_.y) + float(boundingBox_.height) * 0.5f;
|
||||
|
||||
targetSz[0] = float(boundingBox_.width);
|
||||
targetSz[1] = float(boundingBox_.height);
|
||||
|
||||
imgSize = image.size();
|
||||
|
||||
// Extent the bounding box.
|
||||
float sumSz = targetSz[0] + targetSz[1];
|
||||
float wExtent = targetSz[0] + trackState.contextAmount * (sumSz);
|
||||
float hExtent = targetSz[1] + trackState.contextAmount * (sumSz);
|
||||
int sz = int(cv::sqrt(wExtent * hExtent));
|
||||
|
||||
Mat crop;
|
||||
getSubwindow(crop, image, sz, exemplarSize);
|
||||
Mat blob = dnn::blobFromImage(crop, 1.0, Size(), Scalar(), trackState.swapRB);
|
||||
|
||||
backbone.setInput(blob);
|
||||
Mat out = backbone.forward(); // Feature extraction.
|
||||
neckhead.setInput(out, "input1");
|
||||
|
||||
createHanningWindow(hanningWindow, Size(scoreSize, scoreSize), CV_32F);
|
||||
generateGrids();
|
||||
}
|
||||
|
||||
void TrackerNanoImpl::getSubwindow(Mat& dstCrop, Mat& srcImg, int originalSz, int resizeSz)
|
||||
{
|
||||
Scalar avgChans = mean(srcImg);
|
||||
Size imgSz = srcImg.size();
|
||||
int c = (originalSz + 1) / 2;
|
||||
|
||||
int context_xmin = (int)(targetPos[0]) - c;
|
||||
int context_xmax = context_xmin + originalSz - 1;
|
||||
int context_ymin = (int)(targetPos[1]) - c;
|
||||
int context_ymax = context_ymin + originalSz - 1;
|
||||
|
||||
int left_pad = std::max(0, -context_xmin);
|
||||
int top_pad = std::max(0, -context_ymin);
|
||||
int right_pad = std::max(0, context_xmax - imgSz.width + 1);
|
||||
int bottom_pad = std::max(0, context_ymax - imgSz.height + 1);
|
||||
|
||||
context_xmin += left_pad;
|
||||
context_xmax += left_pad;
|
||||
context_ymin += top_pad;
|
||||
context_ymax += top_pad;
|
||||
|
||||
Mat cropImg;
|
||||
if (left_pad == 0 && top_pad == 0 && right_pad == 0 && bottom_pad == 0)
|
||||
{
|
||||
// Crop image without padding.
|
||||
cropImg = srcImg(cv::Rect(context_xmin, context_ymin,
|
||||
context_xmax - context_xmin + 1, context_ymax - context_ymin + 1));
|
||||
}
|
||||
else // Crop image with padding, and the padding value is avgChans
|
||||
{
|
||||
cv::Mat tmpMat;
|
||||
cv::copyMakeBorder(srcImg, tmpMat, top_pad, bottom_pad, left_pad, right_pad, cv::BORDER_CONSTANT, avgChans);
|
||||
cropImg = tmpMat(cv::Rect(context_xmin, context_ymin, context_xmax - context_xmin + 1, context_ymax - context_ymin + 1));
|
||||
}
|
||||
resize(cropImg, dstCrop, Size(resizeSz, resizeSz));
|
||||
}
|
||||
|
||||
bool TrackerNanoImpl::update(InputArray image_, Rect &boundingBoxRes)
|
||||
{
|
||||
image = image_.getMat().clone();
|
||||
int targetSzSum = (int)(targetSz[0] + targetSz[1]);
|
||||
|
||||
float wc = targetSz[0] + trackState.contextAmount * targetSzSum;
|
||||
float hc = targetSz[1] + trackState.contextAmount * targetSzSum;
|
||||
float sz = cv::sqrt(wc * hc);
|
||||
float scale_z = exemplarSize / sz;
|
||||
float sx = sz * (instanceSize / exemplarSize);
|
||||
targetSz[0] *= scale_z;
|
||||
targetSz[1] *= scale_z;
|
||||
|
||||
Mat crop;
|
||||
getSubwindow(crop, image, int(sx), instanceSize);
|
||||
|
||||
Mat blob = dnn::blobFromImage(crop, 1.0, Size(), Scalar(), trackState.swapRB);
|
||||
backbone.setInput(blob);
|
||||
Mat xf = backbone.forward();
|
||||
neckhead.setInput(xf, "input2");
|
||||
std::vector<String> outputName = {"output1", "output2"};
|
||||
std::vector<Mat> outs;
|
||||
neckhead.forward(outs, outputName);
|
||||
|
||||
CV_Assert(outs.size() == 2);
|
||||
|
||||
Mat clsScore = outs[0]; // 1x2x16x16
|
||||
Mat bboxPred = outs[1]; // 1x4x16x16
|
||||
|
||||
clsScore = clsScore.reshape(0, {2, scoreSize, scoreSize});
|
||||
bboxPred = bboxPred.reshape(0, {4, scoreSize, scoreSize});
|
||||
|
||||
Mat scoreSoftmax; // 2x16x16
|
||||
softmax(clsScore, scoreSoftmax);
|
||||
|
||||
Mat score = scoreSoftmax.row(1);
|
||||
score = score.reshape(0, {scoreSize, scoreSize});
|
||||
|
||||
Mat predX1 = grid2searchX - bboxPred.row(0).reshape(0, {scoreSize, scoreSize});
|
||||
Mat predY1 = grid2searchY - bboxPred.row(1).reshape(0, {scoreSize, scoreSize});
|
||||
Mat predX2 = grid2searchX + bboxPred.row(2).reshape(0, {scoreSize, scoreSize});
|
||||
Mat predY2 = grid2searchY + bboxPred.row(3).reshape(0, {scoreSize, scoreSize});
|
||||
|
||||
// size penalty
|
||||
// scale penalty
|
||||
Mat sc = sizeCal(predX2 - predX1, predY2 - predY1)/sizeCal(targetPos[0], targetPos[1]);
|
||||
elementReciprocalMax(sc);
|
||||
|
||||
// ratio penalty
|
||||
float ratioVal = targetSz[0] / targetSz[1];
|
||||
|
||||
Mat ratioM(scoreSize, scoreSize, CV_32FC1, Scalar::all(ratioVal));
|
||||
Mat rc = ratioM / ((predX2 - predX1) / (predY2 - predY1));
|
||||
elementReciprocalMax(rc);
|
||||
|
||||
Mat penalty;
|
||||
exp(((rc.mul(sc) - 1) * trackState.penaltyK * (-1)), penalty);
|
||||
Mat pscore = penalty.mul(score);
|
||||
|
||||
// Window penalty
|
||||
pscore = pscore * (1.0 - trackState.windowInfluence) + hanningWindow * trackState.windowInfluence;
|
||||
|
||||
// get Max
|
||||
int bestID[2] = { 0, 0 };
|
||||
minMaxIdx(pscore, 0, 0, 0, bestID);
|
||||
|
||||
tracking_score = pscore.at<float>(bestID);
|
||||
|
||||
float x1Val = predX1.at<float>(bestID);
|
||||
float x2Val = predX2.at<float>(bestID);
|
||||
float y1Val = predY1.at<float>(bestID);
|
||||
float y2Val = predY2.at<float>(bestID);
|
||||
|
||||
float predXs = (x1Val + x2Val)/2;
|
||||
float predYs = (y1Val + y2Val)/2;
|
||||
float predW = (x2Val - x1Val)/scale_z;
|
||||
float predH = (y2Val - y1Val)/scale_z;
|
||||
|
||||
float diffXs = (predXs - instanceSize / 2) / scale_z;
|
||||
float diffYs = (predYs - instanceSize / 2) / scale_z;
|
||||
|
||||
targetSz[0] /= scale_z;
|
||||
targetSz[1] /= scale_z;
|
||||
|
||||
float lr = penalty.at<float>(bestID) * score.at<float>(bestID) * trackState.lr;
|
||||
|
||||
float resX = targetPos[0] + diffXs;
|
||||
float resY = targetPos[1] + diffYs;
|
||||
float resW = predW * lr + (1 - lr) * targetSz[0];
|
||||
float resH = predH * lr + (1 - lr) * targetSz[1];
|
||||
|
||||
resX = std::max(0.f, std::min((float)imgSize.width, resX));
|
||||
resY = std::max(0.f, std::min((float)imgSize.height, resY));
|
||||
resW = std::max(10.f, std::min((float)imgSize.width, resW));
|
||||
resH = std::max(10.f, std::min((float)imgSize.height, resH));
|
||||
|
||||
targetPos[0] = resX;
|
||||
targetPos[1] = resY;
|
||||
targetSz[0] = resW;
|
||||
targetSz[1] = resH;
|
||||
|
||||
// convert center to Rect.
|
||||
boundingBoxRes = { int(resX - resW/2), int(resY - resH/2), int(resW), int(resH)};
|
||||
return true;
|
||||
}
|
||||
|
||||
float TrackerNanoImpl::getTrackingScore()
|
||||
{
|
||||
return tracking_score;
|
||||
}
|
||||
|
||||
Ptr<TrackerNano> TrackerNano::create(const TrackerNano::Params& parameters)
|
||||
{
|
||||
return makePtr<TrackerNanoImpl>(parameters);
|
||||
}
|
||||
|
||||
Ptr<TrackerNano> TrackerNano::create(const dnn::Net& backbone, const dnn::Net& neckhead)
|
||||
{
|
||||
return makePtr<TrackerNanoImpl>(backbone, neckhead);
|
||||
}
|
||||
|
||||
#else // OPENCV_HAVE_DNN
|
||||
Ptr<TrackerNano> TrackerNano::create(const TrackerNano::Params& parameters)
|
||||
{
|
||||
CV_UNUSED(parameters);
|
||||
CV_Error(cv::Error::StsNotImplemented, "to use NanoTrack, the tracking module needs to be built with opencv_dnn !");
|
||||
}
|
||||
#endif // OPENCV_HAVE_DNN
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
// 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.
|
||||
|
||||
// Author, PengyuLiu, 1872918507@qq.com
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
#include "opencv2/dnn.hpp"
|
||||
#endif
|
||||
|
||||
namespace cv {
|
||||
|
||||
TrackerVit::TrackerVit()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
TrackerVit::~TrackerVit()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
TrackerVit::Params::Params()
|
||||
{
|
||||
net = "vitTracker.onnx";
|
||||
meanvalue = Scalar{0.485, 0.456, 0.406}; // normalized mean (already divided by 255)
|
||||
stdvalue = Scalar{0.229, 0.224, 0.225}; // normalized std (already divided by 255)
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
backend = dnn::DNN_BACKEND_DEFAULT;
|
||||
target = dnn::DNN_TARGET_CPU;
|
||||
#else
|
||||
backend = -1; // invalid value
|
||||
target = -1; // invalid value
|
||||
#endif
|
||||
tracking_score_threshold = 0.20f; // safe threshold to filter out black frames
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
|
||||
class TrackerVitImpl : public TrackerVit
|
||||
{
|
||||
public:
|
||||
TrackerVitImpl(const TrackerVit::Params& parameters)
|
||||
{
|
||||
dnn::EngineType engine = dnn::ENGINE_AUTO;
|
||||
if (parameters.backend != 0 || parameters.target != 0){
|
||||
engine = dnn::ENGINE_CLASSIC;
|
||||
}
|
||||
net = dnn::readNet(parameters.net, "", "", engine);
|
||||
CV_Assert(!net.empty());
|
||||
|
||||
net.setPreferableBackend(parameters.backend);
|
||||
net.setPreferableTarget(parameters.target);
|
||||
|
||||
i2bp.mean = parameters.meanvalue * 255.0;
|
||||
i2bp.scalefactor = (1.0 / parameters.stdvalue) * (1 / 255.0);
|
||||
tracking_score_threshold = parameters.tracking_score_threshold;
|
||||
}
|
||||
|
||||
TrackerVitImpl(const dnn::Net& model, Scalar meanvalue, Scalar stdvalue, float _tracking_score_threshold)
|
||||
{
|
||||
CV_Assert(!model.empty());
|
||||
|
||||
net = model;
|
||||
i2bp.mean = meanvalue * 255.0;
|
||||
i2bp.scalefactor = (1.0 / stdvalue) * (1 / 255.0);
|
||||
tracking_score_threshold = _tracking_score_threshold;
|
||||
}
|
||||
|
||||
void init(InputArray image, const Rect& boundingBox) CV_OVERRIDE;
|
||||
bool update(InputArray image, Rect& boundingBox) CV_OVERRIDE;
|
||||
float getTrackingScore() CV_OVERRIDE;
|
||||
|
||||
Rect rect_last;
|
||||
float tracking_score;
|
||||
|
||||
float tracking_score_threshold;
|
||||
dnn::Image2BlobParams i2bp;
|
||||
|
||||
|
||||
protected:
|
||||
void preprocess(const Mat& src, Mat& dst, Size size);
|
||||
|
||||
const Size searchSize{256, 256};
|
||||
const Size templateSize{128, 128};
|
||||
|
||||
Mat hanningWindow;
|
||||
|
||||
dnn::Net net;
|
||||
};
|
||||
|
||||
static int crop_image(const Mat& src, Mat& dst, Rect box, int factor)
|
||||
{
|
||||
int x = box.x, y = box.y, w = box.width, h = box.height;
|
||||
int crop_sz = cvCeil(sqrt(w * h) * factor);
|
||||
|
||||
int x1 = x + (w - crop_sz) / 2;
|
||||
int x2 = x1 + crop_sz;
|
||||
int y1 = y + (h - crop_sz) / 2;
|
||||
int y2 = y1 + crop_sz;
|
||||
|
||||
int x1_pad = std::max(0, -x1);
|
||||
int y1_pad = std::max(0, -y1);
|
||||
int x2_pad = std::max(x2 - src.size[1] + 1, 0);
|
||||
int y2_pad = std::max(y2 - src.size[0] + 1, 0);
|
||||
|
||||
Rect roi(x1 + x1_pad, y1 + y1_pad, x2 - x2_pad - x1 - x1_pad, y2 - y2_pad - y1 - y1_pad);
|
||||
Mat im_crop = src(roi);
|
||||
copyMakeBorder(im_crop, dst, y1_pad, y2_pad, x1_pad, x2_pad, BORDER_CONSTANT);
|
||||
|
||||
return crop_sz;
|
||||
}
|
||||
|
||||
void TrackerVitImpl::preprocess(const Mat& src, Mat& dst, Size size)
|
||||
{
|
||||
Mat img;
|
||||
resize(src, img, size);
|
||||
|
||||
dst = dnn::blobFromImageWithParams(img, i2bp);
|
||||
}
|
||||
|
||||
static Mat hann1d(int sz, bool centered = true) {
|
||||
Mat hanningWindow(sz, 1, CV_32FC1);
|
||||
float* data = hanningWindow.ptr<float>(0);
|
||||
|
||||
if(centered) {
|
||||
for(int i = 0; i < sz; i++) {
|
||||
float val = 0.5f * (1.f - std::cos(static_cast<float>(2 * M_PI / (sz + 1)) * (i + 1)));
|
||||
data[i] = val;
|
||||
}
|
||||
}
|
||||
else {
|
||||
int half_sz = sz / 2;
|
||||
for(int i = 0; i <= half_sz; i++) {
|
||||
float val = 0.5f * (1.f + std::cos(static_cast<float>(2 * M_PI / (sz + 2)) * i));
|
||||
data[i] = val;
|
||||
data[sz - 1 - i] = val;
|
||||
}
|
||||
}
|
||||
|
||||
return hanningWindow;
|
||||
}
|
||||
|
||||
static Mat hann2d(Size size, bool centered = true) {
|
||||
int rows = size.height;
|
||||
int cols = size.width;
|
||||
|
||||
Mat hanningWindowRows = hann1d(rows, centered);
|
||||
Mat hanningWindowCols = hann1d(cols, centered);
|
||||
|
||||
Mat hanningWindow = hanningWindowRows * hanningWindowCols.t();
|
||||
|
||||
return hanningWindow;
|
||||
}
|
||||
|
||||
static void updateLastRect(float cx, float cy, float w, float h, int crop_size, Rect &rect_last)
|
||||
{
|
||||
int x0 = rect_last.x + (rect_last.width - crop_size) / 2;
|
||||
int y0 = rect_last.y + (rect_last.height - crop_size) / 2;
|
||||
|
||||
float x1 = cx - w / 2, y1 = cy - h / 2;
|
||||
rect_last.x = cvFloor(x1 * crop_size + x0);
|
||||
rect_last.y = cvFloor(y1 * crop_size + y0);
|
||||
rect_last.width = cvFloor(w * crop_size);
|
||||
rect_last.height = cvFloor(h * crop_size);
|
||||
}
|
||||
|
||||
void TrackerVitImpl::init(InputArray image_, const Rect &boundingBox_)
|
||||
{
|
||||
Mat image = image_.getMat();
|
||||
Mat crop;
|
||||
crop_image(image, crop, boundingBox_, 2);
|
||||
Mat blob;
|
||||
preprocess(crop, blob, templateSize);
|
||||
net.setInput(blob, "template");
|
||||
Size size(16, 16);
|
||||
hanningWindow = hann2d(size, true);
|
||||
rect_last = boundingBox_;
|
||||
}
|
||||
|
||||
bool TrackerVitImpl::update(InputArray image_, Rect &boundingBoxRes)
|
||||
{
|
||||
Mat image = image_.getMat();
|
||||
Mat crop;
|
||||
int crop_size = crop_image(image, crop, rect_last, 4); // crop: [crop_size, crop_size]
|
||||
Mat blob;
|
||||
preprocess(crop, blob, searchSize);
|
||||
net.setInput(blob, "search");
|
||||
std::vector<String> outputName = {"output1", "output2", "output3"};
|
||||
std::vector<Mat> outs;
|
||||
net.forward(outs, outputName);
|
||||
CV_Assert(outs.size() == 3);
|
||||
|
||||
Mat conf_map = outs[0].reshape(0, {16, 16});
|
||||
Mat size_map = outs[1].reshape(0, {2, 16, 16});
|
||||
Mat offset_map = outs[2].reshape(0, {2, 16, 16});
|
||||
|
||||
multiply(conf_map, hanningWindow, conf_map);
|
||||
|
||||
double maxVal;
|
||||
Point maxLoc;
|
||||
minMaxLoc(conf_map, nullptr, &maxVal, nullptr, &maxLoc);
|
||||
tracking_score = static_cast<float>(maxVal);
|
||||
|
||||
if (tracking_score >= tracking_score_threshold) {
|
||||
float cx = (maxLoc.x + offset_map.at<float>(0, maxLoc.y, maxLoc.x)) / 16;
|
||||
float cy = (maxLoc.y + offset_map.at<float>(1, maxLoc.y, maxLoc.x)) / 16;
|
||||
float w = size_map.at<float>(0, maxLoc.y, maxLoc.x);
|
||||
float h = size_map.at<float>(1, maxLoc.y, maxLoc.x);
|
||||
|
||||
updateLastRect(cx, cy, w, h, crop_size, rect_last);
|
||||
boundingBoxRes = rect_last;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
float TrackerVitImpl::getTrackingScore()
|
||||
{
|
||||
return tracking_score;
|
||||
}
|
||||
|
||||
Ptr<TrackerVit> TrackerVit::create(const TrackerVit::Params& parameters)
|
||||
{
|
||||
return makePtr<TrackerVitImpl>(parameters);
|
||||
}
|
||||
|
||||
Ptr<TrackerVit> TrackerVit::create(const dnn::Net& model, Scalar meanvalue, Scalar stdvalue, float tracking_score_threshold)
|
||||
{
|
||||
return makePtr<TrackerVitImpl>(model, meanvalue, stdvalue, tracking_score_threshold);
|
||||
}
|
||||
|
||||
#else // OPENCV_HAVE_DNN
|
||||
Ptr<TrackerVit> TrackerVit::create(const TrackerVit::Params& parameters)
|
||||
{
|
||||
CV_UNUSED(parameters);
|
||||
CV_Error(Error::StsNotImplemented, "to use vittrack, the tracking module needs to be built with opencv_dnn !");
|
||||
}
|
||||
#endif // OPENCV_HAVE_DNN
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,145 @@
|
||||
#include "../test_precomp.hpp"
|
||||
#include "opencv2/ts/ocl_test.hpp"
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
|
||||
namespace opencv_test {
|
||||
namespace ocl {
|
||||
|
||||
//////////////////////////Mog2_Update///////////////////////////////////
|
||||
|
||||
namespace
|
||||
{
|
||||
IMPLEMENT_PARAM_CLASS(UseGray, bool)
|
||||
IMPLEMENT_PARAM_CLASS(DetectShadow, bool)
|
||||
IMPLEMENT_PARAM_CLASS(UseFloat, bool)
|
||||
}
|
||||
|
||||
PARAM_TEST_CASE(Mog2_Update, UseGray, DetectShadow,UseFloat)
|
||||
{
|
||||
bool useGray;
|
||||
bool detectShadow;
|
||||
bool useFloat;
|
||||
virtual void SetUp()
|
||||
{
|
||||
useGray = GET_PARAM(0);
|
||||
detectShadow = GET_PARAM(1);
|
||||
useFloat = GET_PARAM(2);
|
||||
}
|
||||
};
|
||||
|
||||
OCL_TEST_P(Mog2_Update, Accuracy)
|
||||
{
|
||||
string inputFile = string(TS::ptr()->get_data_path()) + "video/768x576.avi";
|
||||
VideoCapture cap(inputFile);
|
||||
if (!cap.isOpened())
|
||||
throw SkipTestException("Video file can not be opened");
|
||||
|
||||
Ptr<BackgroundSubtractorMOG2> mog2_cpu = createBackgroundSubtractorMOG2();
|
||||
Ptr<BackgroundSubtractorMOG2> mog2_ocl = createBackgroundSubtractorMOG2();
|
||||
|
||||
mog2_cpu->setDetectShadows(detectShadow);
|
||||
mog2_ocl->setDetectShadows(detectShadow);
|
||||
|
||||
Mat frame, foreground;
|
||||
UMat u_foreground;
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
cap >> frame;
|
||||
ASSERT_FALSE(frame.empty());
|
||||
|
||||
if (useGray)
|
||||
{
|
||||
Mat temp;
|
||||
cvtColor(frame, temp, COLOR_BGR2GRAY);
|
||||
swap(temp, frame);
|
||||
}
|
||||
|
||||
if(useFloat)
|
||||
{
|
||||
Mat temp;
|
||||
frame.convertTo(temp,CV_32F);
|
||||
swap(temp,frame);
|
||||
}
|
||||
|
||||
OCL_OFF(mog2_cpu->apply(frame, foreground));
|
||||
OCL_ON (mog2_ocl->apply(frame, u_foreground));
|
||||
|
||||
if (detectShadow)
|
||||
EXPECT_MAT_SIMILAR(foreground, u_foreground, 15e-3);
|
||||
else
|
||||
EXPECT_MAT_NEAR(foreground, u_foreground, 0);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////Mog2_getBackgroundImage///////////////////////////////////
|
||||
|
||||
PARAM_TEST_CASE(Mog2_getBackgroundImage, DetectShadow, UseFloat)
|
||||
{
|
||||
bool detectShadow;
|
||||
bool useFloat;
|
||||
virtual void SetUp()
|
||||
{
|
||||
detectShadow = GET_PARAM(0);
|
||||
useFloat = GET_PARAM(1);
|
||||
}
|
||||
};
|
||||
|
||||
OCL_TEST_P(Mog2_getBackgroundImage, Accuracy)
|
||||
{
|
||||
string inputFile = string(TS::ptr()->get_data_path()) + "video/768x576.avi";
|
||||
VideoCapture cap(inputFile);
|
||||
if (!cap.isOpened())
|
||||
throw SkipTestException("Video file can not be opened");
|
||||
|
||||
Ptr<BackgroundSubtractorMOG2> mog2_cpu = createBackgroundSubtractorMOG2();
|
||||
Ptr<BackgroundSubtractorMOG2> mog2_ocl = createBackgroundSubtractorMOG2();
|
||||
|
||||
mog2_cpu->setDetectShadows(detectShadow);
|
||||
mog2_ocl->setDetectShadows(detectShadow);
|
||||
|
||||
Mat frame, foreground;
|
||||
UMat u_foreground;
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
cap >> frame;
|
||||
ASSERT_FALSE(frame.empty());
|
||||
|
||||
if(useFloat)
|
||||
{
|
||||
Mat temp;
|
||||
frame.convertTo(temp,CV_32F);
|
||||
swap(temp,frame);
|
||||
}
|
||||
|
||||
OCL_OFF(mog2_cpu->apply(frame, foreground));
|
||||
OCL_ON (mog2_ocl->apply(frame, u_foreground));
|
||||
}
|
||||
|
||||
Mat background;
|
||||
OCL_OFF(mog2_cpu->getBackgroundImage(background));
|
||||
|
||||
UMat u_background;
|
||||
OCL_ON (mog2_ocl->getBackgroundImage(u_background));
|
||||
|
||||
EXPECT_MAT_NEAR(background, u_background, 1.0);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
OCL_INSTANTIATE_TEST_CASE_P(OCL_Video, Mog2_Update, Combine(
|
||||
Values(UseGray(true),UseGray(false)),
|
||||
Values(DetectShadow(true), DetectShadow(false)),
|
||||
Values(UseFloat(false),UseFloat(true)))
|
||||
);
|
||||
|
||||
OCL_INSTANTIATE_TEST_CASE_P(OCL_Video, Mog2_getBackgroundImage, Combine(
|
||||
Values(DetectShadow(true), DetectShadow(false)),
|
||||
Values(UseFloat(false),UseFloat(true)))
|
||||
);
|
||||
|
||||
}}// namespace opencv_test::ocl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,85 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of Intel Corporation may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "../test_precomp.hpp"
|
||||
#include "opencv2/ts/ocl_test.hpp"
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
CV_ENUM(DIS_TestPresets, DISOpticalFlow::PRESET_ULTRAFAST, DISOpticalFlow::PRESET_FAST, DISOpticalFlow::PRESET_MEDIUM)
|
||||
|
||||
typedef ocl::TSTestWithParam<DIS_TestPresets> OCL_DenseOpticalFlow_DIS;
|
||||
|
||||
OCL_TEST_P(OCL_DenseOpticalFlow_DIS, Mat)
|
||||
{
|
||||
int preset = (int)GetParam();
|
||||
Mat frame1, frame2, GT;
|
||||
|
||||
frame1 = imread(TS::ptr()->get_data_path() + "optflow/RubberWhale1.png");
|
||||
frame2 = imread(TS::ptr()->get_data_path() + "optflow/RubberWhale2.png");
|
||||
|
||||
CV_Assert(!frame1.empty() && !frame2.empty());
|
||||
|
||||
cvtColor(frame1, frame1, COLOR_BGR2GRAY);
|
||||
cvtColor(frame2, frame2, COLOR_BGR2GRAY);
|
||||
|
||||
{
|
||||
Mat flow;
|
||||
UMat ocl_flow;
|
||||
|
||||
Ptr<DenseOpticalFlow> algo = DISOpticalFlow::create(preset);
|
||||
OCL_OFF(algo->calc(frame1, frame2, flow));
|
||||
OCL_ON(algo->calc(frame1, frame2, ocl_flow));
|
||||
ASSERT_EQ(flow.rows, ocl_flow.rows);
|
||||
ASSERT_EQ(flow.cols, ocl_flow.cols);
|
||||
|
||||
EXPECT_MAT_SIMILAR(flow, ocl_flow, 6e-3);
|
||||
}
|
||||
}
|
||||
|
||||
OCL_INSTANTIATE_TEST_CASE_P(Video, OCL_DenseOpticalFlow_DIS,
|
||||
DIS_TestPresets::all());
|
||||
|
||||
}} // namespace
|
||||
|
||||
#endif // HAVE_OPENCL
|
||||
@@ -0,0 +1,120 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved.
|
||||
// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved.
|
||||
// Copyright (C) 2010-2012, Multicoreware, Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "../test_precomp.hpp"
|
||||
#include "opencv2/ts/ocl_test.hpp"
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
|
||||
namespace opencv_test {
|
||||
namespace ocl {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// FarnebackOpticalFlow
|
||||
namespace
|
||||
{
|
||||
IMPLEMENT_PARAM_CLASS(PyrScale, double)
|
||||
IMPLEMENT_PARAM_CLASS(PolyN, int)
|
||||
CV_FLAGS(FarnebackOptFlowFlags, 0, OPTFLOW_FARNEBACK_GAUSSIAN)
|
||||
IMPLEMENT_PARAM_CLASS(UseInitFlow, bool)
|
||||
}
|
||||
|
||||
PARAM_TEST_CASE(FarnebackOpticalFlow, PyrScale, PolyN, FarnebackOptFlowFlags, UseInitFlow)
|
||||
{
|
||||
int numLevels;
|
||||
int winSize;
|
||||
int numIters;
|
||||
double pyrScale;
|
||||
int polyN;
|
||||
int flags;
|
||||
bool useInitFlow;
|
||||
|
||||
virtual void SetUp()
|
||||
{
|
||||
numLevels = 5;
|
||||
winSize = 13;
|
||||
numIters = 10;
|
||||
pyrScale = GET_PARAM(0);
|
||||
polyN = GET_PARAM(1);
|
||||
flags = GET_PARAM(2);
|
||||
useInitFlow = GET_PARAM(3);
|
||||
}
|
||||
};
|
||||
|
||||
OCL_TEST_P(FarnebackOpticalFlow, Mat)
|
||||
{
|
||||
cv::Mat frame0 = readImage("optflow/RubberWhale1.png", cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame0.empty());
|
||||
|
||||
cv::Mat frame1 = readImage("optflow/RubberWhale2.png", cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame1.empty());
|
||||
|
||||
double polySigma = polyN <= 5 ? 1.1 : 1.5;
|
||||
|
||||
cv::Mat flow; cv::UMat uflow;
|
||||
if (useInitFlow)
|
||||
{
|
||||
OCL_ON(cv::calcOpticalFlowFarneback(frame0, frame1, uflow, pyrScale, numLevels, winSize, numIters, polyN, polySigma, flags));
|
||||
uflow.copyTo(flow);
|
||||
flags |= cv::OPTFLOW_USE_INITIAL_FLOW;
|
||||
}
|
||||
OCL_OFF(cv::calcOpticalFlowFarneback(frame0, frame1, flow, pyrScale, numLevels, winSize, numIters, polyN, polySigma, flags));
|
||||
OCL_ON(cv::calcOpticalFlowFarneback(frame0, frame1, uflow, pyrScale, numLevels, winSize, numIters, polyN, polySigma, flags));
|
||||
|
||||
EXPECT_MAT_SIMILAR(flow, uflow, 0.1);
|
||||
}
|
||||
|
||||
|
||||
OCL_INSTANTIATE_TEST_CASE_P(Video, FarnebackOpticalFlow,
|
||||
Combine(
|
||||
Values(PyrScale(0.3), PyrScale(0.5), PyrScale(0.8)),
|
||||
Values(PolyN(5), PolyN(7)),
|
||||
Values(FarnebackOptFlowFlags(0), FarnebackOptFlowFlags(cv::OPTFLOW_FARNEBACK_GAUSSIAN)),
|
||||
Values(UseInitFlow(false), UseInitFlow(true))
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
} } // namespace opencv_test::ocl
|
||||
|
||||
#endif // HAVE_OPENCL
|
||||
@@ -0,0 +1,164 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved.
|
||||
// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved.
|
||||
// Copyright (C) 2010-2012, Multicoreware, Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
|
||||
#include "../test_precomp.hpp"
|
||||
#include "opencv2/ts/ocl_test.hpp"
|
||||
#ifdef HAVE_OPENCV_FEATURES
|
||||
#include "opencv2/features.hpp"
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(HAVE_OPENCL) && defined(HAVE_OPENCV_FEATURES)
|
||||
|
||||
namespace opencv_test { namespace ocl {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// PyrLKOpticalFlow
|
||||
|
||||
PARAM_TEST_CASE(PyrLKOpticalFlow, int, int)
|
||||
{
|
||||
Size winSize;
|
||||
int maxLevel;
|
||||
TermCriteria criteria;
|
||||
int flags;
|
||||
double minEigThreshold;
|
||||
|
||||
virtual void SetUp()
|
||||
{
|
||||
winSize = Size(GET_PARAM(0), GET_PARAM(0));
|
||||
maxLevel = GET_PARAM(1);
|
||||
criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 30, 0.01);
|
||||
flags = 0;
|
||||
minEigThreshold = 1e-4f;
|
||||
}
|
||||
};
|
||||
|
||||
OCL_TEST_P(PyrLKOpticalFlow, Mat)
|
||||
{
|
||||
static const int npoints = 1000;
|
||||
static const float eps = 0.03f;
|
||||
static const float erreps = 0.1f;
|
||||
|
||||
cv::Mat frame0 = readImage("optflow/RubberWhale1.png", cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame0.empty());
|
||||
UMat umatFrame0; frame0.copyTo(umatFrame0);
|
||||
|
||||
cv::Mat frame1 = readImage("optflow/RubberWhale2.png", cv::IMREAD_GRAYSCALE);
|
||||
ASSERT_FALSE(frame1.empty());
|
||||
UMat umatFrame1; frame1.copyTo(umatFrame1);
|
||||
|
||||
// SKIP unstable tests
|
||||
#ifdef __linux__
|
||||
if (cvtest::skipUnstableTests && ocl::useOpenCL())
|
||||
{
|
||||
if (ocl::Device::getDefault().isIntel())
|
||||
throw cvtest::SkipTestException("Skip unstable test");
|
||||
}
|
||||
#endif
|
||||
|
||||
std::vector<cv::Point2f> pts;
|
||||
cv::goodFeaturesToTrack(frame0, pts, npoints, 0.01, 0.0);
|
||||
|
||||
std::vector<cv::Point2f> cpuNextPts;
|
||||
std::vector<unsigned char> cpuStatusCPU;
|
||||
std::vector<float> cpuErr;
|
||||
OCL_OFF(cv::calcOpticalFlowPyrLK(frame0, frame1, pts, cpuNextPts, cpuStatusCPU, cpuErr, winSize, maxLevel, criteria, flags, minEigThreshold));
|
||||
|
||||
UMat umatNextPts, umatStatus, umatErr;
|
||||
OCL_ON(cv::calcOpticalFlowPyrLK(umatFrame0, umatFrame1, pts, umatNextPts, umatStatus, umatErr, winSize, maxLevel, criteria, flags, minEigThreshold));
|
||||
std::vector<cv::Point2f> nextPts; umatNextPts.reshape(2, 1).copyTo(nextPts);
|
||||
std::vector<unsigned char> status; umatStatus.reshape(1, 1).copyTo(status);
|
||||
std::vector<float> err; umatErr.reshape(1, 1).copyTo(err);
|
||||
|
||||
ASSERT_EQ(cpuNextPts.size(), nextPts.size());
|
||||
ASSERT_EQ(cpuStatusCPU.size(), status.size());
|
||||
|
||||
size_t mistmatch = 0;
|
||||
size_t errmatch = 0;
|
||||
|
||||
for (size_t i = 0; i < nextPts.size(); ++i)
|
||||
{
|
||||
if (status[i] != cpuStatusCPU[i])
|
||||
{
|
||||
++mistmatch;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (status[i])
|
||||
{
|
||||
cv::Point2i a = nextPts[i];
|
||||
cv::Point2i b = cpuNextPts[i];
|
||||
|
||||
bool eq = std::abs(a.x - b.x) < 1 && std::abs(a.y - b.y) < 1;
|
||||
float errdiff = 0.0f;
|
||||
|
||||
if (!eq || errdiff > 1e-1)
|
||||
{
|
||||
++mistmatch;
|
||||
continue;
|
||||
}
|
||||
|
||||
eq = std::abs(cpuErr[i] - err[i]) <= (0.01 * std::max(1.0f, cpuErr[i]));
|
||||
if(!eq)
|
||||
++errmatch;
|
||||
}
|
||||
}
|
||||
|
||||
double bad_ratio = static_cast<double>(mistmatch) / (nextPts.size());
|
||||
double err_ratio = static_cast<double>(errmatch) / (nextPts.size());
|
||||
|
||||
ASSERT_LE(bad_ratio, eps);
|
||||
ASSERT_LE(err_ratio, erreps);
|
||||
}
|
||||
|
||||
OCL_INSTANTIATE_TEST_CASE_P(Video, PyrLKOpticalFlow,
|
||||
Combine(
|
||||
Values(11, 15, 21, 25),
|
||||
Values(3, 5)
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
}} // namespace
|
||||
#endif // HAVE_OPENCL
|
||||
@@ -0,0 +1,200 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of Intel Corporation may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
static string getDataDir() { return TS::ptr()->get_data_path(); }
|
||||
|
||||
static string getRubberWhaleFrame1() { return getDataDir() + "optflow/RubberWhale1.png"; }
|
||||
|
||||
static string getRubberWhaleFrame2() { return getDataDir() + "optflow/RubberWhale2.png"; }
|
||||
|
||||
static string getRubberWhaleGroundTruth() { return getDataDir() + "optflow/RubberWhale.flo"; }
|
||||
|
||||
static bool isFlowCorrect(float u) { return !cvIsNaN(u) && (fabs(u) < 1e9); }
|
||||
|
||||
static float calcRMSE(Mat flow1, Mat flow2)
|
||||
{
|
||||
float sum = 0;
|
||||
int counter = 0;
|
||||
const int rows = flow1.rows;
|
||||
const int cols = flow1.cols;
|
||||
|
||||
for (int y = 0; y < rows; ++y)
|
||||
{
|
||||
for (int x = 0; x < cols; ++x)
|
||||
{
|
||||
Vec2f flow1_at_point = flow1.at<Vec2f>(y, x);
|
||||
Vec2f flow2_at_point = flow2.at<Vec2f>(y, x);
|
||||
|
||||
float u1 = flow1_at_point[0];
|
||||
float v1 = flow1_at_point[1];
|
||||
float u2 = flow2_at_point[0];
|
||||
float v2 = flow2_at_point[1];
|
||||
|
||||
if (isFlowCorrect(u1) && isFlowCorrect(u2) && isFlowCorrect(v1) && isFlowCorrect(v2))
|
||||
{
|
||||
sum += (u1 - u2) * (u1 - u2) + (v1 - v2) * (v1 - v2);
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (float)sqrt(sum / (1e-9 + counter));
|
||||
}
|
||||
|
||||
bool readRubberWhale(Mat &dst_frame_1, Mat &dst_frame_2, Mat &dst_GT)
|
||||
{
|
||||
const string frame1_path = getRubberWhaleFrame1();
|
||||
const string frame2_path = getRubberWhaleFrame2();
|
||||
const string gt_flow_path = getRubberWhaleGroundTruth();
|
||||
|
||||
dst_frame_1 = imread(frame1_path);
|
||||
dst_frame_2 = imread(frame2_path);
|
||||
dst_GT = readOpticalFlow(gt_flow_path);
|
||||
|
||||
if (dst_frame_1.empty() || dst_frame_2.empty() || dst_GT.empty())
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST(DenseOpticalFlow_DIS, ReferenceAccuracy)
|
||||
{
|
||||
Mat frame1, frame2, GT;
|
||||
ASSERT_TRUE(readRubberWhale(frame1, frame2, GT));
|
||||
int presets[] = {DISOpticalFlow::PRESET_ULTRAFAST, DISOpticalFlow::PRESET_FAST, DISOpticalFlow::PRESET_MEDIUM};
|
||||
float target_RMSE[] = {0.86f, 0.74f, 0.49f};
|
||||
cvtColor(frame1, frame1, COLOR_BGR2GRAY);
|
||||
cvtColor(frame2, frame2, COLOR_BGR2GRAY);
|
||||
|
||||
Ptr<DenseOpticalFlow> algo;
|
||||
|
||||
// iterate over presets:
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
Mat flow;
|
||||
algo = DISOpticalFlow::create(presets[i]);
|
||||
algo->calc(frame1, frame2, flow);
|
||||
ASSERT_EQ(GT.rows, flow.rows);
|
||||
ASSERT_EQ(GT.cols, flow.cols);
|
||||
EXPECT_LE(calcRMSE(GT, flow), target_RMSE[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(DenseOpticalFlow_DIS, InvalidImgSize_CoarsestLevelLessThanZero)
|
||||
{
|
||||
cv::Ptr<cv::DISOpticalFlow> of = cv::DISOpticalFlow::create();
|
||||
const int mat_size = 10;
|
||||
|
||||
cv::Mat x(mat_size, mat_size, CV_8UC1, 42);
|
||||
cv::Mat y(mat_size, mat_size, CV_8UC1, 42);
|
||||
cv::Mat flow;
|
||||
|
||||
ASSERT_THROW(of->calc(x, y, flow), cv::Exception);
|
||||
}
|
||||
|
||||
// make sure that autoSelectPatchSizeAndScales() works properly.
|
||||
TEST(DenseOpticalFlow_DIS, InvalidImgSize_CoarsestLevelLessThanFinestLevel)
|
||||
{
|
||||
cv::Ptr<cv::DISOpticalFlow> of = cv::DISOpticalFlow::create();
|
||||
const int mat_size = 80;
|
||||
|
||||
cv::Mat x(mat_size, mat_size, CV_8UC1, 42);
|
||||
cv::Mat y(mat_size, mat_size, CV_8UC1, 42);
|
||||
cv::Mat flow;
|
||||
|
||||
of->calc(x, y, flow);
|
||||
|
||||
ASSERT_EQ(flow.rows, mat_size);
|
||||
ASSERT_EQ(flow.cols, mat_size);
|
||||
}
|
||||
|
||||
TEST(DenseOpticalFlow_VariationalRefinement, ReferenceAccuracy)
|
||||
{
|
||||
Mat frame1, frame2, GT;
|
||||
ASSERT_TRUE(readRubberWhale(frame1, frame2, GT));
|
||||
float target_RMSE = 0.86f;
|
||||
cvtColor(frame1, frame1, COLOR_BGR2GRAY);
|
||||
cvtColor(frame2, frame2, COLOR_BGR2GRAY);
|
||||
|
||||
Ptr<VariationalRefinement> var_ref;
|
||||
var_ref = VariationalRefinement::create();
|
||||
var_ref->setAlpha(20.0f);
|
||||
var_ref->setDelta(5.0f);
|
||||
var_ref->setGamma(10.0f);
|
||||
var_ref->setSorIterations(25);
|
||||
var_ref->setFixedPointIterations(25);
|
||||
Mat flow(frame1.size(), CV_32FC2);
|
||||
flow.setTo(0.0f);
|
||||
var_ref->calc(frame1, frame2, flow);
|
||||
ASSERT_EQ(GT.rows, flow.rows);
|
||||
ASSERT_EQ(GT.cols, flow.cols);
|
||||
EXPECT_LE(calcRMSE(GT, flow), target_RMSE);
|
||||
}
|
||||
|
||||
TEST(DenseOpticalFlow_DIS, ManualCoarsestScale)
|
||||
{
|
||||
Mat prev = Mat::zeros(Size(320, 240), CV_8UC1);
|
||||
Mat next = Mat::zeros(Size(320, 240), CV_8UC1);
|
||||
randu(prev, 0, 255);
|
||||
randu(next, 0, 255);
|
||||
|
||||
Ptr<DISOpticalFlow> dis = DISOpticalFlow::create(DISOpticalFlow::PRESET_FAST);
|
||||
|
||||
EXPECT_EQ(dis->getCoarsestScale(), -1);
|
||||
|
||||
Mat flow;
|
||||
dis->calc(prev, next, flow);
|
||||
EXPECT_FALSE(flow.empty());
|
||||
int manual_scale = 3;
|
||||
dis->setCoarsestScale(manual_scale);
|
||||
EXPECT_EQ(dis->getCoarsestScale(), manual_scale);
|
||||
|
||||
dis->calc(prev, next, flow);
|
||||
EXPECT_FALSE(flow.empty());
|
||||
|
||||
dis->setCoarsestScale(-1);
|
||||
EXPECT_EQ(dis->getCoarsestScale(), -1);
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,160 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of Intel Corporation may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
typedef tuple<Size> OFParams;
|
||||
typedef TestWithParam<OFParams> DenseOpticalFlow_DIS;
|
||||
typedef TestWithParam<OFParams> DenseOpticalFlow_VariationalRefinement;
|
||||
|
||||
TEST_P(DenseOpticalFlow_DIS, MultithreadReproducibility)
|
||||
{
|
||||
double MAX_DIF = 0.01;
|
||||
double MAX_MEAN_DIF = 0.001;
|
||||
int loopsCount = 2;
|
||||
RNG rng(0);
|
||||
|
||||
OFParams params = GetParam();
|
||||
Size size = get<0>(params);
|
||||
|
||||
int nThreads = cv::getNumThreads();
|
||||
if (nThreads == 1)
|
||||
throw SkipTestException("Single thread environment");
|
||||
for (int iter = 0; iter <= loopsCount; iter++)
|
||||
{
|
||||
Mat frame1(size, CV_8U);
|
||||
randu(frame1, 0, 255);
|
||||
Mat frame2(size, CV_8U);
|
||||
randu(frame2, 0, 255);
|
||||
|
||||
Ptr<DISOpticalFlow> algo = DISOpticalFlow::create();
|
||||
int psz = rng.uniform(4, 16);
|
||||
int pstr = rng.uniform(1, psz - 1);
|
||||
int grad_iter = rng.uniform(1, 64);
|
||||
int var_iter = rng.uniform(0, 10);
|
||||
bool use_mean_normalization = !!rng.uniform(0, 2);
|
||||
bool use_spatial_propagation = !!rng.uniform(0, 2);
|
||||
algo->setFinestScale(0);
|
||||
algo->setPatchSize(psz);
|
||||
algo->setPatchStride(pstr);
|
||||
algo->setGradientDescentIterations(grad_iter);
|
||||
algo->setVariationalRefinementIterations(var_iter);
|
||||
algo->setUseMeanNormalization(use_mean_normalization);
|
||||
algo->setUseSpatialPropagation(use_spatial_propagation);
|
||||
|
||||
cv::setNumThreads(nThreads);
|
||||
Mat resMultiThread;
|
||||
algo->calc(frame1, frame2, resMultiThread);
|
||||
|
||||
cv::setNumThreads(1);
|
||||
Mat resSingleThread;
|
||||
algo->calc(frame1, frame2, resSingleThread);
|
||||
|
||||
EXPECT_LE(cv::norm(resSingleThread, resMultiThread, NORM_INF), MAX_DIF);
|
||||
EXPECT_LE(cv::norm(resSingleThread, resMultiThread, NORM_L1), MAX_MEAN_DIF * frame1.total());
|
||||
|
||||
// resulting flow should be within the frame bounds:
|
||||
double min_val, max_val;
|
||||
minMaxLoc(resMultiThread, &min_val, &max_val);
|
||||
EXPECT_LE(abs(min_val), sqrt( static_cast<double>(size.height * size.height + size.width * size.width)) );
|
||||
EXPECT_LE(abs(max_val), sqrt( static_cast<double>(size.height * size.height + size.width * size.width)) );
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(FullSet, DenseOpticalFlow_DIS, Values(szODD, szQVGA));
|
||||
|
||||
TEST_P(DenseOpticalFlow_VariationalRefinement, MultithreadReproducibility)
|
||||
{
|
||||
double MAX_DIF = 0.01;
|
||||
double MAX_MEAN_DIF = 0.001;
|
||||
float input_flow_rad = 5.0;
|
||||
int loopsCount = 2;
|
||||
RNG rng(0);
|
||||
|
||||
OFParams params = GetParam();
|
||||
Size size = get<0>(params);
|
||||
|
||||
int nThreads = cv::getNumThreads();
|
||||
if (nThreads == 1)
|
||||
throw SkipTestException("Single thread environment");
|
||||
for (int iter = 0; iter <= loopsCount; iter++)
|
||||
{
|
||||
Mat frame1(size, CV_8U);
|
||||
randu(frame1, 0, 255);
|
||||
Mat frame2(size, CV_8U);
|
||||
randu(frame2, 0, 255);
|
||||
Mat flow(size, CV_32FC2);
|
||||
randu(flow, -input_flow_rad, input_flow_rad);
|
||||
|
||||
Ptr<VariationalRefinement> var = VariationalRefinement::create();
|
||||
var->setAlpha(rng.uniform(1.0f, 100.0f));
|
||||
var->setGamma(rng.uniform(0.1f, 10.0f));
|
||||
var->setDelta(rng.uniform(0.1f, 10.0f));
|
||||
var->setSorIterations(rng.uniform(1, 20));
|
||||
var->setFixedPointIterations(rng.uniform(1, 20));
|
||||
var->setOmega(rng.uniform(1.01f, 1.99f));
|
||||
|
||||
cv::setNumThreads(nThreads);
|
||||
Mat resMultiThread;
|
||||
flow.copyTo(resMultiThread);
|
||||
var->calc(frame1, frame2, resMultiThread);
|
||||
|
||||
cv::setNumThreads(1);
|
||||
Mat resSingleThread;
|
||||
flow.copyTo(resSingleThread);
|
||||
var->calc(frame1, frame2, resSingleThread);
|
||||
|
||||
EXPECT_LE(cv::norm(resSingleThread, resMultiThread, NORM_INF), MAX_DIF);
|
||||
EXPECT_LE(cv::norm(resSingleThread, resMultiThread, NORM_L1), MAX_MEAN_DIF * frame1.total());
|
||||
|
||||
// resulting flow should be within the frame bounds:
|
||||
double min_val, max_val;
|
||||
minMaxLoc(resMultiThread, &min_val, &max_val);
|
||||
EXPECT_LE(abs(min_val), sqrt( static_cast<double>(size.height * size.height + size.width * size.width)) );
|
||||
EXPECT_LE(abs(max_val), sqrt( static_cast<double>(size.height * size.height + size.width * size.width)) );
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(FullSet, DenseOpticalFlow_VariationalRefinement, Values(szODD, szQVGA));
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,157 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of Intel Corporation may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
typedef testing::TestWithParam<tuple<Size, int, int> > Video_Acc_Cn4;
|
||||
|
||||
TEST_P(Video_Acc_Cn4, accuracy)
|
||||
{
|
||||
const Size size = get<0>(GetParam());
|
||||
const int pattern = get<1>(GetParam());
|
||||
const int srcType = get<2>(GetParam());
|
||||
|
||||
RNG& rng = theRNG();
|
||||
|
||||
Mat src(size, srcType);
|
||||
Mat dst(size, CV_32FC4);
|
||||
Mat mask(size, CV_8UC1);
|
||||
|
||||
if (srcType == CV_8UC4)
|
||||
rng.fill(src, RNG::UNIFORM, Scalar::all(0), Scalar::all(256));
|
||||
else
|
||||
rng.fill(src, RNG::UNIFORM, Scalar::all(-10.0), Scalar::all(10.0));
|
||||
|
||||
rng.fill(dst, RNG::UNIFORM, Scalar::all(-1000.0), Scalar::all(1000.0));
|
||||
|
||||
for (int y = 0; y < mask.rows; ++y)
|
||||
{
|
||||
uchar* row = mask.ptr<uchar>(y);
|
||||
|
||||
for (int x = 0; x < mask.cols; ++x)
|
||||
{
|
||||
switch (pattern)
|
||||
{
|
||||
case 0:
|
||||
row[x] = 0;
|
||||
break;
|
||||
case 1:
|
||||
row[x] = 255;
|
||||
break;
|
||||
case 2:
|
||||
row[x] = ((x + y) % 2) ? 255 : 0;
|
||||
break;
|
||||
case 3:
|
||||
row[x] = ((x * 13 + y * 7) % 5) ? 255 : 0;
|
||||
break;
|
||||
default:
|
||||
row[x] = ((x * 17 + y * 11) % 3) ? 255 : 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Mat dstRef = dst.clone();
|
||||
|
||||
if (srcType == CV_32FC4)
|
||||
{
|
||||
for (int y = 0; y < src.rows; ++y)
|
||||
{
|
||||
const Vec4f* srcRow = src.ptr<Vec4f>(y);
|
||||
Vec4f* dstRefRow = dstRef.ptr<Vec4f>(y);
|
||||
const uchar* maskRow = mask.ptr<uchar>(y);
|
||||
|
||||
for (int x = 0; x < src.cols; ++x)
|
||||
{
|
||||
if (maskRow[x])
|
||||
{
|
||||
for (int c = 0; c < 4; ++c)
|
||||
dstRefRow[x][c] += srcRow[x][c];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CV_Assert(srcType == CV_8UC4);
|
||||
|
||||
for (int y = 0; y < src.rows; ++y)
|
||||
{
|
||||
const Vec4b* srcRow = src.ptr<Vec4b>(y);
|
||||
Vec4f* dstRefRow = dstRef.ptr<Vec4f>(y);
|
||||
const uchar* maskRow = mask.ptr<uchar>(y);
|
||||
|
||||
for (int x = 0; x < src.cols; ++x)
|
||||
{
|
||||
if (maskRow[x])
|
||||
{
|
||||
for (int c = 0; c < 4; ++c)
|
||||
dstRefRow[x][c] += static_cast<float>(srcRow[x][c]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cv::accumulate(src, dst, mask);
|
||||
|
||||
const double err = cv::norm(dst, dstRef, NORM_INF);
|
||||
|
||||
EXPECT_EQ(0.0, err)
|
||||
<< "size=" << size
|
||||
<< ", pattern=" << pattern
|
||||
<< ", srcType=" << srcType;
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(Accumulate,
|
||||
Video_Acc_Cn4,
|
||||
testing::Combine(
|
||||
testing::Values(Size(1, 1),
|
||||
Size(3, 5),
|
||||
Size(17, 7),
|
||||
Size(37, 19),
|
||||
Size(128, 16),
|
||||
Size(641, 37)),
|
||||
testing::Values(0, 1, 2, 3, 4),
|
||||
testing::Values(CV_32FC4, CV_8UC4)));
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,108 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
#include "opencv2/video/background_segm.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
using namespace cv;
|
||||
|
||||
///////////////////////// MOG2 //////////////////////////////
|
||||
TEST(BackgroundSubtractorMOG2, KnownForegroundMaskShadowsTrue)
|
||||
{
|
||||
Ptr<BackgroundSubtractorMOG2> mog2 = createBackgroundSubtractorMOG2(500, 16, true);
|
||||
|
||||
//Black Frame
|
||||
Mat input = Mat::zeros(480,640 , CV_8UC3);
|
||||
|
||||
//White Rectangle
|
||||
Mat knownFG = Mat::zeros(input.size(), CV_8U);
|
||||
|
||||
rectangle(knownFG, Rect(3,3,5,5), Scalar(255,255,255), -1);
|
||||
|
||||
Mat output;
|
||||
mog2->apply(input, knownFG, output);
|
||||
|
||||
for(int y = 3; y < 8; y++)
|
||||
{
|
||||
for (int x = 3; x < 8; x++){
|
||||
EXPECT_EQ(255,output.at<uchar>(y,x)) << "Expected foreground at (" << x << "," << y << ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(BackgroundSubtractorMOG2, KnownForegroundMaskShadowsFalse)
|
||||
{
|
||||
Ptr<BackgroundSubtractorMOG2> mog2 = createBackgroundSubtractorMOG2(500, 16, false);
|
||||
|
||||
//Black Frame
|
||||
Mat input = Mat::zeros(480,640 , CV_8UC3);
|
||||
|
||||
//White Rectangle
|
||||
Mat knownFG = Mat::zeros(input.size(), CV_8U);
|
||||
|
||||
rectangle(knownFG, Rect(3,3,5,5), Scalar(255,255,255), FILLED);
|
||||
|
||||
Mat output;
|
||||
mog2->apply(input, knownFG, output);
|
||||
|
||||
for(int y = 3; y < 8; y++)
|
||||
{
|
||||
for (int x = 3; x < 8; x++){
|
||||
EXPECT_EQ(255,output.at<uchar>(y,x)) << "Expected foreground at (" << x << "," << y << ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////// KNN //////////////////////////////
|
||||
|
||||
TEST(BackgroundSubtractorKNN, KnownForegroundMaskShadowsTrue)
|
||||
{
|
||||
Ptr<BackgroundSubtractorKNN> knn = createBackgroundSubtractorKNN(500, 400.0, true);
|
||||
|
||||
//Black Frame
|
||||
Mat input = Mat::zeros(480,640 , CV_8UC3);
|
||||
|
||||
//White Rectangle
|
||||
Mat knownFG = Mat::zeros(input.size(), CV_8U);
|
||||
|
||||
rectangle(knownFG, Rect(3,3,5,5), Scalar(255,255,255), FILLED);
|
||||
|
||||
Mat output;
|
||||
knn->apply(input, knownFG, output);
|
||||
|
||||
for(int y = 3; y < 8; y++)
|
||||
{
|
||||
for (int x = 3; x < 8; x++){
|
||||
EXPECT_EQ(255,output.at<uchar>(y,x)) << "Expected foreground at (" << x << "," << y << ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(BackgroundSubtractorKNN, KnownForegroundMaskShadowsFalse)
|
||||
{
|
||||
Ptr<BackgroundSubtractorKNN> knn = createBackgroundSubtractorKNN(500, 400.0, false);
|
||||
|
||||
//Black Frame
|
||||
Mat input = Mat::zeros(480,640 , CV_8UC3);
|
||||
|
||||
//White Rectangle
|
||||
Mat knownFG = Mat::zeros(input.size(), CV_8U);
|
||||
|
||||
rectangle(knownFG, Rect(3,3,5,5), Scalar(255,255,255), FILLED);
|
||||
|
||||
Mat output;
|
||||
knn->apply(input, knownFG, output);
|
||||
|
||||
for(int y = 3; y < 8; y++)
|
||||
{
|
||||
for (int x = 3; x < 8; x++){
|
||||
EXPECT_EQ(255,output.at<uchar>(y,x)) << "Expected foreground at (" << x << "," << y << ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
/* End of file. */
|
||||
@@ -0,0 +1,438 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of Intel Corporation may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
#include "opencv2/video/tracking.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
using namespace cv;
|
||||
|
||||
class CV_TrackBaseTest : public cvtest::BaseTest
|
||||
{
|
||||
public:
|
||||
CV_TrackBaseTest();
|
||||
virtual ~CV_TrackBaseTest();
|
||||
void clear();
|
||||
|
||||
protected:
|
||||
int read_params( const cv::FileStorage& fs );
|
||||
void run_func(void);
|
||||
int prepare_test_case( int test_case_idx );
|
||||
int validate_test_results( int test_case_idx );
|
||||
void generate_object();
|
||||
|
||||
int min_log_size, max_log_size;
|
||||
Mat img;
|
||||
RotatedRect box0;
|
||||
Size img_size;
|
||||
TermCriteria criteria;
|
||||
int img_type;
|
||||
};
|
||||
|
||||
|
||||
CV_TrackBaseTest::CV_TrackBaseTest()
|
||||
{
|
||||
img = 0;
|
||||
test_case_count = 100;
|
||||
min_log_size = 5;
|
||||
max_log_size = 8;
|
||||
}
|
||||
|
||||
|
||||
CV_TrackBaseTest::~CV_TrackBaseTest()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
|
||||
void CV_TrackBaseTest::clear()
|
||||
{
|
||||
img.release();
|
||||
cvtest::BaseTest::clear();
|
||||
}
|
||||
|
||||
|
||||
int CV_TrackBaseTest::read_params( const cv::FileStorage& fs )
|
||||
{
|
||||
int code = cvtest::BaseTest::read_params( fs );
|
||||
if( code < 0 )
|
||||
return code;
|
||||
|
||||
read( find_param( fs, "test_case_count" ), test_case_count, test_case_count );
|
||||
read( find_param( fs, "min_log_size" ), min_log_size, min_log_size );
|
||||
read( find_param( fs, "max_log_size" ), max_log_size, max_log_size );
|
||||
|
||||
min_log_size = cvtest::clipInt( min_log_size, 1, 10 );
|
||||
max_log_size = cvtest::clipInt( max_log_size, 1, 10 );
|
||||
if( min_log_size > max_log_size )
|
||||
{
|
||||
std::swap( min_log_size, max_log_size );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void CV_TrackBaseTest::generate_object()
|
||||
{
|
||||
int x, y;
|
||||
double cx = box0.center.x;
|
||||
double cy = box0.center.y;
|
||||
double width = box0.size.width*0.5;
|
||||
double height = box0.size.height*0.5;
|
||||
double angle = box0.angle*CV_PI/180.;
|
||||
double a = sin(angle), b = -cos(angle);
|
||||
double inv_ww = 1./(width*width), inv_hh = 1./(height*height);
|
||||
|
||||
img = Mat::zeros( img_size.height, img_size.width, img_type );
|
||||
|
||||
// use the straightforward algorithm: for every pixel check if it is inside the ellipse
|
||||
for( y = 0; y < img_size.height; y++ )
|
||||
{
|
||||
uchar* ptr = img.ptr(y);
|
||||
float* fl = (float*)ptr;
|
||||
double x_ = (y - cy)*b, y_ = (y - cy)*a;
|
||||
|
||||
for( x = 0; x < img_size.width; x++ )
|
||||
{
|
||||
double x1 = (x - cx)*a - x_;
|
||||
double y1 = (x - cx)*b + y_;
|
||||
|
||||
if( x1*x1*inv_hh + y1*y1*inv_ww <= 1. )
|
||||
{
|
||||
if( img_type == CV_8U )
|
||||
ptr[x] = (uchar)1;
|
||||
else
|
||||
fl[x] = (float)1.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int CV_TrackBaseTest::prepare_test_case( int test_case_idx )
|
||||
{
|
||||
RNG& rng = ts->get_rng();
|
||||
cvtest::BaseTest::prepare_test_case( test_case_idx );
|
||||
float m;
|
||||
|
||||
clear();
|
||||
|
||||
box0.size.width = (float)exp((cvtest::randReal(rng) * (max_log_size - min_log_size) + min_log_size)*CV_LOG2);
|
||||
box0.size.height = (float)exp((cvtest::randReal(rng) * (max_log_size - min_log_size) + min_log_size)*CV_LOG2);
|
||||
box0.angle = (float)(cvtest::randReal(rng)*180.);
|
||||
|
||||
if( box0.size.width > box0.size.height )
|
||||
{
|
||||
std::swap( box0.size.width, box0.size.height );
|
||||
}
|
||||
|
||||
m = MAX( box0.size.width, box0.size.height );
|
||||
img_size.width = cvRound(cvtest::randReal(rng)*m*0.5 + m + 1);
|
||||
img_size.height = cvRound(cvtest::randReal(rng)*m*0.5 + m + 1);
|
||||
img_type = cvtest::randInt(rng) % 2 ? CV_32F : CV_8U;
|
||||
img_type = CV_8U;
|
||||
|
||||
box0.center.x = (float)(img_size.width*0.5 + (cvtest::randReal(rng)-0.5)*(img_size.width - m));
|
||||
box0.center.y = (float)(img_size.height*0.5 + (cvtest::randReal(rng)-0.5)*(img_size.height - m));
|
||||
|
||||
criteria = TermCriteria( TermCriteria::EPS + TermCriteria::MAX_ITER, 10, 0.1 );
|
||||
|
||||
generate_object();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
void CV_TrackBaseTest::run_func(void)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
int CV_TrackBaseTest::validate_test_results( int /*test_case_idx*/ )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////// CamShift //////////////////////////////
|
||||
|
||||
class CV_CamShiftTest : public CV_TrackBaseTest
|
||||
{
|
||||
public:
|
||||
CV_CamShiftTest();
|
||||
|
||||
protected:
|
||||
void run_func(void);
|
||||
int prepare_test_case( int test_case_idx );
|
||||
int validate_test_results( int test_case_idx );
|
||||
void generate_object();
|
||||
|
||||
RotatedRect box;
|
||||
Rect init_rect;
|
||||
int area0;
|
||||
};
|
||||
|
||||
|
||||
CV_CamShiftTest::CV_CamShiftTest()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
int CV_CamShiftTest::prepare_test_case( int test_case_idx )
|
||||
{
|
||||
RNG& rng = ts->get_rng();
|
||||
double m;
|
||||
int code = CV_TrackBaseTest::prepare_test_case( test_case_idx );
|
||||
int i, area;
|
||||
|
||||
if( code <= 0 )
|
||||
return code;
|
||||
|
||||
area0 = countNonZero(img);
|
||||
|
||||
for(i = 0; i < 100; i++)
|
||||
{
|
||||
m = MAX(box0.size.width,box0.size.height)*0.8;
|
||||
init_rect.x = cvFloor(box0.center.x - m*(0.45 + cvtest::randReal(rng)*0.2));
|
||||
init_rect.y = cvFloor(box0.center.y - m*(0.45 + cvtest::randReal(rng)*0.2));
|
||||
init_rect.width = cvCeil(box0.center.x + m*(0.45 + cvtest::randReal(rng)*0.2) - init_rect.x);
|
||||
init_rect.height = cvCeil(box0.center.y + m*(0.45 + cvtest::randReal(rng)*0.2) - init_rect.y);
|
||||
|
||||
if( init_rect.x < 0 || init_rect.y < 0 ||
|
||||
init_rect.x + init_rect.width >= img_size.width ||
|
||||
init_rect.y + init_rect.height >= img_size.height )
|
||||
continue;
|
||||
|
||||
Mat temp = img(init_rect);
|
||||
area = countNonZero( temp );
|
||||
|
||||
if( area >= 0.1*area0 )
|
||||
break;
|
||||
}
|
||||
|
||||
return i < 100 ? code : 0;
|
||||
}
|
||||
|
||||
|
||||
void CV_CamShiftTest::run_func(void)
|
||||
{
|
||||
box = CamShift( img, init_rect, criteria );
|
||||
}
|
||||
|
||||
|
||||
int CV_CamShiftTest::validate_test_results( int /*test_case_idx*/ )
|
||||
{
|
||||
int code = cvtest::TS::OK;
|
||||
|
||||
double m = MAX(box0.size.width, box0.size.height);
|
||||
double diff_angle;
|
||||
|
||||
if( cvIsNaN(box.size.width) || cvIsInf(box.size.width) || box.size.width <= 0 ||
|
||||
cvIsNaN(box.size.height) || cvIsInf(box.size.height) || box.size.height <= 0 ||
|
||||
cvIsNaN(box.center.x) || cvIsInf(box.center.x) ||
|
||||
cvIsNaN(box.center.y) || cvIsInf(box.center.y) ||
|
||||
cvIsNaN(box.angle) || cvIsInf(box.angle) || box.angle < -180 || box.angle > 180 )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "Invalid RotatedRect was returned by CamShift\n" );
|
||||
code = cvtest::TS::FAIL_INVALID_OUTPUT;
|
||||
goto _exit_;
|
||||
}
|
||||
|
||||
box.angle = (float)(180 - box.angle);
|
||||
|
||||
if( fabs(box.size.width - box0.size.width) > box0.size.width*0.2 ||
|
||||
fabs(box.size.height - box0.size.height) > box0.size.height*0.3 )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "Incorrect CvBox2D size (=%.1f x %.1f, should be %.1f x %.1f)\n",
|
||||
box.size.width, box.size.height, box0.size.width, box0.size.height );
|
||||
code = cvtest::TS::FAIL_BAD_ACCURACY;
|
||||
goto _exit_;
|
||||
}
|
||||
|
||||
if( fabs(box.center.x - box0.center.x) > m*0.1 ||
|
||||
fabs(box.center.y - box0.center.y) > m*0.1 )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "Incorrect CvBox2D position (=(%.1f, %.1f), should be (%.1f, %.1f))\n",
|
||||
box.center.x, box.center.y, box0.center.x, box0.center.y );
|
||||
code = cvtest::TS::FAIL_BAD_ACCURACY;
|
||||
goto _exit_;
|
||||
}
|
||||
|
||||
if( box.angle < 0 )
|
||||
box.angle += 180;
|
||||
|
||||
diff_angle = fabs(box0.angle - box.angle);
|
||||
diff_angle = MIN( diff_angle, fabs(box0.angle - box.angle + 180));
|
||||
|
||||
if( fabs(diff_angle) > 30 && box0.size.height > box0.size.width*1.2 )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "Incorrect CvBox2D angle (=%1.f, should be %1.f)\n",
|
||||
box.angle, box0.angle );
|
||||
code = cvtest::TS::FAIL_BAD_ACCURACY;
|
||||
goto _exit_;
|
||||
}
|
||||
|
||||
_exit_:
|
||||
|
||||
if( code < 0 )
|
||||
{
|
||||
ts->set_failed_test_info( code );
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////// MeanShift //////////////////////////////
|
||||
|
||||
class CV_MeanShiftTest : public CV_TrackBaseTest
|
||||
{
|
||||
public:
|
||||
CV_MeanShiftTest();
|
||||
|
||||
protected:
|
||||
void run_func(void);
|
||||
int prepare_test_case( int test_case_idx );
|
||||
int validate_test_results( int test_case_idx );
|
||||
void generate_object();
|
||||
|
||||
Rect init_rect, rect;
|
||||
int area0, area;
|
||||
};
|
||||
|
||||
|
||||
CV_MeanShiftTest::CV_MeanShiftTest()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
int CV_MeanShiftTest::prepare_test_case( int test_case_idx )
|
||||
{
|
||||
RNG& rng = ts->get_rng();
|
||||
double m;
|
||||
int code = CV_TrackBaseTest::prepare_test_case( test_case_idx );
|
||||
int i;
|
||||
|
||||
if( code <= 0 )
|
||||
return code;
|
||||
|
||||
area0 = countNonZero(img);
|
||||
|
||||
for(i = 0; i < 100; i++)
|
||||
{
|
||||
m = (box0.size.width + box0.size.height)*0.5;
|
||||
init_rect.x = cvFloor(box0.center.x - m*(0.4 + cvtest::randReal(rng)*0.2));
|
||||
init_rect.y = cvFloor(box0.center.y - m*(0.4 + cvtest::randReal(rng)*0.2));
|
||||
init_rect.width = cvCeil(box0.center.x + m*(0.4 + cvtest::randReal(rng)*0.2) - init_rect.x);
|
||||
init_rect.height = cvCeil(box0.center.y + m*(0.4 + cvtest::randReal(rng)*0.2) - init_rect.y);
|
||||
|
||||
if( init_rect.x < 0 || init_rect.y < 0 ||
|
||||
init_rect.x + init_rect.width >= img_size.width ||
|
||||
init_rect.y + init_rect.height >= img_size.height )
|
||||
continue;
|
||||
|
||||
Mat temp = img(init_rect);
|
||||
area = countNonZero( temp );
|
||||
|
||||
if( area >= 0.5*area0 )
|
||||
break;
|
||||
}
|
||||
|
||||
return i < 100 ? code : 0;
|
||||
}
|
||||
|
||||
|
||||
void CV_MeanShiftTest::run_func(void)
|
||||
{
|
||||
rect = init_rect;
|
||||
meanShift( img, rect, criteria );
|
||||
}
|
||||
|
||||
|
||||
int CV_MeanShiftTest::validate_test_results( int /*test_case_idx*/ )
|
||||
{
|
||||
int code = cvtest::TS::OK;
|
||||
Point2f c;
|
||||
double m = MAX(box0.size.width, box0.size.height), delta;
|
||||
|
||||
c.x = (float)(rect.x + rect.width*0.5);
|
||||
c.y = (float)(rect.y + rect.height*0.5);
|
||||
|
||||
if( fabs(c.x - box0.center.x) > m*0.1 ||
|
||||
fabs(c.y - box0.center.y) > m*0.1 )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "Incorrect CvBox2D position (=(%.1f, %.1f), should be (%.1f, %.1f))\n",
|
||||
c.x, c.y, box0.center.x, box0.center.y );
|
||||
code = cvtest::TS::FAIL_BAD_ACCURACY;
|
||||
goto _exit_;
|
||||
}
|
||||
|
||||
delta = m*0.7;
|
||||
|
||||
if( rect.x < box0.center.x - delta ||
|
||||
rect.y < box0.center.y - delta ||
|
||||
rect.x + rect.width > box0.center.x + delta ||
|
||||
rect.y + rect.height > box0.center.y + delta )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG,
|
||||
"Incorrect ConnectedComp ((%d,%d,%d,%d) is not within (%.1f,%.1f,%.1f,%.1f))\n",
|
||||
rect.x, rect.y, rect.x + rect.width, rect.y + rect.height,
|
||||
box0.center.x - delta, box0.center.y - delta, box0.center.x + delta, box0.center.y + delta );
|
||||
code = cvtest::TS::FAIL_BAD_ACCURACY;
|
||||
}
|
||||
|
||||
_exit_:
|
||||
|
||||
if( code < 0 )
|
||||
{
|
||||
ts->set_failed_test_info( code );
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
TEST(Video_CAMShift, accuracy) { CV_CamShiftTest test; test.safe_run(); }
|
||||
TEST(Video_MeanShift, accuracy) { CV_MeanShiftTest test; test.safe_run(); }
|
||||
|
||||
}} // namespace
|
||||
/* End of file. */
|
||||
@@ -0,0 +1,475 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
|
||||
namespace opencv_test {
|
||||
namespace {
|
||||
|
||||
PARAM_TEST_CASE(Video_ECC, int, bool)
|
||||
{
|
||||
int motionType;
|
||||
bool usePyramids;
|
||||
virtual void SetUp()
|
||||
{
|
||||
motionType = GET_PARAM(0);
|
||||
usePyramids = GET_PARAM(1);
|
||||
}
|
||||
};
|
||||
|
||||
class CV_ECC_Test : public cvtest::BaseTest {
|
||||
public:
|
||||
CV_ECC_Test(int motionType, bool usePyramids);
|
||||
virtual ~CV_ECC_Test();
|
||||
|
||||
protected:
|
||||
int motionType;
|
||||
double MAX_RMS; // upper bound for RMS error
|
||||
|
||||
double computeRMS(const Mat& mat1, const Mat& mat2);
|
||||
bool isMapCorrect(const Mat& mat);
|
||||
|
||||
virtual bool test(const Mat img);
|
||||
bool testAllTypes(const Mat img); // run test for all supported data types (U8, U16, F32, F64)
|
||||
bool testAllChNum(const Mat img); // run test for all supported channels count (gray, RGB)
|
||||
|
||||
void run(int);
|
||||
|
||||
bool checkMap(const Mat& map, const Mat& ground);
|
||||
|
||||
int ntests; // number of tests per motion type
|
||||
int ECC_iterations; // number of iterations for ECC
|
||||
double ECC_epsilon; // we choose a negative value, so that
|
||||
// ECC_iterations are always executed
|
||||
TermCriteria criteria;
|
||||
bool usePyramids; // use version of findTransformECC with pyramids
|
||||
};
|
||||
|
||||
|
||||
CV_ECC_Test::CV_ECC_Test(int a_motionType, bool a_usePyramids) : motionType(a_motionType)
|
||||
, MAX_RMS(0.1)
|
||||
, ntests(3)
|
||||
, ECC_iterations(50)
|
||||
, ECC_epsilon(-1)
|
||||
, criteria(TermCriteria::COUNT + TermCriteria::EPS, ECC_iterations, ECC_epsilon)
|
||||
, usePyramids(a_usePyramids)
|
||||
{}
|
||||
|
||||
|
||||
CV_ECC_Test::~CV_ECC_Test() {}
|
||||
|
||||
bool CV_ECC_Test::isMapCorrect(const Mat& map) {
|
||||
bool tr = true;
|
||||
float mapVal;
|
||||
for (int i = 0; i < map.rows; i++)
|
||||
for (int j = 0; j < map.cols; j++) {
|
||||
mapVal = map.at<float>(i, j);
|
||||
tr = tr & (!cvIsNaN(mapVal) && (fabs(mapVal) < 1e9));
|
||||
}
|
||||
|
||||
return tr;
|
||||
}
|
||||
|
||||
double CV_ECC_Test::computeRMS(const Mat& mat1, const Mat& mat2) {
|
||||
CV_Assert(mat1.rows == mat2.rows);
|
||||
CV_Assert(mat1.cols == mat2.cols);
|
||||
|
||||
Mat errorMat;
|
||||
subtract(mat1, mat2, errorMat);
|
||||
|
||||
return sqrt(errorMat.dot(errorMat) / (mat1.rows * mat1.cols * mat1.channels()));
|
||||
}
|
||||
|
||||
bool CV_ECC_Test::checkMap(const Mat& map, const Mat& ground) {
|
||||
if (!isMapCorrect(map)) {
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (computeRMS(map, ground) > MAX_RMS) {
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
|
||||
ts->printf(ts->LOG, "RMS = %f", computeRMS(map, ground));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CV_ECC_Test::test(const Mat img)
|
||||
{
|
||||
cv::RNG rng = ts->get_rng();
|
||||
|
||||
int progress = 0;
|
||||
|
||||
for (int k = 0; k < ntests; k++) {
|
||||
ts->update_context(this, k, true);
|
||||
progress = update_progress(progress, k, ntests, 0);
|
||||
|
||||
Mat groundMap;
|
||||
switch(motionType)
|
||||
{
|
||||
case MOTION_TRANSLATION:
|
||||
groundMap = (Mat_<float>(2, 3) << 1, 0, (rng.uniform(10.f, 20.f)), 0, 1, (rng.uniform(10.f, 20.f)));
|
||||
break;
|
||||
case MOTION_EUCLIDEAN:
|
||||
{
|
||||
double angle = CV_PI / 30 + CV_PI * rng.uniform((double)-2.f, (double)2.f) / 180;
|
||||
groundMap = (Mat_<float>(2, 3) << cos(angle), -sin(angle), (rng.uniform(10.f, 20.f)), sin(angle),
|
||||
cos(angle), (rng.uniform(10.f, 20.f)));
|
||||
break;
|
||||
}
|
||||
case MOTION_AFFINE:
|
||||
groundMap = (Mat_<float>(2, 3) << (1 - rng.uniform(-0.05f, 0.05f)), (rng.uniform(-0.03f, 0.03f)),
|
||||
(rng.uniform(10.f, 20.f)), (rng.uniform(-0.03f, 0.03f)), (1 - rng.uniform(-0.05f, 0.05f)),
|
||||
(rng.uniform(10.f, 20.f)));
|
||||
break;
|
||||
case MOTION_HOMOGRAPHY:
|
||||
groundMap =
|
||||
(Mat_<float>(3, 3) << (1 - rng.uniform(-0.05f, 0.05f)), (rng.uniform(-0.03f, 0.03f)),
|
||||
(rng.uniform(10.f, 20.f)), (rng.uniform(-0.03f, 0.03f)), (1 - rng.uniform(-0.05f, 0.05f)),
|
||||
(rng.uniform(10.f, 20.f)), (rng.uniform(0.0001f, 0.0003f)), (rng.uniform(0.0001f, 0.0003f)), 1.f);
|
||||
break;
|
||||
default:
|
||||
CV_Error(Error::StsBadArg, "Incorrect motion type");
|
||||
break;
|
||||
}
|
||||
|
||||
Mat warpedImage;
|
||||
|
||||
Mat foundMap;
|
||||
if(motionType == MOTION_HOMOGRAPHY)
|
||||
{
|
||||
warpPerspective(img, warpedImage, groundMap, Size(200, 200), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
foundMap = Mat::eye(3, 3, CV_32F);
|
||||
}
|
||||
else
|
||||
{
|
||||
warpAffine(img, warpedImage, groundMap, Size(200, 200), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
foundMap = Mat((Mat_<float>(2, 3) << 1, 0, 0, 0, 1, 0));
|
||||
}
|
||||
|
||||
|
||||
if(usePyramids)
|
||||
{
|
||||
ECCParameters params;
|
||||
params.criteria = criteria;
|
||||
params.motionType = motionType;
|
||||
findTransformECCMultiScale(warpedImage, img, foundMap, params);
|
||||
}
|
||||
else
|
||||
findTransformECC(warpedImage, img, foundMap, motionType, criteria);
|
||||
|
||||
if (!checkMap(foundMap, groundMap))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CV_ECC_Test::testAllTypes(const Mat img) {
|
||||
auto types = {CV_8U, CV_16U, CV_32F, CV_64F};
|
||||
for (auto type : types) {
|
||||
Mat timg;
|
||||
img.convertTo(timg, type);
|
||||
if (!test(timg))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CV_ECC_Test::testAllChNum(const Mat img) {
|
||||
if(!usePyramids)
|
||||
if (!testAllTypes(img))
|
||||
return false;
|
||||
|
||||
Mat gray;
|
||||
cvtColor(img, gray, COLOR_RGB2GRAY);
|
||||
if (!testAllTypes(gray))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CV_ECC_Test::run(int) {
|
||||
Mat img = imread(string(ts->get_data_path()) + "shared/fruits.png");
|
||||
if (img.empty()) {
|
||||
ts->printf(ts->LOG, "test image can not be read");
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA);
|
||||
return;
|
||||
}
|
||||
|
||||
Mat testImg;
|
||||
resize(img, testImg, Size(216, 216), 0, 0, INTER_LINEAR_EXACT);
|
||||
|
||||
testAllChNum(testImg);
|
||||
|
||||
ts->set_failed_test_info(cvtest::TS::OK);
|
||||
}
|
||||
|
||||
TEST_P(Video_ECC, accuracy) {
|
||||
CV_ECC_Test test(motionType, usePyramids);
|
||||
test.safe_run();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(ECCfixtures, Video_ECC,
|
||||
testing::Values(testing::make_tuple(MOTION_TRANSLATION, false),
|
||||
testing::make_tuple(MOTION_TRANSLATION, true),
|
||||
testing::make_tuple(MOTION_EUCLIDEAN, false),
|
||||
testing::make_tuple(MOTION_EUCLIDEAN, true),
|
||||
testing::make_tuple(MOTION_AFFINE, false),
|
||||
testing::make_tuple(MOTION_AFFINE, true),
|
||||
testing::make_tuple(MOTION_HOMOGRAPHY, false),
|
||||
testing::make_tuple(MOTION_HOMOGRAPHY, true)));
|
||||
|
||||
class CV_ECC_Test_Mask : public CV_ECC_Test {
|
||||
public:
|
||||
CV_ECC_Test_Mask();
|
||||
|
||||
protected:
|
||||
bool test(const Mat);
|
||||
};
|
||||
|
||||
CV_ECC_Test_Mask::CV_ECC_Test_Mask():CV_ECC_Test(MOTION_TRANSLATION, false) {}
|
||||
|
||||
bool CV_ECC_Test_Mask::test(const Mat testImg) {
|
||||
cv::RNG rng = ts->get_rng();
|
||||
|
||||
int progress = 0;
|
||||
|
||||
for (int k = 0; k < ntests; k++) {
|
||||
ts->update_context(this, k, true);
|
||||
progress = update_progress(progress, k, ntests, 0);
|
||||
|
||||
Mat translationGround = (Mat_<float>(2, 3) << 1, 0, (rng.uniform(10.f, 20.f)), 0, 1, (rng.uniform(10.f, 20.f)));
|
||||
|
||||
Mat warpedImage;
|
||||
|
||||
warpAffine(testImg, warpedImage, translationGround, Size(200, 200), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
|
||||
Mat mapTranslation = (Mat_<float>(2, 3) << 1, 0, 0, 0, 1, 0);
|
||||
|
||||
Mat_<unsigned char> mask = Mat_<unsigned char>::ones(testImg.rows, testImg.cols);
|
||||
Rect region(testImg.cols * 2 / 3, testImg.rows * 2 / 3, testImg.cols / 3, testImg.rows / 3);
|
||||
|
||||
rectangle(testImg, region, Scalar::all(0), FILLED);
|
||||
rectangle(mask, region, Scalar(0), FILLED);
|
||||
|
||||
findTransformECC(warpedImage, testImg, mapTranslation, 0, criteria, mask);
|
||||
|
||||
if (!checkMap(mapTranslation, translationGround))
|
||||
return false;
|
||||
|
||||
// Test with non-default gaussian blur.
|
||||
findTransformECC(warpedImage, testImg, mapTranslation, 0, criteria, mask, 1);
|
||||
|
||||
if (!checkMap(mapTranslation, translationGround))
|
||||
return false;
|
||||
|
||||
// Test with template mask.
|
||||
Mat_<unsigned char> warpedMask = Mat_<unsigned char>::ones(warpedImage.rows, warpedImage.cols);
|
||||
for (int i=warpedImage.rows*1/3; i<warpedImage.rows*2/3; i++) {
|
||||
for (int j=warpedImage.cols*1/3; j<warpedImage.cols*2/3; j++) {
|
||||
warpedMask(i, j) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
findTransformECCWithMask(warpedImage, testImg, warpedMask, mask, mapTranslation, 0,
|
||||
TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, ECC_iterations, ECC_epsilon));
|
||||
|
||||
if (!checkMap(mapTranslation, translationGround))
|
||||
return false;
|
||||
|
||||
// Test with non-default gaussian blur.
|
||||
findTransformECCWithMask(warpedImage, testImg, warpedMask, mask, mapTranslation, 0, criteria, 1);
|
||||
|
||||
if (!checkMap(mapTranslation, translationGround))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
class CV_ECC_BigPictureTest : public CV_ECC_Test {
|
||||
public:
|
||||
CV_ECC_BigPictureTest(bool a_maskedVersion) : CV_ECC_Test(MOTION_HOMOGRAPHY, true), maskedVersion(a_maskedVersion) {}
|
||||
virtual ~CV_ECC_BigPictureTest() {}
|
||||
protected:
|
||||
void run(int);
|
||||
bool maskedVersion;
|
||||
};
|
||||
|
||||
void CV_ECC_BigPictureTest::run(int)
|
||||
{
|
||||
Mat largeGray0 = imread(string(ts->get_data_path()) + "shared/halmosh0.jpg", IMREAD_GRAYSCALE);
|
||||
Mat largeGray1;
|
||||
Mat roiMask0;
|
||||
Mat roiMask1;
|
||||
Mat expectedRes;
|
||||
bool readError = false;
|
||||
if(maskedVersion)
|
||||
{
|
||||
largeGray1 = imread(string(ts->get_data_path()) + "shared/halmosh2.jpg", IMREAD_GRAYSCALE);
|
||||
roiMask0 = imread(string(ts->get_data_path()) + "shared/halmosh0mask.png", IMREAD_GRAYSCALE);
|
||||
roiMask1 = imread(string(ts->get_data_path()) + "shared/halmosh2mask.png", IMREAD_GRAYSCALE);
|
||||
readError = largeGray0.empty() || largeGray1.empty() || roiMask0.empty() || roiMask1.empty();
|
||||
expectedRes = (Mat_<float>(3, 3) << 1.0225, 0.0606, -28.6452, -0.0475, 1.0314, 11.819, 8.21e-06, -3.65e-07, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
largeGray1 = imread(string(ts->get_data_path()) + "shared/halmosh1.jpg", IMREAD_GRAYSCALE);
|
||||
readError = largeGray0.empty() || largeGray1.empty();
|
||||
expectedRes = (Mat_<float>(3, 3) << 0.9756, -0.0319, 24.685, 0.013, 0.9808, 7.7453, -2.35e-05, -9.12e-06, 1);
|
||||
}
|
||||
|
||||
if(readError)
|
||||
{
|
||||
ts->printf(ts->LOG, "test image can not be read");
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA);
|
||||
return;
|
||||
}
|
||||
|
||||
cv::Mat found = cv::Mat::eye(3, 3, CV_32F);
|
||||
constexpr int N_ITERS = 20;
|
||||
constexpr double TERMINATION_EPS = 1e-6;
|
||||
ECCParameters params;
|
||||
params.criteria = cv::TermCriteria(cv::TermCriteria::COUNT + cv::TermCriteria::EPS, N_ITERS, TERMINATION_EPS);
|
||||
params.motionType = MOTION_HOMOGRAPHY;
|
||||
params.nlevels = 5;
|
||||
params.itersPerLevel = {5, 10, 300, 300, 1000};
|
||||
findTransformECCMultiScale(largeGray0, largeGray1, found, params, roiMask0, roiMask1);
|
||||
ASSERT_EQ(checkMap(found, expectedRes), true);
|
||||
ts->set_failed_test_info(cvtest::TS::OK);
|
||||
}
|
||||
|
||||
void testECCProperties(Mat x, float eps) {
|
||||
// The channels are independent
|
||||
Mat y = x.t();
|
||||
Mat Z = Mat::zeros(x.size(), y.type());
|
||||
Mat O = Mat::ones(x.size(), y.type());
|
||||
|
||||
EXPECT_NEAR(computeECC(x, y), 0.0, eps);
|
||||
if (x.type() != CV_8U && x.type() != CV_8U) {
|
||||
EXPECT_NEAR(computeECC(x + y, x - y), 0.0, eps);
|
||||
}
|
||||
|
||||
EXPECT_NEAR(computeECC(x, x), 1.0, eps);
|
||||
|
||||
Mat R, G, B, X, Y;
|
||||
cv::merge(std::vector<cv::Mat>({O, Z, Z}), R);
|
||||
cv::merge(std::vector<cv::Mat>({Z, O, Z}), G);
|
||||
cv::merge(std::vector<cv::Mat>({Z, Z, O}), B);
|
||||
cv::merge(std::vector<cv::Mat>({x, x, x}), X);
|
||||
cv::merge(std::vector<cv::Mat>({y, y, y}), Y);
|
||||
|
||||
// 1. The channels are orthogonal and independent
|
||||
EXPECT_NEAR(computeECC(X.mul(R), X.mul(G)), 0, eps);
|
||||
EXPECT_NEAR(computeECC(X.mul(R), X.mul(B)), 0, eps);
|
||||
EXPECT_NEAR(computeECC(X.mul(B), X.mul(G)), 0, eps);
|
||||
|
||||
EXPECT_NEAR(computeECC(X.mul(R) + Y.mul(B), X.mul(B) + Y.mul(R)), 0, eps);
|
||||
|
||||
EXPECT_NEAR(computeECC(X.mul(R) + Y.mul(G) + (X + Y).mul(B), Y.mul(R) + X.mul(G) + (X - Y).mul(B)), 0, eps);
|
||||
|
||||
// 2. Each channel contribute equally
|
||||
EXPECT_NEAR(computeECC(X.mul(R) + Y.mul(G + B), X), 1.0 / 3, eps);
|
||||
EXPECT_NEAR(computeECC(X.mul(G) + Y.mul(R + B), X), 1.0 / 3, eps);
|
||||
EXPECT_NEAR(computeECC(X.mul(B) + Y.mul(G + R), X), 1.0 / 3, eps);
|
||||
|
||||
// 3. The coefficient is invariant with respect to the offset of channels
|
||||
EXPECT_NEAR(computeECC(X - R + 2 * G + B, X), 1.0, eps);
|
||||
if (x.type() != CV_8U && x.type() != CV_8U) {
|
||||
EXPECT_NEAR(computeECC(X + R - 2 * G + B, Y), 0.0, eps);
|
||||
}
|
||||
|
||||
// The channels are independent. Check orthogonal combinations
|
||||
// full squares norm = sum of squared norms
|
||||
EXPECT_NEAR(computeECC(X, Y + X), 1.0 / sqrt(2.0), eps);
|
||||
EXPECT_NEAR(computeECC(X, 2 * Y + X), 1.0 / sqrt(5.0), eps);
|
||||
}
|
||||
|
||||
TEST(Video_ECC_Test_Compute, properties) {
|
||||
Mat xline(1, 100, CV_32F), x;
|
||||
for (int i = 0; i < xline.cols; ++i) xline.at<float>(0, i) = (float)i;
|
||||
|
||||
repeat(xline, xline.cols, 1, x);
|
||||
|
||||
Mat x_f64, x_u8, x_u16;
|
||||
x.convertTo(x_f64, CV_64F);
|
||||
x.convertTo(x_u8, CV_8U);
|
||||
x.convertTo(x_u16, CV_16U);
|
||||
|
||||
testECCProperties(x, 1e-5f);
|
||||
testECCProperties(x_f64, 1e-5f);
|
||||
testECCProperties(x_u8, 1);
|
||||
testECCProperties(x_u16, 1);
|
||||
}
|
||||
|
||||
TEST(Video_ECC_Test_Compute, accuracy) {
|
||||
Mat testImg = (Mat_<float>(3, 3) << 1, 0, 0, 1, 0, 0, 1, 0, 0);
|
||||
Mat warpedImage = (Mat_<float>(3, 3) << 0, 1, 0, 0, 1, 0, 0, 1, 0);
|
||||
Mat_<unsigned char> mask = Mat_<unsigned char>::ones(testImg.rows, testImg.cols);
|
||||
double ecc = computeECC(warpedImage, testImg, mask);
|
||||
|
||||
EXPECT_NEAR(ecc, -0.5f, 1e-5f);
|
||||
}
|
||||
|
||||
TEST(Video_ECC_Test_Compute, bug_14657) {
|
||||
/*
|
||||
* Simple test case - a 2 x 2 matrix with 10, 10, 10, 6. When the mean (36 / 4 = 9) is subtracted,
|
||||
* it results in 1, 1, 1, 0 for the unsigned int case - compare to 1, 1, 1, -3 in the signed case.
|
||||
* For this reason, when the same matrix was provided as the input and the template, we didn't get 1 as expected.
|
||||
*/
|
||||
Mat img = (Mat_<uint8_t>(2, 2) << 10, 10, 10, 6);
|
||||
EXPECT_NEAR(computeECC(img, img), 1.0f, 1e-5f);
|
||||
}
|
||||
|
||||
TEST(Video_ECC_Mask, accuracy) {
|
||||
CV_ECC_Test_Mask test;
|
||||
test.safe_run();
|
||||
}
|
||||
TEST(Video_ECC_BigMS, accuracy) {
|
||||
CV_ECC_BigPictureTest test(false);
|
||||
test.safe_run();
|
||||
}
|
||||
TEST(Video_ECC_BigMS_Mask, accuracy) {
|
||||
CV_ECC_BigPictureTest test(true);
|
||||
test.safe_run();
|
||||
}
|
||||
} // namespace
|
||||
} // namespace opencv_test
|
||||
@@ -0,0 +1,184 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
|
||||
// this is test for a deprecated function. let's ignore deprecated warnings in this file
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#elif defined(__GNUC__)
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma warning( disable : 4996)
|
||||
#endif
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
class CV_RigidTransform_Test : public cvtest::BaseTest
|
||||
{
|
||||
public:
|
||||
CV_RigidTransform_Test();
|
||||
~CV_RigidTransform_Test();
|
||||
protected:
|
||||
void run(int);
|
||||
|
||||
bool testNPoints(int);
|
||||
bool testImage();
|
||||
};
|
||||
|
||||
CV_RigidTransform_Test::CV_RigidTransform_Test()
|
||||
{
|
||||
}
|
||||
CV_RigidTransform_Test::~CV_RigidTransform_Test() {}
|
||||
|
||||
struct WrapAff2D
|
||||
{
|
||||
const double *F;
|
||||
WrapAff2D(const Mat& aff) : F(aff.ptr<double>()) {}
|
||||
Point2f operator()(const Point2f& p)
|
||||
{
|
||||
return Point2f( (float)(p.x * F[0] + p.y * F[1] + F[2]),
|
||||
(float)(p.x * F[3] + p.y * F[4] + F[5]) );
|
||||
}
|
||||
};
|
||||
|
||||
bool CV_RigidTransform_Test::testNPoints(int from)
|
||||
{
|
||||
cv::RNG rng = cv::theRNG();
|
||||
|
||||
int progress = 0;
|
||||
int k, ntests = 10000;
|
||||
|
||||
for( k = from; k < ntests; k++ )
|
||||
{
|
||||
ts->update_context( this, k, true );
|
||||
progress = update_progress(progress, k, ntests, 0);
|
||||
|
||||
Mat aff(2, 3, CV_64F);
|
||||
rng.fill(aff, RNG::UNIFORM, Scalar(-2), Scalar(2));
|
||||
|
||||
int n = (unsigned)rng % 100 + 10;
|
||||
|
||||
Mat fpts(1, n, CV_32FC2);
|
||||
Mat tpts(1, n, CV_32FC2);
|
||||
|
||||
rng.fill(fpts, RNG::UNIFORM, Scalar(0,0), Scalar(10,10));
|
||||
std::transform(fpts.ptr<Point2f>(), fpts.ptr<Point2f>() + n, tpts.ptr<Point2f>(), WrapAff2D(aff));
|
||||
|
||||
Mat noise(1, n, CV_32FC2);
|
||||
rng.fill(noise, RNG::NORMAL, Scalar::all(0), Scalar::all(0.001*(n<=7 ? 0 : n <= 30 ? 1 : 10)));
|
||||
tpts += noise;
|
||||
|
||||
Mat aff_est = estimateRigidTransform(fpts, tpts, true);
|
||||
|
||||
double thres = 0.1*cvtest::norm(aff, NORM_L2);
|
||||
double d = cvtest::norm(aff_est, aff, NORM_L2);
|
||||
if (d > thres)
|
||||
{
|
||||
double dB=0, nB=0;
|
||||
if (n <= 4)
|
||||
{
|
||||
Mat A = fpts.reshape(1, 3);
|
||||
Mat B = A - repeat(A.row(0), 3, 1), Bt = B.t();
|
||||
B = Bt*B;
|
||||
dB = cv::determinant(B);
|
||||
nB = cvtest::norm(B, NORM_L2);
|
||||
if( fabs(dB) < 0.01*nB )
|
||||
continue;
|
||||
}
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
|
||||
ts->printf( cvtest::TS::LOG, "Threshold = %f, norm of difference = %f", thres, d );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CV_RigidTransform_Test::testImage()
|
||||
{
|
||||
Mat img;
|
||||
Mat testImg = imread( string(ts->get_data_path()) + "shared/graffiti.png", 1);
|
||||
if (testImg.empty())
|
||||
{
|
||||
ts->printf( ts->LOG, "test image can not be read");
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA);
|
||||
return false;
|
||||
}
|
||||
pyrDown(testImg, img);
|
||||
|
||||
Mat aff = cv::getRotationMatrix2D(Point(img.cols/2, img.rows/2), 1, 0.99);
|
||||
aff.ptr<double>()[2]+=3;
|
||||
aff.ptr<double>()[5]+=3;
|
||||
|
||||
Mat rotated;
|
||||
warpAffine(img, rotated, aff, img.size());
|
||||
|
||||
Mat aff_est = estimateRigidTransform(img, rotated, true);
|
||||
|
||||
const double thres = 0.033;
|
||||
if (cvtest::norm(aff_est, aff, NORM_INF) > thres)
|
||||
{
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
|
||||
ts->printf( cvtest::TS::LOG, "Threshold = %f, norm of difference = %f", thres,
|
||||
cvtest::norm(aff_est, aff, NORM_INF) );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CV_RigidTransform_Test::run( int start_from )
|
||||
{
|
||||
cvtest::DefaultRngAuto dra;
|
||||
|
||||
if (!testNPoints(start_from))
|
||||
return;
|
||||
|
||||
if (!testImage())
|
||||
return;
|
||||
|
||||
ts->set_failed_test_info(cvtest::TS::OK);
|
||||
}
|
||||
|
||||
TEST(Video_RigidFlow, accuracy) { CV_RigidTransform_Test test; test.safe_run(); }
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,113 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of Intel Corporation may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
#include "opencv2/video/tracking.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
class CV_KalmanTest : public cvtest::BaseTest
|
||||
{
|
||||
public:
|
||||
CV_KalmanTest();
|
||||
protected:
|
||||
void run(int);
|
||||
};
|
||||
|
||||
|
||||
CV_KalmanTest::CV_KalmanTest()
|
||||
{
|
||||
}
|
||||
|
||||
void CV_KalmanTest::run( int )
|
||||
{
|
||||
int code = cvtest::TS::OK;
|
||||
const int Dim = 7;
|
||||
const int Steps = 100;
|
||||
const double max_init = 1;
|
||||
const double max_noise = 0.1;
|
||||
|
||||
const double EPSILON = 1.000;
|
||||
RNG& rng = ts->get_rng();
|
||||
int i, j;
|
||||
|
||||
cv::Mat Sample(Dim,1,CV_32F);
|
||||
cv::Mat Temp(Dim,1,CV_32F);
|
||||
|
||||
cv::KalmanFilter Kalm(Dim, Dim);
|
||||
Kalm.transitionMatrix = cv::Mat::eye(Dim, Dim, CV_32F);
|
||||
Kalm.measurementMatrix = cv::Mat::eye(Dim, Dim, CV_32F);
|
||||
Kalm.processNoiseCov = cv::Mat::eye(Dim, Dim, CV_32F);
|
||||
Kalm.errorCovPre = cv::Mat::eye(Dim, Dim, CV_32F);
|
||||
Kalm.errorCovPost = cv::Mat::eye(Dim, Dim, CV_32F);
|
||||
Kalm.measurementNoiseCov = cv::Mat::zeros(Dim, Dim, CV_32F);
|
||||
Kalm.statePre = cv::Mat::zeros(Dim, 1, CV_32F);
|
||||
Kalm.statePost = cv::Mat::zeros(Dim, 1, CV_32F);
|
||||
cvtest::randUni(rng, Sample, Scalar::all(-max_init), Scalar::all(max_init));
|
||||
Kalm.correct(Sample);
|
||||
for(i = 0; i<Steps; i++)
|
||||
{
|
||||
Kalm.predict();
|
||||
const Mat& Dyn = Kalm.transitionMatrix;
|
||||
for(j = 0; j<Dim; j++)
|
||||
{
|
||||
float t = 0;
|
||||
for(int k=0; k<Dim; k++)
|
||||
{
|
||||
t += Dyn.at<float>(j,k)*Sample.at<float>(k);
|
||||
}
|
||||
Temp.at<float>(j) = (float)(t+(cvtest::randReal(rng)*2-1)*max_noise);
|
||||
}
|
||||
Temp.copyTo(Sample);
|
||||
Kalm.correct(Temp);
|
||||
}
|
||||
|
||||
Mat _state_post = Kalm.statePost;
|
||||
code = cvtest::cmpEps2( ts, Sample, _state_post, EPSILON, false, "The final estimated state" );
|
||||
|
||||
if( code < 0 )
|
||||
ts->set_failed_test_info( code );
|
||||
}
|
||||
|
||||
TEST(Video_Kalman, accuracy) { CV_KalmanTest test; test.safe_run(); }
|
||||
|
||||
}} // namespace
|
||||
/* End of file. */
|
||||
@@ -0,0 +1,17 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
#include "test_precomp.hpp"
|
||||
|
||||
#if defined(HAVE_HPX)
|
||||
#include <hpx/hpx_main.hpp>
|
||||
#endif
|
||||
|
||||
static
|
||||
void initTests()
|
||||
{
|
||||
cvtest::addDataSearchEnv("OPENCV_DNN_TEST_DATA_PATH");
|
||||
cvtest::addDataSearchSubDirectory(""); // override "cv" prefix below to access without "../dnn" hacks
|
||||
}
|
||||
|
||||
CV_TEST_MAIN("cv", initTests())
|
||||
@@ -0,0 +1,252 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of Intel Corporation may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
/* ///////////////////// pyrlk_test ///////////////////////// */
|
||||
|
||||
class CV_OptFlowPyrLKTest : public cvtest::BaseTest
|
||||
{
|
||||
public:
|
||||
CV_OptFlowPyrLKTest();
|
||||
protected:
|
||||
void run(int);
|
||||
};
|
||||
|
||||
|
||||
CV_OptFlowPyrLKTest::CV_OptFlowPyrLKTest() {}
|
||||
|
||||
void CV_OptFlowPyrLKTest::run( int )
|
||||
{
|
||||
int code = cvtest::TS::OK;
|
||||
|
||||
const double success_error_level = 0.3;
|
||||
const int bad_points_max = 8;
|
||||
|
||||
/* test parameters */
|
||||
double max_err = 0.;
|
||||
int pt_exceed = 0;
|
||||
int merr_i = 0, merr_nan = 0;
|
||||
char filename[1000];
|
||||
|
||||
cv::Point2f *v = 0, *v2 = 0;
|
||||
cv::Mat _u, _v, _v2;
|
||||
|
||||
cv::Mat imgI, imgJ;
|
||||
|
||||
int n = 0, i = 0;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
snprintf( filename, sizeof(filename), "%soptflow/%s", ts->get_data_path().c_str(), "lk_prev.dat" );
|
||||
|
||||
{
|
||||
FileStorage fs(filename, FileStorage::READ);
|
||||
fs["points"] >> _u;
|
||||
if( _u.empty() )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "could not read %s\n", filename );
|
||||
code = cvtest::TS::FAIL_MISSING_TEST_DATA;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf( filename, sizeof(filename), "%soptflow/%s", ts->get_data_path().c_str(), "lk_next.dat" );
|
||||
|
||||
{
|
||||
FileStorage fs(filename, FileStorage::READ);
|
||||
fs["points"] >> _v;
|
||||
if( _v.empty() )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "could not read %s\n", filename );
|
||||
code = cvtest::TS::FAIL_MISSING_TEST_DATA;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( _u.cols != 2 || _u.type() != CV_32F ||
|
||||
_v.cols != 2 || _v.type() != CV_32F ||
|
||||
_v.rows != _u.rows )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "the loaded matrices of points are not valid\n" );
|
||||
code = cvtest::TS::FAIL_MISSING_TEST_DATA;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
/* read first image */
|
||||
snprintf( filename, sizeof(filename), "%soptflow/%s", ts->get_data_path().c_str(), "rock_1.bmp" );
|
||||
imgI = cv::imread( filename, cv::IMREAD_UNCHANGED );
|
||||
|
||||
if( imgI.empty() )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "could not read %s\n", filename );
|
||||
code = cvtest::TS::FAIL_MISSING_TEST_DATA;
|
||||
break;
|
||||
}
|
||||
|
||||
/* read second image */
|
||||
snprintf( filename, sizeof(filename), "%soptflow/%s", ts->get_data_path().c_str(), "rock_2.bmp" );
|
||||
imgJ = cv::imread( filename, cv::IMREAD_UNCHANGED );
|
||||
|
||||
if( imgJ.empty() )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "could not read %s\n", filename );
|
||||
code = cvtest::TS::FAIL_MISSING_TEST_DATA;
|
||||
break;
|
||||
}
|
||||
|
||||
n = _u.rows;
|
||||
std::vector<uchar> status(n, (uchar)0);
|
||||
|
||||
/* calculate flow */
|
||||
calcOpticalFlowPyrLK(imgI, imgJ, _u, _v2, status, cv::noArray(), Size( 41, 41 ), 4,
|
||||
TermCriteria( TermCriteria::MAX_ITER + TermCriteria::EPS, 30, 0.01f ), 0 );
|
||||
|
||||
v = (cv::Point2f*)_v.ptr();
|
||||
v2 = (cv::Point2f*)_v2.ptr();
|
||||
|
||||
/* compare results */
|
||||
for( i = 0; i < n; i++ )
|
||||
{
|
||||
if( status[i] != 0 )
|
||||
{
|
||||
double err;
|
||||
if( cvIsNaN(v[i].x) || cvIsNaN(v[i].y) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if( cvIsNaN(v2[i].x) || cvIsNaN(v2[i].y) )
|
||||
{
|
||||
merr_nan++;
|
||||
continue;
|
||||
}
|
||||
|
||||
err = fabs(v2[i].x - v[i].x) + fabs(v2[i].y - v[i].y);
|
||||
if( err > max_err )
|
||||
{
|
||||
max_err = err;
|
||||
merr_i = i;
|
||||
}
|
||||
|
||||
pt_exceed += err > success_error_level;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !cvIsNaN( v[i].x ))
|
||||
{
|
||||
merr_i = i;
|
||||
ts->printf( cvtest::TS::LOG, "The algorithm lost the point #%d\n", i );
|
||||
code = cvtest::TS::FAIL_BAD_ACCURACY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( i < n )
|
||||
break;
|
||||
|
||||
if( pt_exceed > bad_points_max )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG,
|
||||
"The number of poorly tracked points is too big (>=%d)\n", pt_exceed );
|
||||
code = cvtest::TS::FAIL_BAD_ACCURACY;
|
||||
break;
|
||||
}
|
||||
|
||||
if( max_err > 1 )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "Maximum tracking error is too big (=%g) at %d\n", max_err, merr_i );
|
||||
code = cvtest::TS::FAIL_BAD_ACCURACY;
|
||||
break;
|
||||
}
|
||||
|
||||
if( merr_nan > 0 )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "NAN tracking result with status != 0 (%d times)\n", merr_nan );
|
||||
code = cvtest::TS::FAIL_BAD_ACCURACY;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if( code < 0 )
|
||||
ts->set_failed_test_info( code );
|
||||
}
|
||||
|
||||
|
||||
TEST(Video_OpticalFlowPyrLK, accuracy) { CV_OptFlowPyrLKTest test; test.safe_run(); }
|
||||
|
||||
TEST(Video_OpticalFlowPyrLK, submat)
|
||||
{
|
||||
// see bug #2075
|
||||
std::string path = cvtest::TS::ptr()->get_data_path() + "../cv/shared/lena.png";
|
||||
|
||||
cv::Mat lenaImg = cv::imread(path);
|
||||
ASSERT_FALSE(lenaImg.empty());
|
||||
|
||||
cv::Mat wholeImage;
|
||||
cv::resize(lenaImg, wholeImage, cv::Size(1024, 1024), 0, 0, cv::INTER_LINEAR_EXACT);
|
||||
|
||||
cv::Mat img1 = wholeImage(cv::Rect(0, 0, 640, 360)).clone();
|
||||
cv::Mat img2 = wholeImage(cv::Rect(40, 60, 640, 360));
|
||||
|
||||
std::vector<uchar> status;
|
||||
std::vector<float> error;
|
||||
std::vector<cv::Point2f> prev;
|
||||
std::vector<cv::Point2f> next;
|
||||
|
||||
cv::RNG rng(123123);
|
||||
|
||||
for(int i = 0; i < 50; ++i)
|
||||
{
|
||||
int x = rng.uniform(0, 640);
|
||||
int y = rng.uniform(0, 360);
|
||||
|
||||
prev.push_back(cv::Point2f((float)x, (float)y));
|
||||
}
|
||||
|
||||
ASSERT_NO_THROW(cv::calcOpticalFlowPyrLK(img1, img2, prev, next, status, error));
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,17 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
#ifndef __OPENCV_TEST_PRECOMP_HPP__
|
||||
#define __OPENCV_TEST_PRECOMP_HPP__
|
||||
|
||||
#include "opencv2/ts.hpp"
|
||||
#include "opencv2/video.hpp"
|
||||
#include "opencv2/geometry.hpp"
|
||||
#include <opencv2/ts/ts_perf.hpp>
|
||||
#include "opencv2/core/utils/configuration.private.hpp"
|
||||
|
||||
namespace opencv_test {
|
||||
using namespace perf;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,164 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
|
||||
//#define DEBUG_TEST
|
||||
#ifdef DEBUG_TEST
|
||||
#include <opencv2/highgui.hpp>
|
||||
#endif
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
//using namespace cv::tracking;
|
||||
|
||||
#define TESTSET_NAMES testing::Values("david", "dudek", "faceocc2")
|
||||
|
||||
const string TRACKING_DIR = "tracking";
|
||||
const string FOLDER_IMG = "data";
|
||||
const string FOLDER_OMIT_INIT = "initOmit";
|
||||
|
||||
#include "test_trackers.impl.hpp"
|
||||
|
||||
//[TESTDATA]
|
||||
PARAM_TEST_CASE(DistanceAndOverlap, string, int)
|
||||
{
|
||||
string dataset;
|
||||
int numFramesLimit;
|
||||
virtual void SetUp()
|
||||
{
|
||||
dataset = GET_PARAM(0);
|
||||
numFramesLimit = GET_PARAM(1);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(DistanceAndOverlap, MIL)
|
||||
{
|
||||
TrackerTest<Tracker, Rect> test(TrackerMIL::create(), dataset, 30, .65f, NoTransform);
|
||||
test.run(numFramesLimit);
|
||||
}
|
||||
|
||||
TEST_P(DistanceAndOverlap, Shifted_Data_MIL)
|
||||
{
|
||||
TrackerTest<Tracker, Rect> test(TrackerMIL::create(), dataset, 30, .6f, CenterShiftLeft);
|
||||
test.run(numFramesLimit);
|
||||
}
|
||||
|
||||
/***************************************************************************************/
|
||||
//Tests with scaled initial window
|
||||
|
||||
TEST_P(DistanceAndOverlap, Scaled_Data_MIL)
|
||||
{
|
||||
TrackerTest<Tracker, Rect> test(TrackerMIL::create(), dataset, 30, .7f, Scale_1_1);
|
||||
test.run(numFramesLimit);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(Tracking, DistanceAndOverlap,
|
||||
testing::Combine(
|
||||
TESTSET_NAMES,
|
||||
testing::Values(0)
|
||||
)
|
||||
);
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(Tracking5Frames, DistanceAndOverlap,
|
||||
testing::Combine(
|
||||
TESTSET_NAMES,
|
||||
testing::Values(5)
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
static bool checkIOU(const Rect& r0, const Rect& r1, double threshold)
|
||||
{
|
||||
int interArea = (r0 & r1).area();
|
||||
double iouVal = (interArea * 1.0 )/ (r0.area() + r1.area() - interArea);;
|
||||
|
||||
if (iouVal > threshold)
|
||||
return true;
|
||||
else
|
||||
{
|
||||
std::cout <<"Unmatched IOU: expect IOU val ("<<iouVal <<") > the IOU threadhold ("<<threshold<<")! Box 0 is "
|
||||
<< r0 <<", and Box 1 is "<<r1<< std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void checkTrackingAccuracy(cv::Ptr<Tracker>& tracker, double iouThreshold = 0.7)
|
||||
{
|
||||
// Template image
|
||||
Mat img0 = imread(findDataFile("tracking/bag/00000001.jpg"), 1);
|
||||
|
||||
// Tracking image sequence.
|
||||
std::vector<Mat> imgs;
|
||||
imgs.push_back(imread(findDataFile("tracking/bag/00000002.jpg"), 1));
|
||||
imgs.push_back(imread(findDataFile("tracking/bag/00000003.jpg"), 1));
|
||||
imgs.push_back(imread(findDataFile("tracking/bag/00000004.jpg"), 1));
|
||||
imgs.push_back(imread(findDataFile("tracking/bag/00000005.jpg"), 1));
|
||||
imgs.push_back(imread(findDataFile("tracking/bag/00000006.jpg"), 1));
|
||||
|
||||
cv::Rect roi(325, 164, 100, 100);
|
||||
std::vector<Rect> targetRois;
|
||||
targetRois.push_back(cv::Rect(278, 133, 99, 104));
|
||||
targetRois.push_back(cv::Rect(293, 88, 93, 110));
|
||||
targetRois.push_back(cv::Rect(287, 76, 89, 116));
|
||||
targetRois.push_back(cv::Rect(297, 74, 82, 122));
|
||||
targetRois.push_back(cv::Rect(311, 83, 78, 125));
|
||||
|
||||
tracker->init(img0, roi);
|
||||
CV_Assert(targetRois.size() == imgs.size());
|
||||
|
||||
for (int i = 0; i < (int)imgs.size(); i++)
|
||||
{
|
||||
bool res = tracker->update(imgs[i], roi);
|
||||
ASSERT_TRUE(res);
|
||||
ASSERT_TRUE(checkIOU(roi, targetRois[i], iouThreshold)) << cv::format("Fail at img %d.",i);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(DaSiamRPN, accuracy)
|
||||
{
|
||||
std::string model = cvtest::findDataFile("dnn/onnx/models/dasiamrpn_model.onnx", false);
|
||||
std::string kernel_r1 = cvtest::findDataFile("dnn/onnx/models/dasiamrpn_kernel_r1.onnx", false);
|
||||
std::string kernel_cls1 = cvtest::findDataFile("dnn/onnx/models/dasiamrpn_kernel_cls1.onnx", false);
|
||||
cv::TrackerDaSiamRPN::Params params;
|
||||
params.model = model;
|
||||
params.kernel_r1 = kernel_r1;
|
||||
params.kernel_cls1 = kernel_cls1;
|
||||
cv::Ptr<Tracker> tracker = TrackerDaSiamRPN::create(params);
|
||||
checkTrackingAccuracy(tracker, 0.7);
|
||||
}
|
||||
|
||||
TEST(NanoTrack, accuracy_NanoTrack_V1)
|
||||
{
|
||||
std::string backbonePath = cvtest::findDataFile("dnn/onnx/models/nanotrack_backbone_sim.onnx", false);
|
||||
std::string neckheadPath = cvtest::findDataFile("dnn/onnx/models/nanotrack_head_sim.onnx", false);
|
||||
|
||||
cv::TrackerNano::Params params;
|
||||
params.backbone = backbonePath;
|
||||
params.neckhead = neckheadPath;
|
||||
cv::Ptr<Tracker> tracker = TrackerNano::create(params);
|
||||
checkTrackingAccuracy(tracker);
|
||||
}
|
||||
|
||||
TEST(NanoTrack, accuracy_NanoTrack_V2)
|
||||
{
|
||||
std::string backbonePath = cvtest::findDataFile("dnn/onnx/models/nanotrack_backbone_sim_v2.onnx", false);
|
||||
std::string neckheadPath = cvtest::findDataFile("dnn/onnx/models/nanotrack_head_sim_v2.onnx", false);
|
||||
|
||||
cv::TrackerNano::Params params;
|
||||
params.backbone = backbonePath;
|
||||
params.neckhead = neckheadPath;
|
||||
cv::Ptr<Tracker> tracker = TrackerNano::create(params);
|
||||
checkTrackingAccuracy(tracker, 0.69);
|
||||
}
|
||||
|
||||
TEST(vittrack, accuracy_vittrack)
|
||||
{
|
||||
std::string model = cvtest::findDataFile("dnn/onnx/models/vitTracker.onnx");
|
||||
cv::TrackerVit::Params params;
|
||||
params.net = model;
|
||||
cv::Ptr<Tracker> tracker = TrackerVit::create(params);
|
||||
checkTrackingAccuracy(tracker, 0.64);
|
||||
}
|
||||
|
||||
}} // namespace opencv_test::
|
||||
@@ -0,0 +1,382 @@
|
||||
// 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.
|
||||
|
||||
/*
|
||||
* The Evaluation Methodologies are partially based on:
|
||||
* ====================================================================================================================
|
||||
* [OTB] Y. Wu, J. Lim, and M.-H. Yang, "Online object tracking: A benchmark," in Computer Vision and Pattern Recognition (CVPR), 2013
|
||||
*
|
||||
*/
|
||||
|
||||
enum BBTransformations
|
||||
{
|
||||
NoTransform = 0,
|
||||
CenterShiftLeft = 1,
|
||||
CenterShiftRight = 2,
|
||||
CenterShiftUp = 3,
|
||||
CenterShiftDown = 4,
|
||||
CornerShiftTopLeft = 5,
|
||||
CornerShiftTopRight = 6,
|
||||
CornerShiftBottomLeft = 7,
|
||||
CornerShiftBottomRight = 8,
|
||||
Scale_0_8 = 9,
|
||||
Scale_0_9 = 10,
|
||||
Scale_1_1 = 11,
|
||||
Scale_1_2 = 12
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
std::vector<std::string> splitString(const std::string& s_, const std::string& delimiter)
|
||||
{
|
||||
std::string s = s_;
|
||||
std::vector<string> token;
|
||||
size_t pos = 0;
|
||||
while ((pos = s.find(delimiter)) != std::string::npos)
|
||||
{
|
||||
token.push_back(s.substr(0, pos));
|
||||
s.erase(0, pos + delimiter.length());
|
||||
}
|
||||
token.push_back(s);
|
||||
return token;
|
||||
}
|
||||
|
||||
float calcDistance(const Rect& a, const Rect& b)
|
||||
{
|
||||
Point2f p_a((float)(a.x + a.width / 2), (float)(a.y + a.height / 2));
|
||||
Point2f p_b((float)(b.x + b.width / 2), (float)(b.y + b.height / 2));
|
||||
Point2f diff = p_a - p_b;
|
||||
return sqrt(diff.dot(diff));
|
||||
}
|
||||
|
||||
float calcOverlap(const Rect& a, const Rect& b)
|
||||
{
|
||||
float rectIntersectionArea = (float)(a & b).area();
|
||||
return rectIntersectionArea / (a.area() + b.area() - rectIntersectionArea);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
template <typename Tracker, typename ROI_t = Rect2d>
|
||||
class TrackerTest
|
||||
{
|
||||
public:
|
||||
TrackerTest(const Ptr<Tracker>& tracker, const string& video, float distanceThreshold,
|
||||
float overlapThreshold, int shift = NoTransform, int segmentIdx = 1, int numSegments = 10);
|
||||
~TrackerTest() {}
|
||||
void run(int numFramesLimit = 0);
|
||||
|
||||
protected:
|
||||
void checkDataTest();
|
||||
|
||||
void distanceAndOverlapTest();
|
||||
|
||||
Ptr<Tracker> tracker;
|
||||
string video;
|
||||
std::vector<Rect> bbs;
|
||||
int startFrame;
|
||||
string suffix;
|
||||
string prefix;
|
||||
float overlapThreshold;
|
||||
float distanceThreshold;
|
||||
int segmentIdx;
|
||||
int shift;
|
||||
int numSegments;
|
||||
|
||||
int gtStartFrame;
|
||||
int endFrame;
|
||||
vector<int> validSequence;
|
||||
|
||||
private:
|
||||
Rect applyShift(const Rect& bb);
|
||||
};
|
||||
|
||||
template <typename Tracker, typename ROI_t>
|
||||
TrackerTest<Tracker, ROI_t>::TrackerTest(const Ptr<Tracker>& _tracker, const string& _video, float _distanceThreshold,
|
||||
float _overlapThreshold, int _shift, int _segmentIdx, int _numSegments)
|
||||
: tracker(_tracker)
|
||||
, video(_video)
|
||||
, overlapThreshold(_overlapThreshold)
|
||||
, distanceThreshold(_distanceThreshold)
|
||||
, segmentIdx(_segmentIdx)
|
||||
, shift(_shift)
|
||||
, numSegments(_numSegments)
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
template <typename Tracker, typename ROI_t>
|
||||
Rect TrackerTest<Tracker, ROI_t>::applyShift(const Rect& bb_)
|
||||
{
|
||||
Rect bb = bb_;
|
||||
Point center(bb.x + (bb.width / 2), bb.y + (bb.height / 2));
|
||||
|
||||
int xLimit = bb.x + bb.width - 1;
|
||||
int yLimit = bb.y + bb.height - 1;
|
||||
|
||||
int h = 0;
|
||||
int w = 0;
|
||||
float ratio = 1.0;
|
||||
|
||||
switch (shift)
|
||||
{
|
||||
case CenterShiftLeft:
|
||||
bb.x = bb.x - (int)ceil(0.1 * bb.width);
|
||||
break;
|
||||
case CenterShiftRight:
|
||||
bb.x = bb.x + (int)ceil(0.1 * bb.width);
|
||||
break;
|
||||
case CenterShiftUp:
|
||||
bb.y = bb.y - (int)ceil(0.1 * bb.height);
|
||||
break;
|
||||
case CenterShiftDown:
|
||||
bb.y = bb.y + (int)ceil(0.1 * bb.height);
|
||||
break;
|
||||
case CornerShiftTopLeft:
|
||||
bb.x = (int)cvRound(bb.x - 0.1 * bb.width);
|
||||
bb.y = (int)cvRound(bb.y - 0.1 * bb.height);
|
||||
|
||||
bb.width = xLimit - bb.x + 1;
|
||||
bb.height = yLimit - bb.y + 1;
|
||||
break;
|
||||
case CornerShiftTopRight:
|
||||
xLimit = (int)cvRound(xLimit + 0.1 * bb.width);
|
||||
|
||||
bb.y = (int)cvRound(bb.y - 0.1 * bb.height);
|
||||
bb.width = xLimit - bb.x + 1;
|
||||
bb.height = yLimit - bb.y + 1;
|
||||
break;
|
||||
case CornerShiftBottomLeft:
|
||||
bb.x = (int)cvRound(bb.x - 0.1 * bb.width);
|
||||
yLimit = (int)cvRound(yLimit + 0.1 * bb.height);
|
||||
|
||||
bb.width = xLimit - bb.x + 1;
|
||||
bb.height = yLimit - bb.y + 1;
|
||||
break;
|
||||
case CornerShiftBottomRight:
|
||||
xLimit = (int)cvRound(xLimit + 0.1 * bb.width);
|
||||
yLimit = (int)cvRound(yLimit + 0.1 * bb.height);
|
||||
|
||||
bb.width = xLimit - bb.x + 1;
|
||||
bb.height = yLimit - bb.y + 1;
|
||||
break;
|
||||
case Scale_0_8:
|
||||
ratio = 0.8f;
|
||||
w = (int)(ratio * bb.width);
|
||||
h = (int)(ratio * bb.height);
|
||||
|
||||
bb = Rect(center.x - (w / 2), center.y - (h / 2), w, h);
|
||||
break;
|
||||
case Scale_0_9:
|
||||
ratio = 0.9f;
|
||||
w = (int)(ratio * bb.width);
|
||||
h = (int)(ratio * bb.height);
|
||||
|
||||
bb = Rect(center.x - (w / 2), center.y - (h / 2), w, h);
|
||||
break;
|
||||
case 11:
|
||||
//scale 1.1
|
||||
ratio = 1.1f;
|
||||
w = (int)(ratio * bb.width);
|
||||
h = (int)(ratio * bb.height);
|
||||
|
||||
bb = Rect(center.x - (w / 2), center.y - (h / 2), w, h);
|
||||
break;
|
||||
case 12:
|
||||
//scale 1.2
|
||||
ratio = 1.2f;
|
||||
w = (int)(ratio * bb.width);
|
||||
h = (int)(ratio * bb.height);
|
||||
|
||||
bb = Rect(center.x - (w / 2), center.y - (h / 2), w, h);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return bb;
|
||||
}
|
||||
|
||||
template <typename Tracker, typename ROI_t>
|
||||
void TrackerTest<Tracker, ROI_t>::distanceAndOverlapTest()
|
||||
{
|
||||
bool initialized = false;
|
||||
|
||||
int fc = (startFrame - gtStartFrame);
|
||||
|
||||
bbs.at(fc) = applyShift(bbs.at(fc));
|
||||
Rect currentBBi = bbs.at(fc);
|
||||
ROI_t currentBB(currentBBi);
|
||||
float sumDistance = 0;
|
||||
float sumOverlap = 0;
|
||||
|
||||
string folder = cvtest::TS::ptr()->get_data_path() + "/" + TRACKING_DIR + "/" + video + "/" + FOLDER_IMG;
|
||||
string videoPath = folder + "/" + video + ".webm";
|
||||
|
||||
VideoCapture c;
|
||||
c.open(videoPath);
|
||||
if (!c.isOpened())
|
||||
throw SkipTestException("Can't open video file");
|
||||
#if 0
|
||||
c.set(CAP_PROP_POS_FRAMES, startFrame);
|
||||
#else
|
||||
if (startFrame)
|
||||
std::cout << "startFrame = " << startFrame << std::endl;
|
||||
for (int i = 0; i < startFrame; i++)
|
||||
{
|
||||
Mat dummy_frame;
|
||||
c >> dummy_frame;
|
||||
ASSERT_FALSE(dummy_frame.empty()) << i << ": " << videoPath;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (int frameCounter = startFrame; frameCounter < endFrame; frameCounter++)
|
||||
{
|
||||
Mat frame;
|
||||
c >> frame;
|
||||
|
||||
ASSERT_FALSE(frame.empty()) << "frameCounter=" << frameCounter << " video=" << videoPath;
|
||||
if (!initialized)
|
||||
{
|
||||
tracker->init(frame, currentBB);
|
||||
std::cout << "frame size = " << frame.size() << std::endl;
|
||||
initialized = true;
|
||||
}
|
||||
else if (initialized)
|
||||
{
|
||||
if (frameCounter >= (int)bbs.size())
|
||||
break;
|
||||
tracker->update(frame, currentBB);
|
||||
}
|
||||
float curDistance = calcDistance(currentBB, bbs.at(fc));
|
||||
float curOverlap = calcOverlap(currentBB, bbs.at(fc));
|
||||
|
||||
#ifdef DEBUG_TEST
|
||||
Mat result;
|
||||
repeat(frame, 1, 2, result);
|
||||
rectangle(result, currentBB, Scalar(0, 255, 0), 1);
|
||||
Rect roi2(frame.cols, 0, frame.cols, frame.rows);
|
||||
rectangle(result(roi2), bbs.at(fc), Scalar(0, 0, 255), 1);
|
||||
imshow("result", result);
|
||||
waitKey(1);
|
||||
#endif
|
||||
|
||||
sumDistance += curDistance;
|
||||
sumOverlap += curOverlap;
|
||||
fc++;
|
||||
}
|
||||
|
||||
float meanDistance = sumDistance / (endFrame - startFrame);
|
||||
float meanOverlap = sumOverlap / (endFrame - startFrame);
|
||||
|
||||
EXPECT_LE(meanDistance, distanceThreshold);
|
||||
EXPECT_GE(meanOverlap, overlapThreshold);
|
||||
}
|
||||
|
||||
template <typename Tracker, typename ROI_t>
|
||||
void TrackerTest<Tracker, ROI_t>::checkDataTest()
|
||||
{
|
||||
|
||||
FileStorage fs;
|
||||
fs.open(cvtest::TS::ptr()->get_data_path() + TRACKING_DIR + "/" + video + "/" + video + ".yml", FileStorage::READ);
|
||||
fs["start"] >> startFrame;
|
||||
fs["prefix"] >> prefix;
|
||||
fs["suffix"] >> suffix;
|
||||
fs.release();
|
||||
|
||||
string gtFile = cvtest::TS::ptr()->get_data_path() + TRACKING_DIR + "/" + video + "/gt.txt";
|
||||
std::ifstream gt;
|
||||
//open the ground truth
|
||||
gt.open(gtFile.c_str());
|
||||
ASSERT_TRUE(gt.is_open()) << gtFile;
|
||||
string line;
|
||||
int bbCounter = 0;
|
||||
while (getline(gt, line))
|
||||
{
|
||||
bbCounter++;
|
||||
}
|
||||
gt.close();
|
||||
|
||||
int seqLength = bbCounter;
|
||||
for (int i = startFrame; i < seqLength; i++)
|
||||
{
|
||||
validSequence.push_back(i);
|
||||
}
|
||||
|
||||
//exclude from the images sequence, the frames where the target is occluded or out of view
|
||||
string omitFile = cvtest::TS::ptr()->get_data_path() + TRACKING_DIR + "/" + video + "/" + FOLDER_OMIT_INIT + "/" + video + ".txt";
|
||||
std::ifstream omit;
|
||||
omit.open(omitFile.c_str());
|
||||
if (omit.is_open())
|
||||
{
|
||||
string omitLine;
|
||||
while (getline(omit, omitLine))
|
||||
{
|
||||
vector<string> tokens = splitString(omitLine, " ");
|
||||
int s_start = atoi(tokens.at(0).c_str());
|
||||
int s_end = atoi(tokens.at(1).c_str());
|
||||
for (int k = s_start; k <= s_end; k++)
|
||||
{
|
||||
std::vector<int>::iterator position = std::find(validSequence.begin(), validSequence.end(), k);
|
||||
if (position != validSequence.end())
|
||||
validSequence.erase(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
omit.close();
|
||||
gtStartFrame = startFrame;
|
||||
//compute the start and the and for each segment
|
||||
int numFrame = (int)(validSequence.size() / numSegments);
|
||||
startFrame += (segmentIdx - 1) * numFrame;
|
||||
endFrame = startFrame + numFrame;
|
||||
|
||||
std::ifstream gt2;
|
||||
//open the ground truth
|
||||
gt2.open(gtFile.c_str());
|
||||
ASSERT_TRUE(gt2.is_open()) << gtFile;
|
||||
string line2;
|
||||
while (getline(gt2, line2))
|
||||
{
|
||||
vector<string> tokens = splitString(line2, ",");
|
||||
Rect bb(atoi(tokens.at(0).c_str()), atoi(tokens.at(1).c_str()), atoi(tokens.at(2).c_str()), atoi(tokens.at(3).c_str()));
|
||||
ASSERT_EQ((size_t)4, tokens.size()) << "Incorrect ground truth file " << gtFile;
|
||||
|
||||
bbs.push_back(bb);
|
||||
}
|
||||
gt2.close();
|
||||
|
||||
if (segmentIdx == numSegments)
|
||||
endFrame = (int)bbs.size();
|
||||
}
|
||||
|
||||
template <typename Tracker, typename ROI_t>
|
||||
void TrackerTest<Tracker, ROI_t>::run(int numFramesLimit)
|
||||
{
|
||||
srand(1); // FIXIT remove that, ensure that there is no "rand()" in implementation
|
||||
|
||||
ASSERT_TRUE(tracker);
|
||||
|
||||
checkDataTest();
|
||||
|
||||
//check for failure
|
||||
if (::testing::Test::HasFatalFailure())
|
||||
return;
|
||||
|
||||
int numFrames = endFrame - startFrame;
|
||||
std::cout << "Number of frames in test data: " << numFrames << std::endl;
|
||||
|
||||
if (numFramesLimit > 0)
|
||||
{
|
||||
numFrames = std::min(numFrames, numFramesLimit);
|
||||
endFrame = startFrame + numFramesLimit;
|
||||
}
|
||||
else
|
||||
{
|
||||
applyTestTag(CV_TEST_TAG_DEBUG_VERYLONG);
|
||||
}
|
||||
|
||||
std::cout << "Number of frames to test: " << numFrames << std::endl;
|
||||
|
||||
distanceAndOverlapTest();
|
||||
}
|
||||
Reference in New Issue
Block a user