facemark_demo_aam.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. /*
  2. This file was part of GSoC Project: Facemark API for OpenCV
  3. Final report: https://gist.github.com/kurnianggoro/74de9121e122ad0bd825176751d47ecc
  4. Student: Laksono Kurnianggoro
  5. Mentor: Delia Passalacqua
  6. */
  7. /*----------------------------------------------
  8. * Usage:
  9. * facemark_demo_aam <face_cascade_model> <eyes_cascade_model> <training_images> <annotation_files> [test_files]
  10. *
  11. * Example:
  12. * facemark_demo_aam ../face_cascade.xml ../eyes_cascade.xml ../images_train.txt ../points_train.txt ../test.txt
  13. *
  14. * Notes:
  15. * the user should provides the list of training images_train
  16. * accompanied by their corresponding landmarks location in separated files.
  17. * example of contents for images_train.txt:
  18. * ../trainset/image_0001.png
  19. * ../trainset/image_0002.png
  20. * example of contents for points_train.txt:
  21. * ../trainset/image_0001.pts
  22. * ../trainset/image_0002.pts
  23. * where the image_xxxx.pts contains the position of each face landmark.
  24. * example of the contents:
  25. * version: 1
  26. * n_points: 68
  27. * {
  28. * 115.167660 220.807529
  29. * 116.164839 245.721357
  30. * 120.208690 270.389841
  31. * ...
  32. * }
  33. * example of the dataset is available at https://ibug.doc.ic.ac.uk/download/annotations/lfpw.zip
  34. *--------------------------------------------------*/
  35. #include <stdio.h>
  36. #include <fstream>
  37. #include <sstream>
  38. #include "opencv2/core.hpp"
  39. #include "opencv2/highgui.hpp"
  40. #include "opencv2/imgproc.hpp"
  41. #include "opencv2/face.hpp"
  42. #include <iostream>
  43. #include <string>
  44. #include <ctime>
  45. using namespace std;
  46. using namespace cv;
  47. using namespace cv::face;
  48. bool myDetector( InputArray image, OutputArray ROIs, CascadeClassifier *face_cascade);
  49. bool getInitialFitting(Mat image, Rect face, std::vector<Point2f> s0,
  50. CascadeClassifier eyes_cascade, Mat & R, Point2f & Trans, float & scale);
  51. bool parseArguments(int argc, char** argv, String & cascade,
  52. String & model, String & images, String & annotations, String & testImages
  53. );
  54. int main(int argc, char** argv )
  55. {
  56. String cascade_path,eyes_cascade_path,images_path, annotations_path, test_images_path;
  57. if(!parseArguments(argc, argv, cascade_path,eyes_cascade_path,images_path, annotations_path, test_images_path))
  58. return -1;
  59. //! [instance_creation]
  60. /*create the facemark instance*/
  61. FacemarkAAM::Params params;
  62. params.scales.push_back(2.0);
  63. params.scales.push_back(4.0);
  64. params.model_filename = "AAM.yaml";
  65. Ptr<FacemarkAAM> facemark = FacemarkAAM::create(params);
  66. //! [instance_creation]
  67. //! [load_dataset]
  68. /*Loads the dataset*/
  69. std::vector<String> images_train;
  70. std::vector<String> landmarks_train;
  71. loadDatasetList(images_path,annotations_path,images_train,landmarks_train);
  72. //! [load_dataset]
  73. //! [add_samples]
  74. Mat image;
  75. std::vector<Point2f> facial_points;
  76. for(size_t i=0;i<images_train.size();i++){
  77. image = imread(images_train[i].c_str());
  78. loadFacePoints(landmarks_train[i],facial_points);
  79. facemark->addTrainingSample(image, facial_points);
  80. }
  81. //! [add_samples]
  82. //! [training]
  83. /* trained model will be saved to AAM.yml */
  84. facemark->training();
  85. //! [training]
  86. //! [load_test_images]
  87. /*test using some images*/
  88. String testFiles(images_path), testPts(annotations_path);
  89. if(!test_images_path.empty()){
  90. testFiles = test_images_path;
  91. testPts = test_images_path; //unused
  92. }
  93. std::vector<String> images;
  94. std::vector<String> facePoints;
  95. loadDatasetList(testFiles, testPts, images, facePoints);
  96. //! [load_test_images]
  97. //! [trainsformation_variables]
  98. float scale ;
  99. Point2f T;
  100. Mat R;
  101. //! [trainsformation_variables]
  102. //! [base_shape]
  103. FacemarkAAM::Data data;
  104. facemark->getData(&data);
  105. std::vector<Point2f> s0 = data.s0;
  106. //! [base_shape]
  107. //! [fitting]
  108. /*fitting process*/
  109. std::vector<Rect> faces;
  110. //! [load_cascade_models]
  111. CascadeClassifier face_cascade(cascade_path);
  112. CascadeClassifier eyes_cascade(eyes_cascade_path);
  113. //! [load_cascade_models]
  114. for(int i=0;i<(int)images.size();i++){
  115. printf("image #%i ", i);
  116. //! [detect_face]
  117. image = imread(images[i]);
  118. myDetector(image, faces, &face_cascade);
  119. //! [detect_face]
  120. if(faces.size()>0){
  121. //! [get_initialization]
  122. std::vector<FacemarkAAM::Config> conf;
  123. std::vector<Rect> faces_eyes;
  124. for(unsigned j=0;j<faces.size();j++){
  125. if(getInitialFitting(image,faces[j],s0,eyes_cascade, R,T,scale)){
  126. conf.push_back(FacemarkAAM::Config(R,T,scale,(int)params.scales.size()-1));
  127. faces_eyes.push_back(faces[j]);
  128. }
  129. }
  130. //! [get_initialization]
  131. //! [fitting_process]
  132. if(conf.size()>0){
  133. printf(" - face with eyes found %i ", (int)conf.size());
  134. std::vector<std::vector<Point2f> > landmarks;
  135. double newtime = (double)getTickCount();
  136. facemark->fitConfig(image, faces_eyes, landmarks, conf);
  137. double fittime = ((getTickCount() - newtime)/getTickFrequency());
  138. for(unsigned j=0;j<landmarks.size();j++){
  139. drawFacemarks(image, landmarks[j],Scalar(0,255,0));
  140. }
  141. printf("%f ms\n",fittime*1000);
  142. imshow("fitting", image);
  143. waitKey(0);
  144. }else{
  145. printf("initialization cannot be computed - skipping\n");
  146. }
  147. //! [fitting_process]
  148. }
  149. } //for
  150. //! [fitting]
  151. }
  152. bool myDetector(InputArray image, OutputArray faces, CascadeClassifier *face_cascade)
  153. {
  154. Mat gray;
  155. if (image.channels() > 1)
  156. cvtColor(image, gray, COLOR_BGR2GRAY);
  157. else
  158. gray = image.getMat().clone();
  159. equalizeHist(gray, gray);
  160. std::vector<Rect> faces_;
  161. face_cascade->detectMultiScale(gray, faces_, 1.4, 2, CASCADE_SCALE_IMAGE, Size(30, 30));
  162. Mat(faces_).copyTo(faces);
  163. return true;
  164. }
  165. bool getInitialFitting(Mat image, Rect face, std::vector<Point2f> s0 ,CascadeClassifier eyes_cascade, Mat & R, Point2f & Trans, float & scale){
  166. std::vector<Point2f> mybase;
  167. std::vector<Point2f> T;
  168. std::vector<Point2f> base = Mat(Mat(s0)+Scalar(image.cols/2,image.rows/2)).reshape(2);
  169. std::vector<Point2f> base_shape,base_shape2 ;
  170. Point2f e1 = Point2f((float)((base[39].x+base[36].x)/2.0),(float)((base[39].y+base[36].y)/2.0)); //eye1
  171. Point2f e2 = Point2f((float)((base[45].x+base[42].x)/2.0),(float)((base[45].y+base[42].y)/2.0)); //eye2
  172. if(face.width==0 || face.height==0) return false;
  173. std::vector<Point2f> eye;
  174. bool found=false;
  175. Mat faceROI = image( face);
  176. std::vector<Rect> eyes;
  177. //-- In each face, detect eyes
  178. eyes_cascade.detectMultiScale( faceROI, eyes, 1.1, 2, CASCADE_SCALE_IMAGE, Size(20, 20) );
  179. if(eyes.size()==2){
  180. found = true;
  181. int j=0;
  182. Point2f c1( (float)(face.x + eyes[j].x + eyes[j].width*0.5), (float)(face.y + eyes[j].y + eyes[j].height*0.5));
  183. j=1;
  184. Point2f c2( (float)(face.x + eyes[j].x + eyes[j].width*0.5), (float)(face.y + eyes[j].y + eyes[j].height*0.5));
  185. Point2f pivot;
  186. double a0,a1;
  187. if(c1.x<c2.x){
  188. pivot = c1;
  189. a0 = atan2(c2.y-c1.y, c2.x-c1.x);
  190. }else{
  191. pivot = c2;
  192. a0 = atan2(c1.y-c2.y, c1.x-c2.x);
  193. }
  194. scale = (float)(norm(Mat(c1)-Mat(c2))/norm(Mat(e1)-Mat(e2)));
  195. mybase= Mat(Mat(s0)*scale).reshape(2);
  196. Point2f ey1 = Point2f((float)((mybase[39].x+mybase[36].x)/2.0),(float)((mybase[39].y+mybase[36].y)/2.0));
  197. Point2f ey2 = Point2f((float)((mybase[45].x+mybase[42].x)/2.0),(float)((mybase[45].y+mybase[42].y)/2.0));
  198. #define TO_DEGREE 180.0/3.14159265
  199. a1 = atan2(ey2.y-ey1.y, ey2.x-ey1.x);
  200. Mat rot = getRotationMatrix2D(Point2f(0,0), (a1-a0)*TO_DEGREE, 1.0);
  201. rot(Rect(0,0,2,2)).convertTo(R, CV_32F);
  202. base_shape = Mat(Mat(R*scale*Mat(Mat(s0).reshape(1)).t()).t()).reshape(2);
  203. ey1 = Point2f((float)((base_shape[39].x+base_shape[36].x)/2.0),(float)((base_shape[39].y+base_shape[36].y)/2.0));
  204. ey2 = Point2f((float)((base_shape[45].x+base_shape[42].x)/2.0),(float)((base_shape[45].y+base_shape[42].y)/2.0));
  205. T.push_back(Point2f(pivot.x-ey1.x,pivot.y-ey1.y));
  206. Trans = Point2f(pivot.x-ey1.x,pivot.y-ey1.y);
  207. return true;
  208. }else{
  209. Trans = Point2f( (float)(face.x + face.width*0.5),(float)(face.y + face.height*0.5));
  210. }
  211. return found;
  212. }
  213. bool parseArguments(int argc, char** argv,
  214. String & cascade,
  215. String & model,
  216. String & images,
  217. String & annotations,
  218. String & test_images
  219. ){
  220. const String keys =
  221. "{ @f face-cascade | | (required) path to the cascade model file for the face detector }"
  222. "{ @e eyes-cascade | | (required) path to the cascade model file for the eyes detector }"
  223. "{ @i images | | (required) path of a text file contains the list of paths to all training images}"
  224. "{ @a annotations | | (required) Path of a text file contains the list of paths to all annotations files}"
  225. "{ @t test-images | | Path of a text file contains the list of paths to the test images}"
  226. "{ help h usage ? | | facemark_demo_aam -face-cascade -eyes-cascade -images -annotations [-t]\n"
  227. " example: facemark_demo_aam ../face_cascade.xml ../eyes_cascade.xml ../images_train.txt ../points_train.txt ../test.txt}"
  228. ;
  229. CommandLineParser parser(argc, argv,keys);
  230. parser.about("hello");
  231. if (parser.has("help")){
  232. parser.printMessage();
  233. return false;
  234. }
  235. cascade = String(parser.get<String>("face-cascade"));
  236. model = String(parser.get<string>("eyes-cascade"));
  237. images = String(parser.get<string>("images"));
  238. annotations = String(parser.get<string>("annotations"));
  239. test_images = String(parser.get<string>("test-images"));
  240. if(cascade.empty() || model.empty() || images.empty() || annotations.empty()){
  241. std::cerr << "one or more required arguments are not found" << '\n';
  242. cout<<"face-cascade : "<<cascade.c_str()<<endl;
  243. cout<<"eyes-cascade : "<<model.c_str()<<endl;
  244. cout<<"images : "<<images.c_str()<<endl;
  245. cout<<"annotations : "<<annotations.c_str()<<endl;
  246. parser.printMessage();
  247. return false;
  248. }
  249. return true;
  250. }