samples/facedetect.cpp#
This program demonstrates usage of the Cascade classifier class
1#include "opencv2/xobjdetect.hpp"
2#include "opencv2/highgui.hpp"
3#include "opencv2/imgproc.hpp"
4#include "opencv2/videoio.hpp"
5#include <iostream>
6
7using namespace std;
8using namespace cv;
9
10static void help(const char** argv)
11{
12 cout << "\nThis program demonstrates the use of cv::CascadeClassifier class to detect objects (Face + eyes). You can use Haar or LBP features.\n"
13 "This classifier can recognize many kinds of rigid objects, once the appropriate classifier is trained.\n"
14 "It's most known use is for faces.\n"
15 "Usage:\n"
16 << argv[0]
17 << " [--cascade=<cascade_path> this is the primary trained classifier such as frontal face]\n"
18 " [--nested-cascade[=nested_cascade_path this an optional secondary classifier such as eyes]]\n"
19 " [--scale=<image scale greater or equal to 1, try 1.3 for example>]\n"
20 " [--try-flip]\n"
21 " [filename|camera_index]\n\n"
22 "example:\n"
23 << argv[0]
24 << " --cascade=\"data/haarcascades/haarcascade_frontalface_alt.xml\" --nested-cascade=\"data/haarcascades/haarcascade_eye_tree_eyeglasses.xml\" --scale=1.3\n\n"
25 "During execution:\n\tHit any key to quit.\n"
26 "\tUsing OpenCV version " << CV_VERSION << "\n" << endl;
27}
28
29void detectAndDraw( Mat& img, CascadeClassifier& cascade,
30 CascadeClassifier& nestedCascade,
31 double scale, bool tryflip );
32
33string cascadeName;
34string nestedCascadeName;
35
36int main( int argc, const char** argv )
37{
38 VideoCapture capture;
39 Mat frame, image;
40 string inputName;
41 bool tryflip;
42 CascadeClassifier cascade, nestedCascade;
43 double scale;
44
45 cv::CommandLineParser parser(argc, argv,
46 "{help h||}"
47 "{cascade|data/haarcascades/haarcascade_frontalface_alt.xml|}"
48 "{nested-cascade|data/haarcascades/haarcascade_eye_tree_eyeglasses.xml|}"
49 "{scale|1|}{try-flip||}{@filename||}"
50 );
51 if (parser.has("help"))
52 {
53 help(argv);
54 return 0;
55 }
56 cascadeName = parser.get<string>("cascade");
57 nestedCascadeName = parser.get<string>("nested-cascade");
58 scale = parser.get<double>("scale");
59 if (scale < 1)
60 scale = 1;
61 tryflip = parser.has("try-flip");
62 inputName = parser.get<string>("@filename");
63 if (!parser.check())
64 {
65 parser.printErrors();
66 return 0;
67 }
68 if (!nestedCascade.load(samples::findFileOrKeep(nestedCascadeName)))
69 cerr << "WARNING: Could not load classifier cascade for nested objects" << endl;
70 if (!cascade.load(samples::findFile(cascadeName)))
71 {
72 cerr << "ERROR: Could not load classifier cascade" << endl;
73 help(argv);
74 return -1;
75 }
76 if( inputName.empty() || (isdigit(inputName[0]) && inputName.size() == 1) )
77 {
78 int camera = inputName.empty() ? 0 : inputName[0] - '0';
79 if(!capture.open(camera))
80 {
81 cout << "Capture from camera #" << camera << " didn't work" << endl;
82 return 1;
83 }
84 }
85 else if (!inputName.empty())
86 {
87 image = imread(samples::findFileOrKeep(inputName), IMREAD_COLOR);
88 if (image.empty())
89 {
90 if (!capture.open(samples::findFileOrKeep(inputName)))
91 {
92 cout << "Could not read " << inputName << endl;
93 return 1;
94 }
95 }
96 }
97 else
98 {
99 image = imread(samples::findFile("lena.jpg"), IMREAD_COLOR);
100 if (image.empty())
101 {
102 cout << "Couldn't read lena.jpg" << endl;
103 return 1;
104 }
105 }
106
107 if( capture.isOpened() )
108 {
109 cout << "Video capturing has been started ..." << endl;
110
111 for(;;)
112 {
113 capture >> frame;
114 if( frame.empty() )
115 break;
116
117 Mat frame1 = frame.clone();
118 detectAndDraw( frame1, cascade, nestedCascade, scale, tryflip );
119
120 char c = (char)waitKey(10);
121 if( c == 27 || c == 'q' || c == 'Q' )
122 break;
123 }
124 }
125 else
126 {
127 cout << "Detecting face(s) in " << inputName << endl;
128 if( !image.empty() )
129 {
130 detectAndDraw( image, cascade, nestedCascade, scale, tryflip );
131 waitKey(0);
132 }
133 else if( !inputName.empty() )
134 {
135 /* assume it is a text file containing the
136 list of the image filenames to be processed - one per line */
137 FILE* f = fopen( inputName.c_str(), "rt" );
138 if( f )
139 {
140 char buf[1000+1];
141 while( fgets( buf, 1000, f ) )
142 {
143 int len = (int)strlen(buf);
144 while( len > 0 && isspace(buf[len-1]) )
145 len--;
146 buf[len] = '\0';
147 cout << "file " << buf << endl;
148 image = imread( buf, IMREAD_COLOR );
149 if( !image.empty() )
150 {
151 detectAndDraw( image, cascade, nestedCascade, scale, tryflip );
152 char c = (char)waitKey(0);
153 if( c == 27 || c == 'q' || c == 'Q' )
154 break;
155 }
156 else
157 {
158 cerr << "Aw snap, couldn't read image " << buf << endl;
159 }
160 }
161 fclose(f);
162 }
163 }
164 }
165
166 return 0;
167}
168
169void detectAndDraw( Mat& img, CascadeClassifier& cascade,
170 CascadeClassifier& nestedCascade,
171 double scale, bool tryflip )
172{
173 double t = 0;
174 vector<Rect> faces, faces2;
175 const static Scalar colors[] =
176 {
177 Scalar(255,0,0),
178 Scalar(255,128,0),
179 Scalar(255,255,0),
180 Scalar(0,255,0),
181 Scalar(0,128,255),
182 Scalar(0,255,255),
183 Scalar(0,0,255),
184 Scalar(255,0,255)
185 };
186 Mat gray, smallImg;
187
188 cvtColor( img, gray, COLOR_BGR2GRAY );
189 double fx = 1 / scale;
190 resize( gray, smallImg, Size(), fx, fx, INTER_LINEAR_EXACT );
191 equalizeHist( smallImg, smallImg );
192
193 t = (double)getTickCount();
194 cascade.detectMultiScale( smallImg, faces,
195 1.1, 2, 0
196 //|CASCADE_FIND_BIGGEST_OBJECT
197 //|CASCADE_DO_ROUGH_SEARCH
198 |CASCADE_SCALE_IMAGE,
199 Size(30, 30) );
200 if( tryflip )
201 {
202 flip(smallImg, smallImg, 1);
203 cascade.detectMultiScale( smallImg, faces2,
204 1.1, 2, 0
205 //|CASCADE_FIND_BIGGEST_OBJECT
206 //|CASCADE_DO_ROUGH_SEARCH
207 |CASCADE_SCALE_IMAGE,
208 Size(30, 30) );
209 for( vector<Rect>::const_iterator r = faces2.begin(); r != faces2.end(); ++r )
210 {
211 faces.push_back(Rect(smallImg.cols - r->x - r->width, r->y, r->width, r->height));
212 }
213 }
214 t = (double)getTickCount() - t;
215 printf( "detection time = %g ms\n", t*1000/getTickFrequency());
216 for ( size_t i = 0; i < faces.size(); i++ )
217 {
218 Rect r = faces[i];
219 Mat smallImgROI;
220 vector<Rect> nestedObjects;
221 Point center;
222 Scalar color = colors[i%8];
223 int radius;
224
225 double aspect_ratio = (double)r.width/r.height;
226 if( 0.75 < aspect_ratio && aspect_ratio < 1.3 )
227 {
228 center.x = cvRound((r.x + r.width*0.5)*scale);
229 center.y = cvRound((r.y + r.height*0.5)*scale);
230 radius = cvRound((r.width + r.height)*0.25*scale);
231 circle( img, center, radius, color, 3, 8, 0 );
232 }
233 else
234 rectangle( img, Point(cvRound(r.x*scale), cvRound(r.y*scale)),
235 Point(cvRound((r.x + r.width-1)*scale), cvRound((r.y + r.height-1)*scale)),
236 color, 3, 8, 0);
237 if( nestedCascade.empty() )
238 continue;
239 smallImgROI = smallImg( r );
240 nestedCascade.detectMultiScale( smallImgROI, nestedObjects,
241 1.1, 2, 0
242 //|CASCADE_FIND_BIGGEST_OBJECT
243 //|CASCADE_DO_ROUGH_SEARCH
244 //|CASCADE_DO_CANNY_PRUNING
245 |CASCADE_SCALE_IMAGE,
246 Size(30, 30) );
247 for ( size_t j = 0; j < nestedObjects.size(); j++ )
248 {
249 Rect nr = nestedObjects[j];
250 center.x = cvRound((r.x + nr.x + nr.width*0.5)*scale);
251 center.y = cvRound((r.y + nr.y + nr.height*0.5)*scale);
252 radius = cvRound((nr.width + nr.height)*0.25*scale);
253 circle( img, center, radius, color, 3, 8, 0 );
254 }
255 }
256 imshow( "result", img );
257}