| /*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() |
| |
| |