opencv/modules/contrib/src/adaptiveskindetector.cpp

289 lines
8.3 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.
//
// Copyright (C) 2009, Farhad Dadgostar
// Intel Corporation and 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*/
#include "precomp.hpp"
#define ASD_INTENSITY_SET_PIXEL(pointer, qq) {(*pointer) = (unsigned char)qq;}
#define ASD_IS_IN_MOTION(pointer, v, threshold) ((abs((*(pointer)) - (v)) > (threshold)) ? true : false)
void CvAdaptiveSkinDetector::initData(IplImage *src, int widthDivider, int heightDivider)
{
CvSize imageSize = cvSize(src->width/widthDivider, src->height/heightDivider);
imgHueFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 1);
imgShrinked = cvCreateImage(imageSize, IPL_DEPTH_8U, src->nChannels);
imgSaturationFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 1);
imgMotionFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 1);
imgTemp = cvCreateImage(imageSize, IPL_DEPTH_8U, 1);
imgFilteredFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 1);
imgGrayFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 1);
imgLastGrayFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 1);
imgHSVFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 3);
};
CvAdaptiveSkinDetector::CvAdaptiveSkinDetector(int samplingDivider, int morphingMethod)
{
nSkinHueLowerBound = GSD_HUE_LT;
nSkinHueUpperBound = GSD_HUE_UT;
fHistogramMergeFactor = 0.05; // empirical result
fHuePercentCovered = 0.95; // empirical result
nMorphingMethod = morphingMethod;
nSamplingDivider = samplingDivider;
nFrameCount = 0;
nStartCounter = 0;
imgHueFrame = NULL;
imgMotionFrame = NULL;
imgTemp = NULL;
imgFilteredFrame = NULL;
imgShrinked = NULL;
imgGrayFrame = NULL;
imgLastGrayFrame = NULL;
imgSaturationFrame = NULL;
imgHSVFrame = NULL;
};
CvAdaptiveSkinDetector::~CvAdaptiveSkinDetector()
{
cvReleaseImage(&imgHueFrame);
cvReleaseImage(&imgSaturationFrame);
cvReleaseImage(&imgMotionFrame);
cvReleaseImage(&imgTemp);
cvReleaseImage(&imgFilteredFrame);
cvReleaseImage(&imgShrinked);
cvReleaseImage(&imgGrayFrame);
cvReleaseImage(&imgLastGrayFrame);
cvReleaseImage(&imgHSVFrame);
};
void CvAdaptiveSkinDetector::process(IplImage *inputBGRImage, IplImage *outputHueMask)
{
IplImage *src = inputBGRImage;
int h, v, i, l;
bool isInit = false;
nFrameCount++;
if (imgHueFrame == NULL)
{
isInit = true;
initData(src, nSamplingDivider, nSamplingDivider);
}
unsigned char *pShrinked, *pHueFrame, *pMotionFrame, *pLastGrayFrame, *pFilteredFrame, *pGrayFrame;
pShrinked = (unsigned char *)imgShrinked->imageData;
pHueFrame = (unsigned char *)imgHueFrame->imageData;
pMotionFrame = (unsigned char *)imgMotionFrame->imageData;
pLastGrayFrame = (unsigned char *)imgLastGrayFrame->imageData;
pFilteredFrame = (unsigned char *)imgFilteredFrame->imageData;
pGrayFrame = (unsigned char *)imgGrayFrame->imageData;
if ((src->width != imgHueFrame->width) || (src->height != imgHueFrame->height))
{
cvResize(src, imgShrinked);
cvCvtColor(imgShrinked, imgHSVFrame, CV_BGR2HSV);
}
else
{
cvCvtColor(src, imgHSVFrame, CV_BGR2HSV);
}
cvSplit(imgHSVFrame, imgHueFrame, imgSaturationFrame, imgGrayFrame, 0);
cvSetZero(imgMotionFrame);
cvSetZero(imgFilteredFrame);
l = imgHueFrame->height * imgHueFrame->width;
for (i = 0; i < l; i++)
{
v = (*pGrayFrame);
if ((v >= GSD_INTENSITY_LT) && (v <= GSD_INTENSITY_UT))
{
h = (*pHueFrame);
if ((h >= GSD_HUE_LT) && (h <= GSD_HUE_UT))
{
if ((h >= nSkinHueLowerBound) && (h <= nSkinHueUpperBound))
ASD_INTENSITY_SET_PIXEL(pFilteredFrame, h);
if (ASD_IS_IN_MOTION(pLastGrayFrame, v, 7))
ASD_INTENSITY_SET_PIXEL(pMotionFrame, h);
}
}
pShrinked += 3;
pGrayFrame++;
pLastGrayFrame++;
pMotionFrame++;
pHueFrame++;
pFilteredFrame++;
}
if (isInit)
cvCalcHist(&imgHueFrame, skinHueHistogram.fHistogram);
cvCopy(imgGrayFrame, imgLastGrayFrame);
cvErode(imgMotionFrame, imgTemp); // eliminate disperse pixels, which occur because of the camera noise
cvDilate(imgTemp, imgMotionFrame);
cvCalcHist(&imgMotionFrame, histogramHueMotion.fHistogram);
skinHueHistogram.mergeWith(&histogramHueMotion, fHistogramMergeFactor);
skinHueHistogram.findCurveThresholds(nSkinHueLowerBound, nSkinHueUpperBound, 1 - fHuePercentCovered);
switch (nMorphingMethod)
{
case MORPHING_METHOD_ERODE :
cvErode(imgFilteredFrame, imgTemp);
cvCopy(imgTemp, imgFilteredFrame);
break;
case MORPHING_METHOD_ERODE_ERODE :
cvErode(imgFilteredFrame, imgTemp);
cvErode(imgTemp, imgFilteredFrame);
break;
case MORPHING_METHOD_ERODE_DILATE :
cvErode(imgFilteredFrame, imgTemp);
cvDilate(imgTemp, imgFilteredFrame);
break;
}
if (outputHueMask != NULL)
cvCopy(imgFilteredFrame, outputHueMask);
};
//------------------------- Histogram for Adaptive Skin Detector -------------------------//
CvAdaptiveSkinDetector::Histogram::Histogram()
{
int histogramSize[] = { HistogramSize };
float range[] = { GSD_HUE_LT, GSD_HUE_UT };
float *ranges[] = { range };
fHistogram = cvCreateHist(1, histogramSize, CV_HIST_ARRAY, ranges, 1);
cvClearHist(fHistogram);
};
CvAdaptiveSkinDetector::Histogram::~Histogram()
{
cvReleaseHist(&fHistogram);
};
int CvAdaptiveSkinDetector::Histogram::findCoverageIndex(double surfaceToCover, int defaultValue)
{
double s = 0;
for (int i = 0; i < HistogramSize; i++)
{
s += cvGetReal1D( fHistogram->bins, i );
if (s >= surfaceToCover)
{
return i;
}
}
return defaultValue;
};
void CvAdaptiveSkinDetector::Histogram::findCurveThresholds(int &x1, int &x2, double percent)
{
double sum = 0;
for (int i = 0; i < HistogramSize; i++)
{
sum += cvGetReal1D( fHistogram->bins, i );
}
x1 = findCoverageIndex(sum * percent, -1);
x2 = findCoverageIndex(sum * (1-percent), -1);
if (x1 == -1)
x1 = GSD_HUE_LT;
else
x1 += GSD_HUE_LT;
if (x2 == -1)
x2 = GSD_HUE_UT;
else
x2 += GSD_HUE_LT;
};
void CvAdaptiveSkinDetector::Histogram::mergeWith(CvAdaptiveSkinDetector::Histogram *source, double weight)
{
float myweight = (float)(1-weight);
float maxVal1 = 0, maxVal2 = 0, *f1, *f2, ff1, ff2;
cvGetMinMaxHistValue(source->fHistogram, NULL, &maxVal2);
if (maxVal2 > 0 )
{
cvGetMinMaxHistValue(fHistogram, NULL, &maxVal1);
if (maxVal1 <= 0)
{
for (int i = 0; i < HistogramSize; i++)
{
f1 = (float*)cvPtr1D(fHistogram->bins, i);
f2 = (float*)cvPtr1D(source->fHistogram->bins, i);
(*f1) = (*f2);
}
}
else
{
for (int i = 0; i < HistogramSize; i++)
{
f1 = (float*)cvPtr1D(fHistogram->bins, i);
f2 = (float*)cvPtr1D(source->fHistogram->bins, i);
ff1 = ((*f1)/maxVal1)*myweight;
if (ff1 < 0)
ff1 = -ff1;
ff2 = (float)(((*f2)/maxVal2)*weight);
if (ff2 < 0)
ff2 = -ff2;
(*f1) = (ff1 + ff2);
}
}
}
};