通过色调、饱和度进行肤色检测
肤色检测领域的大量研究表明,来自不同人种人群的皮肤颜色,可以在色调-饱和度色彩空间中很好的归类,本实例仅适用色调和饱和度值来识别肤色。
cv::Mat skinDetect(const cv::Mat& image, double minHue, double maxHue, double minSat, double maxSat)
{
//创建HSV图像
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, minHue, 255, cv::THRESH_BINARY);
//创建小于最大色调的掩码
cv::Mat mask2;
cv::threshold(channels[0], mask2, maxHue, 255, cv::THRESH_BINARY_INV);
//创建色调掩码
cv::Mat hueMask;
//如果输入的最小色的小于最大色调,取交
if (minHue < maxHue)
{
hueMask = mask1 & mask2;
}
//如果输入的最小色的大于最大色调,即横跨了0度中轴线
else
{
hueMask = mask1 | mask2;
}
//创建饱和度掩码
cv::Mat satMask;
//由于饱和度没有中轴线,直接调用cv::inRange函数创建二值图即可
cv::inRange(channels[1], minSat, maxSat, satMask);
return hueMask & satMask;
}
int main()
{
cv::Mat nana = cv::imread("nana.jpg");
cv::Mat mask;
//设置的色调范围为160~15之间(320~30),饱和度范围在25~180之间(0.1~0.65)
mask = skinDetect(nana, 160, 15, 25, 166);
cv::Mat detected(nana.size(), CV_8UC3, cv::Scalar(0, 0, 0));
//通过copyTo加掩码的方法将原图拷贝到检测图中
nana.copyTo(detected, mask);
//输出原图和检测图
cv::imshow("nana", nana);
cv::imshow("detected", detected);
cv::waitKey();
return 0;
}
本例中没有考虑颜色的亮度,排除较高亮度的颜色可以降低把明亮的淡红色误认为皮肤的可能性。
效果
补充:cv::inRange
函数的使用
函数类似cv::threshold函数(例3.2),可以识别图像中一定范围的像素,并创建二值图,以便作为掩码分割图像。
函数签名
CV_EXPORTS_W void inRange(InputArray src, InputArray lowerb,
InputArray upperb, OutputArray dst);
参数分别为:输入图像,最低阈值,最高阈值,输出掩码图
这里的阈值在单通道为常数uchar
,在多通道时为向量cv::Vec3b
,其中每个通道都必须在给定范围内。在没有中轴线的情况下比threshold函数方便一些。