Update to 2.0.0 tree from current Fremantle build
[opencv] / src / cvaux / cvfacedetection.cpp
diff --git a/src/cvaux/cvfacedetection.cpp b/src/cvaux/cvfacedetection.cpp
new file mode 100644 (file)
index 0000000..da04df7
--- /dev/null
@@ -0,0 +1,486 @@
+/*M///////////////////////////////////////////////////////////////////////////////////////
+//
+//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
+//
+//  By downloading, copying, installing or using the software you agree to this license.
+//  If you do not agree to this license, do not download, install,
+//  copy or use the software.
+//
+//
+//                        Intel License Agreement
+//                For Open Source Computer Vision Library
+//
+// Copyright (C) 2000, Intel Corporation, all rights reserved.
+// Third party copyrights are property of their respective owners.
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+//   * Redistribution's of source code must retain the above copyright notice,
+//     this list of conditions and the following disclaimer.
+//
+//   * Redistribution's in binary form must reproduce the above copyright notice,
+//     this list of conditions and the following disclaimer in the documentation
+//     and/or other materials provided with the distribution.
+//
+//   * The name of Intel Corporation may not be used to endorse or promote products
+//     derived from this software without specific prior written permission.
+//
+// This software is provided by the copyright holders and contributors "as is" and
+// any express or implied warranties, including, but not limited to, the implied
+// warranties of merchantability and fitness for a particular purpose are disclaimed.
+// In no event shall the Intel Corporation or contributors be liable for any direct,
+// indirect, incidental, special, exemplary, or consequential damages
+// (including, but not limited to, procurement of substitute goods or services;
+// loss of use, data, or profits; or business interruption) however caused
+// and on any theory of liability, whether in contract, strict liability,
+// or tort (including negligence or otherwise) arising in any way out of
+// the use of this software, even if advised of the possibility of such damage.
+//
+//M*/
+///////////////////////////////////////////////
+//// Created by Khudyakov V.A. bober@gorodok.net
+//////////////////////////////////////////////
+// FaceDetection.cpp: implementation of the FaceDetection class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#include "_cvaux.h"
+#include "_cvfacedetection.h"
+
+
+int CV_CDECL CompareContourRect(const void* el1, const void* el2, void* userdata);
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+FaceDetection::FaceDetection()
+{
+
+    m_imgGray = NULL;
+    m_imgThresh = NULL;
+    m_mstgContours = NULL;
+    memset(m_seqContours, 0, sizeof(CvSeq*) * MAX_LAYERS);
+    m_mstgRects = NULL;
+    m_seqRects = NULL;
+    m_iNumLayers = 16;
+    assert(m_iNumLayers <= MAX_LAYERS);
+    m_pFaceList = new List();
+    
+
+
+    m_bBoosting = false;
+
+}// FaceDetection()
+
+FaceDetection::~FaceDetection()
+{
+    if (m_imgGray)
+        cvReleaseImage(&m_imgGray);
+
+    if (m_imgThresh)
+        cvReleaseImage(&m_imgThresh);
+
+    if (m_mstgContours)
+        cvReleaseMemStorage(&m_mstgContours);
+
+    if (m_mstgRects)
+        cvReleaseMemStorage(&m_mstgRects);
+    
+
+}// ~FaceDetection()
+
+void FaceDetection::FindContours(IplImage* imgGray)
+{
+    ReallocImage(&m_imgThresh, cvGetSize(imgGray), 1);
+    if (NULL == m_imgThresh)
+        return;
+    //
+    int iNumLayers = m_iNumLayers;
+    int iMinLevel = 0, iMaxLevel = 255, iStep = 255 / iNumLayers;
+    ThresholdingParam(imgGray, iNumLayers, iMinLevel, iMaxLevel, iStep);
+    // init
+    cvReleaseMemStorage(&m_mstgContours);
+    m_mstgContours = cvCreateMemStorage();
+    if (NULL == m_mstgContours)
+        return;
+    memset(m_seqContours, 0, sizeof(CvSeq*) * MAX_LAYERS);
+
+    cvReleaseMemStorage(&m_mstgRects);
+    m_mstgRects = cvCreateMemStorage();
+    if (NULL == m_mstgRects)
+        return;
+    m_seqRects = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvContourRect), m_mstgRects); 
+    if (NULL == m_seqRects)
+        return;
+    // find contours
+    for (int l = iMinLevel, i = 0; l < iMaxLevel; l += iStep, i++)
+    {
+        cvThreshold(imgGray, m_imgThresh, (double)l, (double)255, CV_THRESH_BINARY);
+        if (cvFindContours(m_imgThresh, m_mstgContours, &m_seqContours[i], sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE))
+            AddContours2Rect(m_seqContours[i], l, i);
+    }
+    // sort rects
+    cvSeqSort(m_seqRects, CompareContourRect, NULL);
+}// void FaceDetection::FindContours(IplImage* imgGray)
+
+#define GIST_STEP   10
+#define GIST_NUM    (256 / GIST_STEP)
+#define GIST_MIN    32
+
+void FaceDetection::ThresholdingParam(IplImage *imgGray, int iNumLayers, int &iMinLevel, int &iMaxLevel, int &iStep)
+{
+    assert(imgGray != NULL);
+    assert(imgGray->nChannels == 1);
+    int i, j;
+    // create gistogramm
+    uchar* buffImg = (uchar*)imgGray->imageData;
+    int gistImg[GIST_NUM + 1] = {0};
+
+    for (j = 0; j < imgGray->height; j ++)
+    {
+        for (i = 0; i < imgGray->width; i ++)
+        {
+            int ind = buffImg[i] / GIST_STEP;
+            gistImg[ind] ++;
+        }
+        buffImg += imgGray->widthStep;
+    }
+    // params
+    
+    for (i = 0; i <= GIST_NUM; i ++)
+    {
+        if (gistImg[i] >= GIST_MIN)
+            break;
+    }
+    
+    iMinLevel = i * GIST_STEP;
+    
+    for (i = GIST_NUM; i >= 0; i --)
+    {
+        if (gistImg[i] >= GIST_MIN)
+            break;
+    }
+    
+    iMaxLevel = i * GIST_STEP;
+    
+    int dLevels = iMaxLevel - iMinLevel;
+    if (dLevels <= 0)
+    {
+        iMinLevel = 0;
+        iMaxLevel = 255;
+    }
+    else if (dLevels <= iNumLayers)
+    {
+        iMinLevel = iMaxLevel - iNumLayers;
+        if (iMinLevel < 0)
+        {
+            iMinLevel = 0;
+            iMaxLevel = iNumLayers;
+        }
+    }
+    iStep = (iMaxLevel - iMinLevel) / iNumLayers;
+
+}// void FaceDetection::ThresholdingParam(IplImage *imgGray, int iNumLayers, int &iMinLevel, int &iMaxLevel, int &iStep)
+
+#ifndef MAX_ERROR
+#define MAX_ERROR 0xFFFFFFFF
+#endif //MAX_ERROR
+
+
+void FaceDetection::CreateResults(CvSeq * lpSeq)
+{
+    
+    Face * tmp;
+    
+    double Max  = 0;
+    double CurStat = 0;
+    
+    FaceData tmpData;
+    if (m_bBoosting)
+    {
+        tmp = m_pFaceList->GetData();
+        tmp->CreateFace(&tmpData);
+
+        CvFace tmpFace;
+        tmpFace.MouthRect = tmpData.MouthRect;
+        tmpFace.LeftEyeRect = tmpData.LeftEyeRect;
+        tmpFace.RightEyeRect = tmpData.RightEyeRect;
+
+        cvSeqPush(lpSeq,&tmpFace);
+
+    }else
+    {
+        while ( (tmp = m_pFaceList->GetData()) != 0 )
+        {
+            CurStat = tmp->GetWeight();
+            if (CurStat > Max)
+                Max = CurStat;
+        }
+        
+        while ( (tmp = m_pFaceList->GetData()) != 0 )
+        {
+            tmp->CreateFace(&tmpData);
+            CurStat = tmp->GetWeight();
+            
+            if (CurStat == Max)
+            {
+                CvFace tmpFace;
+                tmpFace.MouthRect = tmpData.MouthRect;
+                tmpFace.LeftEyeRect = tmpData.LeftEyeRect;
+                tmpFace.RightEyeRect = tmpData.RightEyeRect;
+                cvSeqPush(lpSeq,&tmpFace);
+
+                
+            }
+        }
+    }
+}// void FaceDetection::DrawResult(IplImage* img)
+
+void FaceDetection::ResetImage()
+{
+        delete m_pFaceList;
+        m_pFaceList = new List();
+
+}//FaceDetection::ResetImage
+
+void FaceDetection::AddContours2Rect(CvSeq *seq, int color, int iLayer)
+{
+    assert(m_mstgRects != NULL);
+    assert(m_seqRects != NULL);
+
+    CvContourRect cr;
+    for (CvSeq* external = seq; external; external = external->h_next)
+    {
+        cr.r = cvContourBoundingRect(external, 1 );
+        cr.pCenter.x = cr.r.x + cr.r.width / 2;
+        cr.pCenter.y = cr.r.y + cr.r.height / 2;
+        cr.iNumber = iLayer;
+        cr.iType = 6;
+        cr.iFlags = 0;
+        cr.seqContour = external;
+        cr.iContourLength = external->total;
+        cr.iColor = color;
+        cvSeqPush(m_seqRects, &cr);
+        for (CvSeq* internal = external->v_next; internal; internal = internal->h_next)
+        {
+            cr.r = cvContourBoundingRect(internal, 0);    
+            cr.pCenter.x = cr.r.x + cr.r.width / 2;
+            cr.pCenter.y = cr.r.y + cr.r.height / 2;
+            cr.iNumber = iLayer;
+            cr.iType = 12;
+            cr.iFlags = 0;
+            cr.seqContour = internal;
+            cr.iContourLength = internal->total;
+            cr.iColor = color;
+            cvSeqPush(m_seqRects, &cr);
+        }
+    }
+}// void FaceDetection::AddContours2Rect(CvSeq *seq, int color, int iLayer)
+
+int CV_CDECL CompareContourRect(const void* el1, const void* el2, void* /*userdata*/)
+{
+    return (((CvContourRect*)el1)->pCenter.y - ((CvContourRect*)el2)->pCenter.y);
+}// int CV_CDECL CompareContourRect(const void* el1, const void* el2, void* userdata)
+
+void FaceDetection::FindFace(IplImage *img)
+{
+    // find all contours
+    FindContours(img);
+    //
+    ResetImage();
+
+    if (m_bBoosting)
+        PostBoostingFindCandidats(img);
+    else
+        FindCandidats();    
+    
+}// void FaceDetection::FindFace(IplImage *img)
+
+
+void FaceDetection::FindCandidats()
+{
+    bool bFound1 = false;
+    MouthFaceTemplate * lpFaceTemplate1;
+    RFace * lpFace1; 
+    bool bInvalidRect1 = false;
+    CvRect * lpRect1  = NULL;
+    
+    for (int i = 0; i < m_seqRects->total; i++)
+    {
+        CvContourRect* pRect = (CvContourRect*)cvGetSeqElem(m_seqRects, i);
+        CvRect rect = pRect->r;
+        if (rect.width >= 2*rect.height)
+        {
+
+            lpFaceTemplate1 = new MouthFaceTemplate(3,rect,3*(double)rect.width/(double)4,
+                                                           3*(double)rect.width/(double)4,
+                                                             (double)rect.width/(double)2,
+                                                             (double)rect.width/(double)2);
+    
+
+            lpFace1 = new RFace(lpFaceTemplate1);
+            
+            for (int j = 0; j < m_seqRects->total; j++)
+            {
+                CvContourRect* pRect = (CvContourRect*)cvGetSeqElem(m_seqRects, j);
+                
+                if ( !bInvalidRect1 )
+                {
+                    lpRect1 = NULL;
+                    lpRect1 = new CvRect();
+                    *lpRect1 = pRect->r;
+                }else
+                {
+                    delete lpRect1;
+                    lpRect1 = new CvRect();
+                    *lpRect1 = pRect->r;
+                }
+                
+                
+                if ( lpFace1->isFeature(lpRect1) )
+                { 
+                    bFound1 = true;
+                    bInvalidRect1 = false;
+                }else
+                    bInvalidRect1 = true;
+    
+
+            }
+
+            
+            if (bFound1)
+            {
+                m_pFaceList->AddElem(lpFace1);
+                bFound1 = false;
+                lpFace1 = NULL;
+            }else
+            {
+                delete lpFace1;
+                lpFace1 = NULL;
+            }
+
+            
+            delete lpFaceTemplate1;
+        }
+    
+    }
+
+}
+
+
+void FaceDetection::PostBoostingFindCandidats(IplImage * FaceImage)
+{
+    BoostingFaceTemplate * lpFaceTemplate1;
+    RFace * lpFace1; 
+    bool bInvalidRect1 = false;
+    CvRect * lpRect1  = NULL;
+    
+    if ( ( !FaceImage->roi ) )
+        lpFaceTemplate1 = new BoostingFaceTemplate(3,cvRect(0,0,FaceImage->width,FaceImage->height));
+    else
+        lpFaceTemplate1 = new BoostingFaceTemplate(3,cvRect(FaceImage->roi->xOffset,FaceImage->roi->yOffset,
+                                                            FaceImage->roi->width,FaceImage->roi->height));
+    
+    lpFace1 = new RFace(lpFaceTemplate1);
+
+    for (int i = 0; i < m_seqRects->total; i++)
+    {
+        CvContourRect* pRect = (CvContourRect*)cvGetSeqElem(m_seqRects, i);
+        
+        if ( !bInvalidRect1 )
+        {
+            lpRect1 = NULL;
+            lpRect1 = new CvRect();
+            *lpRect1 = pRect->r;
+        }else
+        {
+            delete lpRect1;
+            lpRect1 = new CvRect();
+            *lpRect1 = pRect->r;
+        }
+        
+        
+        if ( lpFace1->isFeature(lpRect1) )
+        { 
+            //bFound1 = true;
+            bInvalidRect1 = false;
+        }else
+            bInvalidRect1 = true;
+
+    
+    }
+    
+    m_pFaceList->AddElem(lpFace1);
+    
+    delete lpFaceTemplate1;
+
+}//void FaceDetection::PostBoostingFindCandidats(IplImage * FaceImage)
+
+/////////////////////////
+//class Face
+
+
+
+//////
+//List Class
+/////
+ListElem::ListElem()
+{
+    m_pNext = this;
+    m_pPrev = this;
+    m_pFace = NULL;
+}///ListElem::ListElem()
+
+ListElem::ListElem(Face * pFace,ListElem * pHead)
+{
+    m_pNext = pHead;
+    m_pPrev = pHead->m_pPrev;
+    pHead->m_pPrev->m_pNext = this;
+    pHead->m_pPrev = this;
+
+    m_pFace = pFace;
+}//ListElem::ListElem(Face * pFace)
+
+
+
+ListElem::~ListElem()
+{
+    delete m_pFace;
+    m_pNext->m_pPrev = m_pPrev;
+    m_pPrev->m_pNext = m_pNext;
+
+}//ListElem::~ListElem()
+
+List::List()
+{
+    m_pHead = new ListElem();
+    m_FacesCount = 0;
+    m_pCurElem = m_pHead;
+}//List::List()
+
+List::~List()
+{
+    void * tmp;
+    while((tmp = m_pHead->m_pNext->m_pFace) != 0)
+        delete m_pHead->m_pNext;
+
+    delete m_pHead;
+
+}//List::~List()
+
+
+int List::AddElem(Face * pFace)
+{
+    new ListElem(pFace,m_pHead);
+    return m_FacesCount++;
+}//List::AddElem(Face * pFace)
+
+Face * List::GetData()
+{
+    m_pCurElem = m_pCurElem->m_pNext;
+    return m_pCurElem->m_pFace;
+}//Face * List::GetData()
+
+