Merge branch 'devel' of git://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 = 2000;\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]; // starting point\r
135                                         float l = (rand() / (float)RAND_MAX)*(xMax[d] - xMin[d]); // width\r
136                                         learners[i][2*d] = x;\r
137                                         learners[i][2*d+1] = l;\r
138                                 }\r
139                         }\r
140                 }\r
141                 currentLearnerType = weakType;\r
142         }\r
143 \r
144         CvMat *trainSamples = cvCreateMat(sampleCnt, learnerCount, CV_32FC1);\r
145         CvMat *trainLabels = cvCreateMat(labels.size(), 1, CV_32FC1);\r
146         CvMat *sampleWeights = cvCreateMat(samples.size(), 1, CV_32FC1);\r
147 \r
148     if(weakType != 1) // random projection or random circle\r
149         {\r
150                 if(dim == 2)\r
151                 {\r
152                         FOR(i, sampleCnt)\r
153                         {\r
154                 fvec sample = samples[perm[i]];\r
155                 FOR(j, learnerCount)\r
156                                 {\r
157                                         float val = 0;\r
158                                         if(!weakType)\r
159                                         {\r
160                         val = sample[0]* learners[j][0] + sample[1]* learners[j][1];\r
161                                         }\r
162                                         else\r
163                                         {\r
164                         val = sqrtf((sample[0] - learners[j][0])*(sample[0] - learners[j][0])+\r
165                             (sample[1] - learners[j][1])*(sample[1] - learners[j][1]));\r
166                                         }\r
167                                         cvSetReal2D(trainSamples, i, j, val);\r
168                                 }\r
169                                 cvSet1D(trainLabels, i, cvScalar((float)labels[perm[i]]));\r
170                                 cvSet1D(sampleWeights, i, cvScalar(1));\r
171                         }\r
172 \r
173                 }\r
174                 else\r
175                 {\r
176                         FOR(i, sampleCnt)\r
177                         {\r
178                                 // project the sample in the direction of the learner \r
179                                 fvec sample = samples[perm[i]];\r
180                                 FOR(j, learnerCount)\r
181                                 {\r
182                                         float val = 0;\r
183                     if(!weakType)\r
184                     {\r
185                         FOR(d, dim) val += sample[d] * learners[j][d];\r
186                     }\r
187                                         else\r
188                                         {\r
189                                                 FOR(d,dim) val += (sample[d] - learners[j][d])*(sample[d] - learners[j][d]);\r
190                                                 val = sqrtf(val);\r
191                                         }\r
192                                         cvSetReal2D(trainSamples, i, j, val);\r
193                                 }\r
194                                 cvSet1D(trainLabels, i, cvScalar((float)labels[perm[i]]));\r
195                                 cvSet1D(sampleWeights, i, cvScalar(1));\r
196                         }\r
197                 }\r
198         }\r
199         else\r
200         {\r
201                 FOR(i, sampleCnt)\r
202                 {\r
203                         // check if the sample is inside the recangle generated by the classifier\r
204                         const fvec sample = samples[perm[i]];\r
205                         FOR(j, learnerCount)\r
206                         {\r
207                                 float val = 1;\r
208                                 FOR(d, dim)\r
209                                 {\r
210                                         if(sample[d] < learners[j][2*d] || sample[d] > learners[j][2*d]+learners[j][2*d+1])\r
211                                         {\r
212                                                 val = 0;\r
213                                                 break;\r
214                                         }\r
215                                 }\r
216                                 cvSetReal2D(trainSamples, i, j, val);\r
217                         }\r
218                         cvSet1D(trainLabels, i, cvScalar((float)labels[perm[i]]));\r
219                         cvSet1D(sampleWeights, i, cvScalar(1));\r
220                 }\r
221         }\r
222         delete [] perm;\r
223 \r
224         CvMat *varType = cvCreateMat(trainSamples->width+1, 1, CV_8UC1);\r
225         FOR(i, trainSamples->width)\r
226         {\r
227                 CV_MAT_ELEM(*varType, u8, i, 0) = CV_VAR_NUMERICAL;\r
228         }\r
229         CV_MAT_ELEM(*varType, u8, trainSamples->width, 0) = CV_VAR_CATEGORICAL;\r
230 \r
231         int maxSplit = 1;\r
232         CvBoostParams params(CvBoost::GENTLE, weakCount, 0.95, maxSplit, false, NULL);\r
233         params.split_criteria = CvBoost::DEFAULT;\r
234         model = new CvBoost();\r
235         model->train(trainSamples, CV_ROW_SAMPLE, trainLabels, NULL, NULL, varType, NULL, params);\r
236 \r
237         scoreMultiplier = 1.f;\r
238         float maxScore=-FLT_MAX, minScore=FLT_MAX;\r
239         FOR(i, samples.size())\r
240         {\r
241                 float score = Test(samples[i]);\r
242                 if(score > maxScore) maxScore = score;\r
243                 if(score < minScore) minScore = score;\r
244         }\r
245         if(minScore != maxScore)\r
246         {\r
247                 scoreMultiplier = 1.f/(max(abs((double)maxScore),abs((double)minScore)))*5.f;\r
248         }\r
249 \r
250         CvSeq *predictors = model->get_weak_predictors();\r
251         int length = cvSliceLength(CV_WHOLE_SEQ, predictors);\r
252         features.clear();\r
253         FOR(i, length)\r
254         {\r
255                 CvBoostTree *predictor = *CV_SEQ_ELEM(predictors, CvBoostTree*, i);\r
256                 CvDTreeSplit *split = predictor->get_root()->split;\r
257         if(!split) continue;\r
258                 features.push_back(split->var_idx);\r
259         }\r
260 \r
261         cvReleaseMat(&trainSamples);\r
262         cvReleaseMat(&trainLabels);\r
263         cvReleaseMat(&sampleWeights);\r
264         cvReleaseMat(&varType);\r
265         trainSamples = 0;\r
266         trainLabels = 0;\r
267         sampleWeights = 0;\r
268         varType = 0;\r
269 }\r
270 \r
271 float ClassifierBoost::Test( const fvec &sample )\r
272 {\r
273         if(!model) return 0;\r
274         if(!learners.size()) return 0;\r
275 \r
276         CvMat *x = cvCreateMat(1, learners.size(), CV_32FC1);\r
277         if(weakType != 1)\r
278         {\r
279                 if(dim == 2)\r
280                 {\r
281                         FOR(i, features.size())\r
282                         {\r
283                                 float val = 0;\r
284                                 if(!weakType)\r
285                                 {\r
286                                         val = sample[0] * learners[features[i]][0] + sample[1] * learners[features[i]][1];\r
287                                 }\r
288                                 else\r
289                                 {\r
290                                         val = sqrtf((sample[0] - learners[features[i]][0])*(sample[0] - learners[features[i]][0])+\r
291                                                 (sample[1] - learners[features[i]][1])*(sample[1] - learners[features[i]][1]));\r
292                                 }\r
293                                 cvSetReal2D(x, 0, features[i], val);\r
294                         }\r
295                 }\r
296                 else\r
297                 {\r
298                         FOR(i, features.size())\r
299                         {\r
300                                 float val = 0;\r
301                                 if(!weakType) sample * learners[features[i]];\r
302                                 else\r
303                                 {\r
304                                         FOR(d,dim) val += (sample[d] - learners[features[i]][d])*(sample[d] - learners[features[i]][d]);\r
305                                         val = sqrtf(val);\r
306                                 }\r
307                                 cvSetReal2D(x, 0, features[i], val);\r
308                         }\r
309                 }\r
310         }\r
311         else\r
312         {\r
313                 FOR(i, features.size())\r
314                 {\r
315                         int val = 1;\r
316                         FOR(d, dim)\r
317                         {\r
318                                 if(sample[d] < learners[features[i]][2*d] ||\r
319                                         sample[d] > learners[features[i]][2*d]+learners[features[i]][2*d+1])\r
320                                 {\r
321                                         val = 0;\r
322                                         break;\r
323                                 }\r
324                         }\r
325                         cvSetReal2D(x, 0, features[i], val);\r
326                 }\r
327         }\r
328 \r
329         // allocate memory for weak learner output\r
330         int length = cvSliceLength(CV_WHOLE_SEQ, model->get_weak_predictors());\r
331         CvMat *weakResponses = cvCreateMat(length, 1, CV_32FC1);\r
332         float y = model->predict(x, NULL, weakResponses, CV_WHOLE_SEQ);\r
333         double score = cvSum(weakResponses).val[0] * scoreMultiplier;\r
334 \r
335         cvReleaseMat(&weakResponses);\r
336         cvReleaseMat(&x);\r
337         return score;\r
338 }\r
339 \r
340 void ClassifierBoost::SetParams( u32 weakCount, int weakType )\r
341 {\r
342         this->weakCount = weakCount;\r
343         this->weakType = weakType ;\r
344 }\r
345 \r
346 char *ClassifierBoost::GetInfoString()\r
347 {\r
348         char *text = new char[1024];\r
349         sprintf(text, "Boosting\n");\r
350         sprintf(text, "%sLearners Count: %d\n", text, weakCount);\r
351         sprintf(text, "%sLearners Type: ", text);\r
352         switch(weakType)\r
353         {\r
354         case 0:\r
355                 sprintf(text, "%sRandom Projections\n", text);\r
356                 break;\r
357         case 1:\r
358                 sprintf(text, "%sRandom Rectangles\n", text);\r
359                 break;\r
360         case 2:\r
361                 sprintf(text, "%sRandom Circles\n", text);\r
362                 break;\r
363         }\r
364         return text;\r
365 }\r