human-sensing
faceLandmarks.cpp
1 /*
2  * Copyright (C) 2011 Department of Robotics Brain and Cognitive Sciences - Istituto Italiano di Tecnologia
3  * Author: Vadim Tikhanoff
4  * email: vadim.tikhanoff@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 <yarp/sig/all.h>
19 #include "faceLandmarks.h"
20 
21 /**********************************************************/
22 bool FACEModule::configure(yarp::os::ResourceFinder &rf)
23 {
24  rf.setVerbose();
25  moduleName = rf.check("name", yarp::os::Value("faceLandmarks"), "module name (string)").asString();
26  predictorFile = rf.check("faceLandmarksFile", yarp::os::Value("shape_predictor_68_face_landmarks.dat"), "path name (string)").asString();
27 
28  std::string firstStr = rf.findFile(predictorFile.c_str());
29 
30  setName(moduleName.c_str());
31 
32  handlerPortName = "/";
33  handlerPortName += getName();
34  handlerPortName += "/rpc:i";
35 
36  cntxHomePath = rf.getHomeContextPath().c_str();
37 
38  if (!rpcPort.open(handlerPortName.c_str()))
39  {
40  yError() << getName().c_str() << " Unable to open port " << handlerPortName.c_str();
41  return false;
42  }
43 
44  attach(rpcPort);
45  closing = false;
46 
47  /* create the thread and pass pointers to the module parameters */
48  faceManager = new FACEManager( moduleName, firstStr, cntxHomePath );
49 
50  /* now start the thread to do the work */
51  faceManager->open();
52 
53  return true ;
54 }
55 
56 /**********************************************************/
57 bool FACEModule::interruptModule()
58 {
59  rpcPort.interrupt();
60  return true;
61 }
62 
63 /**********************************************************/
64 bool FACEModule::close()
65 {
66  //rpcPort.close();
67  yDebug() << "starting the shutdown procedure";
68  faceManager->interrupt();
69  faceManager->close();
70  yDebug() << "deleting thread";
71  delete faceManager;
72  closing = true;
73  yDebug() << "done deleting thread";
74  return true;
75 }
76 
77 /**********************************************************/
78 bool FACEModule::updateModule()
79 {
80  return !closing;
81 }
82 
83 /**********************************************************/
84 double FACEModule::getPeriod()
85 {
86  return 0.01;
87 }
88 
89 /************************************************************************/
90 bool FACEModule::attach(yarp::os::RpcServer &source)
91 {
92  return this->yarp().attachAsServer(source);
93 }
94 
95 /**********************************************************/
96 bool FACEModule::display(const std::string& element, const std::string& value)
97 {
98  bool returnVal = false;
99 
100  if (element == "landmarks" || element == "points" || element == "labels" || element == "dark-mode")
101  {
102  if (element == "landmarks")
103  {
104  if (value=="on")
105  {
106  faceManager->displayLandmarks=true;
107  returnVal = true;
108  }
109  else if (value=="off")
110  {
111  faceManager->displayLandmarks = false;
112  returnVal = true;
113  }
114  else
115  yInfo() << "error setting value for landmarks";
116  }
117  if (element == "points")
118  {
119  if (value=="on")
120  {
121  faceManager->displayPoints=true;
122  returnVal = true;
123  }
124  else if (value=="off")
125  {
126  faceManager->displayPoints = false;
127  returnVal = true;
128  }
129  else
130  yInfo() << "error setting value for points";
131  }
132  if (element == "labels")
133  {
134  if (value=="on")
135  {
136  faceManager->displayLabels=true;
137  returnVal = true;
138  }
139  else if (value=="off")
140  {
141  faceManager->displayLabels = false;
142  returnVal = true;
143  }
144  else
145  yInfo() << "error setting value for labels";
146  }
147  if (element == "dark-mode")
148  {
149  if (value=="on")
150  {
151  faceManager->displayDarkMode=true;
152  returnVal = true;
153  }
154  else if (value=="off")
155  {
156  faceManager->displayDarkMode = false;
157  returnVal = true;
158  }
159  else
160  yInfo() << "error setting value for darkMode";
161  }
162  //yInfo() << "should now display \"landmarks\" " << faceManager->displayLandmarks << "\"points\"" << faceManager->displayPoints << "\"labels\"" << faceManager->displayLabels << "\"dark-mode\"" << faceManager->displayDarkMode;
163  }
164  else
165  {
166  returnVal = false;
167  yInfo() << "Error in display request";
168  }
169  return returnVal;
170 }
171 
172 /**********************************************************/
173 bool FACEModule::quit()
174 {
175  closing = true;
176  return true;
177 }
178 
179 /**********************************************************/
180 FACEManager::~FACEManager()
181 {
182 }
183 
184 /**********************************************************/
185 FACEManager::FACEManager( const std::string &moduleName, const std::string &predictorFile, const std::string &cntxHomePath)
186 {
187  yDebug() << "initialising Variables";
188  this->moduleName = moduleName;
189  this->predictorFile = predictorFile;
190  this->cntxHomePath = cntxHomePath;
191  yInfo() << "contextPATH = " << cntxHomePath.c_str();
192 }
193 
194 /**********************************************************/
195 bool FACEManager::open()
196 {
197  this->useCallback();
198 
199  //create all ports
200  inImgPortName = "/" + moduleName + "/image:i";
201  BufferedPort<yarp::sig::ImageOf<yarp::sig::PixelRgb> >::open( inImgPortName.c_str() );
202 
203  outImgPortName = "/" + moduleName + "/image:o";
204  imageOutPort.open( outImgPortName.c_str() );
205 
206  outTargetPortName = "/" + moduleName + "/target:o";
207  targetOutPort.open( outTargetPortName.c_str() );
208 
209  outLandmarksPortName = "/" + moduleName + "/landmarks:o";
210  landmarksOutPort.open( outLandmarksPortName.c_str() );
211 
212  yDebug() << "path is: " << predictorFile.c_str();
213 
214  faceDetector = dlib::get_frontal_face_detector();
215  dlib::deserialize(predictorFile.c_str()) >> sp;
216 
217  color = cv::Scalar( 0, 255, 0 );
218 
219  displayLandmarks = true;
220  displayPoints = false;
221  displayLabels = false;
222  displayDarkMode = false;
223 
224  return true;
225 }
226 
227 /**********************************************************/
228 void FACEManager::close()
229 {
230  mutex.wait();
231  yDebug() <<"now closing ports...";
232  imageOutPort.writeStrict();
233  imageOutPort.close();
234  imageInPort.close();
235  targetOutPort.close();
236  landmarksOutPort.close();
237  BufferedPort<yarp::sig::ImageOf<yarp::sig::PixelRgb> >::close();
238  mutex.post();
239  yDebug() <<"finished closing the read port...";
240 }
241 
242 /**********************************************************/
243 void FACEManager::interrupt()
244 {
245  yDebug() << "cleaning up...";
246  yDebug() << "attempting to interrupt ports";
247  BufferedPort<yarp::sig::ImageOf<yarp::sig::PixelRgb> >::interrupt();
248  yDebug() << "finished interrupt ports";
249 }
250 
251 /**********************************************************/
252 void FACEManager::onRead(yarp::sig::ImageOf<yarp::sig::PixelRgb> &img)
253 {
254  mutex.wait();
255  yarp::sig::ImageOf<yarp::sig::PixelRgb> &outImg = imageOutPort.prepare();
256  yarp::os::Bottle &target=targetOutPort.prepare();
257  yarp::os::Bottle &landmarks=landmarksOutPort.prepare();
258  target.clear();
259 
260  // Get the image from the yarp port
261  imgMat = cv::cvarrToMat((IplImage*)img.getIplImage());
262 
263  // Change to dlib's image format. No memory is copied.
264  //dlib::cv_image<dlib::bgr_pixel> dlibimg(imgMat);
265 
266  // Convert the opencv image to dlib format
267  dlib::array2d<dlib::rgb_pixel> dlibimg;
268  assign_image(dlibimg, dlib::cv_image<dlib::bgr_pixel>(imgMat));
269  //dlib::cv_image<dlib::bgr_pixel> dlibimg(imgMat);
270 
271  // Make the image larger so we can detect small faces. 2x
272  pyramid_up(dlibimg);
273 
274  // Now tell the face detector to give us a list of bounding boxes
275  // around all the faces in the image.
276  int count = 0;
277  std::vector<dlib::rectangle> dets = faceDetector(dlibimg);
278 
279  std::vector<dlib::full_object_detection> shapes;
280 
281  for (unsigned long j = 0; j < dets.size(); ++j)
282  {
283  dlib::full_object_detection shape = sp(dlibimg, dets[j]);
284  shapes.push_back(shape);
285  }
286 
287  std::vector<std::pair<int, double >> idTargets;
288 
289  for (unsigned long i = 0; i < dets.size(); ++i)
290  {
291  DLIB_CASSERT(shapes[i].num_parts() == 68,
292  "\t std::vector<image_window::overlay_line> render_face_detections()"
293  << "\n\t Invalid inputs were given to this function. "
294  << "\n\t dets["<<i<<"].num_parts(): " << shapes[i].num_parts()
295  );
296 
297  const dlib::full_object_detection& d = shapes[i];
298 
299  if (displayDarkMode)
300  imgMat.setTo(cv::Scalar(0, 0, 0));
301 
302  if (displayLandmarks)
303  drawLandmarks(imgMat, d);
304 
305  //if (landmarksOutPort.getOutputCount()>0)
306  //{
307  landmarks.clear();
308  yarp::os::Bottle &landM = landmarks.addList();
309  for (int f=1; f<shapes[i].num_parts(); f++)
310  {
311 
312  if (f != 17 || f != 22 || f != 27 || f != 42 || f != 48)
313  {
314  yarp::os::Bottle &temp = landM.addList();
315  temp.addInt(d.part(f).x()/2);
316  temp.addInt(d.part(f).y()/2);
317  }
318  }
319  //}
320 
321  if (displayPoints || displayLabels)
322  {
323  int pointSize = landmarks.get(0).asList()->size();
324 
325  if (displayPoints)
326  {
327  for (size_t i = 0; i < pointSize; i++)
328  {
329  int pointx = landmarks.get(0).asList()->get(i).asList()->get(0).asInt();
330  int pointy = landmarks.get(0).asList()->get(i).asList()->get(1).asInt();
331  cv::Point center(pointx, pointy);
332  circle(imgMat, center, 3, cv::Scalar(255, 0 , 0), -1, 8);
333  }
334  }
335  if (displayLabels)
336  {
337  for (size_t i = 0; i < pointSize; i++)
338  {
339  int pointx = landmarks.get(0).asList()->get(i).asList()->get(0).asInt();
340  int pointy = landmarks.get(0).asList()->get(i).asList()->get(1).asInt();
341  cv::Point center(pointx, pointy);
342  std::string s = std::to_string(i);
343  putText(imgMat, s, cvPoint(pointx, pointy), cv::FONT_HERSHEY_COMPLEX_SMALL, 0.6, cvScalar(200,200,250), 1, CV_AA);
344  }
345  }
346  }
347 
348  cv::Point pt1, pt2;
349  pt1.x = dets[i].tl_corner().x()/2;
350  pt1.y = dets[i].tl_corner().y()/2;
351  pt2.x = dets[i].br_corner().x()/2;
352  pt2.y = dets[i].br_corner().y()/2;
353 
354  rightEye.x = d.part(42).x()/2 + ((d.part(45).x()/2) - (d.part(42).x()/2))/2;
355  rightEye.y = d.part(43).y()/2 + ((d.part(46).y()/2) - (d.part(43).y()/2))/2;
356  leftEye.x = d.part(36).x()/2 + ((d.part(39).x()/2) - (d.part(36).x()/2))/2;
357  leftEye.y = d.part(38).y()/2 + ((d.part(41).y()/2) - (d.part(38).y()/2))/2;
358 
359  //yDebug("rightEye %d %d leftEye %d %d ", rightEye.x, rightEye.y, leftEye.x, leftEye.y);
360 
361  //draw center of each eye
362  circle(imgMat, leftEye , 2, cv::Scalar( 0, 0, 255 ), -1);
363  circle(imgMat, rightEye , 2, cv::Scalar( 0, 0, 255 ), -1);
364 
365  double areaCalculation =0.0;
366  areaCalculation = (std::fabs(pt2.x-pt1.x)*std::fabs(pt2.y-pt1.y));
367 
368  idTargets.push_back(std::make_pair(i, areaCalculation));
369  }
370 
371  if (idTargets.size()>0)
372  {
373  std::sort(idTargets.begin(), idTargets.end(), [](const std::pair<int, double> &left, const std::pair<int, double> &right) {
374  return left.second > right.second;
375  });
376  }
377 
378  if (dets.size() > 0 )
379  {
380  for (int i=0; i< idTargets.size(); i++)
381  {
382  cv::Point pt1, pt2;
383  pt1.x = dets[idTargets[i].first].tl_corner().x()/2;
384  pt1.y = dets[idTargets[i].first].tl_corner().y()/2;
385  pt2.x = dets[idTargets[i].first].br_corner().x()/2;
386  pt2.y = dets[idTargets[i].first].br_corner().y()/2;
387 
388  if (pt1.x < 2)
389  pt1.x = 1;
390  if (pt1.x > 318)
391  pt1.x = 319;
392  if (pt1.y < 2)
393  pt1.y = 1;
394  if (pt1.y > 238)
395  pt1.y = 239;
396 
397  if (pt2.x < 2)
398  pt2.x = 1;
399  if (pt2.x > 318)
400  pt2.x = 319;
401  if (pt2.y < 2)
402  pt2.y = 1;
403  if (pt2.y > 238)
404  pt2.y = 239;
405 
406 
407  yarp::os::Bottle &pos = target.addList();
408  pos.addDouble(pt1.x);
409  pos.addDouble(pt1.y);
410  pos.addDouble(pt2.x);
411  pos.addDouble(pt2.y);
412 
413  cv::Point biggestpt1, biggestpt2;
414  biggestpt1.x = dets[idTargets[0].first].tl_corner().x()/2;
415  biggestpt1.y = dets[idTargets[0].first].tl_corner().y()/2;
416  biggestpt2.x = dets[idTargets[0].first].br_corner().x()/2;
417  biggestpt2.y = dets[idTargets[0].first].br_corner().y()/2;
418 
419  rectangle(imgMat, biggestpt1, biggestpt2, cv::Scalar( 0, 255, 0 ), 1, 8, 0);
420 
421  targetOutPort.write();
422  if (landmarksOutPort.getOutputCount()>0)
423  landmarksOutPort.write();
424  }
425  }
426 
427  IplImage yarpImg = imgMat;
428  outImg.resize(yarpImg.width, yarpImg.height);
429  cvCopy( &yarpImg, (IplImage *) outImg.getIplImage());
430 
431  imageOutPort.write();
432 
433  mutex.post();
434 }
435 
436 void FACEManager::drawLandmarks(cv::Mat &mat, const dlib::full_object_detection &d)
437 {
438  //draw face contour, jaw
439  for (unsigned long i = 1; i <= 16; ++i)
440  line(mat, cv::Point(d.part(i).x()/2, d.part(i).y()/2), cv::Point(d.part(i-1).x()/2, d.part(i-1).y()/2), color);
441 
442  //draw right eyebrow
443  for (unsigned long i = 18; i <= 21; ++i)
444  line(mat, cv::Point(d.part(i).x()/2, d.part(i).y()/2), cv::Point(d.part(i-1).x()/2, d.part(i-1).y()/2), color);
445 
446  //draw left eyebrow
447  for (unsigned long i = 23; i <= 26; ++i)
448  line(mat, cv::Point(d.part(i).x()/2, d.part(i).y()/2), cv::Point(d.part(i-1).x()/2, d.part(i-1).y()/2), color);
449 
450  //draw nose
451  for (unsigned long i = 28; i <= 30; ++i)
452  line(mat, cv::Point(d.part(i).x()/2, d.part(i).y()/2), cv::Point(d.part(i-1).x()/2, d.part(i-1).y()/2), color);
453 
454  //draw nostrils
455  line(mat, cv::Point(d.part(30).x()/2, d.part(30).y()/2), cv::Point(d.part(35).x()/2, d.part(35).y()/2), color);
456  for (unsigned long i = 31; i <= 35; ++i)
457  line(mat, cv::Point(d.part(i).x()/2, d.part(i).y()/2), cv::Point(d.part(i-1).x()/2, d.part(i-1).y()/2), color);
458 
459  //draw right eye
460  for (unsigned long i = 37; i <= 41; ++i)
461  line(mat, cv::Point(d.part(i).x()/2, d.part(i).y()/2), cv::Point(d.part(i-1).x()/2, d.part(i-1).y()/2), color);
462  line(mat, cv::Point(d.part(36).x()/2, d.part(36).y()/2), cv::Point(d.part(41).x()/2, d.part(41).y()/2), color);
463 
464  //draw left eye
465  for (unsigned long i = 43; i <= 47; ++i)
466  line(mat, cv::Point(d.part(i).x()/2, d.part(i).y()/2), cv::Point(d.part(i-1).x()/2, d.part(i-1).y()/2), color);
467  line(mat, cv::Point(d.part(42).x()/2, d.part(42).y()/2), cv::Point(d.part(47).x()/2, d.part(47).y()/2), color);
468 
469  //draw outer mouth
470  for (unsigned long i = 49; i <= 59; ++i)
471  line(mat, cv::Point(d.part(i).x()/2, d.part(i).y()/2), cv::Point(d.part(i-1).x()/2, d.part(i-1).y()/2), color);
472  line(mat, cv::Point(d.part(48).x()/2, d.part(48).y()/2), cv::Point(d.part(59).x()/2, d.part(59).y()/2), color);
473 
474  //draw inner mouth
475  line(mat, cv::Point(d.part(60).x()/2, d.part(60).y()/2), cv::Point(d.part(67).x()/2, d.part(67).y()/2), color);
476  for (unsigned long i = 61; i <= 67; ++i)
477  line(mat, cv::Point(d.part(i).x()/2, d.part(i).y()/2), cv::Point(d.part(i-1).x()/2, d.part(i-1).y()/2), color);
478 
479 }
480 //empty line to make gcc happy