iol
classifier.cpp
1 /*
2  * Copyright (C) 2013 iCub Facility - Istituto Italiano di Tecnologia
3  * Author: Sean Ryan Fanello
4  * email: sean.fanello@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 <opencv2/opencv.hpp>
19 #include <yarp/cv/Cv.h>
20 #include "classifier.h"
21 
22 using namespace yarp::cv;
23 
24 #define CMD_TRAIN yarp::os::createVocab32('t','r','a','i')
25 #define CMD_CLASSIFY yarp::os::createVocab32('c','l','a','s')
26 #define CMD_FORGET yarp::os::createVocab32('f','o','r','g')
27 #define CMD_BURST yarp::os::createVocab32('b','u','r','s')
28 #define CMD_LIST yarp::os::createVocab32('l','i','s','t')
29 #define CMD_CHANGE_NAME yarp::os::createVocab32('c','h','n','a')
30 
31 
32 bool Classifier::configure(yarp::os::ResourceFinder &rf)
33 {
34  string moduleName = rf.check("name",Value("himrepClassifier"),"module name (string)").asString();
35  setName(moduleName.c_str());
36 
37  rpcClassifier.open("/"+moduleName+"/classify:rpc");
38  imgInput.open("/"+moduleName+"/img:i");
39  imgSIFTInput.open("/"+moduleName+"/SIFTimg:i");
40  imgOutput.open("/"+moduleName+"/img:o");
41  scoresInput.open("/"+moduleName+"/scores:i");
42  rpcPort.open("/"+moduleName+"/rpc");
43 
44  featureInput.open("/"+moduleName+"/features:i");
45  featureOutput.open("/"+moduleName+"/features:o");
46 
47  imgSIFTOutput.open("/"+moduleName+"/SIFTimg:o");
48  opcPort.open("/"+moduleName+"/opc");
49 
50  attach(rpcPort);
51 
52  sync=true;
53  doTrain=true;
54  burst=false;
55 
56  return true;
57 }
58 
59 
60 bool Classifier::interruptModule()
61 {
62  imgInput.interrupt();
63  imgOutput.interrupt();
64  rpcClassifier.interrupt();
65  scoresInput.interrupt();
66  rpcPort.interrupt();
67  imgSIFTInput.interrupt();
68  imgSIFTOutput.interrupt();
69  opcPort.interrupt();
70  return true;
71 }
72 
73 
74 bool Classifier::close()
75 {
76  imgInput.close();
77  imgOutput.close();
78  rpcClassifier.close();
79  scoresInput.close();
80  rpcPort.close();
81  imgSIFTInput.close();
82  imgSIFTOutput.close();
83  opcPort.close();
84  return true;
85 }
86 
87 
88 bool Classifier::getOPCList(Bottle &names)
89 {
90  names.clear();
91  if (opcPort.getOutputCount()>0)
92  {
93  Bottle opcCmd,opcReply,opcReplyProp;
94  opcCmd.addVocab32("ask");
95  Bottle &content=opcCmd.addList().addList();
96  content.addString("entity");
97  content.addString("==");
98  content.addString("object");
99  opcPort.write(opcCmd,opcReply);
100 
101  if (opcReply.size()>1)
102  {
103  if (opcReply.get(0).asVocab32()==Vocab32::encode("ack"))
104  {
105  if (Bottle *idField=opcReply.get(1).asList())
106  {
107  if (Bottle *idValues=idField->get(1).asList())
108  {
109  // cycle over items
110  for (int i=0; i<idValues->size(); i++)
111  {
112  int id=idValues->get(i).asInt32();
113 
114  // get the relevant properties
115  // [get] (("id" <num>) ("propSet" ("name")))
116  opcCmd.clear();
117  opcCmd.addVocab32("get");
118  Bottle &content=opcCmd.addList();
119  Bottle &list_bid=content.addList();
120  list_bid.addString("id");
121  list_bid.addInt32(id);
122  Bottle &list_propSet=content.addList();
123  list_propSet.addString("propSet");
124  list_propSet.addList().addString("name");
125  opcPort.write(opcCmd,opcReplyProp);
126 
127  // append the name (if any)
128  if (opcReplyProp.get(0).asVocab32()==Vocab32::encode("ack"))
129  if (Bottle *propField=opcReplyProp.get(1).asList())
130  if (propField->check("name"))
131  names.addString(propField->find("name").asString());
132  }
133  }
134  }
135  }
136  }
137 
138  return true;
139  }
140  else
141  return false;
142 }
143 
144 
145 bool Classifier::updateObjDatabase()
146 {
147  lock_guard<mutex> lg(mtx);
148  if ((opcPort.getOutputCount()==0) || (rpcClassifier.getOutputCount()==0))
149  return false;
150 
151  Bottle opcObjList;
152  // Retrieve OPC Object List
153  bool success=getOPCList(opcObjList);
154  if (!success)
155  {
156  printf("Error in communicating with OPC \n");
157  return false;
158  }
159 
160  // Retrieve LinearClassifier Object List
161  Bottle cmdObjClass,objClassList;
162  cmdObjClass.addString("objList");
163  rpcClassifier.write(cmdObjClass,objClassList);
164  if (objClassList.get(0).asString()=="ack")
165  {
166  if (Bottle *objList=objClassList.get(1).asList())
167  {
168  for (int k=0; k<objList->size(); k++)
169  {
170  string currObj=objList->get(k).asString();
171  if (currObj=="background")
172  continue;
173 
174  bool found=false;
175  // check if the object is stored in the opc memory
176  for (int i=0; i<opcObjList.size(); i++)
177  {
178  string opcObj=opcObjList.get(i).asString();
179  if (currObj.compare(opcObj)==0)
180  {
181  found=true;
182  break;
183  }
184  }
185 
186  // if the object is not stored in memory delete it from the LinearClassifier DB
187  if (!found)
188  {
189  printf("****** Deleting %s ..... \n",currObj.c_str());
190  cmdObjClass.clear();
191  cmdObjClass.addString("forget");
192  cmdObjClass.addString(currObj);
193  Bottle repClass;
194  rpcClassifier.write(cmdObjClass,repClass);
195  printf("****** Deleted %s ..... \n",currObj.c_str());
196  }
197 
198  }
199  }
200  }
201 
202  Bottle cmdTr,trReply;
203  cmdTr.addString("train");
204  rpcClassifier.write(cmdTr,trReply);
205  printf("****** Retrained ..... \n");
206 
207  return true;
208 }
209 
210 
211 bool Classifier::train(Bottle *locations, Bottle &reply)
212 {
213  if (locations==NULL)
214  return false;
215 
216  string object_name=locations->get(0).asList()->get(0).asString();
217  if (burst)
218  currObject=object_name;
219 
220  // save features
221  if (doTrain)
222  {
223  Bottle cmdClass;
224  cmdClass.addString("save");
225  cmdClass.addString(object_name);
226 
227  Bottle classReply;
228  printf("Sending training request: %s\n",cmdClass.toString().c_str());
229  rpcClassifier.write(cmdClass,classReply);
230  printf("Received reply: %s\n",classReply.toString().c_str());
231  }
232 
233  // read image and locations
234  ImageOf<PixelRgb> *image=imgInput.read();
235  if (image==NULL)
236  return false;
237 
238  Bottle* bb=locations->get(0).asList()->get(1).asList();
239  int x_min=bb->get(0).asInt32();
240  int y_min=bb->get(1).asInt32();
241  int x_max=bb->get(2).asInt32();
242  int y_max=bb->get(3).asInt32();
243 
244  if (x_min>5)
245  x_min-=5;
246 
247  if (y_min>5)
248  y_min-=5;
249 
250  if ((x_max+5)<image->width())
251  x_max+=5;
252 
253  if ((y_max+5)<image->height())
254  y_max+=5;
255 
256  int blobW=x_max-x_min;
257  int blobH=y_max-y_min;
258 
259  // crop image
260  ImageOf<PixelRgb> tmp_img = *image;
261  ::cv::Mat mat_tmp_img = toCvMat(tmp_img);
262  ::cv::Mat mat_croppedImg = mat_tmp_img(cv::Rect(x_min,y_min,blobW,blobH)).clone();
263  ImageOf<PixelRgb> croppedImg = fromCvMat<PixelRgb>(mat_croppedImg);
264 
265  // send image to SC
266  imgOutput.write(croppedImg);
267 
268  // read coded features
269  Bottle fea;
270  featureInput.read(fea);
271  if (fea.size()==0)
272  return false;
273 
274  // send feature to classifier
275  if (burst)
276  trainingFeature.push_back(fea);
277  else
278  featureOutput.write(fea);
279 
280  // train classifier
281  if (doTrain)
282  {
283  Bottle cmdTr;
284  cmdTr.addString("train");
285  Bottle trReply;
286  printf("Sending training request: %s\n",cmdTr.toString().c_str());
287  rpcClassifier.write(cmdTr,trReply);
288  printf("Received reply: %s\n",trReply.toString().c_str());
289  }
290 
291  reply.addString("ack");
292  return true;
293 }
294 
295 
296 void Classifier::classify(Bottle *blobs, Bottle &reply)
297 {
298  if (blobs==NULL)
299  {
300  reply.addList();
301  return;
302  }
303 
304  if (blobs->size()==0)
305  {
306  reply.addList();
307  return;
308  }
309 
310  if ((imgInput.getInputCount()==0) || (imgSIFTInput.getInputCount()==0) ||
311  (featureInput.getInputCount()==0) || (scoresInput.getInputCount()==0) ||
312  (rpcClassifier.getOutputCount()==0))
313  {
314  reply.addList();
315  return;
316  }
317 
318  // read object classes
319  Bottle cmdObjClass,objClassList;
320  cmdObjClass.addString("objList");
321  rpcClassifier.write(cmdObjClass,objClassList);
322  if (objClassList.get(0).asString()!="ack")
323  return;
324 
325  Bottle *objList=objClassList.get(1).asList();
326  if (objList==NULL)
327  return;
328 
329  if (objList->size()==0)
330  {
331  for (int b=0; b<blobs->size(); b++)
332  {
333  Bottle &blob_scorelist=reply.addList();
334  blob_scorelist.addString(blobs->get(b).asList()->get(0).asString());
335  blob_scorelist.addList();
336  }
337  return;
338  }
339 
340  // start recognition mode
341  Bottle cmdClass,classReply;
342  cmdClass.addString("recognize");
343  rpcClassifier.write(cmdClass,classReply);
344  if (classReply.get(0).asString()=="nack")
345  {
346  reply.addList();
347  return;
348  }
349 
350  // read image
351  ImageOf<PixelRgb> *image=imgInput.read();
352  if (image==NULL)
353  return;
354 
355  // classify each blob
356  printf("Start classification\n");
357  for (int b=0; b<blobs->size(); b++)
358  {
359  // list of the scores
360  Bottle &blob_scorelist=reply.addList();
361  // name of the blob
362  blob_scorelist.addString(blobs->get(b).asList()->get(0).asString());
363  // list of scores
364  Bottle &scores=blob_scorelist.addList();
365  // retrieve bounding box
366  Bottle *bb=blobs->get(b).asList()->get(1).asList();
367  int x_min=(int)bb->get(0).asFloat64();
368  int y_min=(int)bb->get(1).asFloat64();
369  int x_max=(int)bb->get(2).asFloat64();
370  int y_max=(int)bb->get(3).asFloat64();
371 
372  if (x_min>5)
373  x_min-=5;
374 
375  if (y_min>5)
376  y_min-=5;
377 
378  if ((x_max+5)<image->width())
379  x_max+=5;
380 
381  if ((y_max+5)<image->height())
382  y_max+=5;
383 
384  // crop image
385  ImageOf<PixelRgb> tmp_img = *image;
386  ::cv::Mat mat_tmp_img = toCvMat(tmp_img);
387  ::cv::Mat mat_croppedImg = mat_tmp_img(cv::Rect(x_min,y_min,x_max-x_min,y_max-y_min)).clone();
388  ImageOf<PixelRgb> croppedImg = fromCvMat<PixelRgb>(mat_croppedImg);
389 
390  // send image to SC
391  imgOutput.write(croppedImg);
392 
393  // read coded features
394  Bottle fea;
395  featureInput.read(fea);
396  ImageOf<PixelRgb> *imgSift=imgSIFTInput.read();
397  if ((fea.size()==0) || (imgSift==NULL))
398  return;
399 
400  x_max=std::min(x_min+imgSift->width(),image->width()-1);
401  y_max=std::min(y_min+imgSift->height(),image->height()-1);
402  toCvMat(*imgSift)(cv::Rect(0,0,x_max-x_min,y_max-y_min)).copyTo(mat_tmp_img(cv::Rect(x_min,y_min,x_max-x_min,y_max-y_min)));
403  *image = fromCvMat<PixelRgb>(mat_tmp_img);
404 
405  // send feature to classifier
406  featureOutput.write(fea);
407 
408  // read scores
409  Bottle class_scores;
410  scoresInput.read(class_scores);
411  if (class_scores.size()==0)
412  return;
413 
414  // fill the list of the i-th blob
415  printf("Scores received: ");
416  for (int i=0; i<objList->size(); i++)
417  {
418  Bottle *obj=class_scores.get(i).asList();
419  if (obj->get(0).asString()=="background")
420  continue;
421 
422  Bottle &currObj_score=scores.addList();
423  currObj_score.addString(obj->get(0).asString());
424  double normalizedVal=((obj->get(1).asFloat64())+1.0)/2.0;
425  currObj_score.addFloat64(normalizedVal);
426  printf("(%s %g) ",obj->get(0).asString().c_str(),normalizedVal);
427  }
428 
429  printf("\n");
430  }
431 
432  if (imgSIFTOutput.getOutputCount()>0)
433  {
434  imgSIFTOutput.prepare()=*image;
435  imgSIFTOutput.write();
436  }
437 }
438 
439 
440 bool Classifier::respond(const Bottle& command, Bottle& reply)
441 {
442  lock_guard<mutex> lg(mtx);
443  switch(command.get(0).asVocab32())
444  {
445  case CMD_TRAIN:
446  {
447  if (!burst)
448  doTrain=true;
449  train(command.get(1).asList(),reply);
450  return true;
451  }
452 
453  case CMD_CLASSIFY:
454  {
455  classify(command.get(1).asList(),reply);
456  return true;
457  }
458 
459  case CMD_FORGET:
460  {
461  string className=command.get(1).asString();
462  Bottle cmdObjClass;
463  cmdObjClass.addString("forget");
464  cmdObjClass.addString(className);
465  Bottle repClass;
466  printf("Sending training request: %s\n",cmdObjClass.toString().c_str());
467  rpcClassifier.write(cmdObjClass,repClass);
468  printf("Received reply: %s\n",repClass.toString().c_str());
469  reply.addString("ack");
470  return true;
471  }
472 
473  case CMD_BURST:
474  {
475  string cmd=command.get(1).asString();
476  if (cmd=="star")
477  {
478  trainingFeature.clear();
479  burst=true;
480  doTrain=false;
481  }
482  else
483  {
484  burst=false;
485  doTrain=false;
486  Bottle cmdClass;
487  cmdClass.addString("save");
488  cmdClass.addString(currObject);
489  Bottle classReply;
490  rpcClassifier.write(cmdClass,classReply);
491 
492  for (size_t i=0; i<trainingFeature.size(); i++)
493  featureOutput.write(trainingFeature[i]);
494 
495  trainingFeature.clear();
496 
497  Bottle cmdTr;
498  cmdTr.addString("train");
499  Bottle trReply;
500  rpcClassifier.write(cmdTr,trReply);
501  }
502 
503  reply.addString("ack");
504  return true;
505  }
506 
507  case CMD_LIST:
508  {
509  Bottle cmdList;
510  cmdList.addString("objList");
511  printf("Sending list request: %s\n",cmdList.toString().c_str());
512  rpcClassifier.write(cmdList,reply);
513  printf("Received reply: %s\n",reply.toString().c_str());
514  return true;
515  }
516 
517  case CMD_CHANGE_NAME:
518  {
519  string oldName=command.get(1).asString();
520  string newName=command.get(2).asString();
521 
522  Bottle cmdList;
523  cmdList.addString("changeName");
524  cmdList.addString(oldName);
525  cmdList.addString(newName);
526  printf("Sending change name request: %s\n",cmdList.toString().c_str());
527  rpcClassifier.write(cmdList,reply);
528  printf("Received reply: %s\n",reply.toString().c_str());
529  return true;
530  }
531  }
532 
533  reply.addString("nack");
534  return true;
535 }
536 
537 
538 double Classifier::getPeriod()
539 {
540  return 1.0;
541 }
542 
543 
544 bool Classifier::updateModule()
545 {
546  if (sync)
547  {
548  printf("Trying to start Synchronization with OPC... \n");
549  if (updateObjDatabase())
550  {
551  printf("Synchronization with OPC Completed... \n");
552  sync=false;
553  }
554  }
555 
556  return true;
557 }