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 /*----------------------------------------------
9  * Usage:
10  * facemark_demo_lbf <face_cascade_model> <saved_model_filename> <training_images> <annotation_files> [test_files]
11  *
12  * Example:
13  * facemark_demo_lbf ../face_cascade.xml ../LBF.model ../images_train.txt ../points_train.txt ../test.txt
14  *
15  * Notes:
16  * the user should provides the list of training images_train
17  * accompanied by their corresponding landmarks location in separated files.
18  * example of contents for images_train.txt:
19  * ../trainset/image_0001.png
20  * ../trainset/image_0002.png
21  * example of contents for points_train.txt:
22  * ../trainset/image_0001.pts
23  * ../trainset/image_0002.pts
24  * where the image_xxxx.pts contains the position of each face landmark.
25  * example of the contents:
26  *  version: 1
27  *  n_points:  68
28  *  {
29  *  115.167660 220.807529
30  *  116.164839 245.721357
31  *  120.208690 270.389841
32  *  ...
33  *  }
34  * example of the dataset is available at https://ibug.doc.ic.ac.uk/download/annotations/ibug.zip
35  *--------------------------------------------------*/
36 
37 #include <stdio.h>
38 #include <fstream>
39 #include <sstream>
40 #include <iostream>
41 #include "opencv2/core.hpp"
42 #include "opencv2/highgui.hpp"
43 #include "opencv2/imgproc.hpp"
44 #include "opencv2/face.hpp"
45 
46 using namespace std;
47 using namespace cv;
48 using namespace cv::face;
49 
50 static bool myDetector( InputArray image, OutputArray roi, CascadeClassifier *face_detector);
51 static bool parseArguments(int argc, char** argv, String & cascade,
52    String & model, String & images, String & annotations, String & testImages
53 );
54 
main(int argc,char ** argv)55 int main(int argc, char** argv)
56 {
57     String cascade_path,model_path,images_path, annotations_path, test_images_path;
58     if(!parseArguments(argc, argv, cascade_path,model_path,images_path, annotations_path, test_images_path))
59        return -1;
60 
61     /*create the facemark instance*/
62     FacemarkLBF::Params params;
63     params.model_filename = model_path;
64     params.cascade_face = cascade_path;
65     Ptr<FacemarkLBF> facemark = FacemarkLBF::create(params);
66 
67     CascadeClassifier face_cascade;
68     face_cascade.load(params.cascade_face.c_str());
69     facemark->setFaceDetector((FN_FaceDetector)myDetector, &face_cascade);
70 
71     /*Loads the dataset*/
72     std::vector<String> images_train;
73     std::vector<String> landmarks_train;
74     loadDatasetList(images_path,annotations_path,images_train,landmarks_train);
75 
76     Mat image;
77     std::vector<Point2f> facial_points;
78     for(size_t i=0;i<images_train.size();i++){
79         printf("%i/%i :: %s\n", (int)(i+1), (int)images_train.size(),images_train[i].c_str());
80         image = imread(images_train[i].c_str());
81         loadFacePoints(landmarks_train[i],facial_points);
82         facemark->addTrainingSample(image, facial_points);
83     }
84 
85     /*train the Algorithm*/
86     facemark->training();
87 
88     /*test using some images*/
89     String testFiles(images_path), testPts(annotations_path);
90     if(!test_images_path.empty()){
91         testFiles = test_images_path;
92         testPts = test_images_path; //unused
93     }
94     std::vector<String> images;
95     std::vector<String> facePoints;
96     loadDatasetList(testFiles, testPts, images, facePoints);
97 
98     std::vector<Rect> rects;
99     CascadeClassifier cc(params.cascade_face.c_str());
100     for(size_t i=0;i<images.size();i++){
101         std::vector<std::vector<Point2f> > landmarks;
102         cout<<images[i];
103         Mat img = imread(images[i]);
104         facemark->getFaces(img, rects);
105         facemark->fit(img, rects, landmarks);
106 
107         for(size_t j=0;j<rects.size();j++){
108             drawFacemarks(img, landmarks[j], Scalar(0,0,255));
109             rectangle(img, rects[j], Scalar(255,0,255));
110         }
111 
112         if(rects.size()>0){
113             cout<<endl;
114             imshow("result", img);
115             waitKey(0);
116         }else{
117             cout<<"face not found"<<endl;
118         }
119     }
120 }
121 
myDetector(InputArray image,OutputArray faces,CascadeClassifier * face_cascade)122 bool myDetector(InputArray image, OutputArray faces, CascadeClassifier *face_cascade)
123 {
124     Mat gray;
125 
126     if (image.channels() > 1)
127         cvtColor(image, gray, COLOR_BGR2GRAY);
128     else
129         gray = image.getMat().clone();
130 
131     equalizeHist(gray, gray);
132 
133     std::vector<Rect> faces_;
134     face_cascade->detectMultiScale(gray, faces_, 1.4, 2, CASCADE_SCALE_IMAGE, Size(30, 30));
135     Mat(faces_).copyTo(faces);
136     return true;
137 }
138 
parseArguments(int argc,char ** argv,String & cascade,String & model,String & images,String & annotations,String & test_images)139 bool parseArguments(int argc, char** argv,
140     String & cascade,
141     String & model,
142     String & images,
143     String & annotations,
144     String & test_images
145 ){
146     const String keys =
147         "{ @c cascade         |      | (required) path to the face cascade xml file fo the face detector }"
148         "{ @i images          |      | (required) path of a text file contains the list of paths to all training images}"
149         "{ @a annotations     |      | (required) Path of a text file contains the list of paths to all annotations files}"
150         "{ @m model           |      | (required) path to save the trained model }"
151         "{ t test-images      |      | Path of a text file contains the list of paths to the test images}"
152         "{ help h usage ?     |      | facemark_demo_lbf -cascade -images -annotations -model [-t] \n"
153          " example: facemark_demo_lbf ../face_cascade.xml ../images_train.txt ../points_train.txt ../lbf.model}"
154     ;
155     CommandLineParser parser(argc, argv,keys);
156     parser.about("hello");
157 
158     if (parser.has("help")){
159         parser.printMessage();
160         return false;
161     }
162 
163     cascade = String(parser.get<String>("cascade"));
164     model = String(parser.get<string>("model"));
165     images = String(parser.get<string>("images"));
166     annotations = String(parser.get<string>("annotations"));
167     test_images = String(parser.get<string>("t"));
168 
169     cout<<"cascade : "<<cascade.c_str()<<endl;
170     cout<<"model : "<<model.c_str()<<endl;
171     cout<<"images : "<<images.c_str()<<endl;
172     cout<<"annotations : "<<annotations.c_str()<<endl;
173 
174     if(cascade.empty() || model.empty() || images.empty() || annotations.empty()){
175         std::cerr << "one or more required arguments are not found" << '\n';
176 
177         parser.printMessage();
178         return false;
179     }
180 
181     return true;
182 }
183