Update to 2.0.0 tree from current Fremantle build
[opencv] / src / cvaux / cvadaptiveskindetector.cpp
1 /*M///////////////////////////////////////////////////////////////////////////////////////\r
2 //\r
3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.\r
4 //\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
7 //\r
8 // Copyright (C) 2009, Farhad Dadgostar\r
9 // Intel Corporation and third party copyrights are property of their respective owners.\r
10 //\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
13 //\r
14 //   * Redistribution's of source code must retain the above copyright notice,\r
15 //     this list of conditions and the following disclaimer.\r
16 //\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
20 //\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
23 //\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
34 //\r
35 //M*/\r
36 \r
37 #include "_cvaux.h"\r
38 \r
39 #define ASD_INTENSITY_SET_PIXEL(pointer, qq) {(*pointer) = (unsigned char)qq;}\r
40 \r
41 #define ASD_IS_IN_MOTION(pointer, v, threshold) ((abs((*(pointer)) - (v)) > (threshold)) ? true : false)\r
42 \r
43 void CvAdaptiveSkinDetector::initData(IplImage *src, int widthDivider, int heightDivider)\r
44 {\r
45         CvSize imageSize = cvSize(src->width/widthDivider, src->height/heightDivider);\r
46 \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
56 };\r
57 \r
58 CvAdaptiveSkinDetector::CvAdaptiveSkinDetector(int samplingDivider, int morphingMethod)\r
59 {\r
60         nSkinHueLowerBound = GSD_HUE_LT;\r
61         nSkinHueUpperBound = GSD_HUE_UT;\r
62 \r
63         fHistogramMergeFactor = 0.05;   // empirical result\r
64         fHuePercentCovered = 0.95;              // empirical result\r
65 \r
66         nMorphingMethod = morphingMethod;\r
67         nSamplingDivider = samplingDivider;\r
68 \r
69         nFrameCount = 0;\r
70         nStartCounter = 0;\r
71 \r
72         imgHueFrame = NULL;\r
73         imgMotionFrame = NULL;\r
74         imgTemp = NULL;\r
75         imgFilteredFrame = NULL;\r
76         imgShrinked = NULL;\r
77         imgGrayFrame = NULL;\r
78         imgLastGrayFrame = NULL;\r
79         imgSaturationFrame = NULL;\r
80 };\r
81 \r
82 CvAdaptiveSkinDetector::~CvAdaptiveSkinDetector()\r
83 {\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
92 };\r
93 \r
94 void CvAdaptiveSkinDetector::process(IplImage *inputBGRImage, IplImage *outputHueMask)\r
95 {\r
96         IplImage *src = inputBGRImage;\r
97 \r
98         int h, v, i, l;\r
99         bool isInit = false;\r
100 \r
101         nFrameCount++;\r
102 \r
103         if (imgHueFrame == NULL)\r
104         {\r
105                 isInit = true;\r
106                 initData(src, nSamplingDivider, nSamplingDivider);\r
107         }\r
108 \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
116 \r
117         if ((src->width != imgHueFrame->width) || (src->height != imgHueFrame->height))\r
118         {\r
119                 cvResize(src, imgShrinked);\r
120                 cvCvtColor(imgShrinked, imgHSVFrame, CV_BGR2HSV);\r
121         }\r
122         else\r
123         {\r
124                 cvCvtColor(src, imgHSVFrame, CV_BGR2HSV);\r
125         }\r
126 \r
127         cvSplit(imgHSVFrame, imgHueFrame, imgSaturationFrame, imgGrayFrame, 0);\r
128 \r
129         cvSetZero(imgMotionFrame);\r
130         cvSetZero(imgFilteredFrame);\r
131 \r
132         l = imgHueFrame->height * imgHueFrame->width;\r
133 \r
134         for (i = 0; i < l; i++)\r
135         {\r
136                 v = (*pGrayFrame);\r
137                 if ((v >= GSD_INTENSITY_LT) && (v <= GSD_INTENSITY_UT))\r
138                 {\r
139                         h = (*pHueFrame);\r
140                         if ((h >= GSD_HUE_LT) && (h <= GSD_HUE_UT))\r
141                         {\r
142                                 if ((h >= nSkinHueLowerBound) && (h <= nSkinHueUpperBound))\r
143                                         ASD_INTENSITY_SET_PIXEL(pFilteredFrame, h);\r
144 \r
145                                 if (ASD_IS_IN_MOTION(pLastGrayFrame, v, 7))\r
146                                         ASD_INTENSITY_SET_PIXEL(pMotionFrame, h);\r
147                         }\r
148                 }\r
149                 pShrinked += 3;\r
150                 pGrayFrame++;\r
151                 pLastGrayFrame++;\r
152                 pMotionFrame++;\r
153                 pHueFrame++;\r
154                 pFilteredFrame++;\r
155         }\r
156 \r
157         if (isInit)\r
158                 cvCalcHist(&imgHueFrame, skinHueHistogram.fHistogram);\r
159 \r
160         cvCopy(imgGrayFrame, imgLastGrayFrame);\r
161 \r
162         cvErode(imgMotionFrame, imgTemp);  // eliminate disperse pixels, which occur because of the camera noise\r
163         cvDilate(imgTemp, imgMotionFrame);\r
164 \r
165         cvCalcHist(&imgMotionFrame, histogramHueMotion.fHistogram);\r
166 \r
167         skinHueHistogram.mergeWith(&histogramHueMotion, fHistogramMergeFactor);\r
168 \r
169         skinHueHistogram.findCurveThresholds(nSkinHueLowerBound, nSkinHueUpperBound, 1 - fHuePercentCovered);\r
170 \r
171         switch (nMorphingMethod)\r
172         {\r
173                 case MORPHING_METHOD_ERODE :\r
174                         cvErode(imgFilteredFrame, imgTemp);\r
175                         cvCopy(imgTemp, imgFilteredFrame);\r
176                         break;\r
177                 case MORPHING_METHOD_ERODE_ERODE :\r
178                         cvErode(imgFilteredFrame, imgTemp);\r
179                         cvErode(imgTemp, imgFilteredFrame);\r
180                         break;\r
181                 case MORPHING_METHOD_ERODE_DILATE :\r
182                         cvErode(imgFilteredFrame, imgTemp);\r
183                         cvDilate(imgTemp, imgFilteredFrame);\r
184                         break;\r
185         }\r
186 \r
187         if (outputHueMask != NULL)\r
188                 cvCopy(imgFilteredFrame, outputHueMask);\r
189 };\r
190 \r
191 \r
192 //------------------------- Histogram for Adaptive Skin Detector -------------------------//\r
193 \r
194 CvAdaptiveSkinDetector::Histogram::Histogram()\r
195 {\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
201 };\r
202 \r
203 CvAdaptiveSkinDetector::Histogram::~Histogram()\r
204 {\r
205         cvReleaseHist(&fHistogram);\r
206 };\r
207 \r
208 int CvAdaptiveSkinDetector::Histogram::findCoverageIndex(double surfaceToCover, int defaultValue)\r
209 {\r
210         float s = 0;\r
211         for (int i = 0; i < HistogramSize; i++)\r
212         {\r
213                 s += *cvGetHistValue_1D( fHistogram, i );\r
214                 if (s >= surfaceToCover)\r
215                 {\r
216                         return i;\r
217                 }\r
218         }\r
219         return defaultValue;\r
220 };\r
221 \r
222 void CvAdaptiveSkinDetector::Histogram::findCurveThresholds(int &x1, int &x2, double percent)\r
223 {\r
224         float sum = 0;\r
225 \r
226         for (int i = 0; i < HistogramSize; i++)\r
227         {\r
228                 sum += *cvGetHistValue_1D( fHistogram, i );\r
229         }\r
230 \r
231         x1 = findCoverageIndex(sum * percent, -1);\r
232         x2 = findCoverageIndex(sum * (1-percent), -1);\r
233 \r
234         if (x1 == -1)\r
235                 x1 = GSD_HUE_LT;\r
236         else\r
237                 x1 += GSD_HUE_LT;\r
238 \r
239         if (x2 == -1)\r
240                 x2 = GSD_HUE_UT;\r
241         else\r
242                 x2 += GSD_HUE_LT;\r
243 };\r
244 \r
245 void CvAdaptiveSkinDetector::Histogram::mergeWith(CvAdaptiveSkinDetector::Histogram *source, double weight)\r
246 {\r
247         float myweight = (float)(1-weight);\r
248         float maxVal1 = 0, maxVal2 = 0, *f1, *f2, ff1, ff2;\r
249 \r
250         cvGetMinMaxHistValue(source->fHistogram, NULL, &maxVal2);\r
251 \r
252         if (maxVal2 > 0 )\r
253         {\r
254                 cvGetMinMaxHistValue(fHistogram, NULL, &maxVal1);\r
255                 if (maxVal1 <= 0)\r
256                 {\r
257                         for (int i = 0; i < HistogramSize; i++)\r
258                         {\r
259                                 f1 = cvGetHistValue_1D(fHistogram, i);\r
260                                 f2 = cvGetHistValue_1D(source->fHistogram, i);\r
261                                 (*f1) = (*f2);\r
262                         }\r
263                 }\r
264                 else\r
265                 {\r
266                         for (int i = 0; i < HistogramSize; i++)\r
267                         {\r
268                                 f1 = cvGetHistValue_1D(fHistogram, i);\r
269                                 f2 = cvGetHistValue_1D(source->fHistogram, i);\r
270 \r
271                                 ff1 = ((*f1)/maxVal1)*myweight;\r
272                                 if (ff1 < 0)\r
273                                         ff1 = -ff1;\r
274 \r
275                                 ff2 = (float)(((*f2)/maxVal2)*weight);\r
276                                 if (ff2 < 0)\r
277                                         ff2 = -ff2;\r
278 \r
279                                 (*f1) = (ff1 + ff2);\r
280 \r
281                         }\r
282                 }\r
283         }\r
284 };\r
285 \r
286 \r