source: foam/trunk/simple-faceident/src/FaceBank.cpp @ 76

Revision 76, 6.0 KB checked in by dave, 12 years ago (diff)

changed file format to xml, experimental multiple image/face matching, better makefile

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#include "FaceBank.h"
18#include "ImageUtils.h"
19#include "highgui.h"
20#include "tinyxml.h"
21
22#include <vector>
23
24#ifdef WIN32
25#include <string>
26#define snprintf _snprintf
27#endif
28
29using namespace std;
30
31/////////////////////////////////////////////////////////////////////////////////
32       
33FaceBank::FaceBank(unsigned int FaceWidth, unsigned int FaceHeight, float ErrorThresh) :
34m_FaceWidth(FaceWidth),
35m_FaceHeight(FaceHeight),
36m_ErrorThresh(ErrorThresh),
37m_MultiFaceImages(false)
38{
39}
40
41FaceBank::~FaceBank()
42{       
43        Clear();
44}
45
46void FaceBank::Clear()
47{
48        for(map<unsigned int,Face*>::iterator i=m_FaceMap.begin(); i!=m_FaceMap.end(); ++i)
49        {
50                delete i->second;
51        }
52        m_FaceMap.clear();
53}
54
55float FaceBank::Suggest(IplImage *face, unsigned int ID)
56{
57        IplImage *faceresized = cvCreateImage(cvSize(m_FaceWidth,m_FaceHeight),IPL_DEPTH_8U, face->nChannels);
58        cvResize(face, faceresized, CV_INTER_LINEAR );
59        // Subtract the mean as an attempt to deal with global lighting changes
60        SubMean(faceresized);
61       
62        // Get this face
63        map<unsigned int,Face*>::iterator i=m_FaceMap.find(ID);
64       
65        // If it's the first time we've seen this face then record it
66        if (i==m_FaceMap.end())
67        {               
68                // Check it doesn't look like any we have already recorded
69                unsigned int checkid=0;
70                int imagenum=-1;
71                if (Identify(faceresized,checkid,imagenum)>0)
72                {
73                        cerr<<"We've already seen this face: "<<checkid<<":"<<imagenum<<endl;   
74                        return 0;
75                }
76
77                cerr<<"new face "<<ID<<endl;
78                m_FaceMap[ID]=new Face(faceresized);
79                return 1;
80        }
81       
82        // Does this face look like the one we already have for this id?
83        int facenum;
84        float error = i->second->FindSimilar(faceresized,facenum);
85
86        if (error<m_ErrorThresh)
87        {
88                //cerr<<"adding to face:"<<ID<<" image:"<<facenum<<endl;
89                // Blend this face into the one we have already
90                i->second->Learn(faceresized,0.2,facenum);
91                cvReleaseImage(&faceresized);
92                ID=i->first;
93                return 1-error;
94        }
95       
96        if (m_MultiFaceImages)
97        {       
98                // Does it look like any we have already recorded for any face?
99                unsigned int checkid=0;
100                int imagenum=-1;
101                if (Identify(faceresized,checkid,imagenum)>0)
102                {
103                        cerr<<"We've already seen this face: "<<checkid<<":"<<imagenum<<endl;   
104                        return 0;
105                }       
106               
107                cerr<<"too different - adding new image to face "<<error<<" "<<ID<<endl;
108                i->second->AddImage(faceresized);
109        }
110       
111        return 0;
112}
113
114float FaceBank::Identify(IplImage *face, unsigned int &ID, int &imagenum)
115{
116        IplImage *faceresized = cvCreateImage(cvSize(m_FaceWidth,m_FaceHeight),IPL_DEPTH_8U, face->nChannels);
117        cvResize(face, faceresized, CV_INTER_LINEAR );
118        // Subtract the mean as an attempt to deal with global lighting changes
119        SubMean(faceresized);
120
121        // No faces recorded?
122        if (m_FaceMap.empty())
123        {
124                return 0;
125        }
126       
127        float error=FLT_MAX;
128        unsigned int best=0;
129        Face* bestface=NULL;
130        imagenum=-1;
131       
132        // look for the lowest error in the map of faces
133        for(map<unsigned int,Face*>::iterator i=m_FaceMap.begin(); i!=m_FaceMap.end(); ++i)
134        {
135                int similarfacenum;
136                float tmp = i->second->FindSimilar(faceresized,similarfacenum);
137                if (tmp<error)
138                {
139                        error=tmp;
140                        best=i->first;
141                        bestface=i->second;
142                        imagenum=similarfacenum;
143                }
144        }
145       
146        // if the error is less than the threshold, return the id
147        if (error<m_ErrorThresh)
148        {
149                // blend this face into the one we have already
150                bestface->Learn(faceresized,0,imagenum);
151                cvReleaseImage(&faceresized);
152                ID=best;
153                return 1-error;
154        }
155       
156        cerr<<"unrecognised face"<<endl;
157       
158        return 0;
159}
160
161void FaceBank::Save(const std::string &filename) const
162{
163        char fn[256];
164        snprintf(fn,256,"%s.xml",filename.c_str());
165        FILE *f=fopen(fn,"w");
166       
167        if (f==NULL)
168        {
169                cerr<<"could not open file for saving: "<<filename<<endl;
170                return;
171        }
172       
173        TiXmlElement facebank("facebank");
174        facebank.SetAttribute("version", 1);
175
176        for(map<unsigned int,Face*>::const_iterator i=m_FaceMap.begin(); i!=m_FaceMap.end(); ++i)
177        {
178                TiXmlElement face("face");
179                face.SetAttribute("id", i->first);
180                int imagenum=0;
181               
182                for(vector<IplImage *>::iterator im=i->second->m_ImageVec.begin();
183                        im!=i->second->m_ImageVec.end(); im++)
184                {
185                        TiXmlElement image("image");
186                        char fn[256];
187                        snprintf(fn,256,"%s-id%04d-image%04d.png",filename.c_str(),i->first,imagenum);
188                       
189                        cvSaveImage(fn,*im);
190                       
191                        image.SetAttribute("filename", fn);
192                        face.InsertEndChild(image);
193                        imagenum++;
194                }
195               
196                facebank.InsertEndChild(face);
197        }
198
199        facebank.Print(f,0);
200        fclose(f);
201}
202
203void FaceBank::Load(const std::string &filename)
204{
205        Clear();
206        char fn[256];
207        snprintf(fn,256,"%s.xml",filename.c_str());
208       
209        TiXmlDocument doc(fn);
210        if (!doc.LoadFile())
211        {
212                cerr<<"could not load "<<fn<<" error:"<<doc.ErrorDesc()<<endl;
213                return;
214        }
215       
216        TiXmlNode* root = doc.FirstChild("facebank");
217        if(root!=NULL)
218        {
219                // loop over faces
220                TiXmlNode* face = root->FirstChild();
221                while(face!=NULL)
222                {       
223                        TiXmlElement* faceelem = face->ToElement();
224                        if(faceelem)
225                        {
226                                unsigned int ID = atoi(faceelem->Attribute("id"));
227                                int count=0;
228                                // loop over images
229                                TiXmlNode* image = face->FirstChild();
230                                while(image!=NULL)
231                                {
232                                        TiXmlElement* imageelem = image->ToElement();
233                                        if(imageelem)
234                                        {
235                                                string filename=imageelem->Attribute("filename");
236                                                if(count==0) m_FaceMap[ID]=new Face(cvLoadImage(filename.c_str()));
237                                                else m_FaceMap[ID]->AddImage(cvLoadImage(filename.c_str()));
238                                                count++;
239                                                image = image->NextSibling();
240                                        }
241                                }
242                                face = face->NextSibling();
243                        }
244                }
245        }
246        else
247        {
248                cerr<<"error parsing xml in "<<fn<<endl;
249        }
250}
Note: See TracBrowser for help on using the repository browser.