OpenCV使用颜色进行肤色检测

961 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第18天,点击查看活动详情

前言

颜色信息对于特定对象的初始检测非常有用。例如,辅助驾驶应用程序中的道路标志检测可以依靠标准标志的颜色来快速提取潜在的候选道路标志。皮肤颜色的检测是另一个例子,其中检测到的皮肤区域可以用作图像中是否有人的指标;这种方法经常用于手势识别,使用肤色检测来检测手部位置。

使用颜色进行肤色检测

通常,要使用颜色进行对象检测,首先需要收集包含从不同环境条件获取的对象的大型图像样本数据库。需要使用这些图像样本定义分类器参数,并用于分类的颜色表示。对于肤色检测,研究表明,来自不同种族的肤色在色调饱和度空间中能够很好地聚集。出于以上原因,我们将使用色调和饱和度值来识别下图中的肤色:

F8.png

因此,我们定义一个函数 detectHScolor,该函数基于值的区间(最小和最大色调,以及最小和最大饱和度值)将图像的像素分类为皮肤或非皮肤:

void detectHScolor(const cv::Mat& image,    // 输入图像
            double minHue, double maxHue,   // Hue 区间
            double minSat, double maxSat,   // Saturation 区间
            cv::Mat& mask) {                // 输出掩码
    // 转换色彩空间
    cv::Mat hsv;
    cv::cvtColor(image, hsv, cv::COLOR_BGR2HSV);
    // 分割图像通道
    std::vector<cv::Mat> channels;
    cv::split(hsv, channels);
    cv::Mat mask1;
    cv::threshold(channels[0], mask1, maxHue, 255, cv::THRESH_BINARY_INV);
    cv::Mat mask2;
    cv::threshold(channels[0], mask2, minHue, 255, cv::THRESH_BINARY);
    cv::Mat hueMask;
    if (minHue < maxHue) hueMask = mask1 & mask2;
    else hueMask = mask1 | mask2;
    cv::threshold(channels[1], mask1, maxSat, 255, cv::THRESH_BINARY_INV);
    cv::threshold(channels[1], mask2, minSat, 255, cv::THRESH_BINARY);
    cv::Mat satMask;
    satMask = mask1 & mask2;
    // 组合掩码
    mask = hueMask & satMask;
}

有大量包含皮肤(和非皮肤)的图像样本可供使用,我们可以使用概率方法来确定在皮肤类中观察给定颜色的可能性与在非皮肤类中观察到相同颜色的可能性。在本节中,我们根据经验为测试图像定义了一个可接受的色调饱和度间区间( 8 位版本的色调从 0180,饱和度从 0255 ):

detectHScolor(image, 140, 10, 20, 166, mask);
cv::Mat detected(image.size(), CV_8UC3, cv::Scalar(0, 0, 0));
image.copyTo(detected, mask);

执行以上程序,结果得到如下检测图像:

F9.png

为简单起见,我们没有在检测中考虑颜色饱和度。实际上,排除具有高饱和度的颜色会降低将亮红色错误检测为皮肤的可能性。显然,可靠且准确的肤色检测需要基于对大量皮肤样本更加精细的分析。仅使用色调信息很难保证对不同图像的均具有良好的检测效果,因为有许多因素会影响摄影中的色彩效果,例如白平衡和照明条件。尽管如此,仅使用色调信息作为初始检测器也可以得到在可接受范围内的结果。