Changeset 14


Ignore:
Timestamp:
03/19/2009 10:58:22 AM (11 years ago)
Author:
dave
Message:

Proper calibration system, better user interface and fixes

Location:
foam/trunk/simple-faceident
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • foam/trunk/simple-faceident/Makefile

    r1 r14  
    33CV_LINK_PATH = /usr/local/lib 
    44LIBS = -lcv -lcxcore -lhighgui 
     5CCFLAGS = -I$(CV_INCLUDE_PATH) -L$(CV_LINK_PATH) -ggdb -Wall -O3 -ffast-math -Wno-unused 
    56 
    67all: simple-faceident 
    78 
    89simple-faceident: main.o FaceBank.o ImageUtils.o 
    9         $(CC) -I$(CV_INCLUDE_PATH) -L$(CV_LINK_PATH) $(LIBS) main.o FaceBank.o ImageUtils.o -o simple-faceident 
     10        $(CC) $(CCFLAGS) $(LIBS) main.o FaceBank.o ImageUtils.o -o simple-faceident 
    1011 
    1112main.o: src/main.cpp 
    12         $(CC) -I$(CV_INCLUDE_PATH) -c src/main.cpp 
     13        $(CC) $(CCFLAGS) -c src/main.cpp 
    1314 
    1415FaceBank.o: src/FaceBank.cpp 
    15         $(CC) -I$(CV_INCLUDE_PATH) -c src/FaceBank.cpp 
     16        $(CC) $(CCFLAGS) -c src/FaceBank.cpp 
    1617 
    1718ImageUtils.o: src/ImageUtils.cpp 
    18         $(CC) -I$(CV_INCLUDE_PATH) -c src/ImageUtils.cpp 
     19        $(CC) $(CCFLAGS) -c src/ImageUtils.cpp 
    1920 
    2021clean: 
  • foam/trunk/simple-faceident/README

    r1 r14  
    22-------------------------- 
    33 
    4 Just uses OpenCV's face finder and records faces it's seen for 100 frames  
    5 (tweakable at the top of main.cpp). After that it returns the id of the  
    6 nearest match in it's database of faces. The max number of faces it stores is  
    7 also set at the top of main.cpp. 
    8   
     4Uses OpenCV's face finder to record and detect identity. 
     5 
     6Use the number keys to train the system against different individuals. The idea  
     7is that one by one, individuals show their faces, after which the system will  
     8recognise who is who in the camera's view. The program will avoid recording  
     9faces which are too similar to existing recorded ones. Press 'd' to switch to  
     10switch off the training and just detect against existing faces. The boxes  
     11around the faces display the id and the confidence value of the match. 
     12 
    913Requires: OpenCV 
    1014Written and tested on Linux, but it should work in Windows too. 
     
    1822Todo: 
    1923* Command line parameters 
    20 * Calibration mode on a keypress 
    21 * Play with better calibration modes 
    2224* YARP interface 
    23 * Use 'least recently detected' to remove faces from the database 
    2425* Try normalised cross correlation instead of image differencing 
    2526 
  • foam/trunk/simple-faceident/src/FaceBank.cpp

    r1 r14  
    5656///////////////////////////////////////////////////////////////////////////////// 
    5757         
    58 FaceBank::FaceBank(unsigned int MaxFaces, unsigned int FaceWidth, unsigned int FaceHeight, float ErrorThresh) :  
    59 m_MaxFaces(MaxFaces),  
     58FaceBank::FaceBank(unsigned int FaceWidth, unsigned int FaceHeight, float ErrorThresh) :  
    6059m_FaceWidth(FaceWidth), 
    6160m_FaceHeight(FaceHeight), 
     
    6665FaceBank::~FaceBank()  
    6766{        
    68         for(list<Face*>::iterator i=m_FaceList.begin(); i!=m_FaceList.end(); ++i) 
    69         { 
    70                 delete *i; 
    71         } 
     67        Clear(); 
    7268} 
    7369 
    74 float FaceBank::Identify(IplImage *face, unsigned int &ID, bool learn) 
     70void FaceBank::Clear() 
     71{ 
     72        for(map<unsigned int,Face*>::iterator i=m_FaceMap.begin(); i!=m_FaceMap.end(); ++i) 
     73        { 
     74                delete i->second; 
     75        } 
     76        m_FaceMap.clear(); 
     77} 
     78 
     79float FaceBank::Suggest(IplImage *face, unsigned int ID) 
    7580{ 
    7681        IplImage *faceresized = cvCreateImage(cvSize(m_FaceWidth,m_FaceHeight),IPL_DEPTH_8U, face->nChannels); 
    7782        cvResize(face, faceresized, CV_INTER_LINEAR ); 
    78         // subtract the mean as an attempt to deal  
    79         // with global lighting changes 
     83        // Subtract the mean as an attempt to deal with global lighting changes 
     84        SubMean(faceresized); 
     85         
     86        // Get this face 
     87        map<unsigned int,Face*>::iterator i=m_FaceMap.find(ID); 
     88         
     89        // If it's the first time we've seen this face then record it 
     90        if (i==m_FaceMap.end()) 
     91        {                
     92                // Check it doesn't look like any we have already recorded 
     93                unsigned int checkid=0; 
     94                if (Identify(faceresized,checkid)>0) 
     95                { 
     96                        cerr<<"We've already seen this face: "<<checkid<<endl;   
     97                        return 0; 
     98                } 
     99 
     100                cerr<<"new face "<<ID<<endl; 
     101                m_FaceMap[ID]=new Face(faceresized); 
     102                return 1; 
     103        } 
     104         
     105        // Does this face look like the one we already have for this id? 
     106        float error = Diff(faceresized,i->second->m_Image); 
     107 
     108        if (error<m_ErrorThresh)  
     109        { 
     110                cerr<<"adding to face "<<ID<<endl; 
     111                // Blend this face into the one we have already 
     112                i->second->Learn(faceresized,0.2); 
     113                cvReleaseImage(&faceresized); 
     114                ID=i->first; 
     115                return 1-error; 
     116        } 
     117         
     118        cerr<<"false positive? "<<error<<" "<<ID<<endl; 
     119         
     120        return 0; 
     121} 
     122 
     123float FaceBank::Identify(IplImage *face, unsigned int &ID) 
     124{ 
     125        IplImage *faceresized = cvCreateImage(cvSize(m_FaceWidth,m_FaceHeight),IPL_DEPTH_8U, face->nChannels); 
     126        cvResize(face, faceresized, CV_INTER_LINEAR ); 
     127        // Subtract the mean as an attempt to deal with global lighting changes 
    80128        SubMean(faceresized); 
    81129 
    82         // the first face found? 
    83         if (m_FaceList.empty()) 
     130        // No faces recorded? 
     131        if (m_FaceMap.empty()) 
    84132        { 
    85                 m_FaceList.push_back(new Face(faceresized)); 
    86133                return 0; 
    87134        } 
     
    91138        Face* bestface=NULL; 
    92139         
    93         // look for the lowest error in the list of faces 
    94         int c=0; 
    95         for(list<Face*>::iterator i=m_FaceList.begin(); i!=m_FaceList.end(); ++i) 
     140        // look for the lowest error in the map of faces 
     141        for(map<unsigned int,Face*>::iterator i=m_FaceMap.begin(); i!=m_FaceMap.end(); ++i) 
    96142        { 
    97                 float tmp = Diff(faceresized,(*i)->m_Image); 
     143                float tmp = Diff(faceresized,i->second->m_Image); 
    98144                if (tmp<error) 
    99145                { 
    100146                        error=tmp; 
    101                         best=c; 
    102                         bestface=*i; 
     147                        best=i->first; 
     148                        bestface=i->second; 
    103149                } 
    104                 c++; 
    105150        } 
    106151         
    107152        // if the error is less than the threshold, return the id 
    108         if (!learn || error<m_ErrorThresh)  
     153        if (error<m_ErrorThresh) 
    109154        { 
    110155                // blend this face into the one we have already 
    111                 bestface->Learn(faceresized,0.5); 
     156                bestface->Learn(faceresized,0); 
    112157                cvReleaseImage(&faceresized); 
    113158                ID=best; 
    114                 return error; 
     159                return 1-error; 
    115160        } 
    116161         
    117         // we have a new face! 
    118         // if there is no space, drop the oldest face 
    119         // todo: drop least recently detected instead 
    120         if (m_FaceList.size()>=m_MaxFaces) 
    121         { 
    122                 delete *m_FaceList.begin(); 
    123                 m_FaceList.pop_front();  
    124         } 
    125                  
    126         cerr<<"new face found"<<endl; 
    127         m_FaceList.push_back(new Face(faceresized)); 
    128         return 1; 
     162        cerr<<"unrecognised face"<<endl; 
     163         
     164        return 0; 
    129165} 
  • foam/trunk/simple-faceident/src/FaceBank.h

    r1 r14  
    1616 
    1717#include <iostream> 
    18 #include <list> 
     18#include <map> 
    1919#include <assert.h> 
    2020 
     
    4747{ 
    4848public: 
    49         // MaxFaces is the number of faces we want to restrict the facebank to storing,  
    5049        // FaceWidth and FaceHeight are the size for the internal stored image of the face for  
    5150        // comparison, ErrorThresh is the error amount which will trigger a new face to be stored 
    52         FaceBank(unsigned int MaxFaces, unsigned int FaceWidth, unsigned int FaceHeight, float ErrorThresh); 
     51        FaceBank(unsigned int FaceWidth, unsigned int FaceHeight, float ErrorThresh); 
    5352         
    5453        ~FaceBank(); 
     54 
     55        void Clear(); 
    5556         
    56         // gives the id, given a new face, and returns the confidence 
    57         // if it's a new face it returns 1 and stores the id, copying the face image 
    58         float Identify(IplImage *face, unsigned int &ID, bool learn); 
     57        // Learn this face, the face may be a false positive, so we'll discard the  
     58        // suggestion if we've seen it before, and the error is greater than ErrorThresh 
     59        float Suggest(IplImage *face, unsigned int ID); 
     60 
     61        // Gives the id, given a face, and returns the confidence 
     62        float Identify(IplImage *face, unsigned int &ID); 
    5963         
    60         std::list<Face*> &GetFaceList() { return m_FaceList; } 
     64        std::map<unsigned int, Face*> &GetFaceMap() { return m_FaceMap; } 
    6165         
     66        unsigned int GetFaceWidth() { return m_FaceWidth; } 
     67        unsigned int GetFaceHeight() { return m_FaceHeight; } 
     68 
    6269private:         
    6370 
    64         unsigned int m_MaxFaces; 
    6571        unsigned int m_FaceWidth; 
    6672        unsigned int m_FaceHeight; 
    6773        float m_ErrorThresh; 
    6874         
    69         std::list<Face*> m_FaceList; 
     75        std::map<unsigned int, Face*> m_FaceMap; 
    7076}; 
    7177 
  • foam/trunk/simple-faceident/src/ImageUtils.cpp

    r1 r14  
    3030        cvResetImageROI(image); 
    3131        return result; 
     32} 
     33 
     34///////////////////////////////////////////////////////////// 
     35// paste an image over the top of another 
     36 
     37void BlitImage(IplImage *srcimage, IplImage *dstimage, CvPoint pos) 
     38{ 
     39        CvSize size = cvGetSize(srcimage); 
     40        CvSize dstsize = cvGetSize(dstimage); 
     41         
     42        for(int y=0; y<size.height; y++) 
     43        { 
     44        for(int x=0; x<size.width; x++) 
     45                { 
     46                        if (x+pos.x>0 && x+pos.x<dstsize.width && 
     47                                y+pos.y>0 && y+pos.y<dstsize.height) 
     48                        { 
     49                cvSet2D(dstimage,y+pos.y,x+pos.x,cvGet2D(srcimage,y,x)); 
     50                        } 
     51                } 
     52        } 
    3253} 
    3354 
  • foam/trunk/simple-faceident/src/ImageUtils.h

    r1 r14  
    2020#define IMAGE_UTILS 
    2121 
     22// I need to sort this out before it grows too much... 
     23 
    2224IplImage* SubImage(IplImage *image, CvRect roi); 
     25void BlitImage(IplImage *srcimage, IplImage *dstimage, CvPoint pos); 
    2326void SubMean(IplImage *image); 
    2427float Diff(IplImage *imagea, IplImage *imageb); 
  • foam/trunk/simple-faceident/src/main.cpp

    r1 r14  
    5757// These are the tweakable bits - see comments in FaceBank.h 
    5858 
    59 const int nfaces = 3; 
    60 FaceBank facebank(nfaces, 30, 40, 0.05);  
     59FaceBank facebank(30, 40, 0.15);  
    6160 
    6261// number of frames to detect new faces for 
    63 int calibrate_frames = 100; 
     62int calibrate_frames = 200; 
    6463 
    6564// show all faces currently detected  
    66 //#define SHOW_FACES 
     65#define SHOW_FACES 
    6766 
    6867//#define SAVE_FRAMES 
    6968 
    7069// globals 
    71 int frame=0; 
    7270bool learn=true; 
     71int facenum=0; 
     72int framenum=0; 
    7373 
    7474////////////////////////////////////////////////////////// 
     
    140140    cvNamedWindow( "result", 1 ); 
    141141         
    142         #ifdef SHOW_FACES 
    143         char name[256]; 
    144         for (i=0; i<nfaces; i++) 
    145         { 
    146                 sprintf(name,"face%i",i); 
    147         cvNamedWindow(name, 1); 
    148         } 
    149         #endif 
    150          
    151142    if( capture ) 
    152143    { 
     
    159150                break; 
    160151            if( !frame_copy ) 
    161                 frame_copy = cvCreateImage( cvSize(frame->width,frame->height), 
     152                frame_copy = cvCreateImage( cvSize(frame->width/2,frame->height/2), 
    162153                                            IPL_DEPTH_8U, frame->nChannels ); 
    163154            if( frame->origin == IPL_ORIGIN_TL ) 
    164                 cvCopy( frame, frame_copy, 0 ); 
     155                                cvResize(frame, frame_copy, CV_INTER_LINEAR ); 
     156                //cvCopy( frame, frame_copy, 0 ); 
    165157            else 
    166158                cvFlip( frame, frame_copy, 0 ); 
    167159             
    168160            detect_and_draw( frame_copy ); 
    169  
    170             if( cvWaitKey( 10 ) >= 0 ) 
    171                 goto _cleanup_; 
    172         } 
    173  
    174         cvWaitKey(0); 
     161        } 
     162 
    175163_cleanup_: 
    176164        cvReleaseImage( &frame_copy ); 
     
    182170        { 
    183171            detect_and_draw( image ); 
    184             cvWaitKey(0); 
    185172            cvReleaseImage( &image ); 
    186173        } 
     
    203190                    if( image ) 
    204191                    { 
    205                         detect_and_draw( image ); 
    206                         c = cvWaitKey(0); 
    207                         if( c == 27 || c == 'q' || c == 'Q' ) 
    208                             break; 
     192                        detect_and_draw( image );                         
    209193                        cvReleaseImage( &image ); 
    210194                    } 
     
    216200     
    217201    cvDestroyWindow("result"); 
    218         #ifdef SHOW_FACES 
    219         for (i=0; i<nfaces; i++) 
    220         { 
    221                 sprintf(name,"face%i",i); 
    222         cvDestroyWindow(name); 
    223         } 
    224         #endif 
    225  
    226202    return 0; 
    227203} 
    228  
    229204 
    230205void detect_and_draw( IplImage* img ) 
     
    244219    IplImage *gray, *small_img; 
    245220    int j; 
     221        CvSize imgsize = cvGetSize(img); 
    246222 
    247223    gray = cvCreateImage( cvSize(img->width,img->height), 8, 1 ); 
     
    256232        CvFont font; 
    257233    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 ); 
    258240         
    259241    if( cascade ) 
     
    271253        //printf( "detection time = %gms\n", t/((double)cvGetTickFrequency()*1000.) ); 
    272254                 
    273                 if (frame==calibrate_frames)  
     255                framenum++; 
     256                if (framenum==100)  
    274257                { 
    275                         learn=false; 
    276                         cerr<<"finished learning new faces"<<endl; 
     258                        cerr<<"next face"<<endl; 
     259                        facenum++;  
    277260                } 
    278261                 
    279                 frame++; 
    280                  
     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                                 
    281289        for(int i = 0; i < (faces ? faces->total : 0); i++ ) 
    282290        { 
     
    285293                         
    286294                        unsigned int ID=999; 
     295                        float error=0; 
    287296                        // get the face area as a sub image 
    288297                        IplImage *face = SubImage(img, *r); 
    289298                        // pass it into the face bank  
    290                         facebank.Identify(face,ID,learn); 
     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                         
    291309                        cvReleaseImage(&face); 
    292                          
     310                        CvScalar color = colors[ID%8]; 
     311 
    293312                        // if it's recognised the face (should really check the confidence) 
    294313                        if (ID!=999) 
    295314                        { 
    296315                                char s[32]; 
    297                                 sprintf(s,"%d",ID); 
    298                                 cvPutText(img, s, cvPoint(r->x,r->y+25), &font, CV_RGB(1,1,1)); 
     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); 
    299322                        } 
    300323 
    301             CvScalar color = colors[ID%8]; 
    302324                        cvRectangle(img, cvPoint(r->x,r->y), cvPoint(r->x+r->width,r->y+r->height), color); 
    303325        } 
    304326    } 
    305327 
     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         
    306359    cvShowImage( "result", img ); 
    307360 
     
    311364        cvSaveImage(name,img); 
    312365        #endif 
    313          
    314         #ifdef SHOW_FACES 
    315         char name[256]; 
    316         int i=0; 
    317         for(list<Face*>::iterator ii=facebank.GetFaceList().begin();  
    318                 ii!=facebank.GetFaceList().end(); ++ii) 
    319         { 
    320                 sprintf(name,"face%i",i); 
    321         cvShowImage(name,(*ii)->m_Image); 
    322                 i++; 
    323         } 
    324         #endif 
    325366     
    326367        cvReleaseImage( &gray ); 
Note: See TracChangeset for help on using the changeset viewer.