357 lines
9.1 KiB
C++
357 lines
9.1 KiB
C++
// 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
|