Update the changelog
[opencv] / apps / HaarFaceDetect / facedetect.cpp
1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 //  By downloading, copying, installing or using the software you agree to this license.
6 //  If you do not agree to this license, do not download, install,
7 //  copy or use the software.
8 //
9 //
10 //                        Intel License Agreement
11 //                For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2000, Intel Corporation, all rights reserved.
14 // Third party copyrights are property of their respective owners.
15 //
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
18 //
19 //   * Redistribution's of source code must retain the above copyright notice,
20 //     this list of conditions and the following disclaimer.
21 //
22 //   * Redistribution's in binary form must reproduce the above copyright notice,
23 //     this list of conditions and the following disclaimer in the documentation
24 //     and/or other materials provided with the distribution.
25 //
26 //   * The name of Intel Corporation may not be used to endorse or promote products
27 //     derived from this software without specific prior written permission.
28 //
29 // This software is provided by the copyright holders and contributors "as is" and
30 // any express or implied warranties, including, but not limited to, the implied
31 // warranties of merchantability and fitness for a particular purpose are disclaimed.
32 // In no event shall the Intel Corporation or contributors be liable for any direct,
33 // indirect, incidental, special, exemplary, or consequential damages
34 // (including, but not limited to, procurement of substitute goods or services;
35 // loss of use, data, or profits; or business interruption) however caused
36 // and on any theory of liability, whether in contract, strict liability,
37 // or tort (including negligence or otherwise) arising in any way out of
38 // the use of this software, even if advised of the possibility of such damage.
39 //
40 //M*/
41
42 #include "cvfltk.h"
43 #include "highgui.h"
44 #include "cv.h"
45 #include "cvaux.h"
46 #include <string.h>
47 #include <stdio.h>
48 #include <math.h>
49
50 class VideoWindow;
51
52 Fl_Window *root_window=(Fl_Window *)0;
53 VideoWindow* video_window = 0;
54 CvCapture* capture = 0;
55 CvVideoWriter* writer = 0;
56 static int started = 0;
57 double timeout = 0.005;
58 IplImage* video_image = 0;
59 double old_pos = 0;
60 Fl_Value_Slider* video_pos = 0;
61 int guard = 0;
62 int is_avi = 0;
63 int is_recorded = 0;
64 double fps = -1;
65 double fps_alpha = 0.1;
66 char fps_buffer[100];
67 double cpu_freq = 0;
68 double prev_frame_stamp = 0;
69 int start_pos = 0;
70 int total_frames0 = 0;
71 int total_frames = 0;
72 int is_loopy = 0;
73
74 Fl_Button* stop_button = 0;
75 Fl_Button* play_button = 0;
76 Fl_Button* video_button = 0;
77 Fl_Button* cam_button = 0;
78 Fl_Button* record_button = 0;
79 Fl_Box* fps_box = 0;
80
81 #define ORIG_WIN_SIZE  24
82
83 #define MIN_NEIGHBORS 2
84
85 static CvMemStorage* storage = cvCreateMemStorage();
86 static CvHaarClassifierCascade* hid_cascade = 0;
87
88 int InitFaceDetect( const char* classifier_cascade_path )
89 {
90     hid_cascade = cvLoadHaarClassifierCascade(
91         classifier_cascade_path && strlen(classifier_cascade_path) > 0 ?
92         classifier_cascade_path : "<default_face_cascade>",
93         cvSize( ORIG_WIN_SIZE, ORIG_WIN_SIZE ));
94     return hid_cascade != 0;
95 }
96
97 void DetectAndDrawFaces( IplImage* img )
98 {
99     if( hid_cascade && img )
100     {
101         int scale = 2;
102         CvSize img_size = cvGetSize( img );
103         IplImage* temp = cvCreateImage( cvSize(img_size.width/2,img_size.height/2), 8, 3 );
104         IplImage* canvas = cvCreateImage( cvSize(temp->width,temp->height*3/2), 8, 3 );
105         CvPoint offset = cvPoint( 0, temp->height/3 );
106         int i;
107
108         cvZero( canvas );
109         cvPyrDown( img, temp );
110         cvClearMemStorage( storage );
111
112         cvSetImageROI( canvas, cvRect( offset.x, offset.y, temp->width, temp->height ));
113         cvCopy( temp, canvas );
114         cvResetImageROI( canvas );
115     
116         if( hid_cascade )
117         {
118             CvSeq* faces = cvHaarDetectObjects( canvas, hid_cascade, storage, 1.2, 2, 1 );
119             scale = 2;
120
121             for( i = 0; i < (faces ? faces->total : 0); i++ )
122             {
123                 CvRect r = *(CvRect*)cvGetSeqElem( faces, i );
124                 r.x -= offset.x;
125                 r.y -= offset.y;
126                 cvRectangle( img,
127                              cvPoint(r.x*scale,/*img->height - */r.y*scale),
128                              cvPoint((r.x+r.width)*scale,/*img->height - */(r.y+r.height)*scale),
129                              CV_RGB(255,0,0), 3 );
130             }
131         }
132
133         cvReleaseImage( &temp );
134         cvReleaseImage( &canvas );
135     }
136 }
137
138 class VideoWindow : public Fl_Box
139 {
140 public:
141     VideoWindow( int x, int y, int w, int h, const char* t = 0 );
142     ~VideoWindow();
143     void draw();
144 };
145
146 VideoWindow::VideoWindow( int x, int y, int w, int h, const char* t ):
147     Fl_Box( x, y, w, h, t )
148 {
149 }
150
151 VideoWindow::~VideoWindow()
152 {
153 }
154
155 void VideoWindow::draw()
156 {
157 }
158
159 static void end_capture()
160 {
161     started = 0;
162     cvReleaseCapture( &capture );
163 }
164
165 static void cb_Stop( Fl_Widget*, void* );
166 static void cb_StartStopRecord( Fl_Widget*, void* );
167 static void cb_Exit( Fl_Widget*, void* );
168
169 static double get_time_accurate(void)
170 {
171     return (double)cvGetTickCount()*1e-3/(cpu_freq+1e-10);
172 }
173
174 static void get_next_frame(void*)
175 {
176     static int repositioning = 0;
177     IplImage* frame = 0;
178     double new_pos = video_pos->value();
179     
180     if( (new_pos-old_pos >= 1e-10 || new_pos-old_pos <= -1e-10) && !repositioning)
181     {
182         video_window->redraw();
183         cvSetCaptureProperty( capture, CV_CAP_PROP_POS_AVI_RATIO, new_pos );
184         new_pos = cvGetCaptureProperty( capture, CV_CAP_PROP_POS_AVI_RATIO );
185         printf("Repositioning\n");
186         repositioning = 1;
187     }
188     else
189     {
190         new_pos = cvGetCaptureProperty( capture, CV_CAP_PROP_POS_AVI_RATIO );
191         video_pos->value(new_pos);
192         repositioning = 0;
193     }
194     old_pos = new_pos;
195     frame = cvQueryFrame( capture );
196
197     if( frame == 0 && is_avi )
198     {
199         cb_Stop(0,0);
200         return;
201     }
202
203     if( video_window && frame )
204     {
205         if( video_window->w() < frame->width || video_window->h() < frame->height )
206             root_window->size( (short)(frame->width + 40), (short)(frame->height + 150));
207
208         CvRect rect = { video_window->x(), video_window->y(),
209                         frame->width, frame->height };
210         
211         if( !video_image || video_image->width < rect.width ||
212             video_image->height < rect.height )
213         {
214             cvReleaseImage( &video_image );
215             video_image = cvCreateImage( cvSize( rect.width, rect.height ), 8, 3 );
216         }
217
218         cvSetImageROI( video_image, cvRect(0,0,rect.width, rect.height));
219         if( frame->origin == 1 )
220             cvFlip( frame, video_image, 0 );
221         else
222             cvCopy( frame, video_image, 0 );
223
224         DetectAndDrawFaces( video_image );
225         if( writer && is_recorded )
226         {
227             cvWriteToAVI( writer, video_image );
228         }
229         cvCvtColor( video_image, video_image, CV_RGB2BGR );
230
231         uchar* data = 0;
232         int step = 0;
233         CvSize size;
234         cvGetRawData( video_image, &data, &step, &size );
235
236         video_window->redraw();
237         fl_draw_image( (uchar*)data, video_window->x(), video_window->y(),
238                        size.width, size.height, 3, step );
239     }
240
241     if( started )
242     {
243         double cur_frame_stamp = get_time_accurate();
244         // update fps
245         if( fps < 0 )
246             fps = 1000/(cur_frame_stamp - prev_frame_stamp);
247         else
248             fps = (1-fps_alpha)*fps + fps_alpha*1000/(cur_frame_stamp - prev_frame_stamp);
249         prev_frame_stamp = cur_frame_stamp;
250         sprintf( fps_buffer, "FPS: %5.1f", fps );
251         fps_box->label( fps_buffer );
252         fps_box->redraw();
253         if( total_frames > 0 )
254         {
255             if( --total_frames == 0 )
256                 if( !is_loopy )
257                     cb_Exit(0,0);
258                 else
259                 {
260                     total_frames = total_frames0;
261                     cvSetCaptureProperty( capture, CV_CAP_PROP_POS_FRAMES, start_pos );
262                 }
263         }
264         Fl::add_timeout( timeout, get_next_frame, 0 );
265     }
266 }
267
268 static void cb_PauseResume( Fl_Widget*, void* )
269 {
270     video_button->deactivate();
271     cam_button->deactivate();
272     stop_button->activate();
273
274     if( !started )
275     {
276         started = 1;
277         play_button->label("@||");
278         play_button->activate();
279         play_button->redraw();
280         stop_button->activate();
281         stop_button->redraw();
282         record_button->activate();
283
284         video_window->redraw();
285         prev_frame_stamp = get_time_accurate();
286         Fl::add_timeout( timeout, get_next_frame, 0 );
287     }
288     else
289     {
290         started = 0;
291         play_button->label("@>");
292         play_button->redraw();
293     }
294 }
295
296
297 static void cb_Stop( Fl_Widget*, void* )
298 {
299     if( started )
300         cb_PauseResume(0,0);
301     if( is_recorded )
302         cb_StartStopRecord(0,0);
303     cvSetCaptureProperty( capture, CV_CAP_PROP_POS_AVI_RATIO, 0 );
304     video_button->activate();
305     cam_button->activate();
306     stop_button->deactivate();
307     record_button->deactivate();
308 }
309
310 static void cb_StartAVI( const char* newfile, int start_pos, int was_started )
311 {
312     if( newfile != 0 )
313     {
314         end_capture();
315         capture = cvCaptureFromAVI( newfile );
316         if( capture )
317         {
318             was_started = 1;
319             if( start_pos != 0 )
320                 cvSetCaptureProperty( capture, CV_CAP_PROP_POS_FRAMES, start_pos );
321         }
322         is_avi = 1;
323     }
324
325     if( was_started )
326         cb_PauseResume(0,0);
327 }
328
329
330 static void cb_Open( Fl_Widget*, void* )
331 {
332     char filename[1000];
333     filename[0] = '\0';
334     int was_started = 0;
335     
336     if( started )
337     {
338         was_started = 1;
339         cb_PauseResume(0,0);
340     }
341
342     char *newfile = fl_file_chooser("Open File?", "*", filename);
343     cb_StartAVI( newfile, 0, was_started );
344 }
345
346
347 static void cb_StartCam( Fl_Widget*, void* )
348 {
349     if( started )
350         cb_PauseResume(0,0);
351     end_capture();
352 #ifdef WIN32    
353     Sleep(10);
354 #endif    
355     is_avi = 0;
356     capture = cvCaptureFromCAM( -1 );
357     cb_PauseResume(0,0);
358 }
359
360
361 static void cb_StartStopRecord( Fl_Widget*, void* )
362 {
363     if( !is_recorded )
364     {
365         if( video_image )
366         {
367             writer = cvCreateAVIWriter( "c:\\test.avi", -1, 15, cvGetSize( video_image ));
368             if( writer )
369             {
370                 record_button->box( FL_DOWN_BOX );
371                 is_recorded = 1;
372             }
373         }
374     }
375     else
376     {
377         record_button->box( FL_UP_BOX );
378         cvReleaseVideoWriter( &writer );
379         is_recorded = 0;
380     }
381 }
382
383 static void cb_AutoRun( void* _aviname )
384 {
385     char* aviname = (char*)_aviname;
386     char* ext_pos = strrchr( aviname, '.' );
387     if( ext_pos )
388     {
389         char* colon_pos = strchr( ext_pos, ':' );
390         if( colon_pos )
391         {
392             *colon_pos = '\0';
393             start_pos = strtoul( colon_pos+1, &colon_pos, 10 );
394             if( colon_pos && *colon_pos == ':' )
395                 total_frames0 = total_frames = strtoul( colon_pos+1, &colon_pos, 10 );
396             if( colon_pos && *colon_pos == ':' )
397                 is_loopy = 1;
398         }
399     }
400
401     cb_StartAVI( aviname, start_pos, 0 );
402 }
403
404
405 static void cb_Exit( Fl_Widget*, void* )
406 {
407     cvReleaseCapture( &capture );
408     cvReleaseVideoWriter( &writer );
409     exit(0);
410 }
411
412 const int root_w = 400, root_h = 400;
413
414 int main( int argc, char **argv )
415 {
416     const char* facebaseopt="--facebase=";
417     char* classifierbase = 0;
418     char* aviname = 0;
419     int auto_run = 0;
420     
421     if( argc > 1 && argv[argc-1][0] != '-' )
422     {
423         aviname = argv[argc-1];
424         auto_run = 1;
425         argc--;
426     }
427     
428     if( argc > 1 && strncmp(argv[argc-1],facebaseopt,strlen(facebaseopt))==0 )
429     {
430         classifierbase=argv[argc-1] + strlen(facebaseopt);
431         argc--;
432     }
433     
434     if( !InitFaceDetect(classifierbase))
435     {
436         fprintf( stderr, "Could not locate face classifier base at %s\n"
437                          "Use --facebase=<classifier base path> option to specify the base\n",
438                  classifierbase );
439         return -1;
440     }
441         
442     cpu_freq = cvGetTickFrequency(); 
443     printf("Tick frequency (*10^-6): %g\n", cpu_freq );
444  
445     Fl_Window* w;
446     {
447         Fl_Window* o = root_window = new Fl_Window( root_w, root_h );
448         w = o;
449         {
450             Fl_Tabs* o = new Fl_Tabs( 10, 10, root_w - 20, root_h - 100 );
451             // camera tab
452             {
453                 Fl_Group* o = new Fl_Group( 10, 30, root_w - 20, root_h - 110, "Face Detection" );
454                 {
455                     VideoWindow* o = new VideoWindow( 15, 35, root_w - 30, root_h - 120 );
456                     video_window = o;
457                     o->box( FL_BORDER_BOX );
458                     o->color(0);
459                 }
460                 o->end();
461             }
462             o->end();
463             Fl_Group::current()->resizable(o);
464         }
465         {
466             const int bwidth = 30, bheight = 30;
467             play_button = new Fl_Button( 10, root_h - 35, bwidth, bheight, "@>" );
468             play_button->callback((Fl_Callback*)cb_PauseResume);
469             play_button->deactivate();
470             stop_button = new Fl_Button( 10 + bwidth, root_h - 35, bwidth, bheight, "@square" );
471             stop_button->callback((Fl_Callback*)cb_Stop);
472             stop_button->deactivate();
473             video_button = new Fl_Button( 10 + bwidth*2, root_h - 35, bwidth, bheight, "..." );
474             video_button->callback((Fl_Callback*)cb_Open);
475             cam_button = new Fl_Button( 10 + bwidth*3, root_h - 35, bwidth, bheight, "[o]" );
476             cam_button->callback((Fl_Callback*)cb_StartCam);
477             video_pos = new Fl_Value_Slider( 10 + bwidth*4 + 10, root_h - 35, 200, 20, "Position" );
478             video_pos->type( FL_HOR_NICE_SLIDER );
479             record_button = new Fl_Button( 10 + bwidth*4 + 230, root_h - 35, bwidth, bheight, "@circle" );
480             record_button->labelcolor(FL_RED);
481             record_button->callback((Fl_Callback*)cb_StartStopRecord );
482             record_button->deactivate();
483             fps_box = new Fl_Box( 10, root_h - 75, bwidth*4, bheight, "<No data>" );
484             fps_box->box( FL_DOWN_BOX );
485         }
486         o->end();
487     }
488     Fl::visual(FL_RGB);
489     w->show(argc, argv);
490     if( auto_run )
491         Fl::add_timeout( 0.1, cb_AutoRun, aviname );
492     Fl::run();
493     cb_Exit(0,0);
494     return 0;
495 }
496