• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Author: Samyak Datta (datta[dot]samyak[at]gmail.com)
3  *
4  * A program to detect facial feature points using
5  * Haarcascade classifiers for face, eyes, nose and mouth
6  *
7  */
8 
9 #include "opencv2/objdetect/objdetect.hpp"
10 #include "opencv2/highgui/highgui.hpp"
11 #include "opencv2/imgproc/imgproc.hpp"
12 
13 #include <iostream>
14 #include <cstdio>
15 #include <vector>
16 #include <algorithm>
17 
18 using namespace std;
19 using namespace cv;
20 
21 // Functions to parse command-line arguments
22 static string getCommandOption(const vector<string>&, const string&);
23 static void setCommandOptions(vector<string>&, int, char**);
24 static bool doesCmdOptionExist(const vector<string>& , const string&);
25 
26 // Functions for facial feature detection
27 static void help();
28 static void detectFaces(Mat&, vector<Rect_<int> >&, string);
29 static void detectEyes(Mat&, vector<Rect_<int> >&, string);
30 static void detectNose(Mat&, vector<Rect_<int> >&, string);
31 static void detectMouth(Mat&, vector<Rect_<int> >&, string);
32 static void detectFacialFeaures(Mat&, const vector<Rect_<int> >, string, string, string);
33 
34 string input_image_path;
35 string face_cascade_path, eye_cascade_path, nose_cascade_path, mouth_cascade_path;
36 
main(int argc,char ** argv)37 int main(int argc, char** argv)
38 {
39     if(argc < 3)
40     {
41         help();
42         return 1;
43     }
44 
45     // Extract command-line options
46     vector<string> args;
47     setCommandOptions(args, argc, argv);
48 
49     input_image_path = argv[1];
50     face_cascade_path = argv[2];
51     eye_cascade_path = (doesCmdOptionExist(args, "-eyes")) ? getCommandOption(args, "-eyes") : "";
52     nose_cascade_path = (doesCmdOptionExist(args, "-nose")) ? getCommandOption(args, "-nose") : "";
53     mouth_cascade_path = (doesCmdOptionExist(args, "-mouth")) ? getCommandOption(args, "-mouth") : "";
54 
55     // Load image and cascade classifier files
56     Mat image;
57     image = imread(input_image_path);
58 
59     // Detect faces and facial features
60     vector<Rect_<int> > faces;
61     detectFaces(image, faces, face_cascade_path);
62     detectFacialFeaures(image, faces, eye_cascade_path, nose_cascade_path, mouth_cascade_path);
63 
64     imshow("Result", image);
65 
66     waitKey(0);
67     return 0;
68 }
69 
setCommandOptions(vector<string> & args,int argc,char ** argv)70 void setCommandOptions(vector<string>& args, int argc, char** argv)
71 {
72     for(int i = 1; i < argc; ++i)
73     {
74         args.push_back(argv[i]);
75     }
76     return;
77 }
78 
getCommandOption(const vector<string> & args,const string & opt)79 string getCommandOption(const vector<string>& args, const string& opt)
80 {
81     string answer;
82     vector<string>::const_iterator it = find(args.begin(), args.end(), opt);
83     if(it != args.end() && (++it != args.end()))
84         answer = *it;
85     return answer;
86 }
87 
doesCmdOptionExist(const vector<string> & args,const string & opt)88 bool doesCmdOptionExist(const vector<string>& args, const string& opt)
89 {
90     vector<string>::const_iterator it = find(args.begin(), args.end(), opt);
91     return (it != args.end());
92 }
93 
help()94 static void help()
95 {
96     cout << "\nThis file demonstrates facial feature points detection using Haarcascade classifiers.\n"
97         "The program detects a face and eyes, nose and mouth inside the face."
98         "The code has been tested on the Japanese Female Facial Expression (JAFFE) database and found"
99         "to give reasonably accurate results. \n";
100 
101     cout << "\nUSAGE: ./cpp-example-facial_features [IMAGE] [FACE_CASCADE] [OPTIONS]\n"
102         "IMAGE\n\tPath to the image of a face taken as input.\n"
103         "FACE_CASCSDE\n\t Path to a haarcascade classifier for face detection.\n"
104         "OPTIONS: \nThere are 3 options available which are described in detail. There must be a "
105         "space between the option and it's argument (All three options accept arguments).\n"
106         "\t-eyes : Specify the haarcascade classifier for eye detection.\n"
107         "\t-nose : Specify the haarcascade classifier for nose detection.\n"
108         "\t-mouth : Specify the haarcascade classifier for mouth detection.\n";
109 
110 
111     cout << "EXAMPLE:\n"
112         "(1) ./cpp-example-facial_features image.jpg face.xml -eyes eyes.xml -mouth mouth.xml\n"
113         "\tThis will detect the face, eyes and mouth in image.jpg.\n"
114         "(2) ./cpp-example-facial_features image.jpg face.xml -nose nose.xml\n"
115         "\tThis will detect the face and nose in image.jpg.\n"
116         "(3) ./cpp-example-facial_features image.jpg face.xml\n"
117         "\tThis will detect only the face in image.jpg.\n";
118 
119     cout << " \n\nThe classifiers for face and eyes can be downloaded from : "
120         " \nhttps://github.com/Itseez/opencv/tree/master/data/haarcascades";
121 
122     cout << "\n\nThe classifiers for nose and mouth can be downloaded from : "
123         " \nhttps://github.com/Itseez/opencv_contrib/tree/master/modules/face/data/cascades\n";
124 }
125 
detectFaces(Mat & img,vector<Rect_<int>> & faces,string cascade_path)126 static void detectFaces(Mat& img, vector<Rect_<int> >& faces, string cascade_path)
127 {
128     CascadeClassifier face_cascade;
129     face_cascade.load(cascade_path);
130 
131     face_cascade.detectMultiScale(img, faces, 1.15, 3, 0|CASCADE_SCALE_IMAGE, Size(30, 30));
132     return;
133 }
134 
detectFacialFeaures(Mat & img,const vector<Rect_<int>> faces,string eye_cascade,string nose_cascade,string mouth_cascade)135 static void detectFacialFeaures(Mat& img, const vector<Rect_<int> > faces, string eye_cascade,
136         string nose_cascade, string mouth_cascade)
137 {
138     for(unsigned int i = 0; i < faces.size(); ++i)
139     {
140         // Mark the bounding box enclosing the face
141         Rect face = faces[i];
142         rectangle(img, Point(face.x, face.y), Point(face.x+face.width, face.y+face.height),
143                 Scalar(255, 0, 0), 1, 4);
144 
145         // Eyes, nose and mouth will be detected inside the face (region of interest)
146         Mat ROI = img(Rect(face.x, face.y, face.width, face.height));
147 
148         // Check if all features (eyes, nose and mouth) are being detected
149         bool is_full_detection = false;
150         if( (!eye_cascade.empty()) && (!nose_cascade.empty()) && (!mouth_cascade.empty()) )
151             is_full_detection = true;
152 
153         // Detect eyes if classifier provided by the user
154         if(!eye_cascade.empty())
155         {
156             vector<Rect_<int> > eyes;
157             detectEyes(ROI, eyes, eye_cascade);
158 
159             // Mark points corresponding to the centre of the eyes
160             for(unsigned int j = 0; j < eyes.size(); ++j)
161             {
162                 Rect e = eyes[j];
163                 circle(ROI, Point(e.x+e.width/2, e.y+e.height/2), 3, Scalar(0, 255, 0), -1, 8);
164                 /* rectangle(ROI, Point(e.x, e.y), Point(e.x+e.width, e.y+e.height),
165                     Scalar(0, 255, 0), 1, 4); */
166             }
167         }
168 
169         // Detect nose if classifier provided by the user
170         double nose_center_height = 0.0;
171         if(!nose_cascade.empty())
172         {
173             vector<Rect_<int> > nose;
174             detectNose(ROI, nose, nose_cascade);
175 
176             // Mark points corresponding to the centre (tip) of the nose
177             for(unsigned int j = 0; j < nose.size(); ++j)
178             {
179                 Rect n = nose[j];
180                 circle(ROI, Point(n.x+n.width/2, n.y+n.height/2), 3, Scalar(0, 255, 0), -1, 8);
181                 nose_center_height = (n.y + n.height/2);
182             }
183         }
184 
185         // Detect mouth if classifier provided by the user
186         double mouth_center_height = 0.0;
187         if(!mouth_cascade.empty())
188         {
189             vector<Rect_<int> > mouth;
190             detectMouth(ROI, mouth, mouth_cascade);
191 
192             for(unsigned int j = 0; j < mouth.size(); ++j)
193             {
194                 Rect m = mouth[j];
195                 mouth_center_height = (m.y + m.height/2);
196 
197                 // The mouth should lie below the nose
198                 if( (is_full_detection) && (mouth_center_height > nose_center_height) )
199                 {
200                     rectangle(ROI, Point(m.x, m.y), Point(m.x+m.width, m.y+m.height), Scalar(0, 255, 0), 1, 4);
201                 }
202                 else if( (is_full_detection) && (mouth_center_height <= nose_center_height) )
203                     continue;
204                 else
205                     rectangle(ROI, Point(m.x, m.y), Point(m.x+m.width, m.y+m.height), Scalar(0, 255, 0), 1, 4);
206             }
207         }
208 
209     }
210 
211     return;
212 }
213 
detectEyes(Mat & img,vector<Rect_<int>> & eyes,string cascade_path)214 static void detectEyes(Mat& img, vector<Rect_<int> >& eyes, string cascade_path)
215 {
216     CascadeClassifier eyes_cascade;
217     eyes_cascade.load(cascade_path);
218 
219     eyes_cascade.detectMultiScale(img, eyes, 1.20, 5, 0|CASCADE_SCALE_IMAGE, Size(30, 30));
220     return;
221 }
222 
detectNose(Mat & img,vector<Rect_<int>> & nose,string cascade_path)223 static void detectNose(Mat& img, vector<Rect_<int> >& nose, string cascade_path)
224 {
225     CascadeClassifier nose_cascade;
226     nose_cascade.load(cascade_path);
227 
228     nose_cascade.detectMultiScale(img, nose, 1.20, 5, 0|CASCADE_SCALE_IMAGE, Size(30, 30));
229     return;
230 }
231 
detectMouth(Mat & img,vector<Rect_<int>> & mouth,string cascade_path)232 static void detectMouth(Mat& img, vector<Rect_<int> >& mouth, string cascade_path)
233 {
234     CascadeClassifier mouth_cascade;
235     mouth_cascade.load(cascade_path);
236 
237     mouth_cascade.detectMultiScale(img, mouth, 1.20, 5, 0|CASCADE_SCALE_IMAGE, Size(30, 30));
238     return;
239 }
240