c4fbbe000aed88508eba983a5fc70f5f79793e26
[platform/upstream/opencv.git] / modules / contrib / src / adaptiveskindetector.cpp
1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 //  By downloading, copying, installing or using the software you agree to this license.
6 //  If you do not agree to this license, do not download, install, copy or use the software.
7 //
8 // Copyright (C) 2009, Farhad Dadgostar
9 // Intel Corporation and third party copyrights are property of their respective owners.
10 //
11 // Redistribution and use in source and binary forms, with or without modification,
12 // are permitted provided that the following conditions are met:
13 //
14 //   * Redistribution's of source code must retain the above copyright notice,
15 //     this list of conditions and the following disclaimer.
16 //
17 //   * Redistribution's in binary form must reproduce the above copyright notice,
18 //     this list of conditions and the following disclaimer in the documentation
19 //     and/or other materials provided with the distribution.
20 //
21 //   * The name of Intel Corporation may not be used to endorse or promote products
22 //     derived from this software without specific prior written permission.
23 //
24 // This software is provided by the copyright holders and contributors "as is" and
25 // any express or implied warranties, including, but not limited to, the implied
26 // warranties of merchantability and fitness for a particular purpose are disclaimed.
27 // In no event shall the Intel Corporation or contributors be liable for any direct,
28 // indirect, incidental, special, exemplary, or consequential damages
29 // (including, but not limited to, procurement of substitute goods or services;
30 // loss of use, data, or profits; or business interruption) however caused
31 // and on any theory of liability, whether in contract, strict liability,
32 // or tort (including negligence or otherwise) arising in any way out of
33 // the use of this software, even if advised of the possibility of such damage.
34 //
35 //M*/
36
37 #include "precomp.hpp"
38 #include "opencv2/imgproc/imgproc_c.h"
39 #include "opencv2/contrib/compat.hpp"
40
41 #define ASD_INTENSITY_SET_PIXEL(pointer, qq) {(*pointer) = (unsigned char)qq;}
42
43 #define ASD_IS_IN_MOTION(pointer, v, threshold) ((abs((*(pointer)) - (v)) > (threshold)) ? true : false)
44
45 void CvAdaptiveSkinDetector::initData(IplImage *src, int widthDivider, int heightDivider)
46 {
47     CvSize imageSize = cvSize(src->width/widthDivider, src->height/heightDivider);
48
49     imgHueFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 1);
50     imgShrinked = cvCreateImage(imageSize, IPL_DEPTH_8U, src->nChannels);
51     imgSaturationFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 1);
52     imgMotionFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 1);
53     imgTemp = cvCreateImage(imageSize, IPL_DEPTH_8U, 1);
54     imgFilteredFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 1);
55     imgGrayFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 1);
56     imgLastGrayFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 1);
57     imgHSVFrame = cvCreateImage(imageSize, IPL_DEPTH_8U, 3);
58 }
59
60 CvAdaptiveSkinDetector::CvAdaptiveSkinDetector(int samplingDivider, int morphingMethod)
61 {
62     nSkinHueLowerBound = GSD_HUE_LT;
63     nSkinHueUpperBound = GSD_HUE_UT;
64
65     fHistogramMergeFactor = 0.05;       // empirical result
66     fHuePercentCovered = 0.95;          // empirical result
67
68     nMorphingMethod = morphingMethod;
69     nSamplingDivider = samplingDivider;
70
71     nFrameCount = 0;
72     nStartCounter = 0;
73
74     imgHueFrame = NULL;
75     imgMotionFrame = NULL;
76     imgTemp = NULL;
77     imgFilteredFrame = NULL;
78     imgShrinked = NULL;
79     imgGrayFrame = NULL;
80     imgLastGrayFrame = NULL;
81     imgSaturationFrame = NULL;
82     imgHSVFrame = NULL;
83 }
84
85 CvAdaptiveSkinDetector::~CvAdaptiveSkinDetector()
86 {
87     cvReleaseImage(&imgHueFrame);
88     cvReleaseImage(&imgSaturationFrame);
89     cvReleaseImage(&imgMotionFrame);
90     cvReleaseImage(&imgTemp);
91     cvReleaseImage(&imgFilteredFrame);
92     cvReleaseImage(&imgShrinked);
93     cvReleaseImage(&imgGrayFrame);
94     cvReleaseImage(&imgLastGrayFrame);
95     cvReleaseImage(&imgHSVFrame);
96 }
97
98 void CvAdaptiveSkinDetector::process(IplImage *inputBGRImage, IplImage *outputHueMask)
99 {
100     IplImage *src = inputBGRImage;
101
102     int h, v, i, l;
103     bool isInit = false;
104
105     nFrameCount++;
106
107     if (imgHueFrame == NULL)
108     {
109         isInit = true;
110         initData(src, nSamplingDivider, nSamplingDivider);
111     }
112
113     unsigned char *pShrinked, *pHueFrame, *pMotionFrame, *pLastGrayFrame, *pFilteredFrame, *pGrayFrame;
114     pShrinked = (unsigned char *)imgShrinked->imageData;
115     pHueFrame = (unsigned char *)imgHueFrame->imageData;
116     pMotionFrame = (unsigned char *)imgMotionFrame->imageData;
117     pLastGrayFrame = (unsigned char *)imgLastGrayFrame->imageData;
118     pFilteredFrame = (unsigned char *)imgFilteredFrame->imageData;
119     pGrayFrame = (unsigned char *)imgGrayFrame->imageData;
120
121     if ((src->width != imgHueFrame->width) || (src->height != imgHueFrame->height))
122     {
123         cvResize(src, imgShrinked);
124         cvCvtColor(imgShrinked, imgHSVFrame, CV_BGR2HSV);
125     }
126     else
127     {
128         cvCvtColor(src, imgHSVFrame, CV_BGR2HSV);
129     }
130
131     cvSplit(imgHSVFrame, imgHueFrame, imgSaturationFrame, imgGrayFrame, 0);
132
133     cvSetZero(imgMotionFrame);
134     cvSetZero(imgFilteredFrame);
135
136     l = imgHueFrame->height * imgHueFrame->width;
137
138     for (i = 0; i < l; i++)
139     {
140         v = (*pGrayFrame);
141         if ((v >= GSD_INTENSITY_LT) && (v <= GSD_INTENSITY_UT))
142         {
143             h = (*pHueFrame);
144             if ((h >= GSD_HUE_LT) && (h <= GSD_HUE_UT))
145             {
146                 if ((h >= nSkinHueLowerBound) && (h <= nSkinHueUpperBound))
147                     ASD_INTENSITY_SET_PIXEL(pFilteredFrame, h);
148
149                 if (ASD_IS_IN_MOTION(pLastGrayFrame, v, 7))
150                     ASD_INTENSITY_SET_PIXEL(pMotionFrame, h);
151             }
152         }
153         pShrinked += 3;
154         pGrayFrame++;
155         pLastGrayFrame++;
156         pMotionFrame++;
157         pHueFrame++;
158         pFilteredFrame++;
159     }
160
161     if (isInit)
162         cvCalcHist(&imgHueFrame, skinHueHistogram.fHistogram);
163
164     cvCopy(imgGrayFrame, imgLastGrayFrame);
165
166     cvErode(imgMotionFrame, imgTemp);  // eliminate disperse pixels, which occur because of the camera noise
167     cvDilate(imgTemp, imgMotionFrame);
168
169     cvCalcHist(&imgMotionFrame, histogramHueMotion.fHistogram);
170
171     skinHueHistogram.mergeWith(&histogramHueMotion, fHistogramMergeFactor);
172
173     skinHueHistogram.findCurveThresholds(nSkinHueLowerBound, nSkinHueUpperBound, 1 - fHuePercentCovered);
174
175     switch (nMorphingMethod)
176     {
177         case MORPHING_METHOD_ERODE :
178             cvErode(imgFilteredFrame, imgTemp);
179             cvCopy(imgTemp, imgFilteredFrame);
180             break;
181         case MORPHING_METHOD_ERODE_ERODE :
182             cvErode(imgFilteredFrame, imgTemp);
183             cvErode(imgTemp, imgFilteredFrame);
184             break;
185         case MORPHING_METHOD_ERODE_DILATE :
186             cvErode(imgFilteredFrame, imgTemp);
187             cvDilate(imgTemp, imgFilteredFrame);
188             break;
189     }
190
191     if (outputHueMask != NULL)
192         cvCopy(imgFilteredFrame, outputHueMask);
193 }
194
195
196 //------------------------- Histogram for Adaptive Skin Detector -------------------------//
197
198 CvAdaptiveSkinDetector::Histogram::Histogram()
199 {
200     int histogramSize[] = { HistogramSize };
201     float range[] = { GSD_HUE_LT, GSD_HUE_UT };
202     float *ranges[] = { range };
203     fHistogram = cvCreateHist(1, histogramSize, CV_HIST_ARRAY, ranges, 1);
204     cvClearHist(fHistogram);
205 }
206
207 CvAdaptiveSkinDetector::Histogram::~Histogram()
208 {
209     cvReleaseHist(&fHistogram);
210 }
211
212 int CvAdaptiveSkinDetector::Histogram::findCoverageIndex(double surfaceToCover, int defaultValue)
213 {
214     double s = 0;
215     for (int i = 0; i < HistogramSize; i++)
216     {
217         s += cvGetReal1D( fHistogram->bins, i );
218         if (s >= surfaceToCover)
219         {
220             return i;
221         }
222     }
223     return defaultValue;
224 }
225
226 void CvAdaptiveSkinDetector::Histogram::findCurveThresholds(int &x1, int &x2, double percent)
227 {
228     double sum = 0;
229
230     for (int i = 0; i < HistogramSize; i++)
231     {
232         sum += cvGetReal1D( fHistogram->bins, i );
233     }
234
235     x1 = findCoverageIndex(sum * percent, -1);
236     x2 = findCoverageIndex(sum * (1-percent), -1);
237
238     if (x1 == -1)
239         x1 = GSD_HUE_LT;
240     else
241         x1 += GSD_HUE_LT;
242
243     if (x2 == -1)
244         x2 = GSD_HUE_UT;
245     else
246         x2 += GSD_HUE_LT;
247 }
248
249 void CvAdaptiveSkinDetector::Histogram::mergeWith(CvAdaptiveSkinDetector::Histogram *source, double weight)
250 {
251     float myweight = (float)(1-weight);
252     float maxVal1 = 0, maxVal2 = 0, *f1, *f2, ff1, ff2;
253
254     cvGetMinMaxHistValue(source->fHistogram, NULL, &maxVal2);
255
256     if (maxVal2 > 0 )
257     {
258         cvGetMinMaxHistValue(fHistogram, NULL, &maxVal1);
259         if (maxVal1 <= 0)
260         {
261             for (int i = 0; i < HistogramSize; i++)
262             {
263                 f1 = (float*)cvPtr1D(fHistogram->bins, i);
264                 f2 = (float*)cvPtr1D(source->fHistogram->bins, i);
265                 (*f1) = (*f2);
266             }
267         }
268         else
269         {
270             for (int i = 0; i < HistogramSize; i++)
271             {
272                 f1 = (float*)cvPtr1D(fHistogram->bins, i);
273                 f2 = (float*)cvPtr1D(source->fHistogram->bins, i);
274
275                 ff1 = ((*f1)/maxVal1)*myweight;
276                 if (ff1 < 0)
277                     ff1 = -ff1;
278
279                 ff2 = (float)(((*f2)/maxVal2)*weight);
280                 if (ff2 < 0)
281                     ff2 = -ff2;
282
283                 (*f1) = (ff1 + ff2);
284
285             }
286         }
287     }
288 }