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

Revision 61, 7.2 KB checked in by dave, 11 years ago (diff)

maybe fixed a bug in the string split

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