// 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 SortableElementRev { public: T _val; int _ind; SortableElementRev() : _val(), _ind(0) { } SortableElementRev(T val, int ind) { _val = val; _ind = ind; } bool operator<(SortableElementRev& b) { return (_val < b._val); } }; static bool CompareSortableElementRev(const SortableElementRev& i, const SortableElementRev& j) { return i._val < j._val; } template void sort_order_des(std::vector& v, std::vector& order) { uint n = (uint)v.size(); std::vector> 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 Hpos, Hneg; Hpos.clear(); Hneg.clear(); Hpos.resize(posx.rows, 0.0f), Hneg.resize(negx.rows, 0.0f); _selectors.clear(); std::vector posw(posx.rows), negw(negx.rows); std::vector> 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 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 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 ClfMilBoost::classify(const Mat& x, bool logR) { int numsamples = x.rows; std::vector res(numsamples); std::vector 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_& /*posw*/, const Mat_& /*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::min()); _e0 = -1.0f / (2.0f * _sig0 + std::numeric_limits::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::min()); _e0 = -1.0f / (2.0f * _sig0 + std::numeric_limits::min()); } } bool ClfOnlineStump::classify(const Mat& x, int i) { float xx = x.at(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(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 ClfOnlineStump::classifySetF(const Mat& x) { std::vector 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