1/*M///////////////////////////////////////////////////////////////////////////////////////
2//
3//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4//
5//  By downloading, copying, installing or using the software you agree to this license.
6//  If you do not agree to this license, do not download, install,
7//  copy or use the software.
8//
9//
10//                        Intel License Agreement
11//                For Open Source Computer Vision Library
12//
13// Copyright (C) 2000, Intel Corporation, all rights reserved.
14// Third party copyrights are property of their respective owners.
15//
16// Redistribution and use in source and binary forms, with or without modification,
17// are permitted provided that the following conditions are met:
18//
19//   * Redistribution's of source code must retain the above copyright notice,
20//     this list of conditions and the following disclaimer.
21//
22//   * Redistribution's in binary form must reproduce the above copyright notice,
23//     this list of conditions and the following disclaimer in the documentation
24//     and/or other materials provided with the distribution.
25//
26//   * The name of Intel Corporation may not be used to endorse or promote products
27//     derived from this software without specific prior written permission.
28//
29// This software is provided by the copyright holders and contributors "as is" and
30// any express or implied warranties, including, but not limited to, the implied
31// warranties of merchantability and fitness for a particular purpose are disclaimed.
32// In no event shall the Intel Corporation or contributors be liable for any direct,
33// indirect, incidental, special, exemplary, or consequential damages
34// (including, but not limited to, procurement of substitute goods or services;
35// loss of use, data, or profits; or business interruption) however caused
36// and on any theory of liability, whether in contract, strict liability,
37// or tort (including negligence or otherwise) arising in any way out of
38// the use of this software, even if advised of the possibility of such damage.
39//
40//M*/
41///////////////////////////////////////////////
42//// Created by Khudyakov V.A. bober@gorodok.net
43//////////////////////////////////////////////
44// FaceDetection.cpp: implementation of the FaceDetection class.
45//
46//////////////////////////////////////////////////////////////////////
47
48#include "_cvaux.h"
49#include "_cvfacedetection.h"
50
51
52int CV_CDECL CompareContourRect(const void* el1, const void* el2, void* userdata);
53
54//////////////////////////////////////////////////////////////////////
55// Construction/Destruction
56//////////////////////////////////////////////////////////////////////
57
58FaceDetection::FaceDetection()
59{
60
61    m_imgGray = NULL;
62    m_imgThresh = NULL;
63    m_mstgContours = NULL;
64    memset(m_seqContours, 0, sizeof(CvSeq*) * MAX_LAYERS);
65    m_mstgRects = NULL;
66    m_seqRects = NULL;
67    m_iNumLayers = 16;
68    assert(m_iNumLayers <= MAX_LAYERS);
69    m_pFaceList = new List();
70
71
72
73    m_bBoosting = false;
74
75}// FaceDetection()
76
77FaceDetection::~FaceDetection()
78{
79    if (m_imgGray)
80        cvReleaseImage(&m_imgGray);
81
82    if (m_imgThresh)
83        cvReleaseImage(&m_imgThresh);
84
85    if (m_mstgContours)
86        cvReleaseMemStorage(&m_mstgContours);
87
88    if (m_mstgRects)
89        cvReleaseMemStorage(&m_mstgRects);
90
91
92}// ~FaceDetection()
93
94void FaceDetection::FindContours(IplImage* imgGray)
95{
96    ReallocImage(&m_imgThresh, cvGetSize(imgGray), 1);
97    if (NULL == m_imgThresh)
98        return;
99    //
100    int iNumLayers = m_iNumLayers;
101    int iMinLevel = 0, iMaxLevel = 255, iStep = 255 / iNumLayers;
102    ThresholdingParam(imgGray, iNumLayers, iMinLevel, iMaxLevel, iStep);
103    // init
104    cvReleaseMemStorage(&m_mstgContours);
105    m_mstgContours = cvCreateMemStorage();
106    if (NULL == m_mstgContours)
107        return;
108    memset(m_seqContours, 0, sizeof(CvSeq*) * MAX_LAYERS);
109
110    cvReleaseMemStorage(&m_mstgRects);
111    m_mstgRects = cvCreateMemStorage();
112    if (NULL == m_mstgRects)
113        return;
114    m_seqRects = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvContourRect), m_mstgRects);
115    if (NULL == m_seqRects)
116        return;
117    // find contours
118    for (int l = iMinLevel, i = 0; l < iMaxLevel; l += iStep, i++)
119    {
120        cvThreshold(imgGray, m_imgThresh, (double)l, (double)255, CV_THRESH_BINARY);
121        if (cvFindContours(m_imgThresh, m_mstgContours, &m_seqContours[i], sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE))
122            AddContours2Rect(m_seqContours[i], l, i);
123    }
124    // sort rects
125    cvSeqSort(m_seqRects, CompareContourRect, NULL);
126}// void FaceDetection::FindContours(IplImage* imgGray)
127
128#define GIST_STEP   10
129#define GIST_NUM    (256 / GIST_STEP)
130#define GIST_MIN    32
131
132void FaceDetection::ThresholdingParam(IplImage *imgGray, int iNumLayers, int &iMinLevel, int &iMaxLevel, int &iStep)
133{
134    assert(imgGray != NULL);
135    assert(imgGray->nChannels == 1);
136    int i, j;
137    // create gistogramm
138    uchar* buffImg = (uchar*)imgGray->imageData;
139    int gistImg[GIST_NUM + 1] = {0};
140
141    for (j = 0; j < imgGray->height; j ++)
142    {
143        for (i = 0; i < imgGray->width; i ++)
144        {
145            int ind = buffImg[i] / GIST_STEP;
146            gistImg[ind] ++;
147        }
148        buffImg += imgGray->widthStep;
149    }
150    // params
151
152    for (i = 0; i <= GIST_NUM; i ++)
153    {
154        if (gistImg[i] >= GIST_MIN)
155            break;
156    }
157
158    iMinLevel = i * GIST_STEP;
159
160    for (i = GIST_NUM; i >= 0; i --)
161    {
162        if (gistImg[i] >= GIST_MIN)
163            break;
164    }
165
166    iMaxLevel = i * GIST_STEP;
167
168    int dLevels = iMaxLevel - iMinLevel;
169    if (dLevels <= 0)
170    {
171        iMinLevel = 0;
172        iMaxLevel = 255;
173    }
174    else if (dLevels <= iNumLayers)
175    {
176        iMinLevel = iMaxLevel - iNumLayers;
177        if (iMinLevel < 0)
178        {
179            iMinLevel = 0;
180            iMaxLevel = iNumLayers;
181        }
182    }
183    iStep = (iMaxLevel - iMinLevel) / iNumLayers;
184
185}// void FaceDetection::ThresholdingParam(IplImage *imgGray, int iNumLayers, int &iMinLevel, int &iMaxLevel, int &iStep)
186
187#ifndef MAX_ERROR
188#define MAX_ERROR 0xFFFFFFFF
189#endif //MAX_ERROR
190
191
192void FaceDetection::CreateResults(CvSeq * lpSeq)
193{
194
195    Face * tmp;
196
197    double Max  = 0;
198    double CurStat = 0;
199
200    FaceData tmpData;
201    if (m_bBoosting)
202    {
203        tmp = m_pFaceList->GetData();
204        tmp->CreateFace(&tmpData);
205
206        CvFace tmpFace;
207        tmpFace.MouthRect = tmpData.MouthRect;
208        tmpFace.LeftEyeRect = tmpData.LeftEyeRect;
209        tmpFace.RightEyeRect = tmpData.RightEyeRect;
210
211        cvSeqPush(lpSeq,&tmpFace);
212
213    }else
214    {
215        while ( (tmp = m_pFaceList->GetData()) != 0 )
216        {
217            CurStat = tmp->GetWeight();
218            if (CurStat > Max)
219                Max = CurStat;
220        }
221
222        while ( (tmp = m_pFaceList->GetData()) != 0 )
223        {
224            tmp->CreateFace(&tmpData);
225            CurStat = tmp->GetWeight();
226
227            if (CurStat == Max)
228            {
229                CvFace tmpFace;
230                tmpFace.MouthRect = tmpData.MouthRect;
231                tmpFace.LeftEyeRect = tmpData.LeftEyeRect;
232                tmpFace.RightEyeRect = tmpData.RightEyeRect;
233                cvSeqPush(lpSeq,&tmpFace);
234
235
236            }
237        }
238    }
239}// void FaceDetection::DrawResult(IplImage* img)
240
241void FaceDetection::ResetImage()
242{
243        delete m_pFaceList;
244        m_pFaceList = new List();
245
246}//FaceDetection::ResetImage
247
248void FaceDetection::AddContours2Rect(CvSeq *seq, int color, int iLayer)
249{
250    assert(m_mstgRects != NULL);
251    assert(m_seqRects != NULL);
252
253    CvContourRect cr;
254    for (CvSeq* external = seq; external; external = external->h_next)
255    {
256        cr.r = cvContourBoundingRect(external, 1 );
257        cr.pCenter.x = cr.r.x + cr.r.width / 2;
258        cr.pCenter.y = cr.r.y + cr.r.height / 2;
259        cr.iNumber = iLayer;
260        cr.iType = 6;
261        cr.iFlags = 0;
262        cr.seqContour = external;
263        cr.iContourLength = external->total;
264        cr.iColor = color;
265        cvSeqPush(m_seqRects, &cr);
266        for (CvSeq* internal = external->v_next; internal; internal = internal->h_next)
267        {
268            cr.r = cvContourBoundingRect(internal, 0);
269            cr.pCenter.x = cr.r.x + cr.r.width / 2;
270            cr.pCenter.y = cr.r.y + cr.r.height / 2;
271            cr.iNumber = iLayer;
272            cr.iType = 12;
273            cr.iFlags = 0;
274            cr.seqContour = internal;
275            cr.iContourLength = internal->total;
276            cr.iColor = color;
277            cvSeqPush(m_seqRects, &cr);
278        }
279    }
280}// void FaceDetection::AddContours2Rect(CvSeq *seq, int color, int iLayer)
281
282int CV_CDECL CompareContourRect(const void* el1, const void* el2, void* /*userdata*/)
283{
284    return (((CvContourRect*)el1)->pCenter.y - ((CvContourRect*)el2)->pCenter.y);
285}// int CV_CDECL CompareContourRect(const void* el1, const void* el2, void* userdata)
286
287void FaceDetection::FindFace(IplImage *img)
288{
289    // find all contours
290    FindContours(img);
291    //
292    ResetImage();
293
294    if (m_bBoosting)
295        PostBoostingFindCandidats(img);
296    else
297        FindCandidats();
298
299}// void FaceDetection::FindFace(IplImage *img)
300
301
302void FaceDetection::FindCandidats()
303{
304    bool bFound1 = false;
305    MouthFaceTemplate * lpFaceTemplate1;
306    RFace * lpFace1;
307    bool bInvalidRect1 = false;
308    CvRect * lpRect1  = NULL;
309
310    for (int i = 0; i < m_seqRects->total; i++)
311    {
312        CvContourRect* pRect = (CvContourRect*)cvGetSeqElem(m_seqRects, i);
313        CvRect rect = pRect->r;
314        if (rect.width >= 2*rect.height)
315        {
316
317            lpFaceTemplate1 = new MouthFaceTemplate(3,rect,3*(double)rect.width/(double)4,
318                                                           3*(double)rect.width/(double)4,
319                                                             (double)rect.width/(double)2,
320                                                             (double)rect.width/(double)2);
321
322
323            lpFace1 = new RFace(lpFaceTemplate1);
324
325            for (int j = 0; j < m_seqRects->total; j++)
326            {
327                CvContourRect* pRect = (CvContourRect*)cvGetSeqElem(m_seqRects, j);
328
329                if ( !bInvalidRect1 )
330                {
331                    lpRect1 = NULL;
332                    lpRect1 = new CvRect();
333                    *lpRect1 = pRect->r;
334                }else
335                {
336                    delete lpRect1;
337                    lpRect1 = new CvRect();
338                    *lpRect1 = pRect->r;
339                }
340
341
342                if ( lpFace1->isFeature(lpRect1) )
343                {
344                    bFound1 = true;
345                    bInvalidRect1 = false;
346                }else
347                    bInvalidRect1 = true;
348
349
350            }
351
352
353            if (bFound1)
354            {
355                m_pFaceList->AddElem(lpFace1);
356                bFound1 = false;
357                lpFace1 = NULL;
358            }else
359            {
360                delete lpFace1;
361                lpFace1 = NULL;
362            }
363
364
365            delete lpFaceTemplate1;
366        }
367
368    }
369
370}
371
372
373void FaceDetection::PostBoostingFindCandidats(IplImage * FaceImage)
374{
375    BoostingFaceTemplate * lpFaceTemplate1;
376    RFace * lpFace1;
377    bool bInvalidRect1 = false;
378    CvRect * lpRect1  = NULL;
379
380    if ( ( !FaceImage->roi ) )
381        lpFaceTemplate1 = new BoostingFaceTemplate(3,cvRect(0,0,FaceImage->width,FaceImage->height));
382    else
383        lpFaceTemplate1 = new BoostingFaceTemplate(3,cvRect(FaceImage->roi->xOffset,FaceImage->roi->yOffset,
384                                                            FaceImage->roi->width,FaceImage->roi->height));
385
386    lpFace1 = new RFace(lpFaceTemplate1);
387
388    for (int i = 0; i < m_seqRects->total; i++)
389    {
390        CvContourRect* pRect = (CvContourRect*)cvGetSeqElem(m_seqRects, i);
391
392        if ( !bInvalidRect1 )
393        {
394            lpRect1 = NULL;
395            lpRect1 = new CvRect();
396            *lpRect1 = pRect->r;
397        }else
398        {
399            delete lpRect1;
400            lpRect1 = new CvRect();
401            *lpRect1 = pRect->r;
402        }
403
404
405        if ( lpFace1->isFeature(lpRect1) )
406        {
407            //bFound1 = true;
408            bInvalidRect1 = false;
409        }else
410            bInvalidRect1 = true;
411
412
413    }
414
415    m_pFaceList->AddElem(lpFace1);
416
417    delete lpFaceTemplate1;
418
419}//void FaceDetection::PostBoostingFindCandidats(IplImage * FaceImage)
420
421/////////////////////////
422//class Face
423
424
425
426//////
427//List Class
428/////
429ListElem::ListElem()
430{
431    m_pNext = this;
432    m_pPrev = this;
433    m_pFace = NULL;
434}///ListElem::ListElem()
435
436ListElem::ListElem(Face * pFace,ListElem * pHead)
437{
438    m_pNext = pHead;
439    m_pPrev = pHead->m_pPrev;
440    pHead->m_pPrev->m_pNext = this;
441    pHead->m_pPrev = this;
442
443    m_pFace = pFace;
444}//ListElem::ListElem(Face * pFace)
445
446
447
448ListElem::~ListElem()
449{
450    delete m_pFace;
451    m_pNext->m_pPrev = m_pPrev;
452    m_pPrev->m_pNext = m_pNext;
453
454}//ListElem::~ListElem()
455
456List::List()
457{
458    m_pHead = new ListElem();
459    m_FacesCount = 0;
460    m_pCurElem = m_pHead;
461}//List::List()
462
463List::~List()
464{
465    void * tmp;
466    while((tmp = m_pHead->m_pNext->m_pFace) != 0)
467        delete m_pHead->m_pNext;
468
469    delete m_pHead;
470
471}//List::~List()
472
473
474int List::AddElem(Face * pFace)
475{
476    new ListElem(pFace,m_pHead);
477    return m_FacesCount++;
478}//List::AddElem(Face * pFace)
479
480Face * List::GetData()
481{
482    m_pCurElem = m_pCurElem->m_pNext;
483    return m_pCurElem->m_pFace;
484}//Face * List::GetData()
485
486
487