himrep
main.cpp
1 /*
2  * Copyright (C) 2016 iCub Facility - Istituto Italiano di Tecnologia
3  * Author: Giulia Pasquale
4  * email: giulia.pasquale@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 // General includes
19 #include <cstdio>
20 #include <cstdlib> // getenv
21 #include <string>
22 #include <vector>
23 #include <iostream>
24 #include <fstream>
25 #include <utility>
26 #include <mutex>
27 
28 // OpenCV
29 #include <opencv2/opencv.hpp>
30 #include <opencv2/imgproc/types_c.h>
31 
32 #include <yarp/os/Network.h>
33 #include <yarp/os/RFModule.h>
34 #include <yarp/os/Time.h>
35 #include <yarp/os/BufferedPort.h>
36 #include <yarp/os/RpcClient.h>
37 #include <yarp/os/PortReport.h>
38 #include <yarp/os/Stamp.h>
39 
40 #include <yarp/sig/Vector.h>
41 #include <yarp/sig/Image.h>
42 
43 #include <yarp/cv/Cv.h>
44 
45 #include <yarp/math/Math.h>
46 
47 #include "GIEFeatExtractor.h"
48 
49 using namespace std;
50 using namespace yarp;
51 using namespace yarp::os;
52 using namespace yarp::sig;
53 using namespace yarp::cv;
54 using namespace yarp::math;
55 
56 #define CMD_HELP yarp::os::createVocab32('h','e','l','p')
57 #define DUMP_CODE yarp::os::createVocab32('d','u','m','p')
58 #define DUMP_STOP yarp::os::createVocab32('s','t','o','p')
59 
60 class GIECoderPort: public BufferedPort<Image>
61 {
62 private:
63 
64  // Resource Finder and module options
65 
66  ResourceFinder &rf;
67 
68  string contextPath;
69 
70  bool dump_code;
71 
72  double rate;
73  double last_read;
74 
75  // Data (common to all methods)
76 
77  cv::Mat matImg;
78 
79  Port port_out_img;
80  Port port_out_code;
81 
82  FILE *fout_code;
83 
84  mutex mtx;
85 
86  // Data (specific for each method - instantiate only those are needed)
87 
88  GIEFeatExtractor *gie_extractor;
89 
90  void onRead(Image &img)
91  {
92  // Read at specified rate
93  if (Time::now() - last_read < rate)
94  return;
95 
96  lock_guard<mutex> lg(mtx);
97 
98  // If something arrived...
99  if (img.width()>0 && img.height()>0)
100  {
101  // Convert the image and check that it is continuous
102 
103  cv::Mat tmp_mat = toCvMat(img);
104  cv::cvtColor(tmp_mat, matImg, CV_RGB2BGR);
105 
106  // Extract the feature vector
107 
108  std::vector<float> codingVecFloat;
109  float times[2];
110  if (!gie_extractor->extract_singleFeat_1D(matImg, codingVecFloat, times))
111  {
112  std::cout << "GIEFeatExtractor::extract_singleFeat_1D(): failed..." << std::endl;
113  return;
114  }
115  std::vector<double> codingVec(codingVecFloat.begin(), codingVecFloat.end());
116 
117  if (gie_extractor->timing)
118  {
119  std::cout << times[0] << ": PREP " << times[1] << ": NET" << std::endl;
120  }
121 
122  if (dump_code)
123  {
124  fwrite (&codingVec[0], sizeof(double), codingVec.size(), fout_code);
125  }
126 
127  Stamp stamp;
128  this->getEnvelope(stamp);
129 
130  if (port_out_code.getOutputCount())
131  {
132  port_out_code.setEnvelope(stamp);
133  yarp::sig::Vector codingYarpVec(codingVec.size(), &codingVec[0]);
134  port_out_code.write(codingYarpVec);
135  }
136 
137  if (port_out_img.getOutputCount())
138  {
139  port_out_img.write(img);
140  }
141  }
142  }
143 
144 public:
145 
146  GIECoderPort(ResourceFinder &_rf) :BufferedPort<Image>(),rf(_rf)
147  {
148  // Resource Finder and module options
149  contextPath = rf.getHomeContextPath().c_str();
150 
151  // Data initialization (specific for Caffe method)
152 
153  // Binary file (.caffemodel) containing the network's weights
154  string caffemodel_file = rf.check("caffemodel_file", Value("bvlc_googlenet.caffemodel")).asString().c_str();
155  cout << "Setting .caffemodel file to " << caffemodel_file << endl;
156 
157  // Text file (.prototxt) defining the network structure
158  string prototxt_file = rf.check("prototxt_file", Value("deploy.prototxt")).asString().c_str();
159  cout << "Setting .prototxt file to " << prototxt_file << endl;
160 
161  // Name of blobs to be extracted
162  string blob_name = rf.check("blob_name", Value("pool5/7x7_s1")).asString().c_str();
163 
164  // Boolean flag for timing or not the feature extraction
165  bool timing = rf.check("timing",Value(false)).asBool();
166 
167  string binaryproto_meanfile = "";
168  float meanR = -1, meanG = -1, meanB = -1;
169  int resizeWidth = -1, resizeHeight = -1;
170  if (rf.find("binaryproto_meanfile").isNull() && rf.find("meanR").isNull())
171  {
172  cout << "ERROR: missing mean info!!!!!" << endl;
173  }
174  else if (rf.find("binaryproto_meanfile").isNull())
175  {
176  meanR = rf.check("meanR", Value(123)).asFloat64();
177  meanG = rf.check("meanG", Value(117)).asFloat64();
178  meanB = rf.check("meanB", Value(104)).asFloat64();
179  resizeWidth = rf.check("resizeWidth", Value(256)).asFloat64();
180  resizeHeight = rf.check("resizeHeight", Value(256)).asFloat64();
181  std::cout << "Setting mean to " << " R: " << meanR << " G: " << meanG << " B: " << meanB << std::endl;
182  std::cout << "Resizing anysotropically to " << " W: " << resizeWidth << " H: " << resizeHeight << std::endl;
183 
184  }
185  else if (rf.find("meanR").isNull())
186  {
187  binaryproto_meanfile = rf.check("binaryproto_meanfile", Value("imagenet_mean.binaryproto")).asString().c_str();
188  cout << "Setting .binaryproto file to " << binaryproto_meanfile << endl;
189  }
190  else
191  {
192  std::cout << "ERROR: need EITHER mean file (.binaryproto) OR mean pixel values!" << std::endl;
193  }
194 
195  gie_extractor = new GIEFeatExtractor(caffemodel_file, binaryproto_meanfile, meanR, meanG, meanB,
196  prototxt_file, resizeWidth, resizeHeight,
197  blob_name,
198  timing);
199  if( !gie_extractor )
200  {
201  cout << "Failed to initialize GIEFeatExtractor" << endl;
202  }
203 
204  // Data (common to all methods)
205 
206  string name = rf.find("name").asString().c_str();
207 
208  port_out_img.open(("/"+name+"/img:o").c_str());
209  port_out_code.open(("/"+name+"/code:o").c_str());
210 
211  BufferedPort<Image>::useCallback();
212 
213  rate = rf.check("rate",Value(0.0)).asFloat64();
214  last_read = 0.0;
215 
216  dump_code = rf.check("dump_code");
217  if(dump_code)
218  {
219  string code_path = rf.check("dump_code",Value("codes.bin")).asString().c_str();
220  code_path = contextPath + "/" + code_path;
221  string code_write_mode = rf.check("append")?"wb+":"wb";
222 
223  fout_code = fopen(code_path.c_str(),code_write_mode.c_str());
224  }
225  }
226 
227  void interrupt()
228  {
229  lock_guard<mutex> lg(mtx);
230 
231  port_out_code.interrupt();
232  port_out_img.interrupt();
233 
234  BufferedPort<Image>::interrupt();
235  }
236 
237  void resume()
238  {
239  lock_guard<mutex> lg(mtx);
240 
241  port_out_code.resume();
242  port_out_img.resume();
243 
244  BufferedPort<Image>::resume();
245  }
246 
247  void close()
248  {
249  lock_guard<mutex> lg(mtx);
250 
251  if (dump_code)
252  {
253  fclose(fout_code);
254  }
255 
256  port_out_code.close();
257  port_out_img.close();
258 
259  delete gie_extractor;
260 
261  BufferedPort<Image>::close();
262  }
263 
264  bool execReq(const Bottle &command, Bottle &reply)
265  {
266  switch(command.get(0).asVocab32())
267  {
268  case(CMD_HELP):
269  {
270  reply.clear();
271  reply.add(Value::makeVocab32("many"));
272  reply.addString("[dump] [path-to-file] [a] to start dumping the codes in the context directory. Use 'a' for appending.");
273  reply.addString("[stop] to stop dumping.");
274  return true;
275  }
276 
277  case(DUMP_CODE):
278  {
279  lock_guard<mutex> lg(mtx);
280 
281  dump_code = true;
282  string code_path;
283  string code_write_mode;
284 
285  if (command.size()==1)
286  {
287  code_path = contextPath + "/codes.bin";
288  code_write_mode="wb";
289  }
290  else if (command.size()==2)
291  {
292  if (strcmp(command.get(1).asString().c_str(),"a")==0)
293  {
294  code_write_mode="wb+";
295  code_path = contextPath + "/codes.bin";
296  } else
297  {
298  code_write_mode="wb";
299  code_path = command.get(1).asString().c_str();
300  }
301  } else if (command.size()==3)
302  {
303  code_write_mode="wb+";
304  code_path = command.get(2).asString().c_str();
305  }
306 
307  fout_code = fopen(code_path.c_str(),code_write_mode.c_str());
308  reply.addString("Start dumping codes...");
309 
310  return true;
311  }
312 
313  case(DUMP_STOP):
314  {
315  lock_guard<mutex> lg(mtx);
316 
317  dump_code = false;
318  fclose(fout_code);
319  reply.addString("Stopped code dump.");
320 
321  return true;
322  }
323 
324  default:
325  return false;
326  }
327  }
328 };
329 
330 
331 class GIECoderModule: public RFModule
332 {
333 protected:
334  GIECoderPort *GIEPort;
335  Port rpcPort;
336 
337 public:
338 
339  GIECoderModule()
340 {
341  GIEPort=NULL;
342 }
343 
344  bool configure(ResourceFinder &rf)
345  {
346  string name = rf.find("name").asString().c_str();
347 
348  GIEPort = new GIECoderPort(rf);
349 
350  GIEPort->open(("/"+name+"/img:i").c_str());
351 
352  rpcPort.open(("/"+name+"/rpc").c_str());
353  attach(rpcPort);
354 
355  return true;
356  }
357 
358  bool interruptModule()
359  {
360  if (GIEPort!=NULL)
361  GIEPort->interrupt();
362 
363  rpcPort.interrupt();
364 
365  return true;
366  }
367 
368  bool close()
369  {
370  if(GIEPort!=NULL)
371  {
372  GIEPort->close();
373  delete GIEPort;
374  }
375 
376  rpcPort.close();
377 
378  return true;
379  }
380 
381  bool respond(const Bottle &command, Bottle &reply)
382  {
383  if (GIEPort->execReq(command,reply))
384  return true;
385  else
386  return RFModule::respond(command,reply);
387  }
388 
389  double getPeriod() { return 1.0; }
390 
391  bool updateModule()
392  {
393  //GIEPort->update();
394  return true;
395  }
396 };
397 
398 int main(int argc, char *argv[])
399 {
400  Network yarp;
401  if (!yarp.checkNetwork())
402  return 1;
403 
404  ResourceFinder rf;
405  rf.setDefaultContext("himrep");
406  rf.setDefaultConfigFile("GIECoder_googlenet.ini");
407  rf.configure(argc,argv);
408  rf.setDefault("name","GIECoder");
409 
410  GIECoderModule mod;
411  return mod.runModule(rf);
412 }