Merge branch 'devel' of gitorious.org:mldemos/mldemos into devel
[mldemos:baraks-mldemos.git] / _AlgorithmsPlugins / OpenCV / classifierBoost.cpp
1 /*********************************************************************\r
2 MLDemos: A User-Friendly visualization toolkit for machine learning\r
3 Copyright (C) 2010  Basilio Noris\r
4 Contact: mldemos@b4silio.com\r
5 \r
6 This library is free software; you can redistribute it and/or\r
7 modify it under the terms of the GNU Lesser General Public\r
8 License as published by the Free Software Foundation; either\r
9 version 2.1 of the License, or (at your option) any later version.\r
10 \r
11 This library is distributed in the hope that it will be useful,\r
12 but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
14 Library General Public License for more details.\r
15 \r
16 You should have received a copy of the GNU Lesser General Public\r
17 License along with this library; if not, write to the Free\r
18 Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
19 *********************************************************************/\r
20 #include "public.h"\r
21 #include "basicMath.h"\r
22 #include "classifierBoost.h"\r
23 \r
24 using namespace std;\r
25 \r
26 ClassifierBoost::ClassifierBoost()\r
27 : model(0), weakCount(0), scoreMultiplier(1.f)\r
28 {\r
29         bSingleClass = false;\r
30 }\r
31 \r
32 ClassifierBoost::~ClassifierBoost()\r
33 {\r
34         if(model) model->clear();\r
35         DEL(model);\r
36 }\r
37 \r
38 vector<fvec> learners;\r
39 int currentLearnerType = -1;\r
40 \r
41 void ClassifierBoost::Train( std::vector< fvec > samples, ivec labels )\r
42 {\r
43         if(model)model->clear();\r
44         u32 sampleCnt = samples.size();\r
45         if(!sampleCnt) return;\r
46         DEL(model);\r
47         dim = samples[0].size();\r
48         u32 *perm = randPerm(sampleCnt);\r
49 \r
50         int learnerCount = max((!weakType?360 : 1000), (int)weakCount);\r
51     if(dim > 2) learnerCount = 1000;\r
52         if(currentLearnerType != weakType)\r
53         {\r
54                 srand(1); // so we always generate the same weak learner\r
55                 learners.clear();\r
56                 learners.resize(learnerCount);\r
57                 // we generate a bunch of random directions as learners\r
58         //      srand(1);\r
59         if(weakType != 1) // random projection or random circle\r
60                 {\r
61             float minV = -1, maxV = 1;\r
62             if(weakType)\r
63             {\r
64                 FOR(i, samples.size())\r
65                 {\r
66                     FOR(d, dim)\r
67                     {\r
68                         minV = min(minV, samples[i][d]);\r
69                         maxV = max(maxV, samples[i][d]);\r
70                     }\r
71                 }\r
72             }\r
73 \r
74                         if(dim==2)\r
75                         {\r
76                                 FOR(i, learnerCount)\r
77                                 {\r
78                                                 learners[i].resize(dim);\r
79                                                 if(!weakType)\r
80                                                 {\r
81                                                         float theta = i / (float)learnerCount * PIf;\r
82                                                         //                      float theta = rand()/(float)RAND_MAX*PIf;\r
83                                                         learners[i][0] = cosf(theta);\r
84                                                         learners[i][1] = sinf(theta);\r
85                                                 }\r
86                                                 else\r
87                                                 {\r
88                             learners[i][0] =  (rand()/(float)RAND_MAX)*(maxV-minV) + minV;\r
89                             learners[i][1] =  (rand()/(float)RAND_MAX)*(maxV-minV) + minV;\r
90                                                 }\r
91                                 }\r
92                         }\r
93                         else\r
94                         {\r
95                                 FOR(i, learnerCount)\r
96                                 {\r
97                                         learners[i].resize(dim);\r
98                     if(!weakType) // random projection\r
99                                         {\r
100                         fvec projection(dim,0);\r
101                         float norm = 0;\r
102                         FOR(d, dim)\r
103                         {\r
104                             projection[d] = drand48();\r
105                             norm += projection[d];\r
106                         }\r
107                         FOR(d, dim) learners[i][d] = projection[d] / norm;\r
108                     }\r
109                     else // random circles\r
110                                         {\r
111                         FOR(d, dim) learners[i][d] = (rand()/(float)RAND_MAX)*(maxV-minV) + minV;\r
112                                         }\r
113                                 }\r
114                         }\r
115                 }\r
116                 else // random rectangle\r
117                 {\r
118                         // we need to find the boundaries\r
119             fvec xMin(dim, FLT_MAX), xMax(dim, -FLT_MAX);\r
120                         FOR(i, samples.size())\r
121                         {\r
122                                 FOR(d,dim)\r
123                                 {\r
124                                         if(xMin[d] > samples[i][d]) xMin[d] = samples[i][d];\r
125                                         if(xMax[d] < samples[i][d]) xMax[d] = samples[i][d];\r
126                                 }\r
127                         }\r
128 \r
129                         FOR(i, learnerCount)\r
130                         {\r
131                                 learners[i].resize(dim*2);\r
132                                 FOR(d, dim)\r
133                                 {\r
134                     float x = (rand() / (float)RAND_MAX)*(xMax[d] - xMin[d]) + xMin[d]; // rectangle center\r
135                     float l = (rand() / (float)RAND_MAX)*(xMax[d] - xMin[d]); // width\r
136                     //float l = ((rand()+1) / (float)RAND_MAX); // width ratio\r
137                     learners[i][2*d] = x;\r
138                                         learners[i][2*d+1] = l;\r
139                                 }\r
140                         }\r
141                 }\r
142                 currentLearnerType = weakType;\r
143         }\r
144 \r
145         CvMat *trainSamples = cvCreateMat(sampleCnt, learnerCount, CV_32FC1);\r
146         CvMat *trainLabels = cvCreateMat(labels.size(), 1, CV_32FC1);\r
147         CvMat *sampleWeights = cvCreateMat(samples.size(), 1, CV_32FC1);\r
148 \r
149     if(weakType != 1) // random projection or random circle\r
150         {\r
151                 if(dim == 2)\r
152                 {\r
153                         FOR(i, sampleCnt)\r
154                         {\r
155                 fvec sample = samples[perm[i]];\r
156                 FOR(j, learnerCount)\r
157                                 {\r
158                     float val = 0;\r
159                                         if(!weakType)\r
160                                         {\r
161                         val = sample[0]* learners[j][0] + sample[1]* learners[j][1];\r
162                                         }\r
163                                         else\r
164                                         {\r
165                         val = sqrtf((sample[0] - learners[j][0])*(sample[0] - learners[j][0])+\r
166                             (sample[1] - learners[j][1])*(sample[1] - learners[j][1]));\r
167                                         }\r
168                                         cvSetReal2D(trainSamples, i, j, val);\r
169                                 }\r
170                                 cvSet1D(trainLabels, i, cvScalar((float)labels[perm[i]]));\r
171                                 cvSet1D(sampleWeights, i, cvScalar(1));\r
172                         }\r
173 \r
174                 }\r
175                 else\r
176                 {\r
177                         FOR(i, sampleCnt)\r
178                         {\r
179                                 // project the sample in the direction of the learner \r
180                                 fvec sample = samples[perm[i]];\r
181                                 FOR(j, learnerCount)\r
182                                 {\r
183                                         float val = 0;\r
184                     if(!weakType)\r
185                     {\r
186                         FOR(d, dim) val += sample[d] * learners[j][d];\r
187                     }\r
188                                         else\r
189                                         {\r
190                                                 FOR(d,dim) val += (sample[d] - learners[j][d])*(sample[d] - learners[j][d]);\r
191                                                 val = sqrtf(val);\r
192                                         }\r
193                                         cvSetReal2D(trainSamples, i, j, val);\r
194                                 }\r
195                                 cvSet1D(trainLabels, i, cvScalar((float)labels[perm[i]]));\r
196                                 cvSet1D(sampleWeights, i, cvScalar(1));\r
197                         }\r
198                 }\r
199         }\r
200         else\r
201         {\r
202                 FOR(i, sampleCnt)\r
203                 {\r
204                         // check if the sample is inside the recangle generated by the classifier\r
205                         const fvec sample = samples[perm[i]];\r
206                         FOR(j, learnerCount)\r
207                         {\r
208                 float val = 1;\r
209                                 FOR(d, dim)\r
210                                 {\r
211                     //float v = fabs(sample[d] - learners[j][2*d]);\r
212                     //float v = fabs(sample[d] - learners[j][2*d]) / learners[j][2*d+1];\r
213                     //val += v;\r
214 \r
215                     if(sample[d] < learners[j][2*d] || sample[d] > learners[j][2*d]+learners[j][2*d+1])\r
216                     {\r
217                         val = 0;\r
218                         break;\r
219                     }\r
220                 }\r
221                 cvSetReal2D(trainSamples, i, j, val + rand()/(float)RAND_MAX*0.1);\r
222                         }\r
223                         cvSet1D(trainLabels, i, cvScalar((float)labels[perm[i]]));\r
224                         cvSet1D(sampleWeights, i, cvScalar(1));\r
225                 }\r
226         }\r
227         delete [] perm;\r
228 \r
229         CvMat *varType = cvCreateMat(trainSamples->width+1, 1, CV_8UC1);\r
230         FOR(i, trainSamples->width)\r
231         {\r
232                 CV_MAT_ELEM(*varType, u8, i, 0) = CV_VAR_NUMERICAL;\r
233         }\r
234         CV_MAT_ELEM(*varType, u8, trainSamples->width, 0) = CV_VAR_CATEGORICAL;\r
235 \r
236         int maxSplit = 1;\r
237         CvBoostParams params(CvBoost::GENTLE, weakCount, 0.95, maxSplit, false, NULL);\r
238         params.split_criteria = CvBoost::DEFAULT;\r
239         model = new CvBoost();\r
240         model->train(trainSamples, CV_ROW_SAMPLE, trainLabels, NULL, NULL, varType, NULL, params);\r
241 \r
242         scoreMultiplier = 1.f;\r
243         float maxScore=-FLT_MAX, minScore=FLT_MAX;\r
244         FOR(i, samples.size())\r
245         {\r
246                 float score = Test(samples[i]);\r
247                 if(score > maxScore) maxScore = score;\r
248                 if(score < minScore) minScore = score;\r
249         }\r
250         if(minScore != maxScore)\r
251         {\r
252                 scoreMultiplier = 1.f/(max(abs((double)maxScore),abs((double)minScore)))*5.f;\r
253         }\r
254 \r
255         CvSeq *predictors = model->get_weak_predictors();\r
256         int length = cvSliceLength(CV_WHOLE_SEQ, predictors);\r
257         features.clear();\r
258         FOR(i, length)\r
259         {\r
260                 CvBoostTree *predictor = *CV_SEQ_ELEM(predictors, CvBoostTree*, i);\r
261                 CvDTreeSplit *split = predictor->get_root()->split;\r
262         if(!split) continue;\r
263                 features.push_back(split->var_idx);\r
264         }\r
265 \r
266         cvReleaseMat(&trainSamples);\r
267         cvReleaseMat(&trainLabels);\r
268         cvReleaseMat(&sampleWeights);\r
269         cvReleaseMat(&varType);\r
270         trainSamples = 0;\r
271         trainLabels = 0;\r
272         sampleWeights = 0;\r
273         varType = 0;\r
274 }\r
275 \r
276 float ClassifierBoost::Test( const fvec &sample )\r
277 {\r
278         if(!model) return 0;\r
279         if(!learners.size()) return 0;\r
280 \r
281         CvMat *x = cvCreateMat(1, learners.size(), CV_32FC1);\r
282         if(weakType != 1)\r
283         {\r
284                 if(dim == 2)\r
285                 {\r
286                         FOR(i, features.size())\r
287                         {\r
288                                 float val = 0;\r
289                                 if(!weakType)\r
290                                 {\r
291                                         val = sample[0] * learners[features[i]][0] + sample[1] * learners[features[i]][1];\r
292                                 }\r
293                                 else\r
294                                 {\r
295                                         val = sqrtf((sample[0] - learners[features[i]][0])*(sample[0] - learners[features[i]][0])+\r
296                                                 (sample[1] - learners[features[i]][1])*(sample[1] - learners[features[i]][1]));\r
297                                 }\r
298                                 cvSetReal2D(x, 0, features[i], val);\r
299                         }\r
300                 }\r
301                 else\r
302                 {\r
303                         FOR(i, features.size())\r
304                         {\r
305                                 float val = 0;\r
306                 if(!weakType) sample * learners[features[i]];\r
307                                 else\r
308                                 {\r
309                                         FOR(d,dim) val += (sample[d] - learners[features[i]][d])*(sample[d] - learners[features[i]][d]);\r
310                                         val = sqrtf(val);\r
311                                 }\r
312                                 cvSetReal2D(x, 0, features[i], val);\r
313                         }\r
314                 }\r
315         }\r
316         else\r
317         {\r
318                 FOR(i, features.size())\r
319                 {\r
320             int val = 1;\r
321                         FOR(d, dim)\r
322                         {\r
323                 //float v = fabs(sample[d] - learners[features[i]][2*d]);\r
324                 //float v = fabs(sample[d] - learners[features[i]][2*d]) / learners[features[i]][2*d+1];\r
325                 //val += v;\r
326 \r
327                 if(sample[d] < learners[features[i]][2*d] ||\r
328                     sample[d] > learners[features[i]][2*d]+learners[features[i]][2*d+1])\r
329                 {\r
330                     val = 0;\r
331                     break;\r
332                 }\r
333                         }\r
334             cvSetReal2D(x, 0, features[i], val + rand()/(float)RAND_MAX*0.1);\r
335                 }\r
336         }\r
337 \r
338         // allocate memory for weak learner output\r
339         int length = cvSliceLength(CV_WHOLE_SEQ, model->get_weak_predictors());\r
340         CvMat *weakResponses = cvCreateMat(length, 1, CV_32FC1);\r
341         float y = model->predict(x, NULL, weakResponses, CV_WHOLE_SEQ);\r
342         double score = cvSum(weakResponses).val[0] * scoreMultiplier;\r
343 \r
344         cvReleaseMat(&weakResponses);\r
345         cvReleaseMat(&x);\r
346         return score;\r
347 }\r
348 \r
349 void ClassifierBoost::SetParams( u32 weakCount, int weakType )\r
350 {\r
351         this->weakCount = weakCount;\r
352         this->weakType = weakType ;\r
353 }\r
354 \r
355 const char *ClassifierBoost::GetInfoString()\r
356 {\r
357         char *text = new char[1024];\r
358         sprintf(text, "Boosting\n");\r
359         sprintf(text, "%sLearners Count: %d\n", text, weakCount);\r
360         sprintf(text, "%sLearners Type: ", text);\r
361         switch(weakType)\r
362         {\r
363         case 0:\r
364                 sprintf(text, "%sRandom Projections\n", text);\r
365                 break;\r
366         case 1:\r
367                 sprintf(text, "%sRandom Rectangles\n", text);\r
368                 break;\r
369         case 2:\r
370                 sprintf(text, "%sRandom Circles\n", text);\r
371                 break;\r
372         }\r
373         return text;\r
374 }\r