Update to 2.0.0 tree from current Fremantle build
[opencv] / src / cvaux / cvadaptiveskindetector.cpp
diff --git a/src/cvaux/cvadaptiveskindetector.cpp b/src/cvaux/cvadaptiveskindetector.cpp
new file mode 100644 (file)
index 0000000..3ccb347
--- /dev/null
@@ -0,0 +1,286 @@
+/*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