--- /dev/null
+/*M///////////////////////////////////////////////////////////////////////////////////////\r
+//\r
+// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.\r
+//\r
+// By downloading, copying, installing or using the software you agree to this license.\r
+// If you do not agree to this license, do not download, install, copy or use the software.\r
+//\r
+// Copyright (C) 2009, Farhad Dadgostar\r
+// Intel Corporation and third party copyrights are property of their respective owners.\r
+//\r
+// Redistribution and use in source and binary forms, with or without modification,\r
+// are permitted provided that the following conditions are met:\r
+//\r
+// * Redistribution's of source code must retain the above copyright notice,\r
+// this list of conditions and the following disclaimer.\r
+//\r
+// * Redistribution's in binary form must reproduce the above copyright notice,\r
+// this list of conditions and the following disclaimer in the documentation\r
+// and/or other materials provided with the distribution.\r
+//\r
+// * The name of Intel Corporation may not be used to endorse or promote products\r
+// derived from this software without specific prior written permission.\r
+//\r
+// This software is provided by the copyright holders and contributors "as is" and\r
+// any express or implied warranties, including, but not limited to, the implied\r
+// warranties of merchantability and fitness for a particular purpose are disclaimed.\r
+// In no event shall the Intel Corporation or contributors be liable for any direct,\r
+// indirect, incidental, special, exemplary, or consequential damages\r
+// (including, but not limited to, procurement of substitute goods or services;\r
+// loss of use, data, or profits; or business interruption) however caused\r
+// and on any theory of liability, whether in contract, strict liability,\r
+// or tort (including negligence or otherwise) arising in any way out of\r
+// the use of this software, even if advised of the possibility of such damage.\r
+//\r
+//M*/\r
+\r
+#include "_cvaux.h"\r
+\r
+#define ASD_INTENSITY_SET_PIXEL(pointer, qq) {(*pointer) = (unsigned char)qq;}\r
+\r
+#define ASD_IS_IN_MOTION(pointer, v, threshold) ((abs((*(pointer)) - (v)) > (threshold)) ? true : false)\r
+\r
+void CvAdaptiveSkinDetector::initData(IplImage *src, int widthDivider, int heightDivider)\r
+{\r
+ CvSize imageSize = cvSize(src->width/widthDivider, src->height/heightDivider);\r
+\r
+ imgHueFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 1);\r
+ imgShrinked = cvCreateImage(imageSize, IPL_DEPTH_8U, src->nChannels);\r
+ imgSaturationFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 1);\r
+ imgMotionFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 1);\r
+ imgTemp = cvCreateImage(imageSize, IPL_DEPTH_8U, 1);\r
+ imgFilteredFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 1);\r
+ imgGrayFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 1);\r
+ imgLastGrayFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 1);\r
+ imgHSVFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 3);\r
+};\r
+\r
+CvAdaptiveSkinDetector::CvAdaptiveSkinDetector(int samplingDivider, int morphingMethod)\r
+{\r
+ nSkinHueLowerBound = GSD_HUE_LT;\r
+ nSkinHueUpperBound = GSD_HUE_UT;\r
+\r
+ fHistogramMergeFactor = 0.05; // empirical result\r
+ fHuePercentCovered = 0.95; // empirical result\r
+\r
+ nMorphingMethod = morphingMethod;\r
+ nSamplingDivider = samplingDivider;\r
+\r
+ nFrameCount = 0;\r
+ nStartCounter = 0;\r
+\r
+ imgHueFrame = NULL;\r
+ imgMotionFrame = NULL;\r
+ imgTemp = NULL;\r
+ imgFilteredFrame = NULL;\r
+ imgShrinked = NULL;\r
+ imgGrayFrame = NULL;\r
+ imgLastGrayFrame = NULL;\r
+ imgSaturationFrame = NULL;\r
+};\r
+\r
+CvAdaptiveSkinDetector::~CvAdaptiveSkinDetector()\r
+{\r
+ cvReleaseImage(&imgHueFrame);\r
+ cvReleaseImage(&imgSaturationFrame);\r
+ cvReleaseImage(&imgMotionFrame);\r
+ cvReleaseImage(&imgTemp);\r
+ cvReleaseImage(&imgFilteredFrame);\r
+ cvReleaseImage(&imgShrinked);\r
+ cvReleaseImage(&imgGrayFrame);\r
+ cvReleaseImage(&imgLastGrayFrame);\r
+};\r
+\r
+void CvAdaptiveSkinDetector::process(IplImage *inputBGRImage, IplImage *outputHueMask)\r
+{\r
+ IplImage *src = inputBGRImage;\r
+\r
+ int h, v, i, l;\r
+ bool isInit = false;\r
+\r
+ nFrameCount++;\r
+\r
+ if (imgHueFrame == NULL)\r
+ {\r
+ isInit = true;\r
+ initData(src, nSamplingDivider, nSamplingDivider);\r
+ }\r
+\r
+ unsigned char *pShrinked, *pHueFrame, *pMotionFrame, *pLastGrayFrame, *pFilteredFrame, *pGrayFrame;\r
+ pShrinked = (unsigned char *)imgShrinked->imageData;\r
+ pHueFrame = (unsigned char *)imgHueFrame->imageData;\r
+ pMotionFrame = (unsigned char *)imgMotionFrame->imageData;\r
+ pLastGrayFrame = (unsigned char *)imgLastGrayFrame->imageData;\r
+ pFilteredFrame = (unsigned char *)imgFilteredFrame->imageData;\r
+ pGrayFrame = (unsigned char *)imgGrayFrame->imageData;\r
+\r
+ if ((src->width != imgHueFrame->width) || (src->height != imgHueFrame->height))\r
+ {\r
+ cvResize(src, imgShrinked);\r
+ cvCvtColor(imgShrinked, imgHSVFrame, CV_BGR2HSV);\r
+ }\r
+ else\r
+ {\r
+ cvCvtColor(src, imgHSVFrame, CV_BGR2HSV);\r
+ }\r
+\r
+ cvSplit(imgHSVFrame, imgHueFrame, imgSaturationFrame, imgGrayFrame, 0);\r
+\r
+ cvSetZero(imgMotionFrame);\r
+ cvSetZero(imgFilteredFrame);\r
+\r
+ l = imgHueFrame->height * imgHueFrame->width;\r
+\r
+ for (i = 0; i < l; i++)\r
+ {\r
+ v = (*pGrayFrame);\r
+ if ((v >= GSD_INTENSITY_LT) && (v <= GSD_INTENSITY_UT))\r
+ {\r
+ h = (*pHueFrame);\r
+ if ((h >= GSD_HUE_LT) && (h <= GSD_HUE_UT))\r
+ {\r
+ if ((h >= nSkinHueLowerBound) && (h <= nSkinHueUpperBound))\r
+ ASD_INTENSITY_SET_PIXEL(pFilteredFrame, h);\r
+\r
+ if (ASD_IS_IN_MOTION(pLastGrayFrame, v, 7))\r
+ ASD_INTENSITY_SET_PIXEL(pMotionFrame, h);\r
+ }\r
+ }\r
+ pShrinked += 3;\r
+ pGrayFrame++;\r
+ pLastGrayFrame++;\r
+ pMotionFrame++;\r
+ pHueFrame++;\r
+ pFilteredFrame++;\r
+ }\r
+\r
+ if (isInit)\r
+ cvCalcHist(&imgHueFrame, skinHueHistogram.fHistogram);\r
+\r
+ cvCopy(imgGrayFrame, imgLastGrayFrame);\r
+\r
+ cvErode(imgMotionFrame, imgTemp); // eliminate disperse pixels, which occur because of the camera noise\r
+ cvDilate(imgTemp, imgMotionFrame);\r
+\r
+ cvCalcHist(&imgMotionFrame, histogramHueMotion.fHistogram);\r
+\r
+ skinHueHistogram.mergeWith(&histogramHueMotion, fHistogramMergeFactor);\r
+\r
+ skinHueHistogram.findCurveThresholds(nSkinHueLowerBound, nSkinHueUpperBound, 1 - fHuePercentCovered);\r
+\r
+ switch (nMorphingMethod)\r
+ {\r
+ case MORPHING_METHOD_ERODE :\r
+ cvErode(imgFilteredFrame, imgTemp);\r
+ cvCopy(imgTemp, imgFilteredFrame);\r
+ break;\r
+ case MORPHING_METHOD_ERODE_ERODE :\r
+ cvErode(imgFilteredFrame, imgTemp);\r
+ cvErode(imgTemp, imgFilteredFrame);\r
+ break;\r
+ case MORPHING_METHOD_ERODE_DILATE :\r
+ cvErode(imgFilteredFrame, imgTemp);\r
+ cvDilate(imgTemp, imgFilteredFrame);\r
+ break;\r
+ }\r
+\r
+ if (outputHueMask != NULL)\r
+ cvCopy(imgFilteredFrame, outputHueMask);\r
+};\r
+\r
+\r
+//------------------------- Histogram for Adaptive Skin Detector -------------------------//\r
+\r
+CvAdaptiveSkinDetector::Histogram::Histogram()\r
+{\r
+ int histogramSize[] = { HistogramSize };\r
+ float range[] = { GSD_HUE_LT, GSD_HUE_UT };\r
+ float *ranges[] = { range };\r
+ fHistogram = cvCreateHist(1, histogramSize, CV_HIST_ARRAY, ranges, 1);\r
+ cvClearHist(fHistogram);\r
+};\r
+\r
+CvAdaptiveSkinDetector::Histogram::~Histogram()\r
+{\r
+ cvReleaseHist(&fHistogram);\r
+};\r
+\r
+int CvAdaptiveSkinDetector::Histogram::findCoverageIndex(double surfaceToCover, int defaultValue)\r
+{\r
+ float s = 0;\r
+ for (int i = 0; i < HistogramSize; i++)\r
+ {\r
+ s += *cvGetHistValue_1D( fHistogram, i );\r
+ if (s >= surfaceToCover)\r
+ {\r
+ return i;\r
+ }\r
+ }\r
+ return defaultValue;\r
+};\r
+\r
+void CvAdaptiveSkinDetector::Histogram::findCurveThresholds(int &x1, int &x2, double percent)\r
+{\r
+ float sum = 0;\r
+\r
+ for (int i = 0; i < HistogramSize; i++)\r
+ {\r
+ sum += *cvGetHistValue_1D( fHistogram, i );\r
+ }\r
+\r
+ x1 = findCoverageIndex(sum * percent, -1);\r
+ x2 = findCoverageIndex(sum * (1-percent), -1);\r
+\r
+ if (x1 == -1)\r
+ x1 = GSD_HUE_LT;\r
+ else\r
+ x1 += GSD_HUE_LT;\r
+\r
+ if (x2 == -1)\r
+ x2 = GSD_HUE_UT;\r
+ else\r
+ x2 += GSD_HUE_LT;\r
+};\r
+\r
+void CvAdaptiveSkinDetector::Histogram::mergeWith(CvAdaptiveSkinDetector::Histogram *source, double weight)\r
+{\r
+ float myweight = (float)(1-weight);\r
+ float maxVal1 = 0, maxVal2 = 0, *f1, *f2, ff1, ff2;\r
+\r
+ cvGetMinMaxHistValue(source->fHistogram, NULL, &maxVal2);\r
+\r
+ if (maxVal2 > 0 )\r
+ {\r
+ cvGetMinMaxHistValue(fHistogram, NULL, &maxVal1);\r
+ if (maxVal1 <= 0)\r
+ {\r
+ for (int i = 0; i < HistogramSize; i++)\r
+ {\r
+ f1 = cvGetHistValue_1D(fHistogram, i);\r
+ f2 = cvGetHistValue_1D(source->fHistogram, i);\r
+ (*f1) = (*f2);\r
+ }\r
+ }\r
+ else\r
+ {\r
+ for (int i = 0; i < HistogramSize; i++)\r
+ {\r
+ f1 = cvGetHistValue_1D(fHistogram, i);\r
+ f2 = cvGetHistValue_1D(source->fHistogram, i);\r
+\r
+ ff1 = ((*f1)/maxVal1)*myweight;\r
+ if (ff1 < 0)\r
+ ff1 = -ff1;\r
+\r
+ ff2 = (float)(((*f2)/maxVal2)*weight);\r
+ if (ff2 < 0)\r
+ ff2 = -ff2;\r
+\r
+ (*f1) = (ff1 + ff2);\r
+\r
+ }\r
+ }\r
+ }\r
+};\r
+\r
+\r