1 /*M///////////////////////////////////////////////////////////////////////////////////////
\r
3 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
\r
5 // By downloading, copying, installing or using the software you agree to this license.
\r
6 // If you do not agree to this license, do not download, install, copy or use the software.
\r
8 // Copyright (C) 2009, Farhad Dadgostar
\r
9 // Intel Corporation and third party copyrights are property of their respective owners.
\r
11 // Redistribution and use in source and binary forms, with or without modification,
\r
12 // are permitted provided that the following conditions are met:
\r
14 // * Redistribution's of source code must retain the above copyright notice,
\r
15 // this list of conditions and the following disclaimer.
\r
17 // * Redistribution's in binary form must reproduce the above copyright notice,
\r
18 // this list of conditions and the following disclaimer in the documentation
\r
19 // and/or other materials provided with the distribution.
\r
21 // * The name of Intel Corporation may not be used to endorse or promote products
\r
22 // derived from this software without specific prior written permission.
\r
24 // This software is provided by the copyright holders and contributors "as is" and
\r
25 // any express or implied warranties, including, but not limited to, the implied
\r
26 // warranties of merchantability and fitness for a particular purpose are disclaimed.
\r
27 // In no event shall the Intel Corporation or contributors be liable for any direct,
\r
28 // indirect, incidental, special, exemplary, or consequential damages
\r
29 // (including, but not limited to, procurement of substitute goods or services;
\r
30 // loss of use, data, or profits; or business interruption) however caused
\r
31 // and on any theory of liability, whether in contract, strict liability,
\r
32 // or tort (including negligence or otherwise) arising in any way out of
\r
33 // the use of this software, even if advised of the possibility of such damage.
\r
39 #define ASD_INTENSITY_SET_PIXEL(pointer, qq) {(*pointer) = (unsigned char)qq;}
\r
41 #define ASD_IS_IN_MOTION(pointer, v, threshold) ((abs((*(pointer)) - (v)) > (threshold)) ? true : false)
\r
43 void CvAdaptiveSkinDetector::initData(IplImage *src, int widthDivider, int heightDivider)
\r
45 CvSize imageSize = cvSize(src->width/widthDivider, src->height/heightDivider);
\r
47 imgHueFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 1);
\r
48 imgShrinked = cvCreateImage(imageSize, IPL_DEPTH_8U, src->nChannels);
\r
49 imgSaturationFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 1);
\r
50 imgMotionFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 1);
\r
51 imgTemp = cvCreateImage(imageSize, IPL_DEPTH_8U, 1);
\r
52 imgFilteredFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 1);
\r
53 imgGrayFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 1);
\r
54 imgLastGrayFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 1);
\r
55 imgHSVFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 3);
\r
58 CvAdaptiveSkinDetector::CvAdaptiveSkinDetector(int samplingDivider, int morphingMethod)
\r
60 nSkinHueLowerBound = GSD_HUE_LT;
\r
61 nSkinHueUpperBound = GSD_HUE_UT;
\r
63 fHistogramMergeFactor = 0.05; // empirical result
\r
64 fHuePercentCovered = 0.95; // empirical result
\r
66 nMorphingMethod = morphingMethod;
\r
67 nSamplingDivider = samplingDivider;
\r
73 imgMotionFrame = NULL;
\r
75 imgFilteredFrame = NULL;
\r
77 imgGrayFrame = NULL;
\r
78 imgLastGrayFrame = NULL;
\r
79 imgSaturationFrame = NULL;
\r
82 CvAdaptiveSkinDetector::~CvAdaptiveSkinDetector()
\r
84 cvReleaseImage(&imgHueFrame);
\r
85 cvReleaseImage(&imgSaturationFrame);
\r
86 cvReleaseImage(&imgMotionFrame);
\r
87 cvReleaseImage(&imgTemp);
\r
88 cvReleaseImage(&imgFilteredFrame);
\r
89 cvReleaseImage(&imgShrinked);
\r
90 cvReleaseImage(&imgGrayFrame);
\r
91 cvReleaseImage(&imgLastGrayFrame);
\r
94 void CvAdaptiveSkinDetector::process(IplImage *inputBGRImage, IplImage *outputHueMask)
\r
96 IplImage *src = inputBGRImage;
\r
99 bool isInit = false;
\r
103 if (imgHueFrame == NULL)
\r
106 initData(src, nSamplingDivider, nSamplingDivider);
\r
109 unsigned char *pShrinked, *pHueFrame, *pMotionFrame, *pLastGrayFrame, *pFilteredFrame, *pGrayFrame;
\r
110 pShrinked = (unsigned char *)imgShrinked->imageData;
\r
111 pHueFrame = (unsigned char *)imgHueFrame->imageData;
\r
112 pMotionFrame = (unsigned char *)imgMotionFrame->imageData;
\r
113 pLastGrayFrame = (unsigned char *)imgLastGrayFrame->imageData;
\r
114 pFilteredFrame = (unsigned char *)imgFilteredFrame->imageData;
\r
115 pGrayFrame = (unsigned char *)imgGrayFrame->imageData;
\r
117 if ((src->width != imgHueFrame->width) || (src->height != imgHueFrame->height))
\r
119 cvResize(src, imgShrinked);
\r
120 cvCvtColor(imgShrinked, imgHSVFrame, CV_BGR2HSV);
\r
124 cvCvtColor(src, imgHSVFrame, CV_BGR2HSV);
\r
127 cvSplit(imgHSVFrame, imgHueFrame, imgSaturationFrame, imgGrayFrame, 0);
\r
129 cvSetZero(imgMotionFrame);
\r
130 cvSetZero(imgFilteredFrame);
\r
132 l = imgHueFrame->height * imgHueFrame->width;
\r
134 for (i = 0; i < l; i++)
\r
137 if ((v >= GSD_INTENSITY_LT) && (v <= GSD_INTENSITY_UT))
\r
140 if ((h >= GSD_HUE_LT) && (h <= GSD_HUE_UT))
\r
142 if ((h >= nSkinHueLowerBound) && (h <= nSkinHueUpperBound))
\r
143 ASD_INTENSITY_SET_PIXEL(pFilteredFrame, h);
\r
145 if (ASD_IS_IN_MOTION(pLastGrayFrame, v, 7))
\r
146 ASD_INTENSITY_SET_PIXEL(pMotionFrame, h);
\r
158 cvCalcHist(&imgHueFrame, skinHueHistogram.fHistogram);
\r
160 cvCopy(imgGrayFrame, imgLastGrayFrame);
\r
162 cvErode(imgMotionFrame, imgTemp); // eliminate disperse pixels, which occur because of the camera noise
\r
163 cvDilate(imgTemp, imgMotionFrame);
\r
165 cvCalcHist(&imgMotionFrame, histogramHueMotion.fHistogram);
\r
167 skinHueHistogram.mergeWith(&histogramHueMotion, fHistogramMergeFactor);
\r
169 skinHueHistogram.findCurveThresholds(nSkinHueLowerBound, nSkinHueUpperBound, 1 - fHuePercentCovered);
\r
171 switch (nMorphingMethod)
\r
173 case MORPHING_METHOD_ERODE :
\r
174 cvErode(imgFilteredFrame, imgTemp);
\r
175 cvCopy(imgTemp, imgFilteredFrame);
\r
177 case MORPHING_METHOD_ERODE_ERODE :
\r
178 cvErode(imgFilteredFrame, imgTemp);
\r
179 cvErode(imgTemp, imgFilteredFrame);
\r
181 case MORPHING_METHOD_ERODE_DILATE :
\r
182 cvErode(imgFilteredFrame, imgTemp);
\r
183 cvDilate(imgTemp, imgFilteredFrame);
\r
187 if (outputHueMask != NULL)
\r
188 cvCopy(imgFilteredFrame, outputHueMask);
\r
192 //------------------------- Histogram for Adaptive Skin Detector -------------------------//
\r
194 CvAdaptiveSkinDetector::Histogram::Histogram()
\r
196 int histogramSize[] = { HistogramSize };
\r
197 float range[] = { GSD_HUE_LT, GSD_HUE_UT };
\r
198 float *ranges[] = { range };
\r
199 fHistogram = cvCreateHist(1, histogramSize, CV_HIST_ARRAY, ranges, 1);
\r
200 cvClearHist(fHistogram);
\r
203 CvAdaptiveSkinDetector::Histogram::~Histogram()
\r
205 cvReleaseHist(&fHistogram);
\r
208 int CvAdaptiveSkinDetector::Histogram::findCoverageIndex(double surfaceToCover, int defaultValue)
\r
211 for (int i = 0; i < HistogramSize; i++)
\r
213 s += *cvGetHistValue_1D( fHistogram, i );
\r
214 if (s >= surfaceToCover)
\r
219 return defaultValue;
\r
222 void CvAdaptiveSkinDetector::Histogram::findCurveThresholds(int &x1, int &x2, double percent)
\r
226 for (int i = 0; i < HistogramSize; i++)
\r
228 sum += *cvGetHistValue_1D( fHistogram, i );
\r
231 x1 = findCoverageIndex(sum * percent, -1);
\r
232 x2 = findCoverageIndex(sum * (1-percent), -1);
\r
245 void CvAdaptiveSkinDetector::Histogram::mergeWith(CvAdaptiveSkinDetector::Histogram *source, double weight)
\r
247 float myweight = (float)(1-weight);
\r
248 float maxVal1 = 0, maxVal2 = 0, *f1, *f2, ff1, ff2;
\r
250 cvGetMinMaxHistValue(source->fHistogram, NULL, &maxVal2);
\r
254 cvGetMinMaxHistValue(fHistogram, NULL, &maxVal1);
\r
257 for (int i = 0; i < HistogramSize; i++)
\r
259 f1 = cvGetHistValue_1D(fHistogram, i);
\r
260 f2 = cvGetHistValue_1D(source->fHistogram, i);
\r
266 for (int i = 0; i < HistogramSize; i++)
\r
268 f1 = cvGetHistValue_1D(fHistogram, i);
\r
269 f2 = cvGetHistValue_1D(source->fHistogram, i);
\r
271 ff1 = ((*f1)/maxVal1)*myweight;
\r
275 ff2 = (float)(((*f2)/maxVal2)*weight);
\r
279 (*f1) = (ff1 + ff2);
\r