查阅了博客上绝大多数关于皮肤分割和磨皮美颜的算法,然后自己找了各位博主的代码,有自己将各部分代码组合修改调试,最终写了份参照代码。至于引用哪些博主的代码,找的博客太多分不清楚了,有博主看到引用代码可以私聊我加上你的引用。
我感觉做出来的效果不太好,不如别人算法效果好。
此代码只是做了皮肤分割和滤波磨皮,美白的代码还没写。还有就是发布文章我不太懂它的分类选项,只好随便选了。发布文章的规则我也不懂,有什么问题可以给我说下。
代码如下:
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/imgproc/imgproc_c.h>
#include <opencv2/imgproc/types_c.h>
#include <iostream>
#include <vector>
using namespace std;
using namespace cv;
//椭圆检测
Mat ellipse_detect(Mat &src)
{
Mat img = src.clone();
Mat skinCrCbHist = Mat::zeros(Size(256, 256), CV_8UC1);//256*256的矩阵,相当于CrCb分量的横纵坐标
//利用Opencv自带的椭圆生成函数生成一个肤色椭圆模型
ellipse(skinCrCbHist, Point(113, 155.6), Size(23.4, 15.2), 43.0, 0.0, 360.0, Scalar(255, 255, 255), -1);
Mat ycrcb_image;
Mat output_mask = Mat::zeros(img.size(), CV_8UC1);
cvtColor(img, ycrcb_image, CV_BGR2YCrCb); //首先转换成到YCrCb空间
for (int i = 0; i < img.cols; i++)
{
for (int j = 0; j < img.rows; j++)
{
Vec3b ycrcb = ycrcb_image.at<Vec3b>(j, i);
if (skinCrCbHist.at<uchar>(ycrcb[1], ycrcb[2]) > 0)//如果该点落在皮肤模型椭圆区域内,则该点是皮肤像素点。
output_mask.at<uchar>(j, i) = 255;
}
}
return output_mask;
}
/*YCrCb颜色空间Cr分量+Otsu法*/
Mat YCrCb_Otsu_detect(Mat& src)
{
Mat ycrcb_image;
cvtColor(src, ycrcb_image, CV_BGR2YCrCb); //首先转换成到YCrCb空间
vector<Mat> channels;
split(ycrcb_image, channels);//分割颜色通道
Mat output_mask = channels[1];//取Cr通道的图像
threshold(output_mask, output_mask, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);//自动阈值分割
return output_mask;
}
Mat YCrCb_detect(Mat &src)//矩形区域分割
{
Mat img = src.clone();
Mat skinCrCbHist = Mat::zeros(Size(256, 256), CV_8UC1);//256*256的矩阵,相当于CrCb分量的横纵坐标
//利用Opencv自带的椭圆生成函数生成一个肤色椭圆模型
ellipse(skinCrCbHist, Point(113, 155.6), Size(23.4, 15.2), 43.0, 0.0, 360.0, Scalar(255, 255, 255), -1);
Mat ycrcb_image;
Mat output_mask = Mat::zeros(img.size(), CV_8UC1);
cvtColor(img, ycrcb_image, CV_BGR2YCrCb); //首先转换成到YCrCb空间
for (int i = 0; i < img.cols; i++)
{
for (int j = 0; j < img.rows; j++)
{
Vec3b ycrcb = ycrcb_image.at<Vec3b>(j, i);
if ((ycrcb[1] > 137) && (ycrcb[1] < 175) && (ycrcb[2] > 100) && (ycrcb[2] < 118))
output_mask.at<uchar>(j, i) = 255;//皮肤区域
}
}
return output_mask;
}
//锐化函数
Mat sharpen(Mat imageSrc, double threshold, double amount)
{
Mat blurred;
double sigma = 1;
GaussianBlur(imageSrc, blurred, Size(), sigma, sigma);
//int depth = CV_MAT_DEPTH(imageSrc.type());
Mat lowContrastMask = abs(imageSrc - blurred) < threshold;
Mat dst = imageSrc*(1 + amount) + blurred*(-amount); //original + (original - blurred) * amount
imageSrc.copyTo(dst, lowContrastMask); //将imageSrc中lowContrastMask对应的非0部分复制到dst中
//lowContrastMask等于1时,说明高频分量比较小,有可能是噪声,此时不做锐化
return dst;
}
Mat skinDivision(Mat ima)
{
Mat detect0, detect1, detect2;
各分割结果均返回灰度图mask
矩形分割
//Mat mer = YCrCb_detect(ima);
//imshow("矩形分割", detect0);
椭圆分割
//resize(ima, ima, Size(ima.cols / 2, ima.rows / 2));
//Mat mer = ellipse_detect(ima);
//imshow("椭圆分割", detect1);
//Cr+Otsu法阈值分割
Mat mer = YCrCb_Otsu_detect(ima);
imshow("阈值分割", mer);
return mer;
}
Mat wave(Mat detect)//滤波函数
{
Mat ret = detect.clone();
//int g_nBilateralFilterValue = 25; //双边滤波参数值,连通域大小
//bilateralFilter(ret, detect, 40, g_nBilateralFilterValue * 2, g_nBilateralFilterValue / 2);//ret结果图,detect输入
int value1 = 3, value2 = 3;
int dx = value1 * 5; //双边滤波参数之一
double fc = value1*12.5; //双边滤波参数之一
int p = 50;//透明度
Mat temp2;
//双边滤波
bilateralFilter(ret, detect, dx, fc, fc);
temp2 = (detect - ret + 128);
//高斯模糊
GaussianBlur(temp2, temp2, Size(2 * value2 - 1, 2 * value2 - 1), 0, 0);
temp2 = ret + 2 * temp2 - 255;
ret = (ret*(100 - p) + temp2*p) / 100;
return ret;
}
int main()
{
Mat srcImage = imread("d:\\4.jpg");
if (!srcImage.data)
{
printf("could not load image...\n");
return -1;
}
imshow("原图", srcImage);
Mat mer = skinDivision(srcImage);//肤色分割的mask
Mat detect;
srcImage.copyTo(detect, mer);//返回肤色分割图detect
imshow("肤色分割", detect);
//皮肤区域磨皮
Mat ret = wave(detect);//滤波函数
float w = 0.3;
//滤波图像和原图像选择性结合,把背景和皮肤区域结合
for (int i = 0; i < detect.rows ; i++)
{
for (int j = 0; j < detect.cols ; j++)
{
if(mer.at<uchar>(i, j) == 0)
ret.at<Vec3b>(i, j) = srcImage.at<Vec3b>(i, j);
else
{
ret.at<Vec3b>(i, j) = w*srcImage.at<Vec3b>(i, j) + (1-w)*detect.at<Vec3b>(i, j);
}
}
}
imshow("双边滤波", ret);
ret=sharpen(ret,1 ,1);
imshow("锐化", ret);
waitKey(0);
return 0;
}
注释的部分代码是自己调试使用的,其中有关于使用不同皮肤分割的算法。
各位有什么问题可以一起交流呀!