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
8 #include "opencv2/videoio/videoio_c.h"
9 #include "opencv2/highgui/highgui_c.h"
10
11 #include <cctype>
12 #include <iostream>
13 #include <iterator>
14 #include <stdio.h>
15
16 using namespace std;
17 using namespace cv;
18
help()19 static void help()
20 {
21 cout << "\nThis program demonstrates the cascade recognizer. Now you can use Haar or LBP features.\n"
22 "This classifier can recognize many kinds of rigid objects, once the appropriate classifier is trained.\n"
23 "It's most known use is for faces.\n"
24 "Usage:\n"
25 "./facedetect [--cascade=<cascade_path> this is the primary trained classifier such as frontal face]\n"
26 " [--nested-cascade[=nested_cascade_path this an optional secondary classifier such as eyes]]\n"
27 " [--scale=<image scale greater or equal to 1, try 1.3 for example>]\n"
28 " [--try-flip]\n"
29 " [filename|camera_index]\n\n"
30 "see facedetect.cmd for one call:\n"
31 "./facedetect --cascade=\"../../data/haarcascades/haarcascade_frontalface_alt.xml\" --nested-cascade=\"../../data/haarcascades/haarcascade_eye.xml\" --scale=1.3\n\n"
32 "During execution:\n\tHit any key to quit.\n"
33 "\tUsing OpenCV version " << CV_VERSION << "\n" << endl;
34 }
35
36 void detectAndDraw( Mat& img, CascadeClassifier& cascade,
37 CascadeClassifier& nestedCascade,
38 double scale, bool tryflip );
39
40 string cascadeName = "../../data/haarcascades/haarcascade_frontalface_alt.xml";
41 string nestedCascadeName = "../../data/haarcascades/haarcascade_eye_tree_eyeglasses.xml";
42
main(int argc,const char ** argv)43 int main( int argc, const char** argv )
44 {
45 CvCapture* capture = 0;
46 Mat frame, frameCopy, image;
47 const string scaleOpt = "--scale=";
48 size_t scaleOptLen = scaleOpt.length();
49 const string cascadeOpt = "--cascade=";
50 size_t cascadeOptLen = cascadeOpt.length();
51 const string nestedCascadeOpt = "--nested-cascade";
52 size_t nestedCascadeOptLen = nestedCascadeOpt.length();
53 const string tryFlipOpt = "--try-flip";
54 size_t tryFlipOptLen = tryFlipOpt.length();
55 string inputName;
56 bool tryflip = false;
57
58 help();
59
60 CascadeClassifier cascade, nestedCascade;
61 double scale = 1;
62
63 for( int i = 1; i < argc; i++ )
64 {
65 cout << "Processing " << i << " " << argv[i] << endl;
66 if( cascadeOpt.compare( 0, cascadeOptLen, argv[i], cascadeOptLen ) == 0 )
67 {
68 cascadeName.assign( argv[i] + cascadeOptLen );
69 cout << " from which we have cascadeName= " << cascadeName << endl;
70 }
71 else if( nestedCascadeOpt.compare( 0, nestedCascadeOptLen, argv[i], nestedCascadeOptLen ) == 0 )
72 {
73 if( argv[i][nestedCascadeOpt.length()] == '=' )
74 nestedCascadeName.assign( argv[i] + nestedCascadeOpt.length() + 1 );
75 if( !nestedCascade.load( nestedCascadeName ) )
76 cerr << "WARNING: Could not load classifier cascade for nested objects" << endl;
77 }
78 else if( scaleOpt.compare( 0, scaleOptLen, argv[i], scaleOptLen ) == 0 )
79 {
80 if( !sscanf( argv[i] + scaleOpt.length(), "%lf", &scale ) || scale < 1 )
81 scale = 1;
82 cout << " from which we read scale = " << scale << endl;
83 }
84 else if( tryFlipOpt.compare( 0, tryFlipOptLen, argv[i], tryFlipOptLen ) == 0 )
85 {
86 tryflip = true;
87 cout << " will try to flip image horizontally to detect assymetric objects\n";
88 }
89 else if( argv[i][0] == '-' )
90 {
91 cerr << "WARNING: Unknown option %s" << argv[i] << endl;
92 }
93 else
94 inputName.assign( argv[i] );
95 }
96
97 if( !cascade.load( cascadeName ) )
98 {
99 cerr << "ERROR: Could not load classifier cascade" << endl;
100 help();
101 return -1;
102 }
103
104 if( inputName.empty() || (isdigit(inputName.c_str()[0]) && inputName.c_str()[1] == '\0') )
105 {
106 capture = cvCaptureFromCAM( inputName.empty() ? 0 : inputName.c_str()[0] - '0' );
107 int c = inputName.empty() ? 0 : inputName.c_str()[0] - '0' ;
108 if(!capture) cout << "Capture from CAM " << c << " didn't work" << endl;
109 }
110 else if( inputName.size() )
111 {
112 image = imread( inputName, 1 );
113 if( image.empty() )
114 {
115 capture = cvCaptureFromAVI( inputName.c_str() );
116 if(!capture) cout << "Capture from AVI didn't work" << endl;
117 }
118 }
119 else
120 {
121 image = imread( "../data/lena.jpg", 1 );
122 if(image.empty()) cout << "Couldn't read ../data/lena.jpg" << endl;
123 }
124
125 cvNamedWindow( "result", 1 );
126
127 if( capture )
128 {
129 cout << "In capture ..." << endl;
130 for(;;)
131 {
132 IplImage* iplImg = cvQueryFrame( capture );
133 frame = cv::cvarrToMat(iplImg);
134 if( frame.empty() )
135 break;
136 if( iplImg->origin == IPL_ORIGIN_TL )
137 frame.copyTo( frameCopy );
138 else
139 flip( frame, frameCopy, 0 );
140
141 detectAndDraw( frameCopy, cascade, nestedCascade, scale, tryflip );
142
143 if( waitKey( 10 ) >= 0 )
144 goto _cleanup_;
145 }
146
147 waitKey(0);
148
149 _cleanup_:
150 cvReleaseCapture( &capture );
151 }
152 else
153 {
154 cout << "In image read" << endl;
155 if( !image.empty() )
156 {
157 detectAndDraw( image, cascade, nestedCascade, scale, tryflip );
158 waitKey(0);
159 }
160 else if( !inputName.empty() )
161 {
162 /* assume it is a text file containing the
163 list of the image filenames to be processed - one per line */
164 FILE* f = fopen( inputName.c_str(), "rt" );
165 if( f )
166 {
167 char buf[1000+1];
168 while( fgets( buf, 1000, f ) )
169 {
170 int len = (int)strlen(buf), c;
171 while( len > 0 && isspace(buf[len-1]) )
172 len--;
173 buf[len] = '\0';
174 cout << "file " << buf << endl;
175 image = imread( buf, 1 );
176 if( !image.empty() )
177 {
178 detectAndDraw( image, cascade, nestedCascade, scale, tryflip );
179 c = waitKey(0);
180 if( c == 27 || c == 'q' || c == 'Q' )
181 break;
182 }
183 else
184 {
185 cerr << "Aw snap, couldn't read image " << buf << endl;
186 }
187 }
188 fclose(f);
189 }
190 }
191 }
192
193 cvDestroyWindow("result");
194
195 return 0;
196 }
197
detectAndDraw(Mat & img,CascadeClassifier & cascade,CascadeClassifier & nestedCascade,double scale,bool tryflip)198 void detectAndDraw( Mat& img, CascadeClassifier& cascade,
199 CascadeClassifier& nestedCascade,
200 double scale, bool tryflip )
201 {
202 int i = 0;
203 double t = 0;
204 vector<Rect> faces, faces2;
205 const static Scalar colors[] = { CV_RGB(0,0,255),
206 CV_RGB(0,128,255),
207 CV_RGB(0,255,255),
208 CV_RGB(0,255,0),
209 CV_RGB(255,128,0),
210 CV_RGB(255,255,0),
211 CV_RGB(255,0,0),
212 CV_RGB(255,0,255)} ;
213 Mat gray, smallImg( cvRound (img.rows/scale), cvRound(img.cols/scale), CV_8UC1 );
214
215 cvtColor( img, gray, COLOR_BGR2GRAY );
216 resize( gray, smallImg, smallImg.size(), 0, 0, INTER_LINEAR );
217 equalizeHist( smallImg, smallImg );
218
219 t = (double)cvGetTickCount();
220 cascade.detectMultiScale( smallImg, faces,
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 if( tryflip )
228 {
229 flip(smallImg, smallImg, 1);
230 cascade.detectMultiScale( smallImg, faces2,
231 1.1, 2, 0
232 //|CASCADE_FIND_BIGGEST_OBJECT
233 //|CASCADE_DO_ROUGH_SEARCH
234 |CASCADE_SCALE_IMAGE
235 ,
236 Size(30, 30) );
237 for( vector<Rect>::const_iterator r = faces2.begin(); r != faces2.end(); r++ )
238 {
239 faces.push_back(Rect(smallImg.cols - r->x - r->width, r->y, r->width, r->height));
240 }
241 }
242 t = (double)cvGetTickCount() - t;
243 printf( "detection time = %g ms\n", t/((double)cvGetTickFrequency()*1000.) );
244 for( vector<Rect>::const_iterator r = faces.begin(); r != faces.end(); r++, i++ )
245 {
246 Mat smallImgROI;
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( img, center, radius, color, 3, 8, 0 );
259 }
260 else
261 rectangle( img, cvPoint(cvRound(r->x*scale), cvRound(r->y*scale)),
262 cvPoint(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 smallImgROI = smallImg(*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( img, center, radius, color, 3, 8, 0 );
281 }
282 }
283 cv::imshow( "result", img );
284 }
285