himrep
linearClassifierThread.cpp
1 #include "linearClassifierThread.h"
2 
3 
4 
5 linearClassifierThread::linearClassifierThread(yarp::os::ResourceFinder &rf, Port* commPort)
6 {
7  this->commandPort=commPort;
8  currentState=STATE_DONOTHING;
9 
10  this->currPath = rf.getHomeContextPath().c_str();
11 
12  string moduleName = rf.check("name",Value("linearClassifier"), "module name (string)").asString().c_str();
13  this->inputFeatures = "/";
14  this->inputFeatures += moduleName;
15  this->inputFeatures += rf.check("InputPortFeatures",Value("/features:i"),"Input image port (string)").asString().c_str();
16 
17  this->outputPortName = "/";
18  this->outputPortName += moduleName;
19  this->outputPortName += rf.check("OutputPortClassification",Value("/classification:o"),"Input image port (string)").asString().c_str();
20 
21  this->outputScorePortName = "/";
22  this->outputScorePortName += moduleName;
23  this->outputScorePortName += rf.check("OutputPortScores",Value("/scores:o"),"Input image port (string)").asString().c_str();
24 
25  this->bufferSize = rf.check("BufferSize",Value(15),"Buffer Size").asInt32();
26  this->CSVM = rf.check("CSVM",Value(1.0),"CSVM").asFloat64();
27  this->useWeightedSVM= rf.check("WeightedSVM",Value(0),"WeightedSVM").asInt32();
28 
29  printf("WeightedSVM: %d \n",useWeightedSVM);
30 
31  string dbfolder = rf.check("databaseFolder",Value("database"), "module name (string)").asString().c_str();
32  dbfolder="/"+dbfolder;
33  this->currPath=this->currPath+dbfolder;
34 }
35 
36 bool linearClassifierThread::getClassList(Bottle &b)
37 {
38  lock_guard<mutex> lg(mtx);
39  for(int i=0; i<knownObjects.size(); i++)
40  b.addString(knownObjects[i].first.c_str());
41  return true;
42 }
43 
44 bool linearClassifierThread::changeName(const string &old_name, const string &new_name)
45 {
46  lock_guard<mutex> lg(mtx);
47 
48  int j=-1; int k=-1;
49  for (size_t i=0; i<knownObjects.size(); i++)
50  {
51  if (knownObjects[i].first==old_name)
52  j=(int)i;
53  else if (knownObjects[i].first==new_name)
54  k=(int)i;
55  }
56 
57  if ((j<0) || (k>=0))
58  return false;
59 
60  knownObjects[j].first=new_name;
61 
62  string old_path=currPath+"/"+old_name;
63  string new_path=currPath+"/"+new_name;
64  yarp::os::rename(old_path.c_str(),new_path.c_str());
65 
66  return true;
67 }
68 
69 void linearClassifierThread::checkKnownObjects()
70 {
71  knownObjects.clear();
72  linearClassifiers.clear();
73 
74  if(yarp::os::stat(currPath.c_str()))
75  {
76  createFullPath(currPath.c_str());
77  return;
78  }
79 
80  vector<string> files;
81  getdir(currPath,files);
82 
83  for (int i=0; i<files.size(); i++)
84  {
85  if(!files[i].compare(".") || !files[i].compare("..") || !files[i].compare("svmmodel"))
86  continue;
87  string objPath=currPath+"/"+files[i];
88 
89  string svmPath=objPath+"/svmmodel";
90  if(!yarp::os::stat(svmPath.c_str()))
91  {
92  SVMLinear model(files[i]);
93  //model.loadModel(svmPath.c_str());
94  //linearClassifiers.push_back(model);
95  }
96 
97  vector<string> featuresFile;
98  vector<string> tmpFiles;
99  getdir(objPath,featuresFile);
100 
101  for (int j=0; j< featuresFile.size(); j++)
102  {
103  if(!featuresFile[j].compare(".") || !featuresFile[j].compare(".."))
104  continue;
105 
106  string tmp=objPath+"/"+featuresFile[j];
107  tmpFiles.push_back(tmp);
108  }
109 
110  pair<string, vector<string> > obj(files[i],tmpFiles);
111  knownObjects.push_back(obj);
112  }
113 }
114 
115 bool linearClassifierThread::threadInit()
116 {
117  if (!featuresPort.open(inputFeatures.c_str())) {
118  cout << ": unable to open port " << inputFeatures << endl;
119  return false;
120  }
121 
122  if (!outputPort.open(outputPortName.c_str())) {
123  cout << ": unable to open port " << outputPortName << endl;
124  return false;
125  }
126 
127  if (!scorePort.open(outputScorePortName.c_str())) {
128  cout << ": unable to open port " << outputScorePortName << endl;
129  return false;
130  }
131 
132  trainClassifiersHelper();
133  return true;
134 }
135 
136 void linearClassifierThread::run()
137 {
138  int current=0;
139  while (!isStopping())
140  {
141  Bottle *p=featuresPort.read();
142  if (p==NULL) {
143  continue;
144  }
145 
146  lock_guard<mutex> lg(mtx);
147 
148  vector<double> feature;
149  feature.resize(p->size());
150 
151  for (int i=0; i<p->size(); i++)
152  feature[i]=p->get(i).asFloat64();
153 
154  if(currentState==STATE_DONOTHING)
155  continue;
156 
157  if(currentState==STATE_SAVING)
158  {
159  for (int i=0; i<feature.size(); i++)
160  objFeatures << feature[i] << " ";
161  objFeatures << endl;
162  }
163 
164  if(currentState==STATE_RECOGNIZING)
165  {
166  if(linearClassifiers.size()==0)
167  continue;
168 
169  //cout << "ISTANT SCORES: ";
170  double maxVal=-1000;
171  double minValue=1000;
172  double idWin=-1;
173  for(int i =0; i<linearClassifiers.size(); i++)
174  {
175  double value=linearClassifiers[i].predictModel(feature);
176  if(value>maxVal)
177  {
178  maxVal=value;
179  idWin=i;
180  }
181  if(value<minValue)
182  minValue=value;
183  bufferScores[current%bufferSize][i]=(value);
184  countBuffer[current%bufferSize][i]=0;
185  //cout << knownObjects[i].first << " " << value << " ";
186  }
187  countBuffer[current%bufferSize][idWin]=1;
188 
189  vector<double> avgScores(linearClassifiers.size(),0.0);
190  vector<double> bufferVotes(linearClassifiers.size(),0.0);
191 
192  for(int i =0; i<bufferSize; i++)
193  {
194  for(int k =0; k<linearClassifiers.size(); k++)
195  {
196  avgScores[k]=avgScores[k]+bufferScores[i][k];
197  bufferVotes[k]=bufferVotes[k]+countBuffer[i][k];
198  }
199  }
200 
201  double maxValue=-100;
202  double maxVote=0;
203  int indexClass=-1;
204  int indexMaxVote=-1;
205 
206  for(int i =0; i<linearClassifiers.size(); i++)
207  {
208  avgScores[i]=avgScores[i]/bufferSize;
209  if(avgScores[i]>maxValue)
210  {
211  maxValue=avgScores[i];
212  indexClass=i;
213  }
214 
215  if(bufferVotes[i]>maxVote)
216  {
217  maxVote=bufferVotes[i];
218  indexMaxVote=i;
219  }
220  }
221 
222  string winnerClass=knownObjects[indexClass].first;
223  string winnerVote=knownObjects[indexMaxVote].first;
224 
225  if(bufferVotes[indexMaxVote]/bufferSize<0.75)
226  winnerClass="?";
227 
228  if(outputPort.getOutputCount()>0)
229  {
230  Bottle &b=outputPort.prepare();
231  b.clear();
232  b.addString(winnerClass.c_str());
233  outputPort.write();
234  }
235 
236  if(scorePort.getOutputCount()>0)
237  {
238  Bottle allScores;
239  for(int i =0; i<linearClassifiers.size(); i++)
240  {
241  Bottle &b=allScores.addList();
242  b.addString(knownObjects[i].first.c_str());
243  b.addFloat64(bufferScores[current%bufferSize][i]);
244 
245  }
246  scorePort.write(allScores);
247  }
248 
249  current++;
250  }
251  }
252 }
253 
254 void linearClassifierThread::threadRelease()
255 {
256  if(linearClassifiers.size()>0)
257  for (int i=0; i<linearClassifiers.size(); i++)
258  linearClassifiers[i].freeModel();
259 
260  this->commandPort->close();
261  this->featuresPort.close();
262  this->outputPort.close();
263  this->scorePort.close();
264 }
265 
266 void linearClassifierThread::onStop()
267 {
268  this->commandPort->interrupt();
269  this->featuresPort.interrupt();
270  this->outputPort.interrupt();
271  this->scorePort.interrupt();
272 }
273 
274 void linearClassifierThread::prepareObjPath(const string &objName)
275 {
276  lock_guard<mutex> lg(mtx);
277  stopAllHelper();
278 
279  pathObj=currPath+"/"+objName;
280  if(yarp::os::stat(pathObj.c_str()))
281  {
282  createFullPath(pathObj.c_str());
283  pathObj=pathObj+"/1.txt";
284  }
285  else
286  {
287  char tmpPath[255];
288  bool proceed=true;
289 
290  for (int i=1; proceed; i++)
291  {
292  sprintf(tmpPath,"%s/%d.txt",pathObj.c_str(),i);
293  proceed=!yarp::os::stat(tmpPath);
294  sprintf(tmpPath,"%s/%d.txt",pathObj.c_str(),i);
295  }
296 
297  pathObj=tmpPath;
298  }
299 
300  objFeatures.open(pathObj.c_str(),fstream::out | fstream::out);
301  currentState=STATE_SAVING;
302 }
303 
304 void linearClassifierThread::createFullPath(const char * path)
305 {
306  if (yarp::os::stat(path))
307  {
308  string strPath=string(path);
309  size_t found=strPath.find_last_of("/");
310 
311  while (strPath[found]=='/')
312  found--;
313 
314  createFullPath(strPath.substr(0,found+1).c_str());
315  yarp::os::mkdir(strPath.c_str());
316  }
317 }
318 
319 void linearClassifierThread::stopAllHelper()
320 {
321  currentState=STATE_DONOTHING;
322  if(objFeatures.is_open())
323  objFeatures.close();
324 }
325 
326 void linearClassifierThread::stopAll()
327 {
328  lock_guard<mutex> lg(mtx);
329  stopAllHelper();
330 }
331 
332 int linearClassifierThread::getdir(const string &dir, vector<string> &files)
333 {
334  DIR *dp;
335  struct dirent *dirp;
336  if((dp = opendir(dir.c_str())) == NULL) {
337  cout << "Error opening " << dir << endl;
338  return -1;
339  }
340 
341  while ((dirp = readdir(dp)) != NULL) {
342  files.push_back(string(dirp->d_name));
343  }
344  closedir(dp);
345  return 0;
346 }
347 
348 bool linearClassifierThread::loadFeatures()
349 {
350  checkKnownObjects();
351  if(this->knownObjects.size()==0)
352  return false;
353 
354  Features.clear();
355  Features.resize(knownObjects.size());
356  datasetSizes.clear();
357  SVMLinear svmmodel(knownObjects[0].first);
358 
359  for (int i=0; i<knownObjects.size(); i++)
360  {
361  vector<string> obj=knownObjects[i].second;
362  int cnt=0;
363  for (int k=0; k< obj.size(); k++)
364  {
365  vector<vector<double> > tmpF=svmmodel.readFeatures(obj[k]);
366  cnt=cnt+tmpF.size();
367  for (int t =0; t<tmpF.size(); t++)
368  Features[i].push_back(tmpF[t]);
369 
370  }
371 
372  if (cnt>0)
373  this->datasetSizes.push_back(cnt);
374  }
375 
376  return true;
377 }
378 
379 bool linearClassifierThread::trainClassifiersHelper()
380 {
381  stopAllHelper();
382 
383  if(linearClassifiers.size()>0)
384  for (int i=0; i<linearClassifiers.size(); i++)
385  linearClassifiers[i].freeModel();
386 
387  cout << "load features" << endl;
388  loadFeatures();
389  if(this->datasetSizes.size()==0)
390  return false;
391 
392  cout << "features loaded" << endl;
393 
394  linearClassifiers.clear();
395 
396  int nClass=2; // One vs All
397 
398  for (int i=0; i<knownObjects.size(); i++)
399  {
400  int nPositive=0;
401  int nNegative=0;
402  string name=knownObjects[i].first;
403  SVMLinear svmmodel(name);
404  vector<vector<double> > orderedF;
405  vector<double> orderedLabels;
406  for (int k=0; k<knownObjects.size(); k++)
407  {
408  for(int j=0; j<Features[k].size(); j++)
409  if(knownObjects[i].first==knownObjects[k].first)
410  {
411  orderedF.push_back(Features[k][j]);
412  orderedLabels.push_back(1.0);
413  nPositive++;
414  }
415  }
416 
417  for (int k=0; k<knownObjects.size(); k++)
418  {
419  for(int j=0; j<Features[k].size(); j++)
420  if(knownObjects[i].first!=knownObjects[k].first)
421  {
422  orderedF.push_back(Features[k][j]);
423  orderedLabels.push_back(-1.0);
424  nNegative++;
425  }
426  }
427 
428  printf("nClass: %d nPositive: %d nNegative: %d\n",nClass,nPositive,nNegative);
429  parameter param;
430  if(useWeightedSVM)
431  param=svmmodel.initialiseParam(L2R_L2LOSS_SVC,CSVM,0.0,nClass,nPositive,nNegative);
432  else
433  param=svmmodel.initialiseParam(L2R_L2LOSS_SVC_DUAL,CSVM);
434 
435  svmmodel.trainModel(orderedF,orderedLabels,param);
436 
437  linearClassifiers.push_back(svmmodel);
438  /*for (int k=0; k<Features[0][0].size(); k++)
439  cout << svmmodel.modelLinearSVM->w[k] << " ";
440  cout << endl;*/
441 
442  string tmpModelPath=currPath+"/"+knownObjects[i].first+"/svmmodel";
443  //svmmodel.saveModel(tmpModelPath);
444  }
445 
446  cout << "trained" << endl;
447 
448  return true;
449 }
450 
451 bool linearClassifierThread::trainClassifiers()
452 {
453  lock_guard<mutex> lg(mtx);
454  return trainClassifiersHelper();
455 }
456 
457 bool linearClassifierThread::startRecognition()
458 {
459  lock_guard<mutex> lg(mtx);
460  stopAllHelper();
461 
462  if(this->linearClassifiers.size()==0)
463  return false;
464 
465  currentState=STATE_RECOGNIZING;
466 
467  this->countBuffer.resize(bufferSize);
468  this->bufferScores.resize(bufferSize);
469  for (int i=0; i<bufferSize; i++)
470  {
471  bufferScores[i].resize(linearClassifiers.size());
472  countBuffer[i].resize(linearClassifiers.size());
473  }
474 
475  return true;
476 }
477 
478 bool linearClassifierThread::forgetClassHelper(const string &className, const bool retrain)
479 {
480  string classPath=currPath+"/"+className;
481  if(yarp::os::stat(classPath.c_str()))
482  return true;
483 
484  vector<string> files;
485  getdir(classPath,files);
486 
487  for (int i=0; i< files.size(); i++)
488  {
489  if(!files[i].compare(".") || !files[i].compare(".."))
490  continue;
491 
492  string feature=classPath+"/"+files[i];
493  remove(feature.c_str());
494  }
495 
496  bool res=(yarp::os::rmdir(classPath.c_str())==0);
497  if (res && retrain)
498  trainClassifiersHelper();
499 
500  return res;
501 }
502 
503 bool linearClassifierThread::forgetClass(const string &className, const bool retrain)
504 {
505  lock_guard<mutex> lg(mtx);
506  return forgetClassHelper(className,retrain);
507 }
508 
509 bool linearClassifierThread::forgetAll()
510 {
511  lock_guard<mutex> lg(mtx);
512  checkKnownObjects();
513 
514  for (int i=0; i<knownObjects.size(); i++)
515  forgetClassHelper(knownObjects[i].first,false);
516 
517  trainClassifiersHelper();
518  return true;
519 }
520 
521