vendor: OpenCV 5.0.0 snapshot at 40738fb16ceddb5fb3fea747585f7ce6abb0605b

This commit is contained in:
Gitea Mirror Bot
2026-08-22 00:10:33 +08:00
commit f7f077da11
6933 changed files with 2335208 additions and 0 deletions
+18
View File
@@ -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()
+37
View File
@@ -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}
}
+58
View File
@@ -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"
+16
View File
@@ -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");
}
}
+17
View File
@@ -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
View File
@@ -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
+88
View File
@@ -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
+88
View File
@@ -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
+48
View File
@@ -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);
}
}}
+66
View File
@@ -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
+130
View File
@@ -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
+14
View File
@@ -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())
+269
View File
@@ -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
+17
View 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.
#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
+93
View File
@@ -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
+913
View File
@@ -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. */
+996
View 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. */
+220
View 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
+632
View File
@@ -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. */
+888
View 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. */
+126
View 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
+134
View File
@@ -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
+22
View File
@@ -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
+50
View File
@@ -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
+118
View File
@@ -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
+248
View File
@@ -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);
}
}
+282
View File
@@ -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
}
}
+540
View File
@@ -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;
}
}
+558
View File
@@ -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
+129
View File
@@ -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;
}
}
+53
View File
@@ -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
+19
View File
@@ -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
}
+227
View File
@@ -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
+374
View File
@@ -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
}
+242
View File
@@ -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
+145
View File
@@ -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
+85
View File
@@ -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
+200
View File
@@ -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
+157
View File
@@ -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
+108
View File
@@ -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. */
+438
View 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. */
+475
View 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
+184
View File
@@ -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
+113
View File
@@ -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. */
+17
View 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())
+252
View File
@@ -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
+17
View 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.
#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
+164
View File
@@ -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::
+382
View File
@@ -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();
}