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