iol
classifierHandling.cpp
1 /*
2  * Copyright (C) 2011 Department of Robotics Brain and Cognitive Sciences - Istituto Italiano di Tecnologia
3  * Author: Ugo Pattacini
4  * email: ugo.pattacini@iit.it
5  * Permission is granted to copy, distribute, and/or modify this program
6  * under the terms of the GNU General Public License, version 2 or any
7  * later version published by the Free Software Foundation.
8  *
9  * A copy of the license can be found at
10  * http://www.robotcub.org/icub/license/gpl.txt
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15  * Public License for more details
16 */
17 
18 #include <sstream>
19 #include <vector>
20 #include <yarp/os/Log.h>
21 
22 #include "classifierHandling.h"
23 
24 
25 /**********************************************************/
26 void Classifier::push(const Score &s)
27 {
28  window.push_back(s);
29  if (window.size()>winLen)
30  window.pop_front();
31 }
32 
33 
34 /**********************************************************/
35 void Classifier::push(const bool isPositive)
36 {
37  Score s;
38  s.isPositive=isPositive;
39  s.score=newScore;
40  push(s);
41 }
42 
43 
44 // theta = arg min_theta(sum_i(misclassified(score_i(theta))))
45 /**********************************************************/
46 double Classifier::update()
47 {
48  // debug: keep the current threshold
49  return threshold;
50 
51  bool posExitCond=false;
52  bool negExitCond=false;
53  for (size_t i=0; i<window.size(); i++)
54  {
55  if (window[i].isPositive)
56  posExitCond=true;
57  else
58  negExitCond=true;
59 
60  if (posExitCond && negExitCond)
61  break;
62  }
63 
64  double bestTheta=threshold;
65  if (posExitCond && negExitCond)
66  {
67  size_t minPenalty=window.size()+1;
68 
69  for (size_t i=0; i<window.size(); i++)
70  {
71  double theta=window[i].score;
72  size_t penalty=0;
73 
74  for (size_t j=0; j<window.size(); j++)
75  if ((window[j].isPositive && (window[j].score<theta)) ||
76  (!window[j].isPositive && (window[j].score>theta)))
77  penalty++;
78 
79  if (penalty<=minPenalty)
80  {
81  bestTheta=theta;
82  minPenalty=penalty;
83  }
84  }
85  }
86 
87  return bestTheta;
88 }
89 
90 
91 /**********************************************************/
92 void Classifier::init(const double thres)
93 {
94  name="";
95  threshold=thres;
96  winLen=10;
97  window.clear();
98 }
99 
100 
101 /**********************************************************/
102 Classifier::Classifier(const double thres)
103 {
104  init(thres);
105 }
106 
107 
108 /**********************************************************/
109 Classifier::Classifier(const Classifier &classifier)
110 {
111  name=classifier.name;
112  threshold=classifier.threshold;
113  newScore=classifier.newScore;
114  winLen=classifier.winLen;
115  window=classifier.window;
116 }
117 
118 
119 /**********************************************************/
120 Classifier::Classifier(const string &name, const double thres)
121 {
122  init(thres);
123  this->name=name;
124 }
125 
126 
127 /**********************************************************/
128 Classifier::Classifier(const Bottle &options)
129 {
130  fromBottle(options);
131 }
132 
133 
134 /**********************************************************/
135 bool Classifier::isThis(const double val) const
136 {
137  return (val>=threshold);
138 }
139 
140 
141 /**********************************************************/
142 void Classifier::prepare(const double newScore)
143 {
144  yInfo("Classifier %s: stored score %g",name.c_str(),newScore);
145  this->newScore=newScore;
146 }
147 
148 
149 /**********************************************************/
150 void Classifier::declare(const bool isPositive)
151 {
152  push(isPositive);
153  yInfo("Classifier %s: score %g declared as %s",name.c_str(),
154  newScore,isPositive?"positive":"negative");
155 
156  double thresholdLatched=threshold;
157  threshold=update();
158  yInfo("Updating threshold of classifier %s: %g => (%g) => %g",
159  name.c_str(),thresholdLatched,newScore,threshold);
160 }
161 
162 
163 /**********************************************************/
164 void Classifier::negative()
165 {
166  declare(false);
167 }
168 
169 
170 /**********************************************************/
171 void Classifier::positive()
172 {
173  declare(true);
174 }
175 
176 
177 /**********************************************************/
178 void Classifier::fromBottle(const Bottle &options)
179 {
180  init();
181 
182  if (options.check("name"))
183  name=options.find("name").asString();
184 
185  if (options.check("threshold"))
186  threshold=options.find("threshold").asFloat64();
187 
188  if (options.check("winLen"))
189  winLen=options.find("winLen").asInt32();
190 
191  if (Bottle *item_list=options.find("items").asList())
192  {
193  for (int i=0; i<item_list->size()-1; i+=2)
194  {
195  Score s;
196  s.isPositive=(item_list->get(i).asString()=="pos");
197  s.score=item_list->get(i+1).asFloat64();
198  push(s);
199  }
200  }
201 }
202 
203 
204 /**********************************************************/
205 Bottle Classifier::toBottle()
206 {
207  Bottle options;
208 
209  // insert name
210  Bottle &name_list=options.addList();
211  name_list.addString("name");
212  name_list.addString(name);
213 
214  // insert threshold
215  Bottle &threshold_list=options.addList();
216  threshold_list.addString("threshold");
217  threshold_list.addFloat64(threshold);
218 
219  // insert winLen
220  Bottle &winLen_list=options.addList();
221  winLen_list.addString("winLen");
222  winLen_list.addInt32(winLen);
223 
224  // insert window
225  Bottle &window_list=options.addList();
226  window_list.addString("items");
227  Bottle &item_list=window_list.addList();
228  for (size_t i=0; i<window.size(); i++)
229  {
230  item_list.addString(window[i].isPositive?"pos":"neg");
231  item_list.addFloat64(window[i].score);
232  }
233 
234  return options;
235 }
236 
237 
238 /**********************************************************/
239 ClassifiersDataBase::~ClassifiersDataBase()
240 {
241  clear();
242 }
243 
244 
245 /**********************************************************/
246 void ClassifiersDataBase::clear()
247 {
248  for (auto it=begin(); it!=end(); it++)
249  if (it->second!=NULL)
250  delete it->second;
251 
252  map<string,Classifier*>::clear();
253 }
254 
255 
256 /**********************************************************/
257 void ClassifiersDataBase::erase(iterator it)
258 {
259  if (it->second!=NULL)
260  delete it->second;
261 
262  map<string,Classifier*>::erase(it);
263 }
264 
265 
266 /**********************************************************/
267 int ClassifiersDataBase::processScores(Classifier *pClassifier,
268  const Bottle &scores)
269 {
270  int ret=-1;
271  double maxScoreObj=0.0;
272 
273  for (int i=0; i<scores.size(); i++)
274  {
275  ostringstream tag;
276  tag<<"blob_"<<i;
277 
278  double maxScoreNoObj=0.0;
279  double scoreObj=0.0;
280 
281  Bottle *blobScores=scores.find(tag.str()).asList();
282  if (blobScores==NULL)
283  continue;
284 
285  for (int j=0; j<blobScores->size(); j++)
286  {
287  Bottle *item=blobScores->get(j).asList();
288  if (item==NULL)
289  continue;
290 
291  string name=item->get(0).asString();
292  double score=item->get(1).asFloat64();
293 
294  if (name==pClassifier->getName())
295  {
296  if (pClassifier->isThis(score))
297  scoreObj=score;
298  }
299  else if (score>=maxScoreNoObj)
300  maxScoreNoObj=score;
301  }
302 
303  if ((scoreObj>maxScoreNoObj) && (scoreObj>maxScoreObj))
304  {
305  pClassifier->prepare(maxScoreObj=scoreObj);
306  ret=i;
307  }
308  }
309 
310  return ret;
311 }
312 
313 
314 /**********************************************************/
315 string ClassifiersDataBase::findName(const Bottle &scores,
316  const string &tag,
317  double *score)
318 {
319  string retName=OBJECT_UNKNOWN;
320  if(score) *score = 0;
321  Bottle *blobScores=scores.find(tag).asList();
322  if (blobScores==NULL)
323  return retName;
324 
325  // first find the most likely object for the given blob
326  double maxScore=0.0; int imax=0;
327  vector<double> s(blobScores->size(),-1.0);
328  for (int i=0; i<blobScores->size(); i++)
329  {
330  Bottle *item=blobScores->get(i).asList();
331  if (item==NULL)
332  continue;
333 
334  string name=item->get(0).asString();
335  double score=item->get(1).asFloat64();
336  s[i]=score;
337 
338  auto it=find(name);
339  if (it!=end())
340  {
341  if (it->second->isThis(score) && (score>maxScore))
342  {
343  maxScore=score;
344  retName=name;
345  imax=i;
346  }
347  }
348  }
349 
350  // then double-check that the found object is a good prediction:
351  // the remaining scores shall not overcome 3/4 of max score
352  if (retName!=OBJECT_UNKNOWN)
353  {
354  for (size_t i=0; i<s.size(); i++)
355  {
356  if ((i!=imax) && (s[i]>0.75*maxScore))
357  return OBJECT_UNKNOWN;
358  }
359  }
360 
361  if(score) *score = maxScore;
362  return retName;
363 }
364 
365 
366 
367