source: foam/trunk/simple-faceident/src/main.cpp @ 14

Revision 14, 10.8 KB checked in by dave, 11 years ago (diff)

Proper calibration system, better user interface and fixes

Line 
1// Copyright (C) 2009 foam
2//
3// This program is free software; you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation; either version 2 of the License, or
6// (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11// GNU General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License
14// along with this program; if not, write to the Free Software
15// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16
17// this is a hacked version of the opencv face detection sample code
18
19#include "cv.h"
20#include "highgui.h"
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <assert.h>
26#include <math.h>
27#include <float.h>
28#include <limits.h>
29#include <time.h>
30#include <ctype.h>
31
32#include "FaceBank.h"
33#include "ImageUtils.h"
34
35using namespace std;
36
37#ifdef _EiC
38#define WIN32
39#endif
40
41static CvMemStorage* storage = 0;
42static CvHaarClassifierCascade* cascade = 0;
43static CvHaarClassifierCascade* nested_cascade = 0;
44int use_nested_cascade = 0;
45
46void detect_and_draw( IplImage* image );
47
48const char* cascade_name =
49    "/usr/local/share/opencv/haarcascades/haarcascade_frontalface_alt.xml";
50/*    "haarcascade_profileface.xml";*/
51const char* nested_cascade_name =
52    "/usr/local/share/opencv/haarcascades/haarcascade_eye_tree_eyeglasses.xml";
53//    "../../data/haarcascades/haarcascade_eye.xml";
54double scale = 1;
55
56//////////////////////////////////////////////////////////
57// These are the tweakable bits - see comments in FaceBank.h
58
59FaceBank facebank(30, 40, 0.15);
60
61// number of frames to detect new faces for
62int calibrate_frames = 200;
63
64// show all faces currently detected
65#define SHOW_FACES
66
67//#define SAVE_FRAMES
68
69// globals
70bool learn=true;
71int facenum=0;
72int framenum=0;
73
74//////////////////////////////////////////////////////////
75
76int main( int argc, char** argv )
77{
78    CvCapture* capture = 0;
79    IplImage *frame, *frame_copy = 0;
80    IplImage *image = 0;
81    const char* scale_opt = "--scale=";
82    int scale_opt_len = (int)strlen(scale_opt);
83    const char* cascade_opt = "--cascade=";
84    int cascade_opt_len = (int)strlen(cascade_opt);
85    const char* nested_cascade_opt = "--nested-cascade";
86    int nested_cascade_opt_len = (int)strlen(nested_cascade_opt);
87    int i;
88    const char* input_name = 0;
89
90    for( i = 1; i < argc; i++ )
91    {
92        if( strncmp( argv[i], cascade_opt, cascade_opt_len) == 0 )
93            cascade_name = argv[i] + cascade_opt_len;
94        else if( strncmp( argv[i], nested_cascade_opt, nested_cascade_opt_len ) == 0 )
95        {
96            if( argv[i][nested_cascade_opt_len] == '=' )
97                nested_cascade_name = argv[i] + nested_cascade_opt_len + 1;
98            nested_cascade = (CvHaarClassifierCascade*)cvLoad( nested_cascade_name, 0, 0, 0 );
99            if( !nested_cascade )
100                fprintf( stderr, "WARNING: Could not load classifier cascade for nested objects\n" );
101        }
102        else if( strncmp( argv[i], scale_opt, scale_opt_len ) == 0 )
103        {
104            if( !sscanf( argv[i] + scale_opt_len, "%lf", &scale ) || scale < 1 )
105                scale = 1;
106        }
107        else if( argv[i][0] == '-' )
108        {
109            fprintf( stderr, "WARNING: Unknown option %s\n", argv[i] );
110        }
111        else
112            input_name = argv[i];
113    }
114
115    cascade = (CvHaarClassifierCascade*)cvLoad( cascade_name, 0, 0, 0 );
116
117    if( !cascade )
118    {
119        fprintf( stderr, "ERROR: Could not load classifier cascade\n" );
120        fprintf( stderr,
121        "Usage: facedetect [--cascade=\"<cascade_path>\"]\n"
122        "   [--nested-cascade[=\"nested_cascade_path\"]]\n"
123        "   [--scale[=<image scale>\n"
124        "   [filename|camera_index]\n" );
125        return -1;
126    }
127    storage = cvCreateMemStorage(0);
128   
129    if( !input_name || (isdigit(input_name[0]) && input_name[1] == '\0') )
130        capture = cvCaptureFromCAM( !input_name ? 0 : input_name[0] - '0' );
131    else if( input_name )
132    {
133        image = cvLoadImage( input_name, 1 );
134        if( !image )
135            capture = cvCaptureFromAVI( input_name );
136    }
137    else
138        image = cvLoadImage( "lena.jpg", 1 );
139
140    cvNamedWindow( "result", 1 );
141       
142    if( capture )
143    {
144        for(;;)
145        {
146            if( !cvGrabFrame( capture ))
147                break;
148            frame = cvRetrieveFrame( capture );
149            if( !frame )
150                break;
151            if( !frame_copy )
152                frame_copy = cvCreateImage( cvSize(frame->width/2,frame->height/2),
153                                            IPL_DEPTH_8U, frame->nChannels );
154            if( frame->origin == IPL_ORIGIN_TL )
155                                cvResize(frame, frame_copy, CV_INTER_LINEAR );
156                //cvCopy( frame, frame_copy, 0 );
157            else
158                cvFlip( frame, frame_copy, 0 );
159           
160            detect_and_draw( frame_copy );
161        }
162
163_cleanup_:
164        cvReleaseImage( &frame_copy );
165        cvReleaseCapture( &capture );
166    }
167    else
168    {
169        if( image )
170        {
171            detect_and_draw( image );
172            cvReleaseImage( &image );
173        }
174        else if( input_name )
175        {
176            // assume it is a text file containing the
177            //   list of the image filenames to be processed - one per line
178            FILE* f = fopen( input_name, "rt" );
179            if( f )
180            {
181                char buf[1000+1];
182                while( fgets( buf, 1000, f ) )
183                {
184                    int len = (int)strlen(buf), c;
185                    while( len > 0 && isspace(buf[len-1]) )
186                        len--;
187                    buf[len] = '\0';
188                    printf( "file %s\n", buf );
189                    image = cvLoadImage( buf, 1 );
190                    if( image )
191                    {
192                        detect_and_draw( image );                       
193                        cvReleaseImage( &image );
194                    }
195                }
196                fclose(f);
197            }
198        }
199    }
200   
201    cvDestroyWindow("result");
202    return 0;
203}
204
205void detect_and_draw( IplImage* img )
206{
207    static CvScalar colors[] =
208    {
209        {{0,0,255}},
210        {{0,128,255}},
211        {{0,255,255}},
212        {{0,255,0}},
213        {{255,128,0}},
214        {{255,255,0}},
215        {{255,0,0}},
216        {{255,0,255}}
217    };
218
219    IplImage *gray, *small_img;
220    int j;
221        CvSize imgsize = cvGetSize(img);
222
223    gray = cvCreateImage( cvSize(img->width,img->height), 8, 1 );
224    small_img = cvCreateImage( cvSize( cvRound (img->width/scale),
225                         cvRound (img->height/scale)), 8, 1 );
226
227    cvCvtColor( img, gray, CV_BGR2GRAY );
228    cvResize( gray, small_img, CV_INTER_LINEAR );
229    cvEqualizeHist( small_img, small_img );
230    cvClearMemStorage( storage );
231
232        CvFont font;
233    cvInitFont( &font, CV_FONT_HERSHEY_PLAIN, 2, 2, 0, 1, CV_AA );
234
235        CvFont infofont;
236    cvInitFont( &infofont, CV_FONT_HERSHEY_PLAIN, 1, 1, 0, 1, CV_AA );
237
238        CvFont helpfont;
239    cvInitFont( &helpfont, CV_FONT_HERSHEY_PLAIN, 0.5, 0.5, 0, 1, CV_AA );
240       
241    if( cascade )
242    {
243        double t = (double)cvGetTickCount();
244        CvSeq* faces = cvHaarDetectObjects( small_img, cascade, storage,
245                                            1.1, 2, 0
246                                            //|CV_HAAR_FIND_BIGGEST_OBJECT
247                                            //|CV_HAAR_DO_ROUGH_SEARCH
248                                            |CV_HAAR_DO_CANNY_PRUNING
249                                            //|CV_HAAR_SCALE_IMAGE
250                                            ,
251                                            cvSize(30, 30) );
252        t = (double)cvGetTickCount() - t;
253        //printf( "detection time = %gms\n", t/((double)cvGetTickFrequency()*1000.) );
254               
255                framenum++;
256                if (framenum==100)
257                {
258                        cerr<<"next face"<<endl;
259                        facenum++;
260                }
261               
262                if (framenum==200)
263                {
264                        cerr<<"stopped learning"<<endl;
265                        cerr<<facebank.GetFaceMap().size()<<" faces recorded"<<endl;
266                        learn=false; 
267                }
268               
269               
270                int key=cvWaitKey(10);
271               
272                switch (key)
273                {
274                        case 'd': learn=false; break;
275                        case '1': facenum=1; learn=true; break;
276                        case '2': facenum=2; learn=true; break;
277                        case '3': facenum=3; learn=true; break;
278                        case '4': facenum=4; learn=true; break;
279                        case '5': facenum=5; learn=true; break;
280                        case '6': facenum=6; learn=true; break;
281                        case '7': facenum=7; learn=true; break;
282                        case '8': facenum=8; learn=true; break;
283                        case '9': facenum=9; learn=true; break;
284                        case '0': facenum=0; learn=true; break;
285                        case 'c': facebank.Clear(); break;
286                }
287               
288                               
289        for(int i = 0; i < (faces ? faces->total : 0); i++ )
290        {
291            CvRect* r = (CvRect*)cvGetSeqElem( faces, i );
292            CvMat small_img_roi;
293                       
294                        unsigned int ID=999;
295                        float error=0;
296                        // get the face area as a sub image
297                        IplImage *face = SubImage(img, *r);
298                        // pass it into the face bank
299                        if (learn)
300                        {
301                                error=facebank.Suggest(face,facenum);
302                                ID=facenum;
303                        }
304                        else
305                        {       
306                                error=facebank.Identify(face,ID);
307                        }
308                       
309                        cvReleaseImage(&face);
310                        CvScalar color = colors[ID%8];
311
312                        // if it's recognised the face (should really check the confidence)
313                        if (ID!=999)
314                        {
315                                char s[32];
316                                sprintf(s,"%d %0.2f",ID,error);
317                                cvPutText(img, s, cvPoint(r->x,r->y+25), &font, color);
318                                int x=(facebank.GetFaceWidth()+1)*ID;
319                                int y=imgsize.height-facebank.GetFaceHeight();
320                                cvLine(img, cvPoint(r->x+r->width/2,r->y+r->height/2),
321                                        cvPoint(x+facebank.GetFaceWidth()/2,y+facebank.GetFaceHeight()/2), color);
322                        }
323
324                        cvRectangle(img, cvPoint(r->x,r->y), cvPoint(r->x+r->width,r->y+r->height), color);
325        }
326    }
327
328        char info[256];
329        if (learn)
330        {
331                snprintf(info,256,"learning user %d",facenum);
332        }
333        else
334        {
335                snprintf(info,256,"detecting faces");
336        }
337        cvPutText(img, info, cvPoint(20,30), &infofont, CV_RGB(0,0,0));
338
339        snprintf(info,256,"keys:");
340        cvPutText(img, info, cvPoint(20,50), &helpfont, CV_RGB(0,0,0));
341        snprintf(info,256,"number key 0-9 : learn face");
342        cvPutText(img, info, cvPoint(20,60), &helpfont, CV_RGB(0,0,0));
343        snprintf(info,256,"'d' : face detect mode");
344        cvPutText(img, info, cvPoint(20,70), &helpfont, CV_RGB(0,0,0));
345        snprintf(info,256,"'c' : clear all faces");
346        cvPutText(img, info, cvPoint(20,80), &helpfont, CV_RGB(0,0,0));
347
348
349        #ifdef SHOW_FACES
350        for(map<unsigned int,Face*>::iterator ii=facebank.GetFaceMap().begin();
351                ii!=facebank.GetFaceMap().end(); ++ii)
352        {
353                int x=(facebank.GetFaceWidth()+1)*ii->first;
354                int y=imgsize.height-facebank.GetFaceHeight();
355                BlitImage(ii->second->m_Image,img,cvPoint(x,y));
356        }
357        #endif
358       
359    cvShowImage( "result", img );
360
361        #ifdef SAVE_FRAMES
362        char name[256];
363        sprintf(name,"out-%0.4d.jpg",frame);
364        cvSaveImage(name,img);
365        #endif
366   
367        cvReleaseImage( &gray );
368    cvReleaseImage( &small_img );
369}
Note: See TracBrowser for help on using the repository browser.