1 // This file is part of OpenCV project.
2 // It is subject to the license terms in the LICENSE file found in the top-level directory
3 // of this distribution and at http://opencv.org/license.html.
4 
5 #include "../../precomp.hpp"
6 #include "tracking_online_mil.hpp"
7 
8 namespace cv {
9 namespace detail {
10 inline namespace tracking {
11 
12 #define sign(s) ((s > 0) ? 1 : ((s < 0) ? -1 : 0))
13 
14 template <class T>
15 class SortableElementRev
16 {
17 public:
18     T _val;
19     int _ind;
SortableElementRev()20     SortableElementRev()
21         : _val(), _ind(0)
22     {
23     }
SortableElementRev(T val,int ind)24     SortableElementRev(T val, int ind)
25     {
26         _val = val;
27         _ind = ind;
28     }
operator <(SortableElementRev<T> & b)29     bool operator<(SortableElementRev<T>& b)
30     {
31         return (_val < b._val);
32     };
33 };
34 
CompareSortableElementRev(const SortableElementRev<float> & i,const SortableElementRev<float> & j)35 static bool CompareSortableElementRev(const SortableElementRev<float>& i, const SortableElementRev<float>& j)
36 {
37     return i._val < j._val;
38 }
39 
40 template <class T>
sort_order_des(std::vector<T> & v,std::vector<int> & order)41 void sort_order_des(std::vector<T>& v, std::vector<int>& order)
42 {
43     uint n = (uint)v.size();
44     std::vector<SortableElementRev<T>> v2;
45     v2.resize(n);
46     order.clear();
47     order.resize(n);
48     for (uint i = 0; i < n; i++)
49     {
50         v2[i]._ind = i;
51         v2[i]._val = v[i];
52     }
53     //std::sort( v2.begin(), v2.end() );
54     std::sort(v2.begin(), v2.end(), CompareSortableElementRev);
55     for (uint i = 0; i < n; i++)
56     {
57         order[i] = v2[i]._ind;
58         v[i] = v2[i]._val;
59     }
60 };
61 
62 //implementations for strong classifier
63 
Params()64 ClfMilBoost::Params::Params()
65 {
66     _numSel = 50;
67     _numFeat = 250;
68     _lRate = 0.85f;
69 }
70 
ClfMilBoost()71 ClfMilBoost::ClfMilBoost()
72     : _numsamples(0)
73     , _counter(0)
74 {
75     _myParams = ClfMilBoost::Params();
76     _numsamples = 0;
77 }
78 
~ClfMilBoost()79 ClfMilBoost::~ClfMilBoost()
80 {
81     _selectors.clear();
82     for (size_t i = 0; i < _weakclf.size(); i++)
83         delete _weakclf.at(i);
84 }
85 
init(const ClfMilBoost::Params & parameters)86 void ClfMilBoost::init(const ClfMilBoost::Params& parameters)
87 {
88     _myParams = parameters;
89     _numsamples = 0;
90 
91     //_ftrs = Ftr::generate( _myParams->_ftrParams, _myParams->_numFeat );
92     // if( params->_storeFtrHistory )
93     //  Ftr::toViz( _ftrs, "haarftrs" );
94     _weakclf.resize(_myParams._numFeat);
95     for (int k = 0; k < _myParams._numFeat; k++)
96     {
97         _weakclf[k] = new ClfOnlineStump(k);
98         _weakclf[k]->_lRate = _myParams._lRate;
99     }
100     _counter = 0;
101 }
102 
update(const Mat & posx,const Mat & negx)103 void ClfMilBoost::update(const Mat& posx, const Mat& negx)
104 {
105     int numneg = negx.rows;
106     int numpos = posx.rows;
107 
108     // compute ftrs
109     //if( !posx.ftrsComputed() )
110     //  Ftr::compute( posx, _ftrs );
111     //if( !negx.ftrsComputed() )
112     //  Ftr::compute( negx, _ftrs );
113 
114     // initialize H
115     static std::vector<float> Hpos, Hneg;
116     Hpos.clear();
117     Hneg.clear();
118     Hpos.resize(posx.rows, 0.0f), Hneg.resize(negx.rows, 0.0f);
119 
120     _selectors.clear();
121     std::vector<float> posw(posx.rows), negw(negx.rows);
122     std::vector<std::vector<float>> pospred(_weakclf.size()), negpred(_weakclf.size());
123 
124     // train all weak classifiers without weights
125 #ifdef _OPENMP
126 #pragma omp parallel for
127 #endif
128     for (int m = 0; m < _myParams._numFeat; m++)
129     {
130         _weakclf[m]->update(posx, negx);
131         pospred[m] = _weakclf[m]->classifySetF(posx);
132         negpred[m] = _weakclf[m]->classifySetF(negx);
133     }
134 
135     // pick the best features
136     for (int s = 0; s < _myParams._numSel; s++)
137     {
138 
139         // compute errors/likl for all weak clfs
140         std::vector<float> poslikl(_weakclf.size(), 1.0f), neglikl(_weakclf.size()), likl(_weakclf.size());
141 #ifdef _OPENMP
142 #pragma omp parallel for
143 #endif
144         for (int w = 0; w < (int)_weakclf.size(); w++)
145         {
146             float lll = 1.0f;
147             for (int j = 0; j < numpos; j++)
148                 lll *= (1 - sigmoid(Hpos[j] + pospred[w][j]));
149             poslikl[w] = (float)-log(1 - lll + 1e-5);
150 
151             lll = 0.0f;
152             for (int j = 0; j < numneg; j++)
153                 lll += (float)-log(1e-5f + 1 - sigmoid(Hneg[j] + negpred[w][j]));
154             neglikl[w] = lll;
155 
156             likl[w] = poslikl[w] / numpos + neglikl[w] / numneg;
157         }
158 
159         // pick best weak clf
160         std::vector<int> order;
161         sort_order_des(likl, order);
162 
163         // find best weakclf that isn't already included
164         for (uint k = 0; k < order.size(); k++)
165             if (std::count(_selectors.begin(), _selectors.end(), order[k]) == 0)
166             {
167                 _selectors.push_back(order[k]);
168                 break;
169             }
170 
171             // update H = H + h_m
172 #ifdef _OPENMP
173 #pragma omp parallel for
174 #endif
175         for (int k = 0; k < posx.rows; k++)
176             Hpos[k] += pospred[_selectors[s]][k];
177 #ifdef _OPENMP
178 #pragma omp parallel for
179 #endif
180         for (int k = 0; k < negx.rows; k++)
181             Hneg[k] += negpred[_selectors[s]][k];
182     }
183 
184     //if( _myParams->_storeFtrHistory )
185     //for ( uint j = 0; j < _selectors.size(); j++ )
186     // _ftrHist( _selectors[j], _counter ) = 1.0f / ( j + 1 );
187 
188     _counter++;
189     /* */
190     return;
191 }
192 
classify(const Mat & x,bool logR)193 std::vector<float> ClfMilBoost::classify(const Mat& x, bool logR)
194 {
195     int numsamples = x.rows;
196     std::vector<float> res(numsamples);
197     std::vector<float> tr;
198 
199     for (uint w = 0; w < _selectors.size(); w++)
200     {
201         tr = _weakclf[_selectors[w]]->classifySetF(x);
202 #ifdef _OPENMP
203 #pragma omp parallel for
204 #endif
205         for (int j = 0; j < numsamples; j++)
206         {
207             res[j] += tr[j];
208         }
209     }
210 
211     // return probabilities or log odds ratio
212     if (!logR)
213     {
214 #ifdef _OPENMP
215 #pragma omp parallel for
216 #endif
217         for (int j = 0; j < (int)res.size(); j++)
218         {
219             res[j] = sigmoid(res[j]);
220         }
221     }
222 
223     return res;
224 }
225 
226 //implementations for weak classifier
227 
ClfOnlineStump()228 ClfOnlineStump::ClfOnlineStump()
229     : _mu0(0), _mu1(0), _sig0(0), _sig1(0)
230     , _q(0)
231     , _s(0)
232     , _log_n1(0), _log_n0(0)
233     , _e1(0), _e0(0)
234     , _lRate(0)
235 {
236     _trained = false;
237     _ind = -1;
238     init();
239 }
240 
ClfOnlineStump(int ind)241 ClfOnlineStump::ClfOnlineStump(int ind)
242     : _mu0(0), _mu1(0), _sig0(0), _sig1(0)
243     , _q(0)
244     , _s(0)
245     , _log_n1(0), _log_n0(0)
246     , _e1(0), _e0(0)
247     , _lRate(0)
248 {
249     _trained = false;
250     _ind = ind;
251     init();
252 }
init()253 void ClfOnlineStump::init()
254 {
255     _mu0 = 0;
256     _mu1 = 0;
257     _sig0 = 1;
258     _sig1 = 1;
259     _lRate = 0.85f;
260     _trained = false;
261 }
262 
update(const Mat & posx,const Mat & negx,const Mat_<float> &,const Mat_<float> &)263 void ClfOnlineStump::update(const Mat& posx, const Mat& negx, const Mat_<float>& /*posw*/, const Mat_<float>& /*negw*/)
264 {
265     //std::cout << " ClfOnlineStump::update" << _ind << std::endl;
266     float posmu = 0.0, negmu = 0.0;
267     if (posx.cols > 0)
268         posmu = float(mean(posx.col(_ind))[0]);
269     if (negx.cols > 0)
270         negmu = float(mean(negx.col(_ind))[0]);
271 
272     if (_trained)
273     {
274         if (posx.cols > 0)
275         {
276             _mu1 = (_lRate * _mu1 + (1 - _lRate) * posmu);
277             cv::Mat diff = posx.col(_ind) - _mu1;
278             _sig1 = _lRate * _sig1 + (1 - _lRate) * float(mean(diff.mul(diff))[0]);
279         }
280         if (negx.cols > 0)
281         {
282             _mu0 = (_lRate * _mu0 + (1 - _lRate) * negmu);
283             cv::Mat diff = negx.col(_ind) - _mu0;
284             _sig0 = _lRate * _sig0 + (1 - _lRate) * float(mean(diff.mul(diff))[0]);
285         }
286 
287         _q = (_mu1 - _mu0) / 2;
288         _s = sign(_mu1 - _mu0);
289         _log_n0 = std::log(float(1.0f / pow(_sig0, 0.5f)));
290         _log_n1 = std::log(float(1.0f / pow(_sig1, 0.5f)));
291         //_e1 = -1.0f/(2.0f*_sig1+1e-99f);
292         //_e0 = -1.0f/(2.0f*_sig0+1e-99f);
293         _e1 = -1.0f / (2.0f * _sig1 + std::numeric_limits<float>::min());
294         _e0 = -1.0f / (2.0f * _sig0 + std::numeric_limits<float>::min());
295     }
296     else
297     {
298         _trained = true;
299         if (posx.cols > 0)
300         {
301             _mu1 = posmu;
302             cv::Scalar scal_mean, scal_std_dev;
303             cv::meanStdDev(posx.col(_ind), scal_mean, scal_std_dev);
304             _sig1 = float(scal_std_dev[0]) * float(scal_std_dev[0]) + 1e-9f;
305         }
306 
307         if (negx.cols > 0)
308         {
309             _mu0 = negmu;
310             cv::Scalar scal_mean, scal_std_dev;
311             cv::meanStdDev(negx.col(_ind), scal_mean, scal_std_dev);
312             _sig0 = float(scal_std_dev[0]) * float(scal_std_dev[0]) + 1e-9f;
313         }
314 
315         _q = (_mu1 - _mu0) / 2;
316         _s = sign(_mu1 - _mu0);
317         _log_n0 = std::log(float(1.0f / pow(_sig0, 0.5f)));
318         _log_n1 = std::log(float(1.0f / pow(_sig1, 0.5f)));
319         //_e1 = -1.0f/(2.0f*_sig1+1e-99f);
320         //_e0 = -1.0f/(2.0f*_sig0+1e-99f);
321         _e1 = -1.0f / (2.0f * _sig1 + std::numeric_limits<float>::min());
322         _e0 = -1.0f / (2.0f * _sig0 + std::numeric_limits<float>::min());
323     }
324 }
325 
classify(const Mat & x,int i)326 bool ClfOnlineStump::classify(const Mat& x, int i)
327 {
328     float xx = x.at<float>(i, _ind);
329     double log_p0 = (xx - _mu0) * (xx - _mu0) * _e0 + _log_n0;
330     double log_p1 = (xx - _mu1) * (xx - _mu1) * _e1 + _log_n1;
331     return log_p1 > log_p0;
332 }
333 
classifyF(const Mat & x,int i)334 float ClfOnlineStump::classifyF(const Mat& x, int i)
335 {
336     float xx = x.at<float>(i, _ind);
337     double log_p0 = (xx - _mu0) * (xx - _mu0) * _e0 + _log_n0;
338     double log_p1 = (xx - _mu1) * (xx - _mu1) * _e1 + _log_n1;
339     return float(log_p1 - log_p0);
340 }
341 
classifySetF(const Mat & x)342 inline std::vector<float> ClfOnlineStump::classifySetF(const Mat& x)
343 {
344     std::vector<float> res(x.rows);
345 
346 #ifdef _OPENMP
347 #pragma omp parallel for
348 #endif
349     for (int k = 0; k < (int)res.size(); k++)
350     {
351         res[k] = classifyF(x, k);
352     }
353     return res;
354 }
355 
356 }}}  // namespace cv::detail::tracking
357