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