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

Revision 64, 7.3 KB checked in by iolanda, 12 years ago (diff)

changed a few things to work on both on windows and linux

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
21#include <vector>
22
23#ifdef WIN32
24#include <string>
25#define snprintf _snprintf
26#endif
27
28using namespace std;
29
30/////////////////////////////////////////////////////////////////////////////////
31// util for loading
32
33int SplitString(const string& input, const string& delimiter, vector<string>& results, bool includeEmpties)
34{
35    int iPos = 0;
36    int newPos = -1;
37    int sizeS2 = (int)delimiter.size();
38    int isize = (int)input.size();
39
40    if(isize == 0 || sizeS2 == 0)
41    {
42        return 0;
43    }
44
45    vector<int> positions;
46    newPos = input.find (delimiter, 0);
47
48    if( newPos < 0 ) return 0;
49
50    int numFound = 0;
51
52    while( newPos >= iPos )
53    {
54        numFound++;
55        positions.push_back(newPos);
56        iPos = newPos;
57        newPos = input.find (delimiter, iPos+sizeS2);
58    }
59
60    if( numFound == 0 ) return 0;
61
62    for( int i=0; i <= (int)positions.size(); ++i )
63    {
64        string s("");
65        if( i == 0 )
66        {
67            s = input.substr( i, positions[i] );
68        }
69                else
70                {
71                int offset = positions[i-1] + sizeS2;
72                if( offset < isize )
73                {
74                    if( i == (int)positions.size() )
75                    {
76                        s = input.substr(offset);
77                    }
78                    else if( i > 0 )
79                    {
80                        s = input.substr( positions[i-1] + sizeS2,
81                              positions[i] - positions[i-1] - sizeS2 );
82                    }
83                }
84                }
85               
86        if( includeEmpties || ( s.size() > 0 ) )
87        {
88            results.push_back(s);
89        }
90    }
91    return numFound;
92}
93
94/////////////////////////////////////////////////////////////////////////////////
95
96Face::Face(IplImage *image) :
97m_Image(image)
98{
99}
100
101Face::~Face()
102{
103        cvReleaseImage(&m_Image);
104}
105
106void Face::Learn(const IplImage *image, float blend)
107{
108        CvSize sizea = cvGetSize(image);
109        CvSize sizeb = cvGetSize(m_Image);
110
111        assert(sizea.width == sizeb.width);
112        assert(sizea.height == sizeb.height);
113
114        float ret=0;
115
116    for(int y=0; y<sizea.height; y++)
117        {
118        for(int x=0; x<sizea.width; x++)
119                {
120                        cvSet2D(m_Image,y,x,cvScalar(
121                                                cvGet2D(m_Image,y,x).val[0]*(1 - blend) + cvGet2D(image,y,x).val[0]*blend,
122                                                cvGet2D(m_Image,y,x).val[1]*(1 - blend) + cvGet2D(image,y,x).val[1]*blend,
123                                                cvGet2D(m_Image,y,x).val[2]*(1 - blend) + cvGet2D(image,y,x).val[2]*blend));
124                }
125        }
126}
127
128/////////////////////////////////////////////////////////////////////////////////
129       
130FaceBank::FaceBank(unsigned int FaceWidth, unsigned int FaceHeight, float ErrorThresh) :
131m_FaceWidth(FaceWidth),
132m_FaceHeight(FaceHeight),
133m_ErrorThresh(ErrorThresh)
134{
135}
136
137FaceBank::~FaceBank()
138{       
139        Clear();
140}
141
142void FaceBank::Clear()
143{
144        for(map<unsigned int,Face*>::iterator i=m_FaceMap.begin(); i!=m_FaceMap.end(); ++i)
145        {
146                delete i->second;
147        }
148        m_FaceMap.clear();
149}
150
151void FaceBank::Save(const std::string &filename) const
152{
153        char fn[256];
154        snprintf(fn,256,"%s.dat",filename.c_str());
155        FILE *f=fopen(fn,"w");
156       
157        if (f==NULL)
158        {
159                cerr<<"could not open file for saving: "<<filename<<endl;
160                return;
161        }
162
163        char header[1024];
164        snprintf(header,1024,"%d\n%d\n",0,m_FaceMap.size());
165        fwrite(header,strlen(header),1,f);
166       
167        for(map<unsigned int,Face*>::const_iterator i=m_FaceMap.begin(); i!=m_FaceMap.end(); ++i)
168        {
169                char fn[256];
170                snprintf(fn,256,"%d\n%s-%d.png\n",i->first,filename.c_str(),i->first);
171                fwrite(fn,strlen(fn),1,f);
172        }
173        fclose(f);
174       
175        for(map<unsigned int,Face*>::const_iterator i=m_FaceMap.begin(); i!=m_FaceMap.end(); ++i)
176        {
177                char fn[256];
178                snprintf(fn,256,"%s-%d.png",filename.c_str(),i->first);
179                cvSaveImage(fn,i->second->m_Image);
180        }
181}
182
183void FaceBank::Load(const std::string &filename)
184{
185        Clear();
186
187        char fn[256];
188        snprintf(fn,256,"%s.dat",filename.c_str());
189        FILE *f=fopen(fn,"r");
190       
191        if (f==NULL)
192        {
193                cerr<<"file not found: "<<filename<<endl;
194                return;
195        }
196       
197        fseek(f,0,SEEK_END);
198        unsigned int size=ftell(f);
199        fseek(f,0,SEEK_SET);
200       
201        if (size==0)
202        {
203                fclose(f);
204                cerr<<"empty file: "<<filename<<endl;
205                return;
206        }
207
208        if (size<0)
209        {
210                fclose(f);
211                cerr<<"error loading file: "<<filename<<" size: "<<size<<"??"<<endl;
212                return;
213        }
214
215        char *data=new char[size+1];           
216        fread(data,1,size,f);
217        data[size]='\0';
218        fclose(f);
219       
220        vector<string> items;
221        SplitString(data,"\n",items,true);
222
223        if (items.size()>3)
224        {
225                int version=(int)atoi(items[0].c_str());
226                int count=(int)atoi(items[1].c_str());
227                for (int i=2; i<count*2+2; i+=2)
228                {
229                        int ID=(int)atoi(items[i].c_str());
230                        m_FaceMap[ID]=new Face(cvLoadImage(items[i+1].c_str()));
231                }
232        }
233}
234
235float FaceBank::Suggest(IplImage *face, unsigned int ID)
236{
237        IplImage *faceresized = cvCreateImage(cvSize(m_FaceWidth,m_FaceHeight),IPL_DEPTH_8U, face->nChannels);
238        cvResize(face, faceresized, CV_INTER_LINEAR );
239        // Subtract the mean as an attempt to deal with global lighting changes
240        SubMean(faceresized);
241       
242        // Get this face
243        map<unsigned int,Face*>::iterator i=m_FaceMap.find(ID);
244       
245        // If it's the first time we've seen this face then record it
246        if (i==m_FaceMap.end())
247        {               
248                // Check it doesn't look like any we have already recorded
249                unsigned int checkid=0;
250                if (Identify(faceresized,checkid)>0)
251                {
252                        cerr<<"We've already seen this face: "<<checkid<<endl; 
253                        return 0;
254                }
255
256                cerr<<"new face "<<ID<<endl;
257                m_FaceMap[ID]=new Face(faceresized);
258                return 1;
259        }
260       
261        // Does this face look like the one we already have for this id?
262        float error = Diff(faceresized,i->second->m_Image);
263
264        if (error<m_ErrorThresh)
265        {
266                cerr<<"adding to face "<<ID<<endl;
267                // Blend this face into the one we have already
268                i->second->Learn(faceresized,0.2);
269                cvReleaseImage(&faceresized);
270                ID=i->first;
271                return 1-error;
272        }
273       
274        cerr<<"false positive? "<<error<<" "<<ID<<endl;
275       
276        return 0;
277}
278
279float FaceBank::Identify(IplImage *face, unsigned int &ID)
280{
281        IplImage *faceresized = cvCreateImage(cvSize(m_FaceWidth,m_FaceHeight),IPL_DEPTH_8U, face->nChannels);
282        cvResize(face, faceresized, CV_INTER_LINEAR );
283        // Subtract the mean as an attempt to deal with global lighting changes
284        SubMean(faceresized);
285
286        // No faces recorded?
287        if (m_FaceMap.empty())
288        {
289                return 0;
290        }
291       
292        float error=FLT_MAX;
293        unsigned int best=0;
294        Face* bestface=NULL;
295       
296        // look for the lowest error in the map of faces
297        for(map<unsigned int,Face*>::iterator i=m_FaceMap.begin(); i!=m_FaceMap.end(); ++i)
298        {
299                float tmp = Diff(faceresized,i->second->m_Image);
300                if (tmp<error)
301                {
302                        error=tmp;
303                        best=i->first;
304                        bestface=i->second;
305                }
306        }
307       
308        // if the error is less than the threshold, return the id
309        if (error<m_ErrorThresh)
310        {
311                // blend this face into the one we have already
312                bestface->Learn(faceresized,0);
313                cvReleaseImage(&faceresized);
314                ID=best;
315                return 1-error;
316        }
317       
318        cerr<<"unrecognised face"<<endl;
319       
320        return 0;
321}
Note: See TracBrowser for help on using the repository browser.