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