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