基于双边滤波的磨皮算法及优化
一.背景简介
现在视频类应用非常火热,直播、美妆、医美应用层出不穷。用户们在使用这类应用时都希望自己在屏幕上的样子美美的,皮肤细腻光滑。本文就介绍一种实现简单、效果很好的磨皮算法以及对它的优化思路。
二.磨皮算法
磨皮的基本方法
磨皮算法的核心就是让皮肤区域的像素变得平滑,过渡自然,这样皮肤上的瑕疵就不容易察觉了,皮肤看起来十分光滑。这里包括了两部分算法:
1. 图像滤波算法;
2. 皮肤区域检测算法。
整体的流程如下:
皮肤区域检测可参考技术的真相 | 从AR口红试妆了解人工智能试妆技术,此文不再赘述。
常用的滤波方法有盒型滤波(如均值滤波)、统计排序滤波(如中值滤波)、高斯滤波和保边滤波(如双边滤波)等。不同滤波算法产生的效果会有很大区别。其中一些对去除噪点效果很好,如中值滤波;一些产生平滑的画面效果,如均值滤波、高斯滤波、保边滤波等。
为什么要用保边的滤波算法
左图使用的是高斯滤波,权重符合二维高斯分布,中间权重高,越往外权重越低,在各个方向上距中心同一距离的位置上的权重是一致的。这样的卷积核简单粗暴,不管图像颜色信息如何,统一对待,所以在输出图像上可见山石边缘也变得非常模糊。
让我们来看一下双边滤波和高斯滤波应用在真实人像上的效果对比(点击图片可查看大图):
这是一张原始图片
应用双边滤波算法(spaceSigma=4.0,colorSigma=18),可以观察到人物五官基本上仍然是清晰,眼镜边缘也没有什么变化。
三.双边滤波算法
双边滤波的参数和效果
让我们对比一下高斯滤波和双边滤波的效果。这两种滤波器在OpenCV中都有实现,所以直接调用OpenCV的接口得到如下结果(点击图片可查看大图):
注:实验图片使用经典的lena图像,512x512像素,tiff格式。
对比上面几幅图像,可以看到双边滤波可以明显地去处原图上的噪点,并且保留了相对清晰的物体轮廓。对比原图和spaceSigma=4,colorSigma=32的双边滤波结果,人物脸上和肩膀上的斑点都消失了,皮肤看起来光滑水润。随着参数改变,我们看到滤波结果发生了很大变化。
0. 基本实现
最基本的实现方式就是按照上文中的公式,对每个像素进行二维卷积计算。这里使用OpenCV实现基本的双边滤波算法,以RGB3通道为例:
void BilateralBlur(const Mat &srcImage, Mat &dstImage, int kernelSize, double sigmaColor, double sigmaSpace) {
int height = srcImage.rows;
int width = srcImage.cols;
int channel = srcImage.channels();
if (dstImage.rows == 0 || dstImage.cols == 0)
dstImage = Mat::zeros(srcImage.size(), srcImage.type());
double ct = -0.5 / (sigmaColor * sigmaColor);
double st = -0.5 / (sigmaSpace * sigmaSpace);
int radius = (kernelSize - 1) / 2;
double w;
double sw;
double cw;
for (int row = 0; row < height; ++row) {
for (int col = 0; col < width; ++col) {
double sumr = 0, sumg = 0, sumb = 0, sumw = 0;
Vec3b bgr0 = srcImage.at<Vec3b>(row, col);
for (int n = -radius; n <= radius; n += step) {
int y = clamp(row + n, 0, height - 1);
for (int m = -radius; m <= radius; m += step) {
int x = clamp(col + m, 0, width - 1);
Vec3b bgr = srcImage.at<Vec3b>(y, x);
sw = exp((m * m + n * n) * st);
double c = abs(bgr0[0] - bgr[0]) + abs(bgr0[1] - bgr[1]) + abs(bgr0[2] - bgr[2]); // sum of difference of each channel
cw = exp(c * c * ct);
double w = sw * cw;
sumb += b * w;
sumg += g * w;
sumr += r * w;
sumw += w;
}
}
dstImage.at<Vec3b>(row, col) = Vec3b(saturate_cast<uchar>(sumb / sumw),
saturate_cast<uchar>(sumg / sumw),
saturate_cast<uchar>(sumr / sumw));
}
}
}
void BilateralBlur(const Mat &srcImage, Mat &dstImage, int kernelSize, double sigmaColor, double sigmaSpace) {
int height = srcImage.rows;
int width = srcImage.cols;
int channel = srcImage.channels();
if (dstImage.rows == 0 || dstImage.cols == 0)
dstImage = Mat::zeros(srcImage.size(), srcImage.type());
double ct = -0.5 / (sigmaColor * sigmaColor);
double st = -0.5 / (sigmaSpace * sigmaSpace);
int radius = (kernelSize - 1) / 2;
double w;
double sw;
double cw;
for (int row = 0; row < height; ++row) {
for (int col = 0; col < width; ++col) {
double sumr = 0, sumg = 0, sumb = 0, sumw = 0;
Vec3b bgr0 = srcImage.at<Vec3b>(row, col);
for (int n = -radius; n <= radius; n += step) {
int y = clamp(row + n, 0, height - 1);
for (int m = -radius; m <= radius; m += step) {
int x = clamp(col + m, 0, width - 1);
Vec3b bgr = srcImage.at<Vec3b>(y, x);
sw = exp((m * m + n * n) * st);
double c = abs(bgr0[0] - bgr[0]) + abs(bgr0[1] - bgr[1]) + abs(bgr0[2] - bgr[2]); // sum of difference of each channel
cw = exp(c * c * ct);
double w = sw * cw;
sumb += b * w;
sumg += g * w;
sumr += r * w;
sumw += w;
}
}
dstImage.at<Vec3b>(row, col) = Vec3b(saturate_cast<uchar>(sumb / sumw),
saturate_cast<uchar>(sumg / sumw),
saturate_cast<uchar>(sumr / sumw));
}
}
}
就可以把一次二维卷积分解成两次一维卷积,单个像素卷积操作的计算复杂度从
3. 使用递归方法近似计算
R. Deriche在1993年提出了一种递归高斯滤波方法,论文名称是Recursively implementating the Gaussian and its derivatives。在这种方法中,二维高斯卷积核被分解成两个一维高斯卷积核相乘,每一次卷积计算包含6次乘法和6次加法,与卷积核的尺寸无关。计算复杂度下降为O(1)。对于大小超过7x7的卷积核,性能都可以有所提升,卷积核越大提升越明显。
实验参数均为spaceSigma=4,colorSigma=32。
从PSNR数值来看,几种优化方法和标准算法区别不大。肉眼观察,可见一些细微的差异,但整体效果接近。
三.参考文献
Bilateral Filtering for Gray and Color Images. C. Tomasi, R. Manduchi Recursively implementating the Gaussian and its derivatives. R. Deriche Recursive Bilateral Filtering. Q. Yang
交流群
欢迎加入公众号读者群一起和同行交流,目前有美颜、三维视觉、计算摄影、检测、分割、识别、医学影像、GAN、算法竞赛等微信群
个人微信(如果没有备注不拉群!) 请注明:地区+学校/企业+研究方向+昵称
下载1:何恺明顶会分享
在「AI算法与图像处理」公众号后台回复:何恺明,即可下载。总共有6份PDF,涉及 ResNet、Mask RCNN等经典工作的总结分析
下载2:终身受益的编程指南:Google编程风格指南
在「AI算法与图像处理」公众号后台回复:c++,即可下载。历经十年考验,最权威的编程规范!
下载3 CVPR2021 在「AI算法与图像处理」公众号后台回复:CVPR,即可下载1467篇CVPR 2020论文 和 CVPR 2021 最新论文