504 lines
14 KiB
C++
504 lines
14 KiB
C++
/*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 "precomp.hpp"
|
|
#include "_facedetection.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 FaceDetectionList();
|
|
|
|
|
|
|
|
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 FaceDetectionList();
|
|
|
|
}//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 = 0;
|
|
RFace * lpFace1 = 0;
|
|
bool bInvalidRect1 = false;
|
|
CvRect * lpRect1 = NULL;
|
|
|
|
try
|
|
{
|
|
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;
|
|
}
|
|
|
|
}
|
|
}
|
|
catch(...)
|
|
{
|
|
delete lpFaceTemplate1;
|
|
delete lpFace1;
|
|
throw;
|
|
}
|
|
}
|
|
|
|
|
|
void FaceDetection::PostBoostingFindCandidats(IplImage * FaceImage)
|
|
{
|
|
BoostingFaceTemplate * lpFaceTemplate1 = 0;
|
|
RFace * lpFace1 = 0;
|
|
bool bInvalidRect1 = false;
|
|
CvRect * lpRect1 = NULL;
|
|
|
|
try
|
|
{
|
|
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);
|
|
lpFace1 = NULL;
|
|
|
|
delete lpFaceTemplate1;
|
|
}
|
|
catch(...)
|
|
{
|
|
delete lpFace1;
|
|
delete lpFaceTemplate1;
|
|
throw;
|
|
}
|
|
}//void FaceDetection::PostBoostingFindCandidats(IplImage * FaceImage)
|
|
|
|
/////////////////////////
|
|
//class Face
|
|
|
|
|
|
|
|
//////
|
|
//FaceDetectionList Class
|
|
/////
|
|
FaceDetectionListElem::FaceDetectionListElem()
|
|
{
|
|
m_pNext = this;
|
|
m_pPrev = this;
|
|
m_pFace = NULL;
|
|
}///FaceDetectionListElem::FaceDetectionListElem()
|
|
|
|
FaceDetectionListElem::FaceDetectionListElem(Face * pFace,FaceDetectionListElem * pHead)
|
|
{
|
|
m_pNext = pHead;
|
|
m_pPrev = pHead->m_pPrev;
|
|
pHead->m_pPrev->m_pNext = this;
|
|
pHead->m_pPrev = this;
|
|
|
|
m_pFace = pFace;
|
|
}//FaceDetectionListElem::FaceDetectionListElem(Face * pFace)
|
|
|
|
|
|
|
|
FaceDetectionListElem::~FaceDetectionListElem()
|
|
{
|
|
delete m_pFace;
|
|
m_pNext->m_pPrev = m_pPrev;
|
|
m_pPrev->m_pNext = m_pNext;
|
|
|
|
}//FaceDetectionListElem::~FaceDetectionListElem()
|
|
|
|
FaceDetectionList::FaceDetectionList()
|
|
{
|
|
m_pHead = new FaceDetectionListElem();
|
|
m_FacesCount = 0;
|
|
m_pCurElem = m_pHead;
|
|
}//FaceDetectionList::FaceDetectionList()
|
|
|
|
FaceDetectionList::~FaceDetectionList()
|
|
{
|
|
void * tmp;
|
|
while((tmp = m_pHead->m_pNext->m_pFace) != 0)
|
|
delete m_pHead->m_pNext;
|
|
|
|
delete m_pHead;
|
|
|
|
}//FaceDetectionList::~FaceDetectionList()
|
|
|
|
|
|
int FaceDetectionList::AddElem(Face * pFace)
|
|
{
|
|
new FaceDetectionListElem(pFace,m_pHead);
|
|
return m_FacesCount++;
|
|
}//FaceDetectionList::AddElem(Face * pFace)
|
|
|
|
Face * FaceDetectionList::GetData()
|
|
{
|
|
m_pCurElem = m_pCurElem->m_pNext;
|
|
return m_pCurElem->m_pFace;
|
|
}//Face * FaceDetectionList::GetData()
|
|
|
|
|