vendor: OpenCV 5.0.0 snapshot at 40738fb16ceddb5fb3fea747585f7ce6abb0605b
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "../../precomp.hpp"
|
||||
#include "opencv2/video/detail/tracking.detail.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
TrackerFeature::~TrackerFeature()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
void TrackerFeature::compute(const std::vector<Mat>& images, Mat& response)
|
||||
{
|
||||
if (images.empty())
|
||||
return;
|
||||
|
||||
computeImpl(images, response);
|
||||
}
|
||||
|
||||
}}} // namespace cv::detail::tracking
|
||||
@@ -0,0 +1,121 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "../../precomp.hpp"
|
||||
#include "opencv2/video/detail/tracking.detail.hpp"
|
||||
#include "tracking_feature.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
inline namespace internal {
|
||||
|
||||
class TrackerFeatureHAAR : public TrackerFeature
|
||||
{
|
||||
public:
|
||||
struct Params
|
||||
{
|
||||
Params();
|
||||
int numFeatures; //!< # of rects
|
||||
Size rectSize; //!< rect size
|
||||
bool isIntegral; //!< true if input images are integral, false otherwise
|
||||
};
|
||||
|
||||
TrackerFeatureHAAR(const TrackerFeatureHAAR::Params& parameters = TrackerFeatureHAAR::Params());
|
||||
|
||||
virtual ~TrackerFeatureHAAR() CV_OVERRIDE {}
|
||||
|
||||
protected:
|
||||
bool computeImpl(const std::vector<Mat>& images, Mat& response) CV_OVERRIDE;
|
||||
|
||||
private:
|
||||
Params params;
|
||||
Ptr<CvHaarEvaluator> featureEvaluator;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parameters
|
||||
*/
|
||||
|
||||
TrackerFeatureHAAR::Params::Params()
|
||||
{
|
||||
numFeatures = 250;
|
||||
rectSize = Size(100, 100);
|
||||
isIntegral = false;
|
||||
}
|
||||
|
||||
TrackerFeatureHAAR::TrackerFeatureHAAR(const TrackerFeatureHAAR::Params& parameters)
|
||||
: params(parameters)
|
||||
{
|
||||
CvHaarFeatureParams haarParams;
|
||||
haarParams.numFeatures = params.numFeatures;
|
||||
haarParams.isIntegral = params.isIntegral;
|
||||
featureEvaluator = makePtr<CvHaarEvaluator>();
|
||||
featureEvaluator->init(&haarParams, 1, params.rectSize);
|
||||
}
|
||||
|
||||
class Parallel_compute : public cv::ParallelLoopBody
|
||||
{
|
||||
private:
|
||||
Ptr<CvHaarEvaluator> featureEvaluator;
|
||||
std::vector<Mat> images;
|
||||
Mat response;
|
||||
//std::vector<CvHaarEvaluator::FeatureHaar> features;
|
||||
public:
|
||||
Parallel_compute(Ptr<CvHaarEvaluator>& fe, const std::vector<Mat>& img, Mat& resp)
|
||||
: featureEvaluator(fe)
|
||||
, images(img)
|
||||
, response(resp)
|
||||
{
|
||||
|
||||
//features = featureEvaluator->getFeatures();
|
||||
}
|
||||
|
||||
virtual void operator()(const cv::Range& r) const CV_OVERRIDE
|
||||
{
|
||||
for (int jf = r.start; jf != r.end; ++jf)
|
||||
{
|
||||
int cols = images[jf].cols;
|
||||
int rows = images[jf].rows;
|
||||
for (int j = 0; j < featureEvaluator->getNumFeatures(); j++)
|
||||
{
|
||||
float res = 0;
|
||||
featureEvaluator->getFeatures()[j].eval(images[jf], Rect(0, 0, cols, rows), &res);
|
||||
(Mat_<float>(response))(j, jf) = res;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool TrackerFeatureHAAR::computeImpl(const std::vector<Mat>& images, Mat& response)
|
||||
{
|
||||
if (images.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int numFeatures = featureEvaluator->getNumFeatures();
|
||||
|
||||
response = Mat_<float>(Size((int)images.size(), numFeatures));
|
||||
|
||||
std::vector<CvHaarEvaluator::FeatureHaar> f = featureEvaluator->getFeatures();
|
||||
//for each sample compute #n_feature -> put each feature (n Rect) in response
|
||||
parallel_for_(Range(0, (int)images.size()), Parallel_compute(featureEvaluator, images, response));
|
||||
|
||||
/*for ( size_t i = 0; i < images.size(); i++ )
|
||||
{
|
||||
int c = images[i].cols;
|
||||
int r = images[i].rows;
|
||||
for ( int j = 0; j < numFeatures; j++ )
|
||||
{
|
||||
float res = 0;
|
||||
featureEvaluator->getFeatures( j ).eval( images[i], Rect( 0, 0, c, r ), &res );
|
||||
( Mat_<float>( response ) )( j, i ) = res;
|
||||
}
|
||||
}*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}}}} // namespace cv::detail::tracking::internal
|
||||
@@ -0,0 +1,60 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "../../precomp.hpp"
|
||||
#include "opencv2/video/detail/tracking.detail.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
TrackerFeatureSet::TrackerFeatureSet()
|
||||
{
|
||||
blockAddTrackerFeature = false;
|
||||
}
|
||||
|
||||
TrackerFeatureSet::~TrackerFeatureSet()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
void TrackerFeatureSet::extraction(const std::vector<Mat>& images)
|
||||
{
|
||||
blockAddTrackerFeature = true;
|
||||
|
||||
clearResponses();
|
||||
responses.resize(features.size());
|
||||
|
||||
for (size_t i = 0; i < features.size(); i++)
|
||||
{
|
||||
CV_DbgAssert(features[i]);
|
||||
features[i]->compute(images, responses[i]);
|
||||
}
|
||||
}
|
||||
|
||||
bool TrackerFeatureSet::addTrackerFeature(const Ptr<TrackerFeature>& feature)
|
||||
{
|
||||
CV_Assert(!blockAddTrackerFeature);
|
||||
CV_Assert(feature);
|
||||
|
||||
features.push_back(feature);
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<Ptr<TrackerFeature>>& TrackerFeatureSet::getTrackerFeatures() const
|
||||
{
|
||||
return features;
|
||||
}
|
||||
|
||||
const std::vector<Mat>& TrackerFeatureSet::getResponses() const
|
||||
{
|
||||
return responses;
|
||||
}
|
||||
|
||||
void TrackerFeatureSet::clearResponses()
|
||||
{
|
||||
responses.clear();
|
||||
}
|
||||
|
||||
}}} // namespace cv::detail::tracking
|
||||
@@ -0,0 +1,85 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "../../precomp.hpp"
|
||||
#include "tracker_mil_model.hpp"
|
||||
|
||||
/**
|
||||
* TrackerMILModel
|
||||
*/
|
||||
|
||||
namespace cv {
|
||||
inline namespace tracking {
|
||||
namespace impl {
|
||||
|
||||
TrackerMILModel::TrackerMILModel(const Rect& boundingBox)
|
||||
{
|
||||
currentSample.clear();
|
||||
mode = MODE_POSITIVE;
|
||||
width = boundingBox.width;
|
||||
height = boundingBox.height;
|
||||
|
||||
Ptr<TrackerStateEstimatorMILBoosting::TrackerMILTargetState> initState = Ptr<TrackerStateEstimatorMILBoosting::TrackerMILTargetState>(
|
||||
new TrackerStateEstimatorMILBoosting::TrackerMILTargetState(Point2f((float)boundingBox.x, (float)boundingBox.y), boundingBox.width, boundingBox.height,
|
||||
true, Mat()));
|
||||
trajectory.push_back(initState);
|
||||
}
|
||||
|
||||
void TrackerMILModel::responseToConfidenceMap(const std::vector<Mat>& responses, ConfidenceMap& confidenceMap)
|
||||
{
|
||||
if (currentSample.empty())
|
||||
{
|
||||
CV_Error(cv::Error::StsError, "The samples in Model estimation are empty");
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < responses.size(); i++)
|
||||
{
|
||||
//for each column (one sample) there are #num_feature
|
||||
//get informations from currentSample
|
||||
for (int j = 0; j < responses.at(i).cols; j++)
|
||||
{
|
||||
|
||||
Size currentSize;
|
||||
Point currentOfs;
|
||||
currentSample.at(j).locateROI(currentSize, currentOfs);
|
||||
bool foreground = false;
|
||||
if (mode == MODE_POSITIVE || mode == MODE_ESTIMATON)
|
||||
{
|
||||
foreground = true;
|
||||
}
|
||||
else if (mode == MODE_NEGATIVE)
|
||||
{
|
||||
foreground = false;
|
||||
}
|
||||
|
||||
//get the column of the HAAR responses
|
||||
Mat singleResponse = responses.at(i).col(j);
|
||||
|
||||
//create the state
|
||||
Ptr<TrackerStateEstimatorMILBoosting::TrackerMILTargetState> currentState = Ptr<TrackerStateEstimatorMILBoosting::TrackerMILTargetState>(
|
||||
new TrackerStateEstimatorMILBoosting::TrackerMILTargetState(currentOfs, width, height, foreground, singleResponse));
|
||||
|
||||
confidenceMap.push_back(std::make_pair(currentState, 0.0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TrackerMILModel::modelEstimationImpl(const std::vector<Mat>& responses)
|
||||
{
|
||||
responseToConfidenceMap(responses, currentConfidenceMap);
|
||||
}
|
||||
|
||||
void TrackerMILModel::modelUpdateImpl()
|
||||
{
|
||||
}
|
||||
|
||||
void TrackerMILModel::setMode(int trainingMode, const std::vector<Mat>& samples)
|
||||
{
|
||||
currentSample.clear();
|
||||
currentSample = samples;
|
||||
|
||||
mode = trainingMode;
|
||||
}
|
||||
|
||||
}}} // namespace cv::tracking::impl
|
||||
@@ -0,0 +1,67 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef __OPENCV_TRACKER_MIL_MODEL_HPP__
|
||||
#define __OPENCV_TRACKER_MIL_MODEL_HPP__
|
||||
|
||||
#include "opencv2/video/detail/tracking.detail.hpp"
|
||||
#include "tracker_mil_state.hpp"
|
||||
|
||||
namespace cv {
|
||||
inline namespace tracking {
|
||||
namespace impl {
|
||||
|
||||
using namespace cv::detail::tracking;
|
||||
|
||||
/**
|
||||
* \brief Implementation of TrackerModel for MIL algorithm
|
||||
*/
|
||||
class TrackerMILModel : public detail::TrackerModel
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
MODE_POSITIVE = 1, // mode for positive features
|
||||
MODE_NEGATIVE = 2, // mode for negative features
|
||||
MODE_ESTIMATON = 3 // mode for estimation step
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Constructor
|
||||
* \param boundingBox The first boundingBox
|
||||
*/
|
||||
TrackerMILModel(const Rect& boundingBox);
|
||||
|
||||
/**
|
||||
* \brief Destructor
|
||||
*/
|
||||
~TrackerMILModel() {}
|
||||
|
||||
/**
|
||||
* \brief Set the mode
|
||||
*/
|
||||
void setMode(int trainingMode, const std::vector<Mat>& samples);
|
||||
|
||||
/**
|
||||
* \brief Create the ConfidenceMap from a list of responses
|
||||
* \param responses The list of the responses
|
||||
* \param confidenceMap The output
|
||||
*/
|
||||
void responseToConfidenceMap(const std::vector<Mat>& responses, ConfidenceMap& confidenceMap);
|
||||
|
||||
protected:
|
||||
void modelEstimationImpl(const std::vector<Mat>& responses) CV_OVERRIDE;
|
||||
void modelUpdateImpl() CV_OVERRIDE;
|
||||
|
||||
private:
|
||||
int mode;
|
||||
std::vector<Mat> currentSample;
|
||||
|
||||
int width; //initial width of the boundingBox
|
||||
int height; //initial height of the boundingBox
|
||||
};
|
||||
|
||||
}}} // namespace cv::tracking::impl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,159 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "../../precomp.hpp"
|
||||
#include "opencv2/video/detail/tracking.detail.hpp"
|
||||
#include "tracker_mil_state.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
/**
|
||||
* TrackerStateEstimatorMILBoosting::TrackerMILTargetState
|
||||
*/
|
||||
TrackerStateEstimatorMILBoosting::TrackerMILTargetState::TrackerMILTargetState(const Point2f& position, int width, int height, bool foreground,
|
||||
const Mat& features)
|
||||
{
|
||||
setTargetPosition(position);
|
||||
setTargetWidth(width);
|
||||
setTargetHeight(height);
|
||||
setTargetFg(foreground);
|
||||
setFeatures(features);
|
||||
}
|
||||
|
||||
void TrackerStateEstimatorMILBoosting::TrackerMILTargetState::setTargetFg(bool foreground)
|
||||
{
|
||||
isTarget = foreground;
|
||||
}
|
||||
|
||||
void TrackerStateEstimatorMILBoosting::TrackerMILTargetState::setFeatures(const Mat& features)
|
||||
{
|
||||
targetFeatures = features;
|
||||
}
|
||||
|
||||
bool TrackerStateEstimatorMILBoosting::TrackerMILTargetState::isTargetFg() const
|
||||
{
|
||||
return isTarget;
|
||||
}
|
||||
|
||||
Mat TrackerStateEstimatorMILBoosting::TrackerMILTargetState::getFeatures() const
|
||||
{
|
||||
return targetFeatures;
|
||||
}
|
||||
|
||||
TrackerStateEstimatorMILBoosting::TrackerStateEstimatorMILBoosting(int nFeatures)
|
||||
{
|
||||
className = "BOOSTING";
|
||||
trained = false;
|
||||
numFeatures = nFeatures;
|
||||
}
|
||||
|
||||
TrackerStateEstimatorMILBoosting::~TrackerStateEstimatorMILBoosting()
|
||||
{
|
||||
}
|
||||
|
||||
void TrackerStateEstimatorMILBoosting::setCurrentConfidenceMap(ConfidenceMap& confidenceMap)
|
||||
{
|
||||
currentConfidenceMap.clear();
|
||||
currentConfidenceMap = confidenceMap;
|
||||
}
|
||||
|
||||
uint TrackerStateEstimatorMILBoosting::max_idx(const std::vector<float>& v)
|
||||
{
|
||||
const float* findPtr = &(*std::max_element(v.begin(), v.end()));
|
||||
const float* beginPtr = &(*v.begin());
|
||||
return (uint)(findPtr - beginPtr);
|
||||
}
|
||||
|
||||
Ptr<TrackerTargetState> TrackerStateEstimatorMILBoosting::estimateImpl(const std::vector<ConfidenceMap>& /*confidenceMaps*/)
|
||||
{
|
||||
//run ClfMilBoost classify in order to compute next location
|
||||
if (currentConfidenceMap.empty())
|
||||
return Ptr<TrackerTargetState>();
|
||||
|
||||
Mat positiveStates;
|
||||
Mat negativeStates;
|
||||
|
||||
prepareData(currentConfidenceMap, positiveStates, negativeStates);
|
||||
|
||||
std::vector<float> prob = boostMILModel.classify(positiveStates);
|
||||
|
||||
int bestind = max_idx(prob);
|
||||
//float resp = prob[bestind];
|
||||
|
||||
return currentConfidenceMap.at(bestind).first;
|
||||
}
|
||||
|
||||
void TrackerStateEstimatorMILBoosting::prepareData(const ConfidenceMap& confidenceMap, Mat& positive, Mat& negative)
|
||||
{
|
||||
|
||||
int posCounter = 0;
|
||||
int negCounter = 0;
|
||||
|
||||
for (size_t i = 0; i < confidenceMap.size(); i++)
|
||||
{
|
||||
Ptr<TrackerMILTargetState> currentTargetState = confidenceMap.at(i).first.staticCast<TrackerMILTargetState>();
|
||||
CV_DbgAssert(currentTargetState);
|
||||
if (currentTargetState->isTargetFg())
|
||||
posCounter++;
|
||||
else
|
||||
negCounter++;
|
||||
}
|
||||
|
||||
positive.create(posCounter, numFeatures, CV_32FC1);
|
||||
negative.create(negCounter, numFeatures, CV_32FC1);
|
||||
|
||||
//TODO change with mat fast access
|
||||
//initialize trainData (positive and negative)
|
||||
|
||||
int pc = 0;
|
||||
int nc = 0;
|
||||
for (size_t i = 0; i < confidenceMap.size(); i++)
|
||||
{
|
||||
Ptr<TrackerMILTargetState> currentTargetState = confidenceMap.at(i).first.staticCast<TrackerMILTargetState>();
|
||||
Mat stateFeatures = currentTargetState->getFeatures();
|
||||
|
||||
if (currentTargetState->isTargetFg())
|
||||
{
|
||||
for (int j = 0; j < stateFeatures.rows; j++)
|
||||
{
|
||||
//fill the positive trainData with the value of the feature j for sample i
|
||||
positive.at<float>(pc, j) = stateFeatures.at<float>(j, 0);
|
||||
}
|
||||
pc++;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int j = 0; j < stateFeatures.rows; j++)
|
||||
{
|
||||
//fill the negative trainData with the value of the feature j for sample i
|
||||
negative.at<float>(nc, j) = stateFeatures.at<float>(j, 0);
|
||||
}
|
||||
nc++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TrackerStateEstimatorMILBoosting::updateImpl(std::vector<ConfidenceMap>& confidenceMaps)
|
||||
{
|
||||
|
||||
if (!trained)
|
||||
{
|
||||
//this is the first time that the classifier is built
|
||||
//init MIL
|
||||
boostMILModel.init();
|
||||
trained = true;
|
||||
}
|
||||
|
||||
ConfidenceMap lastConfidenceMap = confidenceMaps.back();
|
||||
Mat positiveStates;
|
||||
Mat negativeStates;
|
||||
|
||||
prepareData(lastConfidenceMap, positiveStates, negativeStates);
|
||||
//update MIL
|
||||
boostMILModel.update(positiveStates, negativeStates);
|
||||
}
|
||||
|
||||
}}} // namespace cv::detail::tracking
|
||||
@@ -0,0 +1,87 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef OPENCV_VIDEO_DETAIL_TRACKING_MIL_STATE_HPP
|
||||
#define OPENCV_VIDEO_DETAIL_TRACKING_MIL_STATE_HPP
|
||||
|
||||
#include "opencv2/video/detail/tracking.detail.hpp"
|
||||
#include "tracking_online_mil.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
/** @brief TrackerStateEstimator based on Boosting
|
||||
*/
|
||||
class CV_EXPORTS TrackerStateEstimatorMILBoosting : public TrackerStateEstimator
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Implementation of the target state for TrackerStateEstimatorMILBoosting
|
||||
*/
|
||||
class TrackerMILTargetState : public TrackerTargetState
|
||||
{
|
||||
|
||||
public:
|
||||
/**
|
||||
* \brief Constructor
|
||||
* \param position Top left corner of the bounding box
|
||||
* \param width Width of the bounding box
|
||||
* \param height Height of the bounding box
|
||||
* \param foreground label for target or background
|
||||
* \param features features extracted
|
||||
*/
|
||||
TrackerMILTargetState(const Point2f& position, int width, int height, bool foreground, const Mat& features);
|
||||
|
||||
~TrackerMILTargetState() {}
|
||||
|
||||
/** @brief Set label: true for target foreground, false for background
|
||||
@param foreground Label for background/foreground
|
||||
*/
|
||||
void setTargetFg(bool foreground);
|
||||
/** @brief Set the features extracted from TrackerFeatureSet
|
||||
@param features The features extracted
|
||||
*/
|
||||
void setFeatures(const Mat& features);
|
||||
/** @brief Get the label. Return true for target foreground, false for background
|
||||
*/
|
||||
bool isTargetFg() const;
|
||||
/** @brief Get the features extracted
|
||||
*/
|
||||
Mat getFeatures() const;
|
||||
|
||||
private:
|
||||
bool isTarget;
|
||||
Mat targetFeatures;
|
||||
};
|
||||
|
||||
/** @brief Constructor
|
||||
@param nFeatures Number of features for each sample
|
||||
*/
|
||||
TrackerStateEstimatorMILBoosting(int nFeatures = 250);
|
||||
~TrackerStateEstimatorMILBoosting();
|
||||
|
||||
/** @brief Set the current confidenceMap
|
||||
@param confidenceMap The current :cConfidenceMap
|
||||
*/
|
||||
void setCurrentConfidenceMap(ConfidenceMap& confidenceMap);
|
||||
|
||||
protected:
|
||||
Ptr<TrackerTargetState> estimateImpl(const std::vector<ConfidenceMap>& confidenceMaps) CV_OVERRIDE;
|
||||
void updateImpl(std::vector<ConfidenceMap>& confidenceMaps) CV_OVERRIDE;
|
||||
|
||||
private:
|
||||
uint max_idx(const std::vector<float>& v);
|
||||
void prepareData(const ConfidenceMap& confidenceMap, Mat& positive, Mat& negative);
|
||||
|
||||
ClfMilBoost boostMILModel;
|
||||
bool trained;
|
||||
int numFeatures;
|
||||
|
||||
ConfidenceMap currentConfidenceMap;
|
||||
};
|
||||
|
||||
}}} // namespace cv::detail::tracking
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,132 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "../../precomp.hpp"
|
||||
#include "opencv2/video/detail/tracking.detail.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
TrackerModel::TrackerModel()
|
||||
{
|
||||
stateEstimator = Ptr<TrackerStateEstimator>();
|
||||
maxCMLength = 10;
|
||||
}
|
||||
|
||||
TrackerModel::~TrackerModel()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
bool TrackerModel::setTrackerStateEstimator(Ptr<TrackerStateEstimator> trackerStateEstimator)
|
||||
{
|
||||
if (stateEstimator.get())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
stateEstimator = trackerStateEstimator;
|
||||
return true;
|
||||
}
|
||||
|
||||
Ptr<TrackerStateEstimator> TrackerModel::getTrackerStateEstimator() const
|
||||
{
|
||||
return stateEstimator;
|
||||
}
|
||||
|
||||
void TrackerModel::modelEstimation(const std::vector<Mat>& responses)
|
||||
{
|
||||
modelEstimationImpl(responses);
|
||||
}
|
||||
|
||||
void TrackerModel::clearCurrentConfidenceMap()
|
||||
{
|
||||
currentConfidenceMap.clear();
|
||||
}
|
||||
|
||||
void TrackerModel::modelUpdate()
|
||||
{
|
||||
modelUpdateImpl();
|
||||
|
||||
if (maxCMLength != -1 && (int)confidenceMaps.size() >= maxCMLength - 1)
|
||||
{
|
||||
int l = maxCMLength / 2;
|
||||
confidenceMaps.erase(confidenceMaps.begin(), confidenceMaps.begin() + l);
|
||||
}
|
||||
if (maxCMLength != -1 && (int)trajectory.size() >= maxCMLength - 1)
|
||||
{
|
||||
int l = maxCMLength / 2;
|
||||
trajectory.erase(trajectory.begin(), trajectory.begin() + l);
|
||||
}
|
||||
confidenceMaps.push_back(currentConfidenceMap);
|
||||
stateEstimator->update(confidenceMaps);
|
||||
|
||||
clearCurrentConfidenceMap();
|
||||
}
|
||||
|
||||
bool TrackerModel::runStateEstimator()
|
||||
{
|
||||
if (!stateEstimator)
|
||||
{
|
||||
CV_Error(cv::Error::StsError, "Tracker state estimator is not setted");
|
||||
}
|
||||
Ptr<TrackerTargetState> targetState = stateEstimator->estimate(confidenceMaps);
|
||||
if (!targetState)
|
||||
return false;
|
||||
|
||||
setLastTargetState(targetState);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TrackerModel::setLastTargetState(const Ptr<TrackerTargetState>& lastTargetState)
|
||||
{
|
||||
trajectory.push_back(lastTargetState);
|
||||
}
|
||||
|
||||
Ptr<TrackerTargetState> TrackerModel::getLastTargetState() const
|
||||
{
|
||||
return trajectory.back();
|
||||
}
|
||||
|
||||
const std::vector<ConfidenceMap>& TrackerModel::getConfidenceMaps() const
|
||||
{
|
||||
return confidenceMaps;
|
||||
}
|
||||
|
||||
const ConfidenceMap& TrackerModel::getLastConfidenceMap() const
|
||||
{
|
||||
return confidenceMaps.back();
|
||||
}
|
||||
|
||||
Point2f TrackerTargetState::getTargetPosition() const
|
||||
{
|
||||
return targetPosition;
|
||||
}
|
||||
|
||||
void TrackerTargetState::setTargetPosition(const Point2f& position)
|
||||
{
|
||||
targetPosition = position;
|
||||
}
|
||||
|
||||
int TrackerTargetState::getTargetWidth() const
|
||||
{
|
||||
return targetWidth;
|
||||
}
|
||||
|
||||
void TrackerTargetState::setTargetWidth(int width)
|
||||
{
|
||||
targetWidth = width;
|
||||
}
|
||||
int TrackerTargetState::getTargetHeight() const
|
||||
{
|
||||
return targetHeight;
|
||||
}
|
||||
|
||||
void TrackerTargetState::setTargetHeight(int height)
|
||||
{
|
||||
targetHeight = height;
|
||||
}
|
||||
|
||||
}}} // namespace cv::detail::tracking
|
||||
@@ -0,0 +1,68 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "../../precomp.hpp"
|
||||
|
||||
#include "opencv2/video/detail/tracking.detail.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
TrackerSampler::TrackerSampler()
|
||||
{
|
||||
blockAddTrackerSampler = false;
|
||||
}
|
||||
|
||||
TrackerSampler::~TrackerSampler()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
void TrackerSampler::sampling(const Mat& image, Rect boundingBox)
|
||||
{
|
||||
clearSamples();
|
||||
|
||||
for (size_t i = 0; i < samplers.size(); i++)
|
||||
{
|
||||
CV_DbgAssert(samplers[i]);
|
||||
std::vector<Mat> current_samples;
|
||||
samplers[i]->sampling(image, boundingBox, current_samples);
|
||||
|
||||
//push in samples all current_samples
|
||||
for (size_t j = 0; j < current_samples.size(); j++)
|
||||
{
|
||||
std::vector<Mat>::iterator it = samples.end();
|
||||
samples.insert(it, current_samples.at(j));
|
||||
}
|
||||
}
|
||||
|
||||
blockAddTrackerSampler = true;
|
||||
}
|
||||
|
||||
bool TrackerSampler::addTrackerSamplerAlgorithm(const Ptr<TrackerSamplerAlgorithm>& sampler)
|
||||
{
|
||||
CV_Assert(!blockAddTrackerSampler);
|
||||
CV_Assert(sampler);
|
||||
|
||||
samplers.push_back(sampler);
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::vector<Ptr<TrackerSamplerAlgorithm>>& TrackerSampler::getSamplers() const
|
||||
{
|
||||
return samplers;
|
||||
}
|
||||
|
||||
const std::vector<Mat>& TrackerSampler::getSamples() const
|
||||
{
|
||||
return samples;
|
||||
}
|
||||
|
||||
void TrackerSampler::clearSamples()
|
||||
{
|
||||
samples.clear();
|
||||
}
|
||||
|
||||
}}} // namespace cv::detail::tracking
|
||||
@@ -0,0 +1,124 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "../../precomp.hpp"
|
||||
#include "opencv2/video/detail/tracking.detail.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
TrackerSamplerAlgorithm::~TrackerSamplerAlgorithm()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
TrackerSamplerCSC::Params::Params()
|
||||
{
|
||||
initInRad = 3;
|
||||
initMaxNegNum = 65;
|
||||
searchWinSize = 25;
|
||||
trackInPosRad = 4;
|
||||
trackMaxNegNum = 65;
|
||||
trackMaxPosNum = 100000;
|
||||
}
|
||||
|
||||
TrackerSamplerCSC::TrackerSamplerCSC(const TrackerSamplerCSC::Params& parameters)
|
||||
: params(parameters)
|
||||
{
|
||||
mode = MODE_INIT_POS;
|
||||
rng = theRNG();
|
||||
}
|
||||
|
||||
TrackerSamplerCSC::~TrackerSamplerCSC()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
bool TrackerSamplerCSC::sampling(const Mat& image, const Rect& boundingBox, std::vector<Mat>& sample)
|
||||
{
|
||||
CV_Assert(!image.empty());
|
||||
|
||||
float inrad = 0;
|
||||
float outrad = 0;
|
||||
int maxnum = 0;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case MODE_INIT_POS:
|
||||
inrad = params.initInRad;
|
||||
sample = sampleImage(image, boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height, inrad);
|
||||
break;
|
||||
case MODE_INIT_NEG:
|
||||
inrad = 2.0f * params.searchWinSize;
|
||||
outrad = 1.5f * params.initInRad;
|
||||
maxnum = params.initMaxNegNum;
|
||||
sample = sampleImage(image, boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height, inrad, outrad, maxnum);
|
||||
break;
|
||||
case MODE_TRACK_POS:
|
||||
inrad = params.trackInPosRad;
|
||||
outrad = 0;
|
||||
maxnum = params.trackMaxPosNum;
|
||||
sample = sampleImage(image, boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height, inrad, outrad, maxnum);
|
||||
break;
|
||||
case MODE_TRACK_NEG:
|
||||
inrad = 1.5f * params.searchWinSize;
|
||||
outrad = params.trackInPosRad + 5;
|
||||
maxnum = params.trackMaxNegNum;
|
||||
sample = sampleImage(image, boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height, inrad, outrad, maxnum);
|
||||
break;
|
||||
case MODE_DETECT:
|
||||
inrad = params.searchWinSize;
|
||||
sample = sampleImage(image, boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height, inrad);
|
||||
break;
|
||||
default:
|
||||
inrad = params.initInRad;
|
||||
sample = sampleImage(image, boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height, inrad);
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TrackerSamplerCSC::setMode(int samplingMode)
|
||||
{
|
||||
mode = samplingMode;
|
||||
}
|
||||
|
||||
std::vector<Mat> TrackerSamplerCSC::sampleImage(const Mat& img, int x, int y, int w, int h, float inrad, float outrad, int maxnum)
|
||||
{
|
||||
int rowsz = img.rows - h - 1;
|
||||
int colsz = img.cols - w - 1;
|
||||
float inradsq = inrad * inrad;
|
||||
float outradsq = outrad * outrad;
|
||||
int dist;
|
||||
|
||||
uint minrow = max(0, (int)y - (int)inrad);
|
||||
uint maxrow = min((int)rowsz - 1, (int)y + (int)inrad);
|
||||
uint mincol = max(0, (int)x - (int)inrad);
|
||||
uint maxcol = min((int)colsz - 1, (int)x + (int)inrad);
|
||||
|
||||
//fprintf(stderr,"inrad=%f minrow=%d maxrow=%d mincol=%d maxcol=%d\n",inrad,minrow,maxrow,mincol,maxcol);
|
||||
|
||||
std::vector<Mat> samples;
|
||||
samples.resize((maxrow - minrow + 1) * (maxcol - mincol + 1));
|
||||
int i = 0;
|
||||
|
||||
float prob = ((float)(maxnum)) / samples.size();
|
||||
|
||||
for (int r = minrow; r <= int(maxrow); r++)
|
||||
for (int c = mincol; c <= int(maxcol); c++)
|
||||
{
|
||||
dist = (y - r) * (y - r) + (x - c) * (x - c);
|
||||
if (float(rng.uniform(0.f, 1.f)) < prob && dist < inradsq && dist >= outradsq)
|
||||
{
|
||||
samples[i] = img(Rect(c, r, w, h));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
samples.resize(min(i, maxnum));
|
||||
return samples;
|
||||
}
|
||||
|
||||
}}} // namespace cv::detail::tracking
|
||||
@@ -0,0 +1,37 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "../../precomp.hpp"
|
||||
#include "opencv2/video/detail/tracking.detail.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
TrackerStateEstimator::~TrackerStateEstimator()
|
||||
{
|
||||
}
|
||||
|
||||
Ptr<TrackerTargetState> TrackerStateEstimator::estimate(const std::vector<ConfidenceMap>& confidenceMaps)
|
||||
{
|
||||
if (confidenceMaps.empty())
|
||||
return Ptr<TrackerTargetState>();
|
||||
|
||||
return estimateImpl(confidenceMaps);
|
||||
}
|
||||
|
||||
void TrackerStateEstimator::update(std::vector<ConfidenceMap>& confidenceMaps)
|
||||
{
|
||||
if (confidenceMaps.empty())
|
||||
return;
|
||||
|
||||
return updateImpl(confidenceMaps);
|
||||
}
|
||||
|
||||
String TrackerStateEstimator::getClassName() const
|
||||
{
|
||||
return className;
|
||||
}
|
||||
|
||||
}}} // namespace cv::detail::tracking
|
||||
@@ -0,0 +1,582 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "../../precomp.hpp"
|
||||
#include "opencv2/video/detail/tracking.detail.hpp"
|
||||
#include "tracking_feature.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
/*
|
||||
* TODO This implementation is based on apps/traincascade/
|
||||
* TODO Changed CvHaarEvaluator based on ADABOOSTING implementation (Grabner et al.)
|
||||
*/
|
||||
|
||||
CvParams::CvParams()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
//---------------------------- FeatureParams --------------------------------------
|
||||
|
||||
CvFeatureParams::CvFeatureParams()
|
||||
: maxCatCount(0)
|
||||
, featSize(1)
|
||||
, numFeatures(1)
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
//------------------------------------- FeatureEvaluator ---------------------------------------
|
||||
|
||||
void CvFeatureEvaluator::init(const CvFeatureParams* _featureParams, int _maxSampleCount, Size _winSize)
|
||||
{
|
||||
CV_Assert(_featureParams);
|
||||
CV_Assert(_maxSampleCount > 0);
|
||||
featureParams = (CvFeatureParams*)_featureParams;
|
||||
winSize = _winSize;
|
||||
numFeatures = _featureParams->numFeatures;
|
||||
cls.create((int)_maxSampleCount, 1, CV_32FC1);
|
||||
generateFeatures();
|
||||
}
|
||||
|
||||
void CvFeatureEvaluator::setImage(const Mat& img, uchar clsLabel, int idx)
|
||||
{
|
||||
winSize.width = img.cols;
|
||||
winSize.height = img.rows;
|
||||
//CV_Assert( img.cols == winSize.width );
|
||||
//CV_Assert( img.rows == winSize.height );
|
||||
CV_Assert(idx < cls.rows);
|
||||
cls.ptr<float>(idx)[0] = clsLabel;
|
||||
}
|
||||
|
||||
CvHaarFeatureParams::CvHaarFeatureParams()
|
||||
{
|
||||
isIntegral = false;
|
||||
}
|
||||
|
||||
//--------------------- HaarFeatureEvaluator ----------------
|
||||
|
||||
void CvHaarEvaluator::init(const CvFeatureParams* _featureParams, int /*_maxSampleCount*/, Size _winSize)
|
||||
{
|
||||
CV_Assert(_featureParams);
|
||||
int cols = (_winSize.width + 1) * (_winSize.height + 1);
|
||||
sum.create((int)1, cols, CV_32SC1);
|
||||
isIntegral = ((CvHaarFeatureParams*)_featureParams)->isIntegral;
|
||||
CvFeatureEvaluator::init(_featureParams, 1, _winSize);
|
||||
}
|
||||
|
||||
void CvHaarEvaluator::setImage(const Mat& img, uchar /*clsLabel*/, int /*idx*/)
|
||||
{
|
||||
CV_DbgAssert(!sum.empty());
|
||||
|
||||
winSize.width = img.cols;
|
||||
winSize.height = img.rows;
|
||||
|
||||
CvFeatureEvaluator::setImage(img, 1, 0);
|
||||
if (!isIntegral)
|
||||
{
|
||||
std::vector<Mat_<float>> ii_imgs;
|
||||
compute_integral(img, ii_imgs);
|
||||
_ii_img = ii_imgs[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
_ii_img = img;
|
||||
}
|
||||
}
|
||||
|
||||
void CvHaarEvaluator::generateFeatures()
|
||||
{
|
||||
generateFeatures(featureParams->numFeatures);
|
||||
}
|
||||
|
||||
void CvHaarEvaluator::generateFeatures(int nFeatures)
|
||||
{
|
||||
for (int i = 0; i < nFeatures; i++)
|
||||
{
|
||||
CvHaarEvaluator::FeatureHaar feature(Size(winSize.width, winSize.height));
|
||||
features.push_back(feature);
|
||||
}
|
||||
}
|
||||
|
||||
#define INITSIGMA(numAreas) (static_cast<float>(sqrt(256.0f * 256.0f / 12.0f * (numAreas))));
|
||||
|
||||
CvHaarEvaluator::FeatureHaar::FeatureHaar(Size patchSize)
|
||||
{
|
||||
try
|
||||
{
|
||||
generateRandomFeature(patchSize);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// FIXIT
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void CvHaarEvaluator::FeatureHaar::generateRandomFeature(Size patchSize)
|
||||
{
|
||||
cv::Point2i position;
|
||||
Size baseDim;
|
||||
Size sizeFactor;
|
||||
int area;
|
||||
|
||||
CV_Assert(!patchSize.empty());
|
||||
|
||||
//Size minSize = Size( 3, 3 );
|
||||
int minArea = 9;
|
||||
|
||||
bool valid = false;
|
||||
while (!valid)
|
||||
{
|
||||
//choose position and scale
|
||||
position.y = rand() % (patchSize.height);
|
||||
position.x = rand() % (patchSize.width);
|
||||
|
||||
baseDim.width = (int)((1 - sqrt(1 - (float)rand() * (float)(1.0 / RAND_MAX))) * patchSize.width);
|
||||
baseDim.height = (int)((1 - sqrt(1 - (float)rand() * (float)(1.0 / RAND_MAX))) * patchSize.height);
|
||||
|
||||
//select types
|
||||
//float probType[11] = {0.0909f, 0.0909f, 0.0909f, 0.0909f, 0.0909f, 0.0909f, 0.0909f, 0.0909f, 0.0909f, 0.0909f, 0.0950f};
|
||||
float probType[11] = { 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.2f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
float prob = (float)rand() * (float)(1.0 / RAND_MAX);
|
||||
|
||||
if (prob < probType[0])
|
||||
{
|
||||
//check if feature is valid
|
||||
sizeFactor.height = 2;
|
||||
sizeFactor.width = 1;
|
||||
if (position.y + baseDim.height * sizeFactor.height >= patchSize.height || position.x + baseDim.width * sizeFactor.width >= patchSize.width)
|
||||
continue;
|
||||
area = baseDim.height * sizeFactor.height * baseDim.width * sizeFactor.width;
|
||||
if (area < minArea)
|
||||
continue;
|
||||
|
||||
m_type = 1;
|
||||
m_numAreas = 2;
|
||||
m_weights.resize(m_numAreas);
|
||||
m_weights[0] = 1;
|
||||
m_weights[1] = -1;
|
||||
m_areas.resize(m_numAreas);
|
||||
m_areas[0].x = position.x;
|
||||
m_areas[0].y = position.y;
|
||||
m_areas[0].height = baseDim.height;
|
||||
m_areas[0].width = baseDim.width;
|
||||
m_areas[1].x = position.x;
|
||||
m_areas[1].y = position.y + baseDim.height;
|
||||
m_areas[1].height = baseDim.height;
|
||||
m_areas[1].width = baseDim.width;
|
||||
m_initMean = 0;
|
||||
m_initSigma = INITSIGMA(m_numAreas);
|
||||
|
||||
valid = true;
|
||||
}
|
||||
else if (prob < probType[0] + probType[1])
|
||||
{
|
||||
//check if feature is valid
|
||||
sizeFactor.height = 1;
|
||||
sizeFactor.width = 2;
|
||||
if (position.y + baseDim.height * sizeFactor.height >= patchSize.height || position.x + baseDim.width * sizeFactor.width >= patchSize.width)
|
||||
continue;
|
||||
area = baseDim.height * sizeFactor.height * baseDim.width * sizeFactor.width;
|
||||
if (area < minArea)
|
||||
continue;
|
||||
|
||||
m_type = 2;
|
||||
m_numAreas = 2;
|
||||
m_weights.resize(m_numAreas);
|
||||
m_weights[0] = 1;
|
||||
m_weights[1] = -1;
|
||||
m_areas.resize(m_numAreas);
|
||||
m_areas[0].x = position.x;
|
||||
m_areas[0].y = position.y;
|
||||
m_areas[0].height = baseDim.height;
|
||||
m_areas[0].width = baseDim.width;
|
||||
m_areas[1].x = position.x + baseDim.width;
|
||||
m_areas[1].y = position.y;
|
||||
m_areas[1].height = baseDim.height;
|
||||
m_areas[1].width = baseDim.width;
|
||||
m_initMean = 0;
|
||||
m_initSigma = INITSIGMA(m_numAreas);
|
||||
valid = true;
|
||||
}
|
||||
else if (prob < probType[0] + probType[1] + probType[2])
|
||||
{
|
||||
//check if feature is valid
|
||||
sizeFactor.height = 4;
|
||||
sizeFactor.width = 1;
|
||||
if (position.y + baseDim.height * sizeFactor.height >= patchSize.height || position.x + baseDim.width * sizeFactor.width >= patchSize.width)
|
||||
continue;
|
||||
area = baseDim.height * sizeFactor.height * baseDim.width * sizeFactor.width;
|
||||
if (area < minArea)
|
||||
continue;
|
||||
|
||||
m_type = 3;
|
||||
m_numAreas = 3;
|
||||
m_weights.resize(m_numAreas);
|
||||
m_weights[0] = 1;
|
||||
m_weights[1] = -2;
|
||||
m_weights[2] = 1;
|
||||
m_areas.resize(m_numAreas);
|
||||
m_areas[0].x = position.x;
|
||||
m_areas[0].y = position.y;
|
||||
m_areas[0].height = baseDim.height;
|
||||
m_areas[0].width = baseDim.width;
|
||||
m_areas[1].x = position.x;
|
||||
m_areas[1].y = position.y + baseDim.height;
|
||||
m_areas[1].height = 2 * baseDim.height;
|
||||
m_areas[1].width = baseDim.width;
|
||||
m_areas[2].y = position.y + 3 * baseDim.height;
|
||||
m_areas[2].x = position.x;
|
||||
m_areas[2].height = baseDim.height;
|
||||
m_areas[2].width = baseDim.width;
|
||||
m_initMean = 0;
|
||||
m_initSigma = INITSIGMA(m_numAreas);
|
||||
valid = true;
|
||||
}
|
||||
else if (prob < probType[0] + probType[1] + probType[2] + probType[3])
|
||||
{
|
||||
//check if feature is valid
|
||||
sizeFactor.height = 1;
|
||||
sizeFactor.width = 4;
|
||||
if (position.y + baseDim.height * sizeFactor.height >= patchSize.height || position.x + baseDim.width * sizeFactor.width >= patchSize.width)
|
||||
continue;
|
||||
area = baseDim.height * sizeFactor.height * baseDim.width * sizeFactor.width;
|
||||
if (area < minArea)
|
||||
continue;
|
||||
|
||||
m_type = 3;
|
||||
m_numAreas = 3;
|
||||
m_weights.resize(m_numAreas);
|
||||
m_weights[0] = 1;
|
||||
m_weights[1] = -2;
|
||||
m_weights[2] = 1;
|
||||
m_areas.resize(m_numAreas);
|
||||
m_areas[0].x = position.x;
|
||||
m_areas[0].y = position.y;
|
||||
m_areas[0].height = baseDim.height;
|
||||
m_areas[0].width = baseDim.width;
|
||||
m_areas[1].x = position.x + baseDim.width;
|
||||
m_areas[1].y = position.y;
|
||||
m_areas[1].height = baseDim.height;
|
||||
m_areas[1].width = 2 * baseDim.width;
|
||||
m_areas[2].y = position.y;
|
||||
m_areas[2].x = position.x + 3 * baseDim.width;
|
||||
m_areas[2].height = baseDim.height;
|
||||
m_areas[2].width = baseDim.width;
|
||||
m_initMean = 0;
|
||||
m_initSigma = INITSIGMA(m_numAreas);
|
||||
valid = true;
|
||||
}
|
||||
else if (prob < probType[0] + probType[1] + probType[2] + probType[3] + probType[4])
|
||||
{
|
||||
//check if feature is valid
|
||||
sizeFactor.height = 2;
|
||||
sizeFactor.width = 2;
|
||||
if (position.y + baseDim.height * sizeFactor.height >= patchSize.height || position.x + baseDim.width * sizeFactor.width >= patchSize.width)
|
||||
continue;
|
||||
area = baseDim.height * sizeFactor.height * baseDim.width * sizeFactor.width;
|
||||
if (area < minArea)
|
||||
continue;
|
||||
|
||||
m_type = 5;
|
||||
m_numAreas = 4;
|
||||
m_weights.resize(m_numAreas);
|
||||
m_weights[0] = 1;
|
||||
m_weights[1] = -1;
|
||||
m_weights[2] = -1;
|
||||
m_weights[3] = 1;
|
||||
m_areas.resize(m_numAreas);
|
||||
m_areas[0].x = position.x;
|
||||
m_areas[0].y = position.y;
|
||||
m_areas[0].height = baseDim.height;
|
||||
m_areas[0].width = baseDim.width;
|
||||
m_areas[1].x = position.x + baseDim.width;
|
||||
m_areas[1].y = position.y;
|
||||
m_areas[1].height = baseDim.height;
|
||||
m_areas[1].width = baseDim.width;
|
||||
m_areas[2].y = position.y + baseDim.height;
|
||||
m_areas[2].x = position.x;
|
||||
m_areas[2].height = baseDim.height;
|
||||
m_areas[2].width = baseDim.width;
|
||||
m_areas[3].y = position.y + baseDim.height;
|
||||
m_areas[3].x = position.x + baseDim.width;
|
||||
m_areas[3].height = baseDim.height;
|
||||
m_areas[3].width = baseDim.width;
|
||||
m_initMean = 0;
|
||||
m_initSigma = INITSIGMA(m_numAreas);
|
||||
valid = true;
|
||||
}
|
||||
else if (prob < probType[0] + probType[1] + probType[2] + probType[3] + probType[4] + probType[5])
|
||||
{
|
||||
//check if feature is valid
|
||||
sizeFactor.height = 3;
|
||||
sizeFactor.width = 3;
|
||||
if (position.y + baseDim.height * sizeFactor.height >= patchSize.height || position.x + baseDim.width * sizeFactor.width >= patchSize.width)
|
||||
continue;
|
||||
area = baseDim.height * sizeFactor.height * baseDim.width * sizeFactor.width;
|
||||
if (area < minArea)
|
||||
continue;
|
||||
|
||||
m_type = 6;
|
||||
m_numAreas = 2;
|
||||
m_weights.resize(m_numAreas);
|
||||
m_weights[0] = 1;
|
||||
m_weights[1] = -9;
|
||||
m_areas.resize(m_numAreas);
|
||||
m_areas[0].x = position.x;
|
||||
m_areas[0].y = position.y;
|
||||
m_areas[0].height = 3 * baseDim.height;
|
||||
m_areas[0].width = 3 * baseDim.width;
|
||||
m_areas[1].x = position.x + baseDim.width;
|
||||
m_areas[1].y = position.y + baseDim.height;
|
||||
m_areas[1].height = baseDim.height;
|
||||
m_areas[1].width = baseDim.width;
|
||||
m_initMean = -8 * 128;
|
||||
m_initSigma = INITSIGMA(m_numAreas);
|
||||
valid = true;
|
||||
}
|
||||
else if (prob < probType[0] + probType[1] + probType[2] + probType[3] + probType[4] + probType[5] + probType[6])
|
||||
{
|
||||
//check if feature is valid
|
||||
sizeFactor.height = 3;
|
||||
sizeFactor.width = 1;
|
||||
if (position.y + baseDim.height * sizeFactor.height >= patchSize.height || position.x + baseDim.width * sizeFactor.width >= patchSize.width)
|
||||
continue;
|
||||
area = baseDim.height * sizeFactor.height * baseDim.width * sizeFactor.width;
|
||||
if (area < minArea)
|
||||
continue;
|
||||
|
||||
m_type = 7;
|
||||
m_numAreas = 3;
|
||||
m_weights.resize(m_numAreas);
|
||||
m_weights[0] = 1;
|
||||
m_weights[1] = -2;
|
||||
m_weights[2] = 1;
|
||||
m_areas.resize(m_numAreas);
|
||||
m_areas[0].x = position.x;
|
||||
m_areas[0].y = position.y;
|
||||
m_areas[0].height = baseDim.height;
|
||||
m_areas[0].width = baseDim.width;
|
||||
m_areas[1].x = position.x;
|
||||
m_areas[1].y = position.y + baseDim.height;
|
||||
m_areas[1].height = baseDim.height;
|
||||
m_areas[1].width = baseDim.width;
|
||||
m_areas[2].y = position.y + baseDim.height * 2;
|
||||
m_areas[2].x = position.x;
|
||||
m_areas[2].height = baseDim.height;
|
||||
m_areas[2].width = baseDim.width;
|
||||
m_initMean = 0;
|
||||
m_initSigma = INITSIGMA(m_numAreas);
|
||||
valid = true;
|
||||
}
|
||||
else if (prob < probType[0] + probType[1] + probType[2] + probType[3] + probType[4] + probType[5] + probType[6] + probType[7])
|
||||
{
|
||||
//check if feature is valid
|
||||
sizeFactor.height = 1;
|
||||
sizeFactor.width = 3;
|
||||
if (position.y + baseDim.height * sizeFactor.height >= patchSize.height || position.x + baseDim.width * sizeFactor.width >= patchSize.width)
|
||||
continue;
|
||||
|
||||
area = baseDim.height * sizeFactor.height * baseDim.width * sizeFactor.width;
|
||||
|
||||
if (area < minArea)
|
||||
continue;
|
||||
|
||||
m_type = 8;
|
||||
m_numAreas = 3;
|
||||
m_weights.resize(m_numAreas);
|
||||
m_weights[0] = 1;
|
||||
m_weights[1] = -2;
|
||||
m_weights[2] = 1;
|
||||
m_areas.resize(m_numAreas);
|
||||
m_areas[0].x = position.x;
|
||||
m_areas[0].y = position.y;
|
||||
m_areas[0].height = baseDim.height;
|
||||
m_areas[0].width = baseDim.width;
|
||||
m_areas[1].x = position.x + baseDim.width;
|
||||
m_areas[1].y = position.y;
|
||||
m_areas[1].height = baseDim.height;
|
||||
m_areas[1].width = baseDim.width;
|
||||
m_areas[2].y = position.y;
|
||||
m_areas[2].x = position.x + 2 * baseDim.width;
|
||||
m_areas[2].height = baseDim.height;
|
||||
m_areas[2].width = baseDim.width;
|
||||
m_initMean = 0;
|
||||
m_initSigma = INITSIGMA(m_numAreas);
|
||||
valid = true;
|
||||
}
|
||||
else if (prob < probType[0] + probType[1] + probType[2] + probType[3] + probType[4] + probType[5] + probType[6] + probType[7] + probType[8])
|
||||
{
|
||||
//check if feature is valid
|
||||
sizeFactor.height = 3;
|
||||
sizeFactor.width = 3;
|
||||
if (position.y + baseDim.height * sizeFactor.height >= patchSize.height || position.x + baseDim.width * sizeFactor.width >= patchSize.width)
|
||||
continue;
|
||||
area = baseDim.height * sizeFactor.height * baseDim.width * sizeFactor.width;
|
||||
if (area < minArea)
|
||||
continue;
|
||||
|
||||
m_type = 9;
|
||||
m_numAreas = 2;
|
||||
m_weights.resize(m_numAreas);
|
||||
m_weights[0] = 1;
|
||||
m_weights[1] = -2;
|
||||
m_areas.resize(m_numAreas);
|
||||
m_areas[0].x = position.x;
|
||||
m_areas[0].y = position.y;
|
||||
m_areas[0].height = 3 * baseDim.height;
|
||||
m_areas[0].width = 3 * baseDim.width;
|
||||
m_areas[1].x = position.x + baseDim.width;
|
||||
m_areas[1].y = position.y + baseDim.height;
|
||||
m_areas[1].height = baseDim.height;
|
||||
m_areas[1].width = baseDim.width;
|
||||
m_initMean = 0;
|
||||
m_initSigma = INITSIGMA(m_numAreas);
|
||||
valid = true;
|
||||
}
|
||||
else if (prob
|
||||
< probType[0] + probType[1] + probType[2] + probType[3] + probType[4] + probType[5] + probType[6] + probType[7] + probType[8] + probType[9])
|
||||
{
|
||||
//check if feature is valid
|
||||
sizeFactor.height = 3;
|
||||
sizeFactor.width = 1;
|
||||
if (position.y + baseDim.height * sizeFactor.height >= patchSize.height || position.x + baseDim.width * sizeFactor.width >= patchSize.width)
|
||||
continue;
|
||||
area = baseDim.height * sizeFactor.height * baseDim.width * sizeFactor.width;
|
||||
if (area < minArea)
|
||||
continue;
|
||||
|
||||
m_type = 10;
|
||||
m_numAreas = 3;
|
||||
m_weights.resize(m_numAreas);
|
||||
m_weights[0] = 1;
|
||||
m_weights[1] = -1;
|
||||
m_weights[2] = 1;
|
||||
m_areas.resize(m_numAreas);
|
||||
m_areas[0].x = position.x;
|
||||
m_areas[0].y = position.y;
|
||||
m_areas[0].height = baseDim.height;
|
||||
m_areas[0].width = baseDim.width;
|
||||
m_areas[1].x = position.x;
|
||||
m_areas[1].y = position.y + baseDim.height;
|
||||
m_areas[1].height = baseDim.height;
|
||||
m_areas[1].width = baseDim.width;
|
||||
m_areas[2].y = position.y + baseDim.height * 2;
|
||||
m_areas[2].x = position.x;
|
||||
m_areas[2].height = baseDim.height;
|
||||
m_areas[2].width = baseDim.width;
|
||||
m_initMean = 128;
|
||||
m_initSigma = INITSIGMA(m_numAreas);
|
||||
valid = true;
|
||||
}
|
||||
else if (prob
|
||||
< probType[0] + probType[1] + probType[2] + probType[3] + probType[4] + probType[5] + probType[6] + probType[7] + probType[8] + probType[9]
|
||||
+ probType[10])
|
||||
{
|
||||
//check if feature is valid
|
||||
sizeFactor.height = 1;
|
||||
sizeFactor.width = 3;
|
||||
if (position.y + baseDim.height * sizeFactor.height >= patchSize.height || position.x + baseDim.width * sizeFactor.width >= patchSize.width)
|
||||
continue;
|
||||
area = baseDim.height * sizeFactor.height * baseDim.width * sizeFactor.width;
|
||||
if (area < minArea)
|
||||
continue;
|
||||
|
||||
m_type = 11;
|
||||
m_numAreas = 3;
|
||||
m_weights.resize(m_numAreas);
|
||||
m_weights[0] = 1;
|
||||
m_weights[1] = -1;
|
||||
m_weights[2] = 1;
|
||||
m_areas.resize(m_numAreas);
|
||||
m_areas[0].x = position.x;
|
||||
m_areas[0].y = position.y;
|
||||
m_areas[0].height = baseDim.height;
|
||||
m_areas[0].width = baseDim.width;
|
||||
m_areas[1].x = position.x + baseDim.width;
|
||||
m_areas[1].y = position.y;
|
||||
m_areas[1].height = baseDim.height;
|
||||
m_areas[1].width = baseDim.width;
|
||||
m_areas[2].y = position.y;
|
||||
m_areas[2].x = position.x + 2 * baseDim.width;
|
||||
m_areas[2].height = baseDim.height;
|
||||
m_areas[2].width = baseDim.width;
|
||||
m_initMean = 128;
|
||||
m_initSigma = INITSIGMA(m_numAreas);
|
||||
valid = true;
|
||||
}
|
||||
else
|
||||
CV_Error(Error::StsAssert, "");
|
||||
}
|
||||
|
||||
m_initSize = patchSize;
|
||||
m_curSize = m_initSize;
|
||||
m_scaleFactorWidth = m_scaleFactorHeight = 1.0f;
|
||||
m_scaleAreas.resize(m_numAreas);
|
||||
m_scaleWeights.resize(m_numAreas);
|
||||
for (int curArea = 0; curArea < m_numAreas; curArea++)
|
||||
{
|
||||
m_scaleAreas[curArea] = m_areas[curArea];
|
||||
m_scaleWeights[curArea] = (float)m_weights[curArea] / (float)(m_areas[curArea].width * m_areas[curArea].height);
|
||||
}
|
||||
}
|
||||
|
||||
bool CvHaarEvaluator::FeatureHaar::eval(const Mat& image, Rect /*ROI*/, float* result) const
|
||||
{
|
||||
|
||||
*result = 0.0f;
|
||||
|
||||
for (int curArea = 0; curArea < m_numAreas; curArea++)
|
||||
{
|
||||
*result += (float)getSum(image, Rect(m_areas[curArea].x, m_areas[curArea].y, m_areas[curArea].width, m_areas[curArea].height))
|
||||
* m_scaleWeights[curArea];
|
||||
}
|
||||
|
||||
/*
|
||||
if( image->getUseVariance() )
|
||||
{
|
||||
float variance = (float) image->getVariance( ROI );
|
||||
*result /= variance;
|
||||
}
|
||||
*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
float CvHaarEvaluator::FeatureHaar::getSum(const Mat& image, Rect imageROI) const
|
||||
{
|
||||
// left upper Origin
|
||||
int OriginX = imageROI.x;
|
||||
int OriginY = imageROI.y;
|
||||
|
||||
// Check and fix width and height
|
||||
int Width = imageROI.width;
|
||||
int Height = imageROI.height;
|
||||
|
||||
if (OriginX + Width >= image.cols - 1)
|
||||
Width = (image.cols - 1) - OriginX;
|
||||
if (OriginY + Height >= image.rows - 1)
|
||||
Height = (image.rows - 1) - OriginY;
|
||||
|
||||
float value = 0;
|
||||
int depth = image.depth();
|
||||
|
||||
if (depth == CV_8U || depth == CV_32S)
|
||||
value = static_cast<float>(image.at<int>(OriginY + Height, OriginX + Width) + image.at<int>(OriginY, OriginX) - image.at<int>(OriginY, OriginX + Width)
|
||||
- image.at<int>(OriginY + Height, OriginX));
|
||||
else if (depth == CV_64F)
|
||||
value = static_cast<float>(image.at<double>(OriginY + Height, OriginX + Width) + image.at<double>(OriginY, OriginX)
|
||||
- image.at<double>(OriginY, OriginX + Width) - image.at<double>(OriginY + Height, OriginX));
|
||||
else if (depth == CV_32F)
|
||||
value = static_cast<float>(image.at<float>(OriginY + Height, OriginX + Width) + image.at<float>(OriginY, OriginX) - image.at<float>(OriginY, OriginX + Width)
|
||||
- image.at<float>(OriginY + Height, OriginX));
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
}}} // namespace cv::detail::tracking
|
||||
@@ -0,0 +1,168 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef OPENCV_VIDEO_DETAIL_TRACKING_FEATURE_HPP
|
||||
#define OPENCV_VIDEO_DETAIL_TRACKING_FEATURE_HPP
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
|
||||
/*
|
||||
* TODO This implementation is based on apps/traincascade/
|
||||
* TODO Changed CvHaarEvaluator based on ADABOOSTING implementation (Grabner et al.)
|
||||
*/
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
//! @addtogroup tracking_detail
|
||||
//! @{
|
||||
|
||||
inline namespace feature {
|
||||
|
||||
class CvParams
|
||||
{
|
||||
public:
|
||||
CvParams();
|
||||
virtual ~CvParams()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class CvFeatureParams : public CvParams
|
||||
{
|
||||
public:
|
||||
enum FeatureType
|
||||
{
|
||||
HAAR = 0,
|
||||
LBP = 1,
|
||||
HOG = 2
|
||||
};
|
||||
|
||||
CvFeatureParams();
|
||||
static Ptr<CvFeatureParams> create(CvFeatureParams::FeatureType featureType);
|
||||
int maxCatCount; // 0 in case of numerical features
|
||||
int featSize; // 1 in case of simple features (HAAR, LBP) and N_BINS(9)*N_CELLS(4) in case of Dalal's HOG features
|
||||
int numFeatures;
|
||||
};
|
||||
|
||||
class CvFeatureEvaluator
|
||||
{
|
||||
public:
|
||||
virtual ~CvFeatureEvaluator()
|
||||
{
|
||||
}
|
||||
virtual void init(const CvFeatureParams* _featureParams, int _maxSampleCount, Size _winSize);
|
||||
virtual void setImage(const Mat& img, uchar clsLabel, int idx);
|
||||
static Ptr<CvFeatureEvaluator> create(CvFeatureParams::FeatureType type);
|
||||
|
||||
int getNumFeatures() const
|
||||
{
|
||||
return numFeatures;
|
||||
}
|
||||
int getMaxCatCount() const
|
||||
{
|
||||
return featureParams->maxCatCount;
|
||||
}
|
||||
int getFeatureSize() const
|
||||
{
|
||||
return featureParams->featSize;
|
||||
}
|
||||
const Mat& getCls() const
|
||||
{
|
||||
return cls;
|
||||
}
|
||||
float getCls(int si) const
|
||||
{
|
||||
return cls.at<float>(si, 0);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void generateFeatures() = 0;
|
||||
|
||||
int npos, nneg;
|
||||
int numFeatures;
|
||||
Size winSize;
|
||||
CvFeatureParams* featureParams;
|
||||
Mat cls;
|
||||
};
|
||||
|
||||
class CvHaarFeatureParams : public CvFeatureParams
|
||||
{
|
||||
public:
|
||||
CvHaarFeatureParams();
|
||||
bool isIntegral;
|
||||
};
|
||||
|
||||
class CvHaarEvaluator : public CvFeatureEvaluator
|
||||
{
|
||||
public:
|
||||
class FeatureHaar
|
||||
{
|
||||
|
||||
public:
|
||||
FeatureHaar(Size patchSize);
|
||||
bool eval(const Mat& image, Rect ROI, float* result) const;
|
||||
inline int getNumAreas() const { return m_numAreas; }
|
||||
inline const std::vector<float>& getWeights() const { return m_weights; }
|
||||
inline const std::vector<Rect>& getAreas() const { return m_areas; }
|
||||
|
||||
private:
|
||||
int m_type;
|
||||
int m_numAreas;
|
||||
std::vector<float> m_weights;
|
||||
float m_initMean;
|
||||
float m_initSigma;
|
||||
void generateRandomFeature(Size imageSize);
|
||||
float getSum(const Mat& image, Rect imgROI) const;
|
||||
std::vector<Rect> m_areas; // areas within the patch over which to compute the feature
|
||||
cv::Size m_initSize; // size of the patch used during training
|
||||
cv::Size m_curSize; // size of the patches currently under investigation
|
||||
float m_scaleFactorHeight; // scaling factor in vertical direction
|
||||
float m_scaleFactorWidth; // scaling factor in horizontal direction
|
||||
std::vector<Rect> m_scaleAreas; // areas after scaling
|
||||
std::vector<float> m_scaleWeights; // weights after scaling
|
||||
};
|
||||
|
||||
virtual void init(const CvFeatureParams* _featureParams, int _maxSampleCount, Size _winSize) CV_OVERRIDE;
|
||||
virtual void setImage(const Mat& img, uchar clsLabel = 0, int idx = 1) CV_OVERRIDE;
|
||||
inline const std::vector<CvHaarEvaluator::FeatureHaar>& getFeatures() const { return features; }
|
||||
inline CvHaarEvaluator::FeatureHaar& getFeatures(int idx)
|
||||
{
|
||||
return features[idx];
|
||||
}
|
||||
inline void setWinSize(Size patchSize) { winSize = patchSize; }
|
||||
inline Size getWinSize() const { return winSize; }
|
||||
virtual void generateFeatures() CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* \brief Overload the original generateFeatures in order to limit the number of the features
|
||||
* @param numFeatures Number of the features
|
||||
*/
|
||||
virtual void generateFeatures(int numFeatures);
|
||||
|
||||
protected:
|
||||
bool isIntegral;
|
||||
|
||||
/* TODO Added from MIL implementation */
|
||||
Mat _ii_img;
|
||||
void compute_integral(const cv::Mat& img, std::vector<cv::Mat_<float>>& ii_imgs)
|
||||
{
|
||||
Mat ii_img;
|
||||
integral(img, ii_img, CV_32F);
|
||||
split(ii_img, ii_imgs);
|
||||
}
|
||||
|
||||
std::vector<FeatureHaar> features;
|
||||
Mat sum; /* sum images (each row represents image) */
|
||||
};
|
||||
|
||||
} // namespace feature
|
||||
|
||||
//! @}
|
||||
|
||||
}}} // namespace cv::detail::tracking
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,356 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "../../precomp.hpp"
|
||||
#include "tracking_online_mil.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
#define sign(s) ((s > 0) ? 1 : ((s < 0) ? -1 : 0))
|
||||
|
||||
template <class T>
|
||||
class SortableElementRev
|
||||
{
|
||||
public:
|
||||
T _val;
|
||||
int _ind;
|
||||
SortableElementRev()
|
||||
: _val(), _ind(0)
|
||||
{
|
||||
}
|
||||
SortableElementRev(T val, int ind)
|
||||
{
|
||||
_val = val;
|
||||
_ind = ind;
|
||||
}
|
||||
bool operator<(SortableElementRev<T>& b)
|
||||
{
|
||||
return (_val < b._val);
|
||||
}
|
||||
};
|
||||
|
||||
static bool CompareSortableElementRev(const SortableElementRev<float>& i, const SortableElementRev<float>& j)
|
||||
{
|
||||
return i._val < j._val;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void sort_order_des(std::vector<T>& v, std::vector<int>& order)
|
||||
{
|
||||
uint n = (uint)v.size();
|
||||
std::vector<SortableElementRev<T>> v2;
|
||||
v2.resize(n);
|
||||
order.clear();
|
||||
order.resize(n);
|
||||
for (uint i = 0; i < n; i++)
|
||||
{
|
||||
v2[i]._ind = i;
|
||||
v2[i]._val = v[i];
|
||||
}
|
||||
//std::sort( v2.begin(), v2.end() );
|
||||
std::sort(v2.begin(), v2.end(), CompareSortableElementRev);
|
||||
for (uint i = 0; i < n; i++)
|
||||
{
|
||||
order[i] = v2[i]._ind;
|
||||
v[i] = v2[i]._val;
|
||||
}
|
||||
}
|
||||
|
||||
//implementations for strong classifier
|
||||
|
||||
ClfMilBoost::Params::Params()
|
||||
{
|
||||
_numSel = 50;
|
||||
_numFeat = 250;
|
||||
_lRate = 0.85f;
|
||||
}
|
||||
|
||||
ClfMilBoost::ClfMilBoost()
|
||||
: _numsamples(0)
|
||||
, _counter(0)
|
||||
{
|
||||
_myParams = ClfMilBoost::Params();
|
||||
_numsamples = 0;
|
||||
}
|
||||
|
||||
ClfMilBoost::~ClfMilBoost()
|
||||
{
|
||||
_selectors.clear();
|
||||
for (size_t i = 0; i < _weakclf.size(); i++)
|
||||
delete _weakclf.at(i);
|
||||
}
|
||||
|
||||
void ClfMilBoost::init(const ClfMilBoost::Params& parameters)
|
||||
{
|
||||
_myParams = parameters;
|
||||
_numsamples = 0;
|
||||
|
||||
//_ftrs = Ftr::generate( _myParams->_ftrParams, _myParams->_numFeat );
|
||||
// if( params->_storeFtrHistory )
|
||||
// Ftr::toViz( _ftrs, "haarftrs" );
|
||||
_weakclf.resize(_myParams._numFeat);
|
||||
for (int k = 0; k < _myParams._numFeat; k++)
|
||||
{
|
||||
_weakclf[k] = new ClfOnlineStump(k);
|
||||
_weakclf[k]->_lRate = _myParams._lRate;
|
||||
}
|
||||
_counter = 0;
|
||||
}
|
||||
|
||||
void ClfMilBoost::update(const Mat& posx, const Mat& negx)
|
||||
{
|
||||
int numneg = negx.rows;
|
||||
int numpos = posx.rows;
|
||||
|
||||
// compute ftrs
|
||||
//if( !posx.ftrsComputed() )
|
||||
// Ftr::compute( posx, _ftrs );
|
||||
//if( !negx.ftrsComputed() )
|
||||
// Ftr::compute( negx, _ftrs );
|
||||
|
||||
// initialize H
|
||||
static std::vector<float> Hpos, Hneg;
|
||||
Hpos.clear();
|
||||
Hneg.clear();
|
||||
Hpos.resize(posx.rows, 0.0f), Hneg.resize(negx.rows, 0.0f);
|
||||
|
||||
_selectors.clear();
|
||||
std::vector<float> posw(posx.rows), negw(negx.rows);
|
||||
std::vector<std::vector<float>> pospred(_weakclf.size()), negpred(_weakclf.size());
|
||||
|
||||
// train all weak classifiers without weights
|
||||
#ifdef _OPENMP
|
||||
#pragma omp parallel for
|
||||
#endif
|
||||
for (int m = 0; m < _myParams._numFeat; m++)
|
||||
{
|
||||
_weakclf[m]->update(posx, negx);
|
||||
pospred[m] = _weakclf[m]->classifySetF(posx);
|
||||
negpred[m] = _weakclf[m]->classifySetF(negx);
|
||||
}
|
||||
|
||||
// pick the best features
|
||||
for (int s = 0; s < _myParams._numSel; s++)
|
||||
{
|
||||
|
||||
// compute errors/likl for all weak clfs
|
||||
std::vector<float> poslikl(_weakclf.size(), 1.0f), neglikl(_weakclf.size()), likl(_weakclf.size());
|
||||
#ifdef _OPENMP
|
||||
#pragma omp parallel for
|
||||
#endif
|
||||
for (int w = 0; w < (int)_weakclf.size(); w++)
|
||||
{
|
||||
float lll = 1.0f;
|
||||
for (int j = 0; j < numpos; j++)
|
||||
lll *= (1 - sigmoid(Hpos[j] + pospred[w][j]));
|
||||
poslikl[w] = (float)-log(1 - lll + 1e-5);
|
||||
|
||||
lll = 0.0f;
|
||||
for (int j = 0; j < numneg; j++)
|
||||
lll += (float)-log(1e-5f + 1 - sigmoid(Hneg[j] + negpred[w][j]));
|
||||
neglikl[w] = lll;
|
||||
|
||||
likl[w] = poslikl[w] / numpos + neglikl[w] / numneg;
|
||||
}
|
||||
|
||||
// pick best weak clf
|
||||
std::vector<int> order;
|
||||
sort_order_des(likl, order);
|
||||
|
||||
// find best weakclf that isn't already included
|
||||
for (uint k = 0; k < order.size(); k++)
|
||||
if (std::count(_selectors.begin(), _selectors.end(), order[k]) == 0)
|
||||
{
|
||||
_selectors.push_back(order[k]);
|
||||
break;
|
||||
}
|
||||
|
||||
// update H = H + h_m
|
||||
#ifdef _OPENMP
|
||||
#pragma omp parallel for
|
||||
#endif
|
||||
for (int k = 0; k < posx.rows; k++)
|
||||
Hpos[k] += pospred[_selectors[s]][k];
|
||||
#ifdef _OPENMP
|
||||
#pragma omp parallel for
|
||||
#endif
|
||||
for (int k = 0; k < negx.rows; k++)
|
||||
Hneg[k] += negpred[_selectors[s]][k];
|
||||
}
|
||||
|
||||
//if( _myParams->_storeFtrHistory )
|
||||
//for ( uint j = 0; j < _selectors.size(); j++ )
|
||||
// _ftrHist( _selectors[j], _counter ) = 1.0f / ( j + 1 );
|
||||
|
||||
_counter++;
|
||||
/* */
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<float> ClfMilBoost::classify(const Mat& x, bool logR)
|
||||
{
|
||||
int numsamples = x.rows;
|
||||
std::vector<float> res(numsamples);
|
||||
std::vector<float> tr;
|
||||
|
||||
for (uint w = 0; w < _selectors.size(); w++)
|
||||
{
|
||||
tr = _weakclf[_selectors[w]]->classifySetF(x);
|
||||
#ifdef _OPENMP
|
||||
#pragma omp parallel for
|
||||
#endif
|
||||
for (int j = 0; j < numsamples; j++)
|
||||
{
|
||||
res[j] += tr[j];
|
||||
}
|
||||
}
|
||||
|
||||
// return probabilities or log odds ratio
|
||||
if (!logR)
|
||||
{
|
||||
#ifdef _OPENMP
|
||||
#pragma omp parallel for
|
||||
#endif
|
||||
for (int j = 0; j < (int)res.size(); j++)
|
||||
{
|
||||
res[j] = sigmoid(res[j]);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
//implementations for weak classifier
|
||||
|
||||
ClfOnlineStump::ClfOnlineStump()
|
||||
: _mu0(0), _mu1(0), _sig0(0), _sig1(0)
|
||||
, _q(0)
|
||||
, _s(0)
|
||||
, _log_n1(0), _log_n0(0)
|
||||
, _e1(0), _e0(0)
|
||||
, _lRate(0)
|
||||
{
|
||||
_trained = false;
|
||||
_ind = -1;
|
||||
init();
|
||||
}
|
||||
|
||||
ClfOnlineStump::ClfOnlineStump(int ind)
|
||||
: _mu0(0), _mu1(0), _sig0(0), _sig1(0)
|
||||
, _q(0)
|
||||
, _s(0)
|
||||
, _log_n1(0), _log_n0(0)
|
||||
, _e1(0), _e0(0)
|
||||
, _lRate(0)
|
||||
{
|
||||
_trained = false;
|
||||
_ind = ind;
|
||||
init();
|
||||
}
|
||||
void ClfOnlineStump::init()
|
||||
{
|
||||
_mu0 = 0;
|
||||
_mu1 = 0;
|
||||
_sig0 = 1;
|
||||
_sig1 = 1;
|
||||
_lRate = 0.85f;
|
||||
_trained = false;
|
||||
}
|
||||
|
||||
void ClfOnlineStump::update(const Mat& posx, const Mat& negx, const Mat_<float>& /*posw*/, const Mat_<float>& /*negw*/)
|
||||
{
|
||||
//std::cout << " ClfOnlineStump::update" << _ind << std::endl;
|
||||
float posmu = 0.0, negmu = 0.0;
|
||||
if (posx.cols > 0)
|
||||
posmu = float(mean(posx.col(_ind))[0]);
|
||||
if (negx.cols > 0)
|
||||
negmu = float(mean(negx.col(_ind))[0]);
|
||||
|
||||
if (_trained)
|
||||
{
|
||||
if (posx.cols > 0)
|
||||
{
|
||||
_mu1 = (_lRate * _mu1 + (1 - _lRate) * posmu);
|
||||
cv::Mat diff = posx.col(_ind) - _mu1;
|
||||
_sig1 = _lRate * _sig1 + (1 - _lRate) * float(mean(diff.mul(diff))[0]);
|
||||
}
|
||||
if (negx.cols > 0)
|
||||
{
|
||||
_mu0 = (_lRate * _mu0 + (1 - _lRate) * negmu);
|
||||
cv::Mat diff = negx.col(_ind) - _mu0;
|
||||
_sig0 = _lRate * _sig0 + (1 - _lRate) * float(mean(diff.mul(diff))[0]);
|
||||
}
|
||||
|
||||
_q = (_mu1 - _mu0) / 2;
|
||||
_s = sign(_mu1 - _mu0);
|
||||
_log_n0 = std::log(float(1.0f / std::pow(_sig0, 0.5f)));
|
||||
_log_n1 = std::log(float(1.0f / std::pow(_sig1, 0.5f)));
|
||||
//_e1 = -1.0f/(2.0f*_sig1+1e-99f);
|
||||
//_e0 = -1.0f/(2.0f*_sig0+1e-99f);
|
||||
_e1 = -1.0f / (2.0f * _sig1 + std::numeric_limits<float>::min());
|
||||
_e0 = -1.0f / (2.0f * _sig0 + std::numeric_limits<float>::min());
|
||||
}
|
||||
else
|
||||
{
|
||||
_trained = true;
|
||||
if (posx.cols > 0)
|
||||
{
|
||||
_mu1 = posmu;
|
||||
cv::Scalar scal_mean, scal_std_dev;
|
||||
cv::meanStdDev(posx.col(_ind), scal_mean, scal_std_dev);
|
||||
_sig1 = float(scal_std_dev[0]) * float(scal_std_dev[0]) + 1e-9f;
|
||||
}
|
||||
|
||||
if (negx.cols > 0)
|
||||
{
|
||||
_mu0 = negmu;
|
||||
cv::Scalar scal_mean, scal_std_dev;
|
||||
cv::meanStdDev(negx.col(_ind), scal_mean, scal_std_dev);
|
||||
_sig0 = float(scal_std_dev[0]) * float(scal_std_dev[0]) + 1e-9f;
|
||||
}
|
||||
|
||||
_q = (_mu1 - _mu0) / 2;
|
||||
_s = sign(_mu1 - _mu0);
|
||||
_log_n0 = std::log(float(1.0f / std::pow(_sig0, 0.5f)));
|
||||
_log_n1 = std::log(float(1.0f / std::pow(_sig1, 0.5f)));
|
||||
//_e1 = -1.0f/(2.0f*_sig1+1e-99f);
|
||||
//_e0 = -1.0f/(2.0f*_sig0+1e-99f);
|
||||
_e1 = -1.0f / (2.0f * _sig1 + std::numeric_limits<float>::min());
|
||||
_e0 = -1.0f / (2.0f * _sig0 + std::numeric_limits<float>::min());
|
||||
}
|
||||
}
|
||||
|
||||
bool ClfOnlineStump::classify(const Mat& x, int i)
|
||||
{
|
||||
float xx = x.at<float>(i, _ind);
|
||||
double log_p0 = (xx - _mu0) * (xx - _mu0) * _e0 + _log_n0;
|
||||
double log_p1 = (xx - _mu1) * (xx - _mu1) * _e1 + _log_n1;
|
||||
return log_p1 > log_p0;
|
||||
}
|
||||
|
||||
float ClfOnlineStump::classifyF(const Mat& x, int i)
|
||||
{
|
||||
float xx = x.at<float>(i, _ind);
|
||||
double log_p0 = (xx - _mu0) * (xx - _mu0) * _e0 + _log_n0;
|
||||
double log_p1 = (xx - _mu1) * (xx - _mu1) * _e1 + _log_n1;
|
||||
return float(log_p1 - log_p0);
|
||||
}
|
||||
|
||||
inline std::vector<float> ClfOnlineStump::classifySetF(const Mat& x)
|
||||
{
|
||||
std::vector<float> res(x.rows);
|
||||
|
||||
#ifdef _OPENMP
|
||||
#pragma omp parallel for
|
||||
#endif
|
||||
for (int k = 0; k < (int)res.size(); k++)
|
||||
{
|
||||
res[k] = classifyF(x, k);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
}}} // namespace cv::detail::tracking
|
||||
@@ -0,0 +1,79 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef OPENCV_VIDEO_DETAIL_TRACKING_ONLINE_MIL_HPP
|
||||
#define OPENCV_VIDEO_DETAIL_TRACKING_ONLINE_MIL_HPP
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace cv {
|
||||
namespace detail {
|
||||
inline namespace tracking {
|
||||
|
||||
//! @addtogroup tracking_detail
|
||||
//! @{
|
||||
|
||||
//TODO based on the original implementation
|
||||
//http://vision.ucsd.edu/~bbabenko/project_miltrack.shtml
|
||||
|
||||
class ClfOnlineStump;
|
||||
|
||||
class CV_EXPORTS ClfMilBoost
|
||||
{
|
||||
public:
|
||||
struct CV_EXPORTS Params
|
||||
{
|
||||
Params();
|
||||
int _numSel;
|
||||
int _numFeat;
|
||||
float _lRate;
|
||||
};
|
||||
|
||||
ClfMilBoost();
|
||||
~ClfMilBoost();
|
||||
void init(const ClfMilBoost::Params& parameters = ClfMilBoost::Params());
|
||||
void update(const Mat& posx, const Mat& negx);
|
||||
std::vector<float> classify(const Mat& x, bool logR = true);
|
||||
|
||||
inline float sigmoid(float x)
|
||||
{
|
||||
return 1.0f / (1.0f + exp(-x));
|
||||
}
|
||||
|
||||
private:
|
||||
uint _numsamples;
|
||||
ClfMilBoost::Params _myParams;
|
||||
std::vector<int> _selectors;
|
||||
std::vector<ClfOnlineStump*> _weakclf;
|
||||
uint _counter;
|
||||
};
|
||||
|
||||
class ClfOnlineStump
|
||||
{
|
||||
public:
|
||||
float _mu0, _mu1, _sig0, _sig1;
|
||||
float _q;
|
||||
int _s;
|
||||
float _log_n1, _log_n0;
|
||||
float _e1, _e0;
|
||||
float _lRate;
|
||||
|
||||
ClfOnlineStump();
|
||||
ClfOnlineStump(int ind);
|
||||
void init();
|
||||
void update(const Mat& posx, const Mat& negx, const cv::Mat_<float>& posw = cv::Mat_<float>(), const cv::Mat_<float>& negw = cv::Mat_<float>());
|
||||
bool classify(const Mat& x, int i);
|
||||
float classifyF(const Mat& x, int i);
|
||||
std::vector<float> classifySetF(const Mat& x);
|
||||
|
||||
private:
|
||||
bool _trained;
|
||||
int _ind;
|
||||
};
|
||||
|
||||
//! @}
|
||||
|
||||
}}} // namespace cv::detail::tracking
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "../precomp.hpp"
|
||||
|
||||
namespace cv {
|
||||
|
||||
Tracker::Tracker()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
Tracker::~Tracker()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
} // namespace cv
|
||||
@@ -0,0 +1,453 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "../precomp.hpp"
|
||||
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
#include "opencv2/dnn.hpp"
|
||||
#endif
|
||||
|
||||
namespace cv {
|
||||
|
||||
TrackerDaSiamRPN::TrackerDaSiamRPN()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
TrackerDaSiamRPN::~TrackerDaSiamRPN()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
TrackerDaSiamRPN::Params::Params()
|
||||
{
|
||||
model = "dasiamrpn_model.onnx";
|
||||
kernel_cls1 = "dasiamrpn_kernel_cls1.onnx";
|
||||
kernel_r1 = "dasiamrpn_kernel_r1.onnx";
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
backend = dnn::DNN_BACKEND_DEFAULT;
|
||||
target = dnn::DNN_TARGET_CPU;
|
||||
#else
|
||||
backend = -1; // invalid value
|
||||
target = -1; // invalid value
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
|
||||
template <typename T> static
|
||||
T sizeCal(const T& w, const T& h)
|
||||
{
|
||||
T pad = (w + h) * T(0.5);
|
||||
T sz2 = (w + pad) * (h + pad);
|
||||
return sqrt(sz2);
|
||||
}
|
||||
|
||||
template <>
|
||||
Mat sizeCal(const Mat& w, const Mat& h)
|
||||
{
|
||||
Mat pad = (w + h) * 0.5;
|
||||
Mat sz2 = (w + pad).mul((h + pad));
|
||||
|
||||
cv::sqrt(sz2, sz2);
|
||||
return sz2;
|
||||
}
|
||||
|
||||
class TrackerDaSiamRPNImpl : public TrackerDaSiamRPN
|
||||
{
|
||||
public:
|
||||
TrackerDaSiamRPNImpl(const TrackerDaSiamRPN::Params& parameters)
|
||||
{
|
||||
siamRPN = dnn::readNet(parameters.model);
|
||||
siamKernelCL1 = dnn::readNet(parameters.kernel_cls1);
|
||||
siamKernelR1 = dnn::readNet(parameters.kernel_r1);
|
||||
|
||||
CV_Assert(!siamRPN.empty());
|
||||
CV_Assert(!siamKernelCL1.empty());
|
||||
CV_Assert(!siamKernelR1.empty());
|
||||
|
||||
siamRPN.setPreferableBackend(parameters.backend);
|
||||
siamRPN.setPreferableTarget(parameters.target);
|
||||
siamKernelR1.setPreferableBackend(parameters.backend);
|
||||
siamKernelR1.setPreferableTarget(parameters.target);
|
||||
siamKernelCL1.setPreferableBackend(parameters.backend);
|
||||
siamKernelCL1.setPreferableTarget(parameters.target);
|
||||
}
|
||||
|
||||
TrackerDaSiamRPNImpl(const dnn::Net& siam_rpn, const dnn::Net& kernel_cls1, const dnn::Net& kernel_r1)
|
||||
{
|
||||
CV_Assert(!siam_rpn.empty());
|
||||
CV_Assert(!kernel_cls1.empty());
|
||||
CV_Assert(!kernel_r1.empty());
|
||||
|
||||
siamRPN = siam_rpn;
|
||||
siamKernelCL1 = kernel_cls1;
|
||||
siamKernelR1 = kernel_r1;
|
||||
}
|
||||
|
||||
void init(InputArray image, const Rect& boundingBox) CV_OVERRIDE;
|
||||
bool update(InputArray image, Rect& boundingBox) CV_OVERRIDE;
|
||||
float getTrackingScore() CV_OVERRIDE;
|
||||
|
||||
protected:
|
||||
dnn::Net siamRPN, siamKernelR1, siamKernelCL1;
|
||||
Rect boundingBox_;
|
||||
Mat image_;
|
||||
struct trackerConfig
|
||||
{
|
||||
float windowInfluence = 0.43f;
|
||||
float lr = 0.4f;
|
||||
int scale = 8;
|
||||
bool swapRB = false;
|
||||
int totalStride = 8;
|
||||
float penaltyK = 0.055f;
|
||||
int exemplarSize = 127;
|
||||
int instanceSize = 271;
|
||||
float contextAmount = 0.5f;
|
||||
std::vector<float> ratios = { 0.33f, 0.5f, 1.0f, 2.0f, 3.0f };
|
||||
int anchorNum = int(ratios.size());
|
||||
Mat anchors;
|
||||
Mat windows;
|
||||
Scalar avgChans;
|
||||
Size imgSize = { 0, 0 };
|
||||
Rect2f targetBox = { 0, 0, 0, 0 };
|
||||
int scoreSize = (instanceSize - exemplarSize) / totalStride + 1;
|
||||
float tracking_score;
|
||||
|
||||
void update_scoreSize()
|
||||
{
|
||||
scoreSize = int((instanceSize - exemplarSize) / totalStride + 1);
|
||||
}
|
||||
};
|
||||
trackerConfig trackState;
|
||||
|
||||
void softmax(const Mat& src, Mat& dst);
|
||||
void elementMax(Mat& src);
|
||||
Mat generateHanningWindow();
|
||||
Mat generateAnchors();
|
||||
Mat getSubwindow(Mat& img, const Rect2f& targetBox, float originalSize, Scalar avgChans);
|
||||
void trackerInit(Mat img);
|
||||
void trackerEval(Mat img);
|
||||
};
|
||||
|
||||
void TrackerDaSiamRPNImpl::init(InputArray image, const Rect& boundingBox)
|
||||
{
|
||||
image_ = image.getMat().clone();
|
||||
|
||||
trackState.update_scoreSize();
|
||||
trackState.targetBox = Rect2f(
|
||||
float(boundingBox.x) + float(boundingBox.width) * 0.5f, // FIXIT don't use center in Rect structures, it is confusing
|
||||
float(boundingBox.y) + float(boundingBox.height) * 0.5f,
|
||||
float(boundingBox.width),
|
||||
float(boundingBox.height)
|
||||
);
|
||||
trackerInit(image_);
|
||||
}
|
||||
|
||||
void TrackerDaSiamRPNImpl::trackerInit(Mat img)
|
||||
{
|
||||
Rect2f targetBox = trackState.targetBox;
|
||||
Mat anchors = generateAnchors();
|
||||
trackState.anchors = anchors;
|
||||
|
||||
Mat windows = generateHanningWindow();
|
||||
|
||||
trackState.windows = windows;
|
||||
trackState.imgSize = img.size();
|
||||
|
||||
trackState.avgChans = mean(img);
|
||||
float wc = targetBox.width + trackState.contextAmount * (targetBox.width + targetBox.height);
|
||||
float hc = targetBox.height + trackState.contextAmount * (targetBox.width + targetBox.height);
|
||||
float sz = (float)cvRound(sqrt(wc * hc));
|
||||
|
||||
Mat zCrop = getSubwindow(img, targetBox, sz, trackState.avgChans);
|
||||
Mat blob;
|
||||
|
||||
dnn::blobFromImage(zCrop, blob, 1.0, Size(trackState.exemplarSize, trackState.exemplarSize), Scalar(), trackState.swapRB, false, CV_32F);
|
||||
siamRPN.setInput(blob);
|
||||
Mat out1;
|
||||
siamRPN.forward(out1, "onnx_node_output_0!63");
|
||||
|
||||
siamKernelCL1.setInput(out1);
|
||||
siamKernelR1.setInput(out1);
|
||||
|
||||
Mat cls1 = siamKernelCL1.forward();
|
||||
Mat r1 = siamKernelR1.forward();
|
||||
std::vector<int> r1_shape = { 20, 256, 4, 4 }, cls1_shape = { 10, 256, 4, 4 };
|
||||
|
||||
siamRPN.setParam("onnx_node_output_0!65", 0, r1.reshape(0, r1_shape));
|
||||
siamRPN.setParam("onnx_node_output_0!68", 0, cls1.reshape(0, cls1_shape));
|
||||
}
|
||||
|
||||
bool TrackerDaSiamRPNImpl::update(InputArray image, Rect& boundingBox)
|
||||
{
|
||||
image_ = image.getMat().clone();
|
||||
trackerEval(image_);
|
||||
boundingBox = {
|
||||
int(trackState.targetBox.x - int(trackState.targetBox.width / 2)),
|
||||
int(trackState.targetBox.y - int(trackState.targetBox.height / 2)),
|
||||
int(trackState.targetBox.width),
|
||||
int(trackState.targetBox.height)
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
void TrackerDaSiamRPNImpl::trackerEval(Mat img)
|
||||
{
|
||||
Rect2f targetBox = trackState.targetBox;
|
||||
|
||||
float wc = targetBox.height + trackState.contextAmount * (targetBox.width + targetBox.height);
|
||||
float hc = targetBox.width + trackState.contextAmount * (targetBox.width + targetBox.height);
|
||||
|
||||
float sz = sqrt(wc * hc);
|
||||
float scaleZ = trackState.exemplarSize / sz;
|
||||
|
||||
float searchSize = float((trackState.instanceSize - trackState.exemplarSize) / 2);
|
||||
float pad = searchSize / scaleZ;
|
||||
float sx = sz + 2 * pad;
|
||||
|
||||
Mat xCrop = getSubwindow(img, targetBox, (float)cvRound(sx), trackState.avgChans);
|
||||
|
||||
Mat blob;
|
||||
std::vector<Mat> outs;
|
||||
std::vector<String> outNames;
|
||||
Mat delta, score;
|
||||
Mat sc, rc, penalty, pscore;
|
||||
|
||||
dnn::blobFromImage(xCrop, blob, 1.0, Size(trackState.instanceSize, trackState.instanceSize), Scalar(), trackState.swapRB, false, CV_32F);
|
||||
|
||||
siamRPN.setInput(blob);
|
||||
|
||||
outNames = siamRPN.getUnconnectedOutLayersNames();
|
||||
siamRPN.forward(outs, outNames);
|
||||
|
||||
delta = outs[0];
|
||||
score = outs[1];
|
||||
|
||||
score = score.reshape(0, { 2, trackState.anchorNum, trackState.scoreSize, trackState.scoreSize });
|
||||
delta = delta.reshape(0, { 4, trackState.anchorNum, trackState.scoreSize, trackState.scoreSize });
|
||||
|
||||
softmax(score, score);
|
||||
|
||||
targetBox.width *= scaleZ;
|
||||
targetBox.height *= scaleZ;
|
||||
|
||||
score = score.row(1);
|
||||
score = score.reshape(0, { 5, 19, 19 });
|
||||
|
||||
// Post processing
|
||||
delta.row(0) = delta.row(0).mul(trackState.anchors.row(2)) + trackState.anchors.row(0);
|
||||
delta.row(1) = delta.row(1).mul(trackState.anchors.row(3)) + trackState.anchors.row(1);
|
||||
exp(delta.row(2), delta.row(2));
|
||||
delta.row(2) = delta.row(2).mul(trackState.anchors.row(2));
|
||||
exp(delta.row(3), delta.row(3));
|
||||
delta.row(3) = delta.row(3).mul(trackState.anchors.row(3));
|
||||
|
||||
sc = sizeCal(delta.row(2), delta.row(3)) / sizeCal(targetBox.width, targetBox.height);
|
||||
elementMax(sc);
|
||||
|
||||
rc = delta.row(2).mul(1 / delta.row(3));
|
||||
rc = (targetBox.width / targetBox.height) / rc;
|
||||
elementMax(rc);
|
||||
|
||||
// Calculating the penalty
|
||||
exp(((rc.mul(sc) - 1.) * trackState.penaltyK * (-1.0)), penalty);
|
||||
penalty = penalty.reshape(0, { trackState.anchorNum, trackState.scoreSize, trackState.scoreSize });
|
||||
|
||||
pscore = penalty.mul(score);
|
||||
pscore = pscore * (1.0 - trackState.windowInfluence) + trackState.windows * trackState.windowInfluence;
|
||||
|
||||
int bestID[2] = { 0, 0 };
|
||||
// Find the index of best score.
|
||||
minMaxIdx(pscore.reshape(0, { trackState.anchorNum * trackState.scoreSize * trackState.scoreSize, 1 }), 0, 0, 0, bestID);
|
||||
delta = delta.reshape(0, { 4, trackState.anchorNum * trackState.scoreSize * trackState.scoreSize });
|
||||
penalty = penalty.reshape(0, { trackState.anchorNum * trackState.scoreSize * trackState.scoreSize, 1 });
|
||||
score = score.reshape(0, { trackState.anchorNum * trackState.scoreSize * trackState.scoreSize, 1 });
|
||||
|
||||
int index[2] = { 0, bestID[0] };
|
||||
Rect2f resBox = { 0, 0, 0, 0 };
|
||||
|
||||
resBox.x = delta.at<float>(index) / scaleZ;
|
||||
index[0] = 1;
|
||||
resBox.y = delta.at<float>(index) / scaleZ;
|
||||
index[0] = 2;
|
||||
resBox.width = delta.at<float>(index) / scaleZ;
|
||||
index[0] = 3;
|
||||
resBox.height = delta.at<float>(index) / scaleZ;
|
||||
|
||||
float lr = penalty.at<float>(bestID) * score.at<float>(bestID) * trackState.lr;
|
||||
|
||||
resBox.x = resBox.x + targetBox.x;
|
||||
resBox.y = resBox.y + targetBox.y;
|
||||
targetBox.width /= scaleZ;
|
||||
targetBox.height /= scaleZ;
|
||||
|
||||
resBox.width = targetBox.width * (1 - lr) + resBox.width * lr;
|
||||
resBox.height = targetBox.height * (1 - lr) + resBox.height * lr;
|
||||
|
||||
resBox.x = float(fmax(0., fmin(float(trackState.imgSize.width), resBox.x)));
|
||||
resBox.y = float(fmax(0., fmin(float(trackState.imgSize.height), resBox.y)));
|
||||
resBox.width = float(fmax(10., fmin(float(trackState.imgSize.width), resBox.width)));
|
||||
resBox.height = float(fmax(10., fmin(float(trackState.imgSize.height), resBox.height)));
|
||||
|
||||
trackState.targetBox = resBox;
|
||||
trackState.tracking_score = score.at<float>(bestID);
|
||||
}
|
||||
|
||||
float TrackerDaSiamRPNImpl::getTrackingScore()
|
||||
{
|
||||
return trackState.tracking_score;
|
||||
}
|
||||
|
||||
void TrackerDaSiamRPNImpl::softmax(const Mat& src, Mat& dst)
|
||||
{
|
||||
Mat maxVal;
|
||||
cv::max(src.row(1), src.row(0), maxVal);
|
||||
|
||||
src.row(1) -= maxVal;
|
||||
src.row(0) -= maxVal;
|
||||
|
||||
exp(src, dst);
|
||||
|
||||
Mat sumVal = dst.row(0) + dst.row(1);
|
||||
dst.row(0) = dst.row(0) / sumVal;
|
||||
dst.row(1) = dst.row(1) / sumVal;
|
||||
}
|
||||
|
||||
void TrackerDaSiamRPNImpl::elementMax(Mat& src)
|
||||
{
|
||||
int* p = src.size.p;
|
||||
int index[4] = { 0, 0, 0, 0 };
|
||||
for (int n = 0; n < *p; n++)
|
||||
{
|
||||
for (int k = 0; k < *(p + 1); k++)
|
||||
{
|
||||
for (int i = 0; i < *(p + 2); i++)
|
||||
{
|
||||
for (int j = 0; j < *(p + 3); j++)
|
||||
{
|
||||
index[0] = n, index[1] = k, index[2] = i, index[3] = j;
|
||||
float& v = src.at<float>(index);
|
||||
v = fmax(v, 1.0f / v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Mat TrackerDaSiamRPNImpl::generateHanningWindow()
|
||||
{
|
||||
Mat baseWindows, HanningWindows;
|
||||
|
||||
createHanningWindow(baseWindows, Size(trackState.scoreSize, trackState.scoreSize), CV_32F);
|
||||
baseWindows = baseWindows.reshape(0, { 1, trackState.scoreSize, trackState.scoreSize });
|
||||
HanningWindows = baseWindows.clone();
|
||||
for (int i = 1; i < trackState.anchorNum; i++)
|
||||
{
|
||||
HanningWindows.push_back(baseWindows);
|
||||
}
|
||||
|
||||
return HanningWindows;
|
||||
}
|
||||
|
||||
Mat TrackerDaSiamRPNImpl::generateAnchors()
|
||||
{
|
||||
int totalStride = trackState.totalStride, scales = trackState.scale, scoreSize = trackState.scoreSize;
|
||||
std::vector<float> ratios = trackState.ratios;
|
||||
std::vector<Rect2f> baseAnchors;
|
||||
int anchorNum = int(ratios.size());
|
||||
int size = totalStride * totalStride;
|
||||
|
||||
float ori = -(float(scoreSize / 2)) * float(totalStride);
|
||||
|
||||
for (auto i = 0; i < anchorNum; i++)
|
||||
{
|
||||
int ws = int(sqrt(size / ratios[i]));
|
||||
int hs = int(ws * ratios[i]);
|
||||
|
||||
float wws = float(ws) * scales;
|
||||
float hhs = float(hs) * scales;
|
||||
Rect2f anchor = { 0, 0, wws, hhs };
|
||||
baseAnchors.push_back(anchor);
|
||||
}
|
||||
|
||||
int anchorIndex[4] = { 0, 0, 0, 0 };
|
||||
const int sizes[4] = { 4, (int)ratios.size(), scoreSize, scoreSize };
|
||||
Mat anchors(4, sizes, CV_32F);
|
||||
|
||||
for (auto i = 0; i < scoreSize; i++)
|
||||
{
|
||||
for (auto j = 0; j < scoreSize; j++)
|
||||
{
|
||||
for (auto k = 0; k < anchorNum; k++)
|
||||
{
|
||||
anchorIndex[0] = 1, anchorIndex[1] = k, anchorIndex[2] = i, anchorIndex[3] = j;
|
||||
anchors.at<float>(anchorIndex) = ori + totalStride * i;
|
||||
|
||||
anchorIndex[0] = 0;
|
||||
anchors.at<float>(anchorIndex) = ori + totalStride * j;
|
||||
|
||||
anchorIndex[0] = 2;
|
||||
anchors.at<float>(anchorIndex) = baseAnchors[k].width;
|
||||
|
||||
anchorIndex[0] = 3;
|
||||
anchors.at<float>(anchorIndex) = baseAnchors[k].height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return anchors;
|
||||
}
|
||||
|
||||
Mat TrackerDaSiamRPNImpl::getSubwindow(Mat& img, const Rect2f& targetBox, float originalSize, Scalar avgChans)
|
||||
{
|
||||
Mat zCrop, dst;
|
||||
Size imgSize = img.size();
|
||||
float c = (originalSize + 1) / 2;
|
||||
float xMin = (float)cvRound(targetBox.x - c);
|
||||
float xMax = xMin + originalSize - 1;
|
||||
float yMin = (float)cvRound(targetBox.y - c);
|
||||
float yMax = yMin + originalSize - 1;
|
||||
|
||||
int leftPad = (int)(fmax(0., -xMin));
|
||||
int topPad = (int)(fmax(0., -yMin));
|
||||
int rightPad = (int)(fmax(0., xMax - imgSize.width + 1));
|
||||
int bottomPad = (int)(fmax(0., yMax - imgSize.height + 1));
|
||||
|
||||
xMin = xMin + leftPad;
|
||||
xMax = xMax + leftPad;
|
||||
yMax = yMax + topPad;
|
||||
yMin = yMin + topPad;
|
||||
|
||||
if (topPad == 0 && bottomPad == 0 && leftPad == 0 && rightPad == 0)
|
||||
{
|
||||
img(Rect(int(xMin), int(yMin), int(xMax - xMin + 1), int(yMax - yMin + 1))).copyTo(zCrop);
|
||||
}
|
||||
else
|
||||
{
|
||||
copyMakeBorder(img, dst, topPad, bottomPad, leftPad, rightPad, BORDER_CONSTANT, avgChans);
|
||||
dst(Rect(int(xMin), int(yMin), int(xMax - xMin + 1), int(yMax - yMin + 1))).copyTo(zCrop);
|
||||
}
|
||||
|
||||
return zCrop;
|
||||
}
|
||||
|
||||
Ptr<TrackerDaSiamRPN> TrackerDaSiamRPN::create(const TrackerDaSiamRPN::Params& parameters)
|
||||
{
|
||||
return makePtr<TrackerDaSiamRPNImpl>(parameters);
|
||||
}
|
||||
|
||||
Ptr<TrackerDaSiamRPN> TrackerDaSiamRPN::create(const dnn::Net& siam_rpn, const dnn::Net& kernel_cls1, const dnn::Net& kernel_r1)
|
||||
{
|
||||
return makePtr<TrackerDaSiamRPNImpl>(siam_rpn, kernel_cls1, kernel_r1);
|
||||
}
|
||||
|
||||
#else // OPENCV_HAVE_DNN
|
||||
Ptr<TrackerDaSiamRPN> TrackerDaSiamRPN::create(const TrackerDaSiamRPN::Params& parameters)
|
||||
{
|
||||
(void)(parameters);
|
||||
CV_Error(cv::Error::StsNotImplemented, "to use DaSiamRPN, the tracking module needs to be built with opencv_dnn !");
|
||||
}
|
||||
#endif // OPENCV_HAVE_DNN
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include "detail/tracker_mil_model.hpp"
|
||||
|
||||
#include "detail/tracker_feature_haar.impl.hpp"
|
||||
|
||||
namespace cv {
|
||||
inline namespace tracking {
|
||||
namespace impl {
|
||||
|
||||
using cv::detail::tracking::internal::TrackerFeatureHAAR;
|
||||
|
||||
|
||||
class TrackerMILImpl CV_FINAL : public TrackerMIL
|
||||
{
|
||||
public:
|
||||
TrackerMILImpl(const TrackerMIL::Params& parameters);
|
||||
|
||||
virtual void init(InputArray image, const Rect& boundingBox) CV_OVERRIDE;
|
||||
virtual bool update(InputArray image, Rect& boundingBox) CV_OVERRIDE;
|
||||
|
||||
void compute_integral(const Mat& img, Mat& ii_img);
|
||||
|
||||
TrackerMIL::Params params;
|
||||
|
||||
Ptr<TrackerMILModel> model;
|
||||
Ptr<TrackerSampler> sampler;
|
||||
Ptr<TrackerFeatureSet> featureSet;
|
||||
};
|
||||
|
||||
TrackerMILImpl::TrackerMILImpl(const TrackerMIL::Params& parameters)
|
||||
: params(parameters)
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
void TrackerMILImpl::compute_integral(const Mat& img, Mat& ii_img)
|
||||
{
|
||||
Mat ii;
|
||||
std::vector<Mat> ii_imgs;
|
||||
integral(img, ii, CV_32F); // FIXIT split first
|
||||
split(ii, ii_imgs);
|
||||
ii_img = ii_imgs[0];
|
||||
}
|
||||
|
||||
void TrackerMILImpl::init(InputArray image, const Rect& boundingBox)
|
||||
{
|
||||
sampler = makePtr<TrackerSampler>();
|
||||
featureSet = makePtr<TrackerFeatureSet>();
|
||||
|
||||
Mat intImage;
|
||||
compute_integral(image.getMat(), intImage);
|
||||
TrackerSamplerCSC::Params CSCparameters;
|
||||
CSCparameters.initInRad = params.samplerInitInRadius;
|
||||
CSCparameters.searchWinSize = params.samplerSearchWinSize;
|
||||
CSCparameters.initMaxNegNum = params.samplerInitMaxNegNum;
|
||||
CSCparameters.trackInPosRad = params.samplerTrackInRadius;
|
||||
CSCparameters.trackMaxPosNum = params.samplerTrackMaxPosNum;
|
||||
CSCparameters.trackMaxNegNum = params.samplerTrackMaxNegNum;
|
||||
|
||||
Ptr<TrackerSamplerAlgorithm> CSCSampler = makePtr<TrackerSamplerCSC>(CSCparameters);
|
||||
CV_Assert(sampler->addTrackerSamplerAlgorithm(CSCSampler));
|
||||
|
||||
//or add CSC sampler with default parameters
|
||||
//sampler->addTrackerSamplerAlgorithm( "CSC" );
|
||||
|
||||
//Positive sampling
|
||||
CSCSampler.staticCast<TrackerSamplerCSC>()->setMode(TrackerSamplerCSC::MODE_INIT_POS);
|
||||
sampler->sampling(intImage, boundingBox);
|
||||
std::vector<Mat> posSamples = sampler->getSamples();
|
||||
|
||||
//Negative sampling
|
||||
CSCSampler.staticCast<TrackerSamplerCSC>()->setMode(TrackerSamplerCSC::MODE_INIT_NEG);
|
||||
sampler->sampling(intImage, boundingBox);
|
||||
std::vector<Mat> negSamples = sampler->getSamples();
|
||||
|
||||
CV_Assert(!posSamples.empty());
|
||||
CV_Assert(!negSamples.empty());
|
||||
|
||||
//compute HAAR features
|
||||
TrackerFeatureHAAR::Params HAARparameters;
|
||||
HAARparameters.numFeatures = params.featureSetNumFeatures;
|
||||
HAARparameters.rectSize = Size((int)boundingBox.width, (int)boundingBox.height);
|
||||
HAARparameters.isIntegral = true;
|
||||
Ptr<TrackerFeature> trackerFeature = makePtr<TrackerFeatureHAAR>(HAARparameters);
|
||||
featureSet->addTrackerFeature(trackerFeature);
|
||||
|
||||
featureSet->extraction(posSamples);
|
||||
const std::vector<Mat> posResponse = featureSet->getResponses();
|
||||
|
||||
featureSet->extraction(negSamples);
|
||||
const std::vector<Mat> negResponse = featureSet->getResponses();
|
||||
|
||||
model = makePtr<TrackerMILModel>(boundingBox);
|
||||
Ptr<TrackerStateEstimatorMILBoosting> stateEstimator = makePtr<TrackerStateEstimatorMILBoosting>(params.featureSetNumFeatures);
|
||||
model->setTrackerStateEstimator(stateEstimator);
|
||||
|
||||
//Run model estimation and update
|
||||
model.staticCast<TrackerMILModel>()->setMode(TrackerMILModel::MODE_POSITIVE, posSamples);
|
||||
model->modelEstimation(posResponse);
|
||||
model.staticCast<TrackerMILModel>()->setMode(TrackerMILModel::MODE_NEGATIVE, negSamples);
|
||||
model->modelEstimation(negResponse);
|
||||
model->modelUpdate();
|
||||
}
|
||||
|
||||
bool TrackerMILImpl::update(InputArray image, Rect& boundingBox)
|
||||
{
|
||||
Mat intImage;
|
||||
compute_integral(image.getMat(), intImage);
|
||||
|
||||
//get the last location [AAM] X(k-1)
|
||||
Ptr<TrackerTargetState> lastLocation = model->getLastTargetState();
|
||||
Rect lastBoundingBox((int)lastLocation->getTargetPosition().x, (int)lastLocation->getTargetPosition().y, lastLocation->getTargetWidth(),
|
||||
lastLocation->getTargetHeight());
|
||||
|
||||
//sampling new frame based on last location
|
||||
auto& samplers = sampler->getSamplers();
|
||||
CV_Assert(!samplers.empty());
|
||||
CV_Assert(samplers[0]);
|
||||
samplers[0].staticCast<TrackerSamplerCSC>()->setMode(TrackerSamplerCSC::MODE_DETECT);
|
||||
sampler->sampling(intImage, lastBoundingBox);
|
||||
std::vector<Mat> detectSamples = sampler->getSamples();
|
||||
if (detectSamples.empty())
|
||||
return false;
|
||||
|
||||
/*//TODO debug samples
|
||||
Mat f;
|
||||
image.copyTo(f);
|
||||
|
||||
for( size_t i = 0; i < detectSamples.size(); i=i+10 )
|
||||
{
|
||||
Size sz;
|
||||
Point off;
|
||||
detectSamples.at(i).locateROI(sz, off);
|
||||
rectangle(f, Rect(off.x,off.y,detectSamples.at(i).cols,detectSamples.at(i).rows), Scalar(255,0,0), 1);
|
||||
}*/
|
||||
|
||||
//extract features from new samples
|
||||
featureSet->extraction(detectSamples);
|
||||
std::vector<Mat> response = featureSet->getResponses();
|
||||
|
||||
//predict new location
|
||||
ConfidenceMap cmap;
|
||||
model.staticCast<TrackerMILModel>()->setMode(TrackerMILModel::MODE_ESTIMATON, detectSamples);
|
||||
model.staticCast<TrackerMILModel>()->responseToConfidenceMap(response, cmap);
|
||||
model->getTrackerStateEstimator().staticCast<TrackerStateEstimatorMILBoosting>()->setCurrentConfidenceMap(cmap);
|
||||
|
||||
if (!model->runStateEstimator())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Ptr<TrackerTargetState> currentState = model->getLastTargetState();
|
||||
boundingBox = Rect((int)currentState->getTargetPosition().x, (int)currentState->getTargetPosition().y, currentState->getTargetWidth(),
|
||||
currentState->getTargetHeight());
|
||||
|
||||
/*//TODO debug
|
||||
rectangle(f, lastBoundingBox, Scalar(0,255,0), 1);
|
||||
rectangle(f, boundingBox, Scalar(0,0,255), 1);
|
||||
imshow("f", f);
|
||||
//waitKey( 0 );*/
|
||||
|
||||
//sampling new frame based on new location
|
||||
//Positive sampling
|
||||
samplers[0].staticCast<TrackerSamplerCSC>()->setMode(TrackerSamplerCSC::MODE_INIT_POS);
|
||||
sampler->sampling(intImage, boundingBox);
|
||||
std::vector<Mat> posSamples = sampler->getSamples();
|
||||
|
||||
//Negative sampling
|
||||
samplers[0].staticCast<TrackerSamplerCSC>()->setMode(TrackerSamplerCSC::MODE_INIT_NEG);
|
||||
sampler->sampling(intImage, boundingBox);
|
||||
std::vector<Mat> negSamples = sampler->getSamples();
|
||||
|
||||
if (posSamples.empty() || negSamples.empty())
|
||||
return false;
|
||||
|
||||
//extract features
|
||||
featureSet->extraction(posSamples);
|
||||
std::vector<Mat> posResponse = featureSet->getResponses();
|
||||
|
||||
featureSet->extraction(negSamples);
|
||||
std::vector<Mat> negResponse = featureSet->getResponses();
|
||||
|
||||
//model estimate
|
||||
model.staticCast<TrackerMILModel>()->setMode(TrackerMILModel::MODE_POSITIVE, posSamples);
|
||||
model->modelEstimation(posResponse);
|
||||
model.staticCast<TrackerMILModel>()->setMode(TrackerMILModel::MODE_NEGATIVE, negSamples);
|
||||
model->modelEstimation(negResponse);
|
||||
|
||||
//model update
|
||||
model->modelUpdate();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}} // namespace tracking::impl
|
||||
|
||||
TrackerMIL::Params::Params()
|
||||
{
|
||||
samplerInitInRadius = 3;
|
||||
samplerSearchWinSize = 25;
|
||||
samplerInitMaxNegNum = 65;
|
||||
samplerTrackInRadius = 4;
|
||||
samplerTrackMaxPosNum = 100000;
|
||||
samplerTrackMaxNegNum = 65;
|
||||
featureSetNumFeatures = 250;
|
||||
}
|
||||
|
||||
TrackerMIL::TrackerMIL()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
TrackerMIL::~TrackerMIL()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
Ptr<TrackerMIL> TrackerMIL::create(const TrackerMIL::Params& parameters)
|
||||
{
|
||||
return makePtr<tracking::impl::TrackerMILImpl>(parameters);
|
||||
}
|
||||
|
||||
} // namespace cv
|
||||
@@ -0,0 +1,374 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
// This file is modified from the https://github.com/HonglinChu/NanoTrack/blob/master/ncnn_macos_nanotrack/nanotrack.cpp
|
||||
// Author, HongLinChu, 1628464345@qq.com
|
||||
// Adapt to OpenCV, ZihaoMu: zihaomu@outlook.com
|
||||
|
||||
// Link to original inference code: https://github.com/HonglinChu/NanoTrack
|
||||
// Link to original training repo: https://github.com/HonglinChu/SiamTrackers/tree/master/NanoTrack
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
#include "opencv2/dnn.hpp"
|
||||
#endif
|
||||
|
||||
namespace cv {
|
||||
|
||||
TrackerNano::TrackerNano()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
TrackerNano::~TrackerNano()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
TrackerNano::Params::Params()
|
||||
{
|
||||
backbone = "backbone.onnx";
|
||||
neckhead = "neckhead.onnx";
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
backend = dnn::DNN_BACKEND_DEFAULT;
|
||||
target = dnn::DNN_TARGET_CPU;
|
||||
#else
|
||||
backend = -1; // invalid value
|
||||
target = -1; // invalid value
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
static void softmax(const Mat& src, Mat& dst)
|
||||
{
|
||||
Mat maxVal;
|
||||
cv::max(src.row(1), src.row(0), maxVal);
|
||||
|
||||
src.row(1) -= maxVal;
|
||||
src.row(0) -= maxVal;
|
||||
|
||||
exp(src, dst);
|
||||
|
||||
Mat sumVal = dst.row(0) + dst.row(1);
|
||||
dst.row(0) = dst.row(0) / sumVal;
|
||||
dst.row(1) = dst.row(1) / sumVal;
|
||||
}
|
||||
|
||||
static float sizeCal(float w, float h)
|
||||
{
|
||||
float pad = (w + h) * 0.5f;
|
||||
float sz2 = (w + pad) * (h + pad);
|
||||
return sqrt(sz2);
|
||||
}
|
||||
|
||||
static Mat sizeCal(const Mat& w, const Mat& h)
|
||||
{
|
||||
Mat pad = (w + h) * 0.5;
|
||||
Mat sz2 = (w + pad).mul((h + pad));
|
||||
|
||||
cv::sqrt(sz2, sz2);
|
||||
return sz2;
|
||||
}
|
||||
|
||||
// Similar python code: r = np.maximum(r, 1. / r) # r is matrix
|
||||
static void elementReciprocalMax(Mat& srcDst)
|
||||
{
|
||||
size_t totalV = srcDst.total();
|
||||
float* ptr = srcDst.ptr<float>(0);
|
||||
for (size_t i = 0; i < totalV; i++)
|
||||
{
|
||||
float val = *(ptr + i);
|
||||
*(ptr + i) = std::max(val, 1.0f/val);
|
||||
}
|
||||
}
|
||||
|
||||
class TrackerNanoImpl : public TrackerNano
|
||||
{
|
||||
public:
|
||||
TrackerNanoImpl(const TrackerNano::Params& parameters)
|
||||
{
|
||||
dnn::EngineType engine = dnn::ENGINE_AUTO;
|
||||
if (parameters.backend != 0 || parameters.target != 0){
|
||||
engine = dnn::ENGINE_CLASSIC;
|
||||
}
|
||||
backbone = dnn::readNet(parameters.backbone, "", "", engine);
|
||||
neckhead = dnn::readNet(parameters.neckhead, "", "", engine);
|
||||
|
||||
CV_Assert(!backbone.empty());
|
||||
CV_Assert(!neckhead.empty());
|
||||
|
||||
backbone.setPreferableBackend(parameters.backend);
|
||||
backbone.setPreferableTarget(parameters.target);
|
||||
neckhead.setPreferableBackend(parameters.backend);
|
||||
neckhead.setPreferableTarget(parameters.target);
|
||||
}
|
||||
|
||||
TrackerNanoImpl(const dnn::Net& _backbone, const dnn::Net& _neckhead)
|
||||
{
|
||||
CV_Assert(!_backbone.empty());
|
||||
CV_Assert(!_neckhead.empty());
|
||||
|
||||
backbone = _backbone;
|
||||
neckhead = _neckhead;
|
||||
}
|
||||
|
||||
void init(InputArray image, const Rect& boundingBox) CV_OVERRIDE;
|
||||
bool update(InputArray image, Rect& boundingBox) CV_OVERRIDE;
|
||||
float getTrackingScore() CV_OVERRIDE;
|
||||
|
||||
// Save the target bounding box for each frame.
|
||||
std::vector<float> targetSz = {0, 0}; // H and W of bounding box
|
||||
std::vector<float> targetPos = {0, 0}; // center point of bounding box (x, y)
|
||||
float tracking_score;
|
||||
|
||||
struct trackerConfig
|
||||
{
|
||||
float windowInfluence = 0.455f;
|
||||
float lr = 0.37f;
|
||||
float contextAmount = 0.5;
|
||||
bool swapRB = true;
|
||||
int totalStride = 16;
|
||||
float penaltyK = 0.055f;
|
||||
};
|
||||
|
||||
protected:
|
||||
const int exemplarSize = 127;
|
||||
const int instanceSize = 255;
|
||||
|
||||
trackerConfig trackState;
|
||||
int scoreSize;
|
||||
Size imgSize = {0, 0};
|
||||
Mat hanningWindow;
|
||||
Mat grid2searchX, grid2searchY;
|
||||
|
||||
dnn::Net backbone, neckhead;
|
||||
Mat image;
|
||||
|
||||
void getSubwindow(Mat& dstCrop, Mat& srcImg, int originalSz, int resizeSz);
|
||||
void generateGrids();
|
||||
};
|
||||
|
||||
void TrackerNanoImpl::generateGrids()
|
||||
{
|
||||
int sz = scoreSize;
|
||||
const int sz2 = sz / 2;
|
||||
|
||||
std::vector<float> x1Vec(sz, 0);
|
||||
|
||||
for (int i = 0; i < sz; i++)
|
||||
{
|
||||
x1Vec[i] = (float)(i - sz2);
|
||||
}
|
||||
|
||||
Mat x1M(1, sz, CV_32FC1, x1Vec.data());
|
||||
|
||||
cv::repeat(x1M, sz, 1, grid2searchX);
|
||||
cv::repeat(x1M.t(), 1, sz, grid2searchY);
|
||||
|
||||
grid2searchX *= trackState.totalStride;
|
||||
grid2searchY *= trackState.totalStride;
|
||||
|
||||
grid2searchX += instanceSize/2;
|
||||
grid2searchY += instanceSize/2;
|
||||
}
|
||||
|
||||
void TrackerNanoImpl::init(InputArray image_, const Rect &boundingBox_)
|
||||
{
|
||||
scoreSize = (instanceSize - exemplarSize) / trackState.totalStride + 8;
|
||||
trackState = trackerConfig();
|
||||
image = image_.getMat().clone();
|
||||
|
||||
// convert Rect2d from left-up to center.
|
||||
targetPos[0] = float(boundingBox_.x) + float(boundingBox_.width) * 0.5f;
|
||||
targetPos[1] = float(boundingBox_.y) + float(boundingBox_.height) * 0.5f;
|
||||
|
||||
targetSz[0] = float(boundingBox_.width);
|
||||
targetSz[1] = float(boundingBox_.height);
|
||||
|
||||
imgSize = image.size();
|
||||
|
||||
// Extent the bounding box.
|
||||
float sumSz = targetSz[0] + targetSz[1];
|
||||
float wExtent = targetSz[0] + trackState.contextAmount * (sumSz);
|
||||
float hExtent = targetSz[1] + trackState.contextAmount * (sumSz);
|
||||
int sz = int(cv::sqrt(wExtent * hExtent));
|
||||
|
||||
Mat crop;
|
||||
getSubwindow(crop, image, sz, exemplarSize);
|
||||
Mat blob = dnn::blobFromImage(crop, 1.0, Size(), Scalar(), trackState.swapRB);
|
||||
|
||||
backbone.setInput(blob);
|
||||
Mat out = backbone.forward(); // Feature extraction.
|
||||
neckhead.setInput(out, "input1");
|
||||
|
||||
createHanningWindow(hanningWindow, Size(scoreSize, scoreSize), CV_32F);
|
||||
generateGrids();
|
||||
}
|
||||
|
||||
void TrackerNanoImpl::getSubwindow(Mat& dstCrop, Mat& srcImg, int originalSz, int resizeSz)
|
||||
{
|
||||
Scalar avgChans = mean(srcImg);
|
||||
Size imgSz = srcImg.size();
|
||||
int c = (originalSz + 1) / 2;
|
||||
|
||||
int context_xmin = (int)(targetPos[0]) - c;
|
||||
int context_xmax = context_xmin + originalSz - 1;
|
||||
int context_ymin = (int)(targetPos[1]) - c;
|
||||
int context_ymax = context_ymin + originalSz - 1;
|
||||
|
||||
int left_pad = std::max(0, -context_xmin);
|
||||
int top_pad = std::max(0, -context_ymin);
|
||||
int right_pad = std::max(0, context_xmax - imgSz.width + 1);
|
||||
int bottom_pad = std::max(0, context_ymax - imgSz.height + 1);
|
||||
|
||||
context_xmin += left_pad;
|
||||
context_xmax += left_pad;
|
||||
context_ymin += top_pad;
|
||||
context_ymax += top_pad;
|
||||
|
||||
Mat cropImg;
|
||||
if (left_pad == 0 && top_pad == 0 && right_pad == 0 && bottom_pad == 0)
|
||||
{
|
||||
// Crop image without padding.
|
||||
cropImg = srcImg(cv::Rect(context_xmin, context_ymin,
|
||||
context_xmax - context_xmin + 1, context_ymax - context_ymin + 1));
|
||||
}
|
||||
else // Crop image with padding, and the padding value is avgChans
|
||||
{
|
||||
cv::Mat tmpMat;
|
||||
cv::copyMakeBorder(srcImg, tmpMat, top_pad, bottom_pad, left_pad, right_pad, cv::BORDER_CONSTANT, avgChans);
|
||||
cropImg = tmpMat(cv::Rect(context_xmin, context_ymin, context_xmax - context_xmin + 1, context_ymax - context_ymin + 1));
|
||||
}
|
||||
resize(cropImg, dstCrop, Size(resizeSz, resizeSz));
|
||||
}
|
||||
|
||||
bool TrackerNanoImpl::update(InputArray image_, Rect &boundingBoxRes)
|
||||
{
|
||||
image = image_.getMat().clone();
|
||||
int targetSzSum = (int)(targetSz[0] + targetSz[1]);
|
||||
|
||||
float wc = targetSz[0] + trackState.contextAmount * targetSzSum;
|
||||
float hc = targetSz[1] + trackState.contextAmount * targetSzSum;
|
||||
float sz = cv::sqrt(wc * hc);
|
||||
float scale_z = exemplarSize / sz;
|
||||
float sx = sz * (instanceSize / exemplarSize);
|
||||
targetSz[0] *= scale_z;
|
||||
targetSz[1] *= scale_z;
|
||||
|
||||
Mat crop;
|
||||
getSubwindow(crop, image, int(sx), instanceSize);
|
||||
|
||||
Mat blob = dnn::blobFromImage(crop, 1.0, Size(), Scalar(), trackState.swapRB);
|
||||
backbone.setInput(blob);
|
||||
Mat xf = backbone.forward();
|
||||
neckhead.setInput(xf, "input2");
|
||||
std::vector<String> outputName = {"output1", "output2"};
|
||||
std::vector<Mat> outs;
|
||||
neckhead.forward(outs, outputName);
|
||||
|
||||
CV_Assert(outs.size() == 2);
|
||||
|
||||
Mat clsScore = outs[0]; // 1x2x16x16
|
||||
Mat bboxPred = outs[1]; // 1x4x16x16
|
||||
|
||||
clsScore = clsScore.reshape(0, {2, scoreSize, scoreSize});
|
||||
bboxPred = bboxPred.reshape(0, {4, scoreSize, scoreSize});
|
||||
|
||||
Mat scoreSoftmax; // 2x16x16
|
||||
softmax(clsScore, scoreSoftmax);
|
||||
|
||||
Mat score = scoreSoftmax.row(1);
|
||||
score = score.reshape(0, {scoreSize, scoreSize});
|
||||
|
||||
Mat predX1 = grid2searchX - bboxPred.row(0).reshape(0, {scoreSize, scoreSize});
|
||||
Mat predY1 = grid2searchY - bboxPred.row(1).reshape(0, {scoreSize, scoreSize});
|
||||
Mat predX2 = grid2searchX + bboxPred.row(2).reshape(0, {scoreSize, scoreSize});
|
||||
Mat predY2 = grid2searchY + bboxPred.row(3).reshape(0, {scoreSize, scoreSize});
|
||||
|
||||
// size penalty
|
||||
// scale penalty
|
||||
Mat sc = sizeCal(predX2 - predX1, predY2 - predY1)/sizeCal(targetPos[0], targetPos[1]);
|
||||
elementReciprocalMax(sc);
|
||||
|
||||
// ratio penalty
|
||||
float ratioVal = targetSz[0] / targetSz[1];
|
||||
|
||||
Mat ratioM(scoreSize, scoreSize, CV_32FC1, Scalar::all(ratioVal));
|
||||
Mat rc = ratioM / ((predX2 - predX1) / (predY2 - predY1));
|
||||
elementReciprocalMax(rc);
|
||||
|
||||
Mat penalty;
|
||||
exp(((rc.mul(sc) - 1) * trackState.penaltyK * (-1)), penalty);
|
||||
Mat pscore = penalty.mul(score);
|
||||
|
||||
// Window penalty
|
||||
pscore = pscore * (1.0 - trackState.windowInfluence) + hanningWindow * trackState.windowInfluence;
|
||||
|
||||
// get Max
|
||||
int bestID[2] = { 0, 0 };
|
||||
minMaxIdx(pscore, 0, 0, 0, bestID);
|
||||
|
||||
tracking_score = pscore.at<float>(bestID);
|
||||
|
||||
float x1Val = predX1.at<float>(bestID);
|
||||
float x2Val = predX2.at<float>(bestID);
|
||||
float y1Val = predY1.at<float>(bestID);
|
||||
float y2Val = predY2.at<float>(bestID);
|
||||
|
||||
float predXs = (x1Val + x2Val)/2;
|
||||
float predYs = (y1Val + y2Val)/2;
|
||||
float predW = (x2Val - x1Val)/scale_z;
|
||||
float predH = (y2Val - y1Val)/scale_z;
|
||||
|
||||
float diffXs = (predXs - instanceSize / 2) / scale_z;
|
||||
float diffYs = (predYs - instanceSize / 2) / scale_z;
|
||||
|
||||
targetSz[0] /= scale_z;
|
||||
targetSz[1] /= scale_z;
|
||||
|
||||
float lr = penalty.at<float>(bestID) * score.at<float>(bestID) * trackState.lr;
|
||||
|
||||
float resX = targetPos[0] + diffXs;
|
||||
float resY = targetPos[1] + diffYs;
|
||||
float resW = predW * lr + (1 - lr) * targetSz[0];
|
||||
float resH = predH * lr + (1 - lr) * targetSz[1];
|
||||
|
||||
resX = std::max(0.f, std::min((float)imgSize.width, resX));
|
||||
resY = std::max(0.f, std::min((float)imgSize.height, resY));
|
||||
resW = std::max(10.f, std::min((float)imgSize.width, resW));
|
||||
resH = std::max(10.f, std::min((float)imgSize.height, resH));
|
||||
|
||||
targetPos[0] = resX;
|
||||
targetPos[1] = resY;
|
||||
targetSz[0] = resW;
|
||||
targetSz[1] = resH;
|
||||
|
||||
// convert center to Rect.
|
||||
boundingBoxRes = { int(resX - resW/2), int(resY - resH/2), int(resW), int(resH)};
|
||||
return true;
|
||||
}
|
||||
|
||||
float TrackerNanoImpl::getTrackingScore()
|
||||
{
|
||||
return tracking_score;
|
||||
}
|
||||
|
||||
Ptr<TrackerNano> TrackerNano::create(const TrackerNano::Params& parameters)
|
||||
{
|
||||
return makePtr<TrackerNanoImpl>(parameters);
|
||||
}
|
||||
|
||||
Ptr<TrackerNano> TrackerNano::create(const dnn::Net& backbone, const dnn::Net& neckhead)
|
||||
{
|
||||
return makePtr<TrackerNanoImpl>(backbone, neckhead);
|
||||
}
|
||||
|
||||
#else // OPENCV_HAVE_DNN
|
||||
Ptr<TrackerNano> TrackerNano::create(const TrackerNano::Params& parameters)
|
||||
{
|
||||
CV_UNUSED(parameters);
|
||||
CV_Error(cv::Error::StsNotImplemented, "to use NanoTrack, the tracking module needs to be built with opencv_dnn !");
|
||||
}
|
||||
#endif // OPENCV_HAVE_DNN
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
// Author, PengyuLiu, 1872918507@qq.com
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
#include "opencv2/dnn.hpp"
|
||||
#endif
|
||||
|
||||
namespace cv {
|
||||
|
||||
TrackerVit::TrackerVit()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
TrackerVit::~TrackerVit()
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
TrackerVit::Params::Params()
|
||||
{
|
||||
net = "vitTracker.onnx";
|
||||
meanvalue = Scalar{0.485, 0.456, 0.406}; // normalized mean (already divided by 255)
|
||||
stdvalue = Scalar{0.229, 0.224, 0.225}; // normalized std (already divided by 255)
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
backend = dnn::DNN_BACKEND_DEFAULT;
|
||||
target = dnn::DNN_TARGET_CPU;
|
||||
#else
|
||||
backend = -1; // invalid value
|
||||
target = -1; // invalid value
|
||||
#endif
|
||||
tracking_score_threshold = 0.20f; // safe threshold to filter out black frames
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
|
||||
class TrackerVitImpl : public TrackerVit
|
||||
{
|
||||
public:
|
||||
TrackerVitImpl(const TrackerVit::Params& parameters)
|
||||
{
|
||||
dnn::EngineType engine = dnn::ENGINE_AUTO;
|
||||
if (parameters.backend != 0 || parameters.target != 0){
|
||||
engine = dnn::ENGINE_CLASSIC;
|
||||
}
|
||||
net = dnn::readNet(parameters.net, "", "", engine);
|
||||
CV_Assert(!net.empty());
|
||||
|
||||
net.setPreferableBackend(parameters.backend);
|
||||
net.setPreferableTarget(parameters.target);
|
||||
|
||||
i2bp.mean = parameters.meanvalue * 255.0;
|
||||
i2bp.scalefactor = (1.0 / parameters.stdvalue) * (1 / 255.0);
|
||||
tracking_score_threshold = parameters.tracking_score_threshold;
|
||||
}
|
||||
|
||||
TrackerVitImpl(const dnn::Net& model, Scalar meanvalue, Scalar stdvalue, float _tracking_score_threshold)
|
||||
{
|
||||
CV_Assert(!model.empty());
|
||||
|
||||
net = model;
|
||||
i2bp.mean = meanvalue * 255.0;
|
||||
i2bp.scalefactor = (1.0 / stdvalue) * (1 / 255.0);
|
||||
tracking_score_threshold = _tracking_score_threshold;
|
||||
}
|
||||
|
||||
void init(InputArray image, const Rect& boundingBox) CV_OVERRIDE;
|
||||
bool update(InputArray image, Rect& boundingBox) CV_OVERRIDE;
|
||||
float getTrackingScore() CV_OVERRIDE;
|
||||
|
||||
Rect rect_last;
|
||||
float tracking_score;
|
||||
|
||||
float tracking_score_threshold;
|
||||
dnn::Image2BlobParams i2bp;
|
||||
|
||||
|
||||
protected:
|
||||
void preprocess(const Mat& src, Mat& dst, Size size);
|
||||
|
||||
const Size searchSize{256, 256};
|
||||
const Size templateSize{128, 128};
|
||||
|
||||
Mat hanningWindow;
|
||||
|
||||
dnn::Net net;
|
||||
};
|
||||
|
||||
static int crop_image(const Mat& src, Mat& dst, Rect box, int factor)
|
||||
{
|
||||
int x = box.x, y = box.y, w = box.width, h = box.height;
|
||||
int crop_sz = cvCeil(sqrt(w * h) * factor);
|
||||
|
||||
int x1 = x + (w - crop_sz) / 2;
|
||||
int x2 = x1 + crop_sz;
|
||||
int y1 = y + (h - crop_sz) / 2;
|
||||
int y2 = y1 + crop_sz;
|
||||
|
||||
int x1_pad = std::max(0, -x1);
|
||||
int y1_pad = std::max(0, -y1);
|
||||
int x2_pad = std::max(x2 - src.size[1] + 1, 0);
|
||||
int y2_pad = std::max(y2 - src.size[0] + 1, 0);
|
||||
|
||||
Rect roi(x1 + x1_pad, y1 + y1_pad, x2 - x2_pad - x1 - x1_pad, y2 - y2_pad - y1 - y1_pad);
|
||||
Mat im_crop = src(roi);
|
||||
copyMakeBorder(im_crop, dst, y1_pad, y2_pad, x1_pad, x2_pad, BORDER_CONSTANT);
|
||||
|
||||
return crop_sz;
|
||||
}
|
||||
|
||||
void TrackerVitImpl::preprocess(const Mat& src, Mat& dst, Size size)
|
||||
{
|
||||
Mat img;
|
||||
resize(src, img, size);
|
||||
|
||||
dst = dnn::blobFromImageWithParams(img, i2bp);
|
||||
}
|
||||
|
||||
static Mat hann1d(int sz, bool centered = true) {
|
||||
Mat hanningWindow(sz, 1, CV_32FC1);
|
||||
float* data = hanningWindow.ptr<float>(0);
|
||||
|
||||
if(centered) {
|
||||
for(int i = 0; i < sz; i++) {
|
||||
float val = 0.5f * (1.f - std::cos(static_cast<float>(2 * M_PI / (sz + 1)) * (i + 1)));
|
||||
data[i] = val;
|
||||
}
|
||||
}
|
||||
else {
|
||||
int half_sz = sz / 2;
|
||||
for(int i = 0; i <= half_sz; i++) {
|
||||
float val = 0.5f * (1.f + std::cos(static_cast<float>(2 * M_PI / (sz + 2)) * i));
|
||||
data[i] = val;
|
||||
data[sz - 1 - i] = val;
|
||||
}
|
||||
}
|
||||
|
||||
return hanningWindow;
|
||||
}
|
||||
|
||||
static Mat hann2d(Size size, bool centered = true) {
|
||||
int rows = size.height;
|
||||
int cols = size.width;
|
||||
|
||||
Mat hanningWindowRows = hann1d(rows, centered);
|
||||
Mat hanningWindowCols = hann1d(cols, centered);
|
||||
|
||||
Mat hanningWindow = hanningWindowRows * hanningWindowCols.t();
|
||||
|
||||
return hanningWindow;
|
||||
}
|
||||
|
||||
static void updateLastRect(float cx, float cy, float w, float h, int crop_size, Rect &rect_last)
|
||||
{
|
||||
int x0 = rect_last.x + (rect_last.width - crop_size) / 2;
|
||||
int y0 = rect_last.y + (rect_last.height - crop_size) / 2;
|
||||
|
||||
float x1 = cx - w / 2, y1 = cy - h / 2;
|
||||
rect_last.x = cvFloor(x1 * crop_size + x0);
|
||||
rect_last.y = cvFloor(y1 * crop_size + y0);
|
||||
rect_last.width = cvFloor(w * crop_size);
|
||||
rect_last.height = cvFloor(h * crop_size);
|
||||
}
|
||||
|
||||
void TrackerVitImpl::init(InputArray image_, const Rect &boundingBox_)
|
||||
{
|
||||
Mat image = image_.getMat();
|
||||
Mat crop;
|
||||
crop_image(image, crop, boundingBox_, 2);
|
||||
Mat blob;
|
||||
preprocess(crop, blob, templateSize);
|
||||
net.setInput(blob, "template");
|
||||
Size size(16, 16);
|
||||
hanningWindow = hann2d(size, true);
|
||||
rect_last = boundingBox_;
|
||||
}
|
||||
|
||||
bool TrackerVitImpl::update(InputArray image_, Rect &boundingBoxRes)
|
||||
{
|
||||
Mat image = image_.getMat();
|
||||
Mat crop;
|
||||
int crop_size = crop_image(image, crop, rect_last, 4); // crop: [crop_size, crop_size]
|
||||
Mat blob;
|
||||
preprocess(crop, blob, searchSize);
|
||||
net.setInput(blob, "search");
|
||||
std::vector<String> outputName = {"output1", "output2", "output3"};
|
||||
std::vector<Mat> outs;
|
||||
net.forward(outs, outputName);
|
||||
CV_Assert(outs.size() == 3);
|
||||
|
||||
Mat conf_map = outs[0].reshape(0, {16, 16});
|
||||
Mat size_map = outs[1].reshape(0, {2, 16, 16});
|
||||
Mat offset_map = outs[2].reshape(0, {2, 16, 16});
|
||||
|
||||
multiply(conf_map, hanningWindow, conf_map);
|
||||
|
||||
double maxVal;
|
||||
Point maxLoc;
|
||||
minMaxLoc(conf_map, nullptr, &maxVal, nullptr, &maxLoc);
|
||||
tracking_score = static_cast<float>(maxVal);
|
||||
|
||||
if (tracking_score >= tracking_score_threshold) {
|
||||
float cx = (maxLoc.x + offset_map.at<float>(0, maxLoc.y, maxLoc.x)) / 16;
|
||||
float cy = (maxLoc.y + offset_map.at<float>(1, maxLoc.y, maxLoc.x)) / 16;
|
||||
float w = size_map.at<float>(0, maxLoc.y, maxLoc.x);
|
||||
float h = size_map.at<float>(1, maxLoc.y, maxLoc.x);
|
||||
|
||||
updateLastRect(cx, cy, w, h, crop_size, rect_last);
|
||||
boundingBoxRes = rect_last;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
float TrackerVitImpl::getTrackingScore()
|
||||
{
|
||||
return tracking_score;
|
||||
}
|
||||
|
||||
Ptr<TrackerVit> TrackerVit::create(const TrackerVit::Params& parameters)
|
||||
{
|
||||
return makePtr<TrackerVitImpl>(parameters);
|
||||
}
|
||||
|
||||
Ptr<TrackerVit> TrackerVit::create(const dnn::Net& model, Scalar meanvalue, Scalar stdvalue, float tracking_score_threshold)
|
||||
{
|
||||
return makePtr<TrackerVitImpl>(model, meanvalue, stdvalue, tracking_score_threshold);
|
||||
}
|
||||
|
||||
#else // OPENCV_HAVE_DNN
|
||||
Ptr<TrackerVit> TrackerVit::create(const TrackerVit::Params& parameters)
|
||||
{
|
||||
CV_UNUSED(parameters);
|
||||
CV_Error(Error::StsNotImplemented, "to use vittrack, the tracking module needs to be built with opencv_dnn !");
|
||||
}
|
||||
#endif // OPENCV_HAVE_DNN
|
||||
}
|
||||
Reference in New Issue
Block a user