/*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); } } } };