Merged steph's changes.
[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(currentLearnerType != weakType)\r
52         {\r
53                 srand(1); // so we always generate the same weak learner\r
54                 learners.clear();\r
55                 learners.resize(learnerCount);\r
56                 // we generate a bunch of random directions as learners\r
57         //      srand(1);\r
58                 if(weakType != 1) // random projection\r
59                 {\r
60                         if(dim==2)\r
61                         {\r
62                                 FOR(i, learnerCount)\r
63                                 {\r
64                                                 learners[i].resize(dim);\r
65                                                 if(!weakType)\r
66                                                 {\r
67                                                         float theta = i / (float)learnerCount * PIf;\r
68                                                         //                      float theta = rand()/(float)RAND_MAX*PIf;\r
69                                                         learners[i][0] = cosf(theta);\r
70                                                         learners[i][1] = sinf(theta);\r
71                                                 }\r
72                                                 else\r
73                                                 {\r
74                                                         learners[i][0] = rand()/(float)RAND_MAX;\r
75                                                         learners[i][1] = rand()/(float)RAND_MAX;\r
76                                                 }\r
77                                 }\r
78                         }\r
79                         else\r
80                         {\r
81                 float minV = -1, maxV = 1;\r
82                 if(weakType)\r
83                 {\r
84                     FOR(i, samples.size())\r
85                     {\r
86                         FOR(d, dim)\r
87                         {\r
88                             minV = min(minV, samples[i][d]);\r
89                             maxV = max(maxV, samples[i][d]);\r
90                         }\r
91                     }\r
92                 }\r
93                                 FOR(i, learnerCount)\r
94                                 {\r
95                                         learners[i].resize(dim);\r
96                                         if(!weakType)\r
97                                         {\r
98                                                 FOR(d, dim) learners[i][d] = rand()/(float)RAND_MAX*2. -1.;\r
99                                         }\r
100                                         else\r
101                                         {\r
102                         FOR(d, dim) learners[i][d] = (rand()/(float)RAND_MAX)*(maxV-minV) + minV;\r
103                                         }\r
104                                 }\r
105                         }\r
106                 }\r
107                 else // random rectangle\r
108                 {\r
109                         // we need to find the boundaries\r
110                         float *xMin = new float[dim];\r
111                         float *xMax = new float[dim];\r
112                         FOR(i, samples.size())\r
113                         {\r
114                                 FOR(d,dim)\r
115                                 {\r
116                                         if(xMin[d] > samples[i][d]) xMin[d] = samples[i][d];\r
117                                         if(xMax[d] < samples[i][d]) xMax[d] = samples[i][d];\r
118                                 }\r
119                         }\r
120 \r
121                         FOR(i, learnerCount)\r
122                         {\r
123                                 learners[i].resize(dim*2);\r
124                                 FOR(d, dim)\r
125                                 {\r
126                                         float x = (rand() / (float)RAND_MAX)*(xMax[d] - xMin[d]) + xMin[d]; // starting point\r
127                                         float l = (rand() / (float)RAND_MAX)*(xMax[d] - xMin[d]); // width\r
128                                         learners[i][2*d] = x;\r
129                                         learners[i][2*d+1] = l;\r
130                                 }\r
131                         }\r
132                         delete [] xMin;\r
133                         delete [] xMax;\r
134                 }\r
135                 currentLearnerType = weakType;\r
136         }\r
137 \r
138         CvMat *trainSamples = cvCreateMat(sampleCnt, learnerCount, CV_32FC1);\r
139         CvMat *trainLabels = cvCreateMat(labels.size(), 1, CV_32FC1);\r
140         CvMat *sampleWeights = cvCreateMat(samples.size(), 1, CV_32FC1);\r
141 \r
142         if(weakType != 1)\r
143         {\r
144                 if(dim == 2)\r
145                 {\r
146                         FOR(i, sampleCnt)\r
147                         {\r
148                                 FOR(j, learnerCount)\r
149                                 {\r
150                                         float val = 0;\r
151                                         if(!weakType)\r
152                                         {\r
153                                                 val = samples[perm[i]][0]* learners[j][0] + samples[perm[i]][1]* learners[j][1];\r
154                                         }\r
155                                         else\r
156                                         {\r
157                                                 val = sqrtf((samples[perm[i]][0] - learners[j][0])*(samples[perm[i]][0] - learners[j][0])+\r
158                                                         (samples[perm[i]][1] - learners[j][1])*(samples[perm[i]][1] - learners[j][1]));\r
159                                         }\r
160                                         cvSetReal2D(trainSamples, i, j, val);\r
161                                 }\r
162                                 cvSet1D(trainLabels, i, cvScalar((float)labels[perm[i]]));\r
163                                 cvSet1D(sampleWeights, i, cvScalar(1));\r
164                         }\r
165 \r
166                 }\r
167                 else\r
168                 {\r
169                         FOR(i, sampleCnt)\r
170                         {\r
171                                 // project the sample in the direction of the learner \r
172                                 fvec sample = samples[perm[i]];\r
173                                 FOR(j, learnerCount)\r
174                                 {\r
175                                         float val = 0;\r
176                     if(!weakType) val = sample * learners[j];\r
177                                         else\r
178                                         {\r
179                                                 FOR(d,dim) val += (sample[d] - learners[j][d])*(sample[d] - learners[j][d]);\r
180                                                 val = sqrtf(val);\r
181                                         }\r
182                                         cvSetReal2D(trainSamples, i, j, val);\r
183                                 }\r
184                                 cvSet1D(trainLabels, i, cvScalar((float)labels[perm[i]]));\r
185                                 cvSet1D(sampleWeights, i, cvScalar(1));\r
186                         }\r
187                 }\r
188         }\r
189         else\r
190         {\r
191                 FOR(i, sampleCnt)\r
192                 {\r
193                         // check if the sample is inside the recangle generated by the classifier\r
194                         const fvec sample = samples[perm[i]];\r
195                         FOR(j, learnerCount)\r
196                         {\r
197                                 float val = 1;\r
198                                 FOR(d, dim)\r
199                                 {\r
200                                         if(sample[d] < learners[j][2*d] || sample[d] > learners[j][2*d]+learners[j][2*d+1])\r
201                                         {\r
202                                                 val = 0;\r
203                                                 break;\r
204                                         }\r
205                                 }\r
206                                 cvSetReal2D(trainSamples, i, j, val);\r
207                         }\r
208                         cvSet1D(trainLabels, i, cvScalar((float)labels[perm[i]]));\r
209                         cvSet1D(sampleWeights, i, cvScalar(1));\r
210                 }\r
211         }\r
212         delete [] perm;\r
213 \r
214         CvMat *varType = cvCreateMat(trainSamples->width+1, 1, CV_8UC1);\r
215         FOR(i, trainSamples->width)\r
216         {\r
217                 CV_MAT_ELEM(*varType, u8, i, 0) = CV_VAR_NUMERICAL;\r
218         }\r
219         CV_MAT_ELEM(*varType, u8, trainSamples->width, 0) = CV_VAR_CATEGORICAL;\r
220 \r
221         int maxSplit = 1;\r
222         CvBoostParams params(CvBoost::GENTLE, weakCount, 0.95, maxSplit, false, NULL);\r
223         params.split_criteria = CvBoost::DEFAULT;\r
224         model = new CvBoost();\r
225         model->train(trainSamples, CV_ROW_SAMPLE, trainLabels, NULL, NULL, varType, NULL, params);\r
226 \r
227         scoreMultiplier = 1.f;\r
228         float maxScore=-FLT_MAX, minScore=FLT_MAX;\r
229         FOR(i, samples.size())\r
230         {\r
231                 float score = Test(samples[i]);\r
232                 if(score > maxScore) maxScore = score;\r
233                 if(score < minScore) minScore = score;\r
234         }\r
235         if(minScore != maxScore)\r
236         {\r
237                 scoreMultiplier = 1.f/(max(abs((double)maxScore),abs((double)minScore)))*5.f;\r
238         }\r
239 \r
240         CvSeq *predictors = model->get_weak_predictors();\r
241         int length = cvSliceLength(CV_WHOLE_SEQ, predictors);\r
242         features.clear();\r
243         FOR(i, length)\r
244         {\r
245                 CvBoostTree *predictor = *CV_SEQ_ELEM(predictors, CvBoostTree*, i);\r
246                 CvDTreeSplit *split = predictor->get_root()->split;\r
247                 features.push_back(split->var_idx);\r
248         }\r
249 \r
250         cvReleaseMat(&trainSamples);\r
251         cvReleaseMat(&trainLabels);\r
252         cvReleaseMat(&sampleWeights);\r
253         cvReleaseMat(&varType);\r
254         trainSamples = 0;\r
255         trainLabels = 0;\r
256         sampleWeights = 0;\r
257         varType = 0;\r
258 }\r
259 \r
260 float ClassifierBoost::Test( const fvec &sample )\r
261 {\r
262         if(!model) return 0;\r
263         if(!learners.size()) return 0;\r
264 \r
265         CvMat *x = cvCreateMat(1, learners.size(), CV_32FC1);\r
266         if(weakType != 1)\r
267         {\r
268                 if(dim == 2)\r
269                 {\r
270                         FOR(i, features.size())\r
271                         {\r
272                                 float val = 0;\r
273                                 if(!weakType)\r
274                                 {\r
275                                         val = sample[0] * learners[features[i]][0] + sample[1] * learners[features[i]][1];\r
276                                 }\r
277                                 else\r
278                                 {\r
279                                         val = sqrtf((sample[0] - learners[features[i]][0])*(sample[0] - learners[features[i]][0])+\r
280                                                 (sample[1] - learners[features[i]][1])*(sample[1] - learners[features[i]][1]));\r
281                                 }\r
282                                 cvSetReal2D(x, 0, features[i], val);\r
283                         }\r
284                 }\r
285                 else\r
286                 {\r
287                         FOR(i, features.size())\r
288                         {\r
289                                 float val = 0;\r
290                                 if(!weakType) sample * learners[features[i]];\r
291                                 else\r
292                                 {\r
293                                         FOR(d,dim) val += (sample[d] - learners[features[i]][d])*(sample[d] - learners[features[i]][d]);\r
294                                         val = sqrtf(val);\r
295                                 }\r
296                                 cvSetReal2D(x, 0, features[i], val);\r
297                         }\r
298                 }\r
299         }\r
300         else\r
301         {\r
302                 FOR(i, features.size())\r
303                 {\r
304                         int val = 1;\r
305                         FOR(d, dim)\r
306                         {\r
307                                 if(sample[d] < learners[features[i]][2*d] ||\r
308                                         sample[d] > learners[features[i]][2*d]+learners[features[i]][2*d+1])\r
309                                 {\r
310                                         val = 0;\r
311                                         break;\r
312                                 }\r
313                         }\r
314                         cvSetReal2D(x, 0, features[i], val);\r
315                 }\r
316         }\r
317 \r
318         // allocate memory for weak learner output\r
319         int length = cvSliceLength(CV_WHOLE_SEQ, model->get_weak_predictors());\r
320         CvMat *weakResponses = cvCreateMat(length, 1, CV_32FC1);\r
321         float y = model->predict(x, NULL, weakResponses, CV_WHOLE_SEQ);\r
322         double score = cvSum(weakResponses).val[0] * scoreMultiplier;\r
323 \r
324         cvReleaseMat(&weakResponses);\r
325         cvReleaseMat(&x);\r
326         return score;\r
327 }\r
328 \r
329 void ClassifierBoost::SetParams( u32 weakCount, int weakType )\r
330 {\r
331         this->weakCount = weakCount;\r
332         this->weakType = weakType ;\r
333 }\r
334 \r
335 char *ClassifierBoost::GetInfoString()\r
336 {\r
337         char *text = new char[1024];\r
338         sprintf(text, "Boosting\n");\r
339         sprintf(text, "%sLearners Count: %d\n", text, weakCount);\r
340         sprintf(text, "%sLearners Type: ", text);\r
341         switch(weakType)\r
342         {\r
343         case 0:\r
344                 sprintf(text, "%sRandom Projections\n", text);\r
345                 break;\r
346         case 1:\r
347                 sprintf(text, "%sRandom Rectangles\n", text);\r
348                 break;\r
349         case 2:\r
350                 sprintf(text, "%sRandom Circles\n", text);\r
351                 break;\r
352         }\r
353         return text;\r
354 }\r