一个简单好用的磨皮祛斑算法理论和python实现

前言

最近看了一个磨皮算法祛斑感觉效果不错,效果图看文末就行,个人觉得效果非常不错滴。

国际惯例,参考博客:

磨皮算法的源码:YUCIHighPassSkinSmoothing

How To Smooth And Soften Skin With Photoshop

图像算法—磨皮算法研究汇总

妹纸们的最爱 - 美颜算法,美颜SDK

.Net里面的coreImage

IOS里面的coreImage

Core Image Kernel Language Reference

Core Image Filter Reference

如何分析ps中的曲线?曲线都能做哪些方面的调整?原理是什么?

Core Image框架详细解析(十三) —— 在写一个自定义滤波器之前你需要知道什么?

Python Pillow – Adjust Image Sharpness

先看看它惊艳的结果

在这里插入图片描述

源码剖析

本博文重点是解析源码结构和简单的python复现,不会深究其原理,因为作者也说了,是按照PhotoShop的一个磨皮祛斑步骤(参考博客二)实现的。

源码的核心实现在YUCIHighPassSkinSmoothing.m中,最后一个output函数记录了整个算法的流程。

以下图为例

在这里插入图片描述

整体步骤

首先是通过类似于高反差保留的代码,制作一个mask,这个可以看参考博客四的解释

YUCIHighPassSkinSmoothingMaskGenerator *maskGenerator = [[YUCIHighPassSkinSmoothingMaskGenerator alloc] init];maskGenerator.inputRadius = self.inputRadius;maskGenerator.inputImage = self.inputImage;

然后通过Photoshop里面的曲线操作,调整图像亮度

YUCIRGBToneCurve *skinToneCurveFilter = self.skinToneCurveFilter;
skinToneCurveFilter.inputImage = self.inputImage;
skinToneCurveFilter.inputIntensity = self.inputAmount;

亮度调整完毕以后,就将原图和亮度图按照mask定义的混合系数进行混合

double sharpnessValue = self.inputSharpnessFactor.doubleValue * self.inputAmount.doubleValue;if (sharpnessValue > 0) {CIFilter *shapenFilter = [CIFilter filterWithName:@"CISharpenLuminance"];[shapenFilter setValue:@(sharpnessValue) forKey:@"inputSharpness"];[shapenFilter setValue:blendWithMaskFilter.outputImage forKey:kCIInputImageKey];return shapenFilter.outputImage;

高反差保留

进入到YUCIHighPassSkinSmoothingMaskGenerator的实现中,发现有四个步骤:

  • 先进行曝光度调整CIExposureAdjust,系数为−1.0-1.01.0

  • 然后进行绿色和蓝色通道的混合叠加YUCIGreenBlueChannelOverlayBlend,代码也很简单

    vec4 base = vec4(image.g,image.g,image.g,1.0);
    vec4 overlay = vec4(image.b,image.b,image.b,1.0);
    float ba = 2.0 * overlay.b * base.b + overlay.b * (1.0 - base.a) + base.b * (1.0 - overlay.a);
    

    使用的时候直接2×Gchannel×Bchannel2\times G_{channel} \times B_{channel}2×Gchannel×Bchannel

  • 接下来进入高通滤波YUCIHighPass的环节,非常简单,先高斯模糊一下子,然后跟原图做个混合,别人说这应该是就是高反差保留的计算方法:

    CIFilter *blurFilter = [CIFilter filterWithName:@"CIGaussianBlur"];
    [blurFilter setValue:self.inputImage.imageByClampingToExtent forKey:kCIInputImageKey];
    
    image.rgb - blurredImage.rgb + vec3(0.5,0.5,0.5)
    
  • 最后要做三次增强YUCIHighPassSkinSmoothingMaskBoost,也是一个公式
    p′={p∗p∗2p≤0.51−(1−p)(1−p)∗2p>0.5p'=\begin{cases} p*p*2&p\leq 0.5\\ 1-(1-p)(1-p)*2&p >0.5 \end{cases} p={pp21(1p)(1p)2p0.5p>0.5
    对上面的公式循环三次

    float hardLightColor = image.b;
    for (int i = 0; i < 3; ++i) {if (hardLightColor < 0.5) {hardLightColor = hardLightColor  * hardLightColor * 2.;} else {hardLightColor = 1. - (1. - hardLightColor) * (1. - hardLightColor) * 2.;}
    }
    

    但是源码实现的时候,又加入了进一步的操作

    const float k = 255.0 / (164.0 - 75.0);hardLightColor = (hardLightColor - 75.0 / 255.0) * k;
    

至此,计算mask的流程结束

曲线调亮

这个源码部分实现很复杂,但是从这个issue可以发现,源码的实现其实就是和PhotoShop里面的曲线调整一模一样,去搜索PS的曲线调整原理(参考博客有),就会发现很简单,直接用三次样条曲线,将旧的像素值映射到新的像素值就行了,源码里面计算三次样条的锚点有三个(0,0),(120/255.0,146/255.0),(1,1)(0,0),(120/255.0,146/255.0),(1,1)(0,0),(120/255.0,146/255.0),(1,1),在python里面有很多库都有这个函数。

简单的融合与锐化

最后计算按照高反差保留计算得到的mask,将曲线调亮后的图与原图进行融合,关于CIBlendWithMask这个API的解释看这里,系数为0时候得到背景,系数为1的时候得到前景。

因为上面图像有点模糊,所以就加了锐化,其实还有很多超高清算法或者人脸mask划分的方法去优化最终成图。

python实现的核心

因为代码贴的有点多,这里只介绍复现时候遇到的坑,完整python源码文末获取。

第一个坑就是计算mask中有一步要高斯模糊,但是opencv的高斯模糊函数除了半径外,还有一个sigma系数,怎么调都和源码不一样,后来看了PIL库,貌似没有这个多余的系数,和源码效果一模一样,所以这一步不要用opencv的函数,而是用PIL去处理

# YUCIHighPass
# 先进行高斯模糊 
# PIL的方法
pil_img = np2pil(ba_img)
pil_blur = pil_img.filter(ImageFilter.GaussianBlur(radius))
blur_img = np.asarray(pil_blur,np.float32)/255.0
plt.figure(figsize=(8,8))
plt.imshow(blur_img)
plt.axis('off')

第二个坑是,我们的理解中像素值以0-1为标准,但是在进行三次增强时候,作者加入了其它的一个操作,导致值超出了这个范围,所以需要对结果加个clip 规整到0-1

# YUCIHighPassSkinSmoothingMaskBoost
hardLightColor = hp_img[...,2]
[x1,y1] = np.where(hardLightColor<0.5)
[x2,y2] = np.where(hardLightColor>=0.5)
for i in range(3):hardLightColor[x1,y1] = hardLightColor[x1,y1]*hardLightColor[x1,y1]*2.0hardLightColor[x2,y2] = 1.0 - (1.0 - hardLightColor[x2,y2]) * (1.0 - hardLightColor[x2,y2]) * 2.0
k = 255.0/(164.0-75.0);
hardLightColor = (hardLightColor - 75.0/255.0) * k
hpss_img = np.zeros((hardLightColor.shape[0],hardLightColor.shape[1],3))
hpss_img[...,0] = hardLightColor
hpss_img[...,1] = hardLightColor
hpss_img[...,2] = hardLightColorhpss_img = np.clip(hpss_img,0,1)

最终得到的mask类似于这样

在这里插入图片描述

第四个坑,就是做RGB tone curve的时候,一直不知道是啥操作,最后去看PS的方法,实现了一个粗略的版本,从结果来看,和源码跑出来的结果差不多

from scipy.interpolate import CubicSpline
x = [0,120.0/255.0,1]
y = [0,146.0/255.0,1]#146
cs = CubicSpline(x,y)
tc_img = cs(input_img)

在这里插入图片描述

最后进行加和就行了,结果对比如下:

自己的python脚本结果

在这里插入图片描述

源码结果:
在这里插入图片描述
效果差不多,只不过我的图片貌似糊了一点,无伤大雅,斑点没了就行。

后记

博客只是做了最基本的理论实现,里面有很多优化方向,涉及商业机密就不放出来了,最基本理论实现就是按照源码一步步走。

完整的python脚本实现放在微信公众号的简介中描述的github中,有兴趣可以去找找,同时文章也同步到微信公众号中,有疑问或者兴趣欢迎公众号私信。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/246571.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OpenVINO——配置与道路分割案例

前言 最近看到了一个深度学习库OpenVINO&#xff0c;专门用于Intel硬件上部署深度学习模型&#xff0c;其内置了非常非常多使用的预训练模型&#xff0c;比如道路分割、人脸提取、3D姿态估计等等。但是配置和调用有点小恶心&#xff0c;这里以道路分割为例&#xff0c;展示如何…

图像颜色迁移《color transfer between images》

前言 前段时间&#xff0c;在深度学习领域不是有个比较火的方向叫风格迁移的嘛&#xff0c;对于我这种不喜欢深度学习那种不稳定结果的人来说&#xff0c;还是想看看传统图像处理领域有什么类似的技术&#xff0c;发现了一个颜色迁移的算法&#xff0c;很久前的论文了。 国际…

ColorSpace颜色空间简介

前言 如果看过之前的介绍的图像颜色迁移《color transfer between images》和颜色协调模型Color Harmoniztion就会发现&#xff0c;大部分图像处理算法虽然输入输出是RGB像素值&#xff0c;但是中间进行算法处理时很少直接更改RGB值&#xff0c;而是转换到其它空间&#xff0c…

Ogre共享骨骼与两种骨骼驱动方法

前言 最近业务中用到Ogre做基于3D关键点虚拟角色骨骼驱动&#xff0c;但是遇到两个问题&#xff1a; 身体、头、眼睛、衣服等mesh的骨骼是分开的&#xff0c;但是骨骼结构都是一样的&#xff0c;需要设置共享骨骼驱动的时候可以直接修改骨骼旋转量&#xff0c;或者将旋转量存…

仿射变换和透视变换

前言 在前面做换脸的博客中提到了使用仿射变换和透视变换将两张不同的人脸基于关键点进行对齐&#xff0c;保证一张人脸贴到另一张人脸时&#xff0c;大小完全一致&#xff1b;所以有必要理解一下这两个概念的区别&#xff0c;由于以实用性为目的&#xff0c;所以所有的图像算…

obj格式解析

前言 最近处理一些网格渲染的时候&#xff0c;需要解析Obj文件&#xff0c;从Free3D上随便找了个免费的人体obj模型解析测试一波 国际惯例&#xff0c;参考博客&#xff1a; 本文所使用的从Free3D下载的模型 .obj文件格式与.mtl文件格式 详解3D中的obj文件格式 3D中OBJ文…

Flask服务部署与简单内网穿透

前言 最近学习部署的时候&#xff0c;想到深度学习里面通常用的部署方法是flask做服务端&#xff0c;然后使用nginx做负载均衡&#xff0c;貌似也能做内网穿透。不过我不太懂负载均衡&#xff0c;只想利用本地电脑搭建一个简单的服务器&#xff0c;实现外部调用API服务的功能。…

OpenCV学习——轮廓检测

前言 轮廓检测是传统视觉中非常常用的功能&#xff0c;这里简单记录一下opencv中的轮廓检测算法使用方法&#xff0c;至于理论&#xff0c;后续有机会再去细品。 国际惯例&#xff1a; OpenCV官方的轮廓检测教程python版 OpenCV中的二值化方法教程 OpenCV轮廓层级官方文档…

RBF神经网络理论与实现

前言 最近发现有挺多人喜欢径向基函数(Radial Basis Function,RBF)神经网络&#xff0c;其实它就是将RBF作为神经网络层间的一种连接方式而已。这里做一个简单的描述和找了个代码解读。 之前也写过一篇&#xff0c;不过排版不好看&#xff0c;可以戳这里跳转 国际惯例&#x…

基于python和unity交互的卡通角色肢体和表情驱动(深度学习)

前言 最近看到了好多卡通角色的肢体驱动的东东&#xff0c;感觉是时候发挥一下读研时候学的东西了&#xff0c;而且虽然现在不炼丹了&#xff0c;但是还是得保持吃丹的技能。这个项目找了很多很多代码进行测试&#xff0c;最终集成了一个3D姿态估计和人脸关键点提取的代码。 …

OpenCV学习——形态学

前言 继续学习图像里面的形态学知识——结构元、腐蚀、膨胀、开运算、闭运算、击中/不击中变换。以及部分基本形态学算法&#xff0c;包括边界提取、空洞填充、连通分量的提取、凸壳、细化、粗化、骨架、裁剪、形态学重建。 其实就是对冈萨雷斯的《数字图像处理》中第9章节《…

径向基函数RBF三维网格变形

前言 之前写过径向基函数(RBF)神经网络做分类或者拟合。然后挖了个坑说在《Phase-Functioned Neural Networks for Character Control》里面提到了用于做地形编辑&#xff0c;所以这篇博客就是解析一下如何用RBF做网格编辑系统。 参考博客&#xff1a; Noe’s tutorial on d…

OBJ可视化——UV还原(修正)

前言 前面写过一篇obj格式解析的博客&#xff0c;但是这篇文章中可视化的工作是参考PRNet的源码进行的&#xff0c;后来细细思考了一下&#xff0c;有点问题&#xff0c;具体看下面。 问题来源 在PRNet源码的render.py中有个函数render_texture&#xff0c;是作者用于将uv展…

Unity中BVH骨骼动画驱动的可视化理论与实现

前言 找了很久使用BVH到unity中驱动骨骼动画的代码&#xff0c;但是都不是特别好用&#xff0c;自己以前写过&#xff0c;原理很简单&#xff0c;这里记录一下。 理论 初始姿态 在BVH或者其它骨骼动画中&#xff0c;一般涉及到三种姿势&#xff1a;A-pose&#xff0c;T-pos…

卡通驱动项目ThreeDPoseTracker——模型驱动解析

前言 之前解析过ThreeDPoseTracker这个项目中的深度学习模型&#xff0c;公众号有兄弟私信一些问题&#xff0c;我刚好对这个项目实现有兴趣&#xff0c;就分析一波源码&#xff0c;顺便把问题解答一下。 这个源码其实包括很多内容&#xff1a;3D姿态估计&#xff0c;坐标平滑…

卡通驱动项目ThreeDPoseTracker——关键点平滑方案解析

前言 之前对ThreeDPoseTracker的深度学习模型和unity中的驱动方法进行过解析&#xff0c;还有一个比较重要的就是从深度学习模型出来的3D关键点数据会有抖动&#xff0c;在ThreeDPoseTracker源码中有做两次平滑&#xff0c;一部分是卡尔曼滤波&#xff0c;还有一部分是低通滤波…

卡通角色表情驱动系列一

前言 分析完ThreeDPoseTracker来做卡通角色的身体驱动&#xff0c;接下来在卡通驱动领域还有一个是表情驱动。对这个真的是一窍不通啊&#xff0c;只能慢慢看论文了。 国际惯例&#xff0c;参考博客/论文&#xff1a; 《Landmark-guided deformation transfer of template f…

opencv相机标定和人头姿态估计案例

前言 头部驱动除了之前关注的表情驱动外&#xff0c;还有眼球驱动和头部方向驱动。本博客基于opencv官方文档和部分开源代码来研究如何基于人脸关键点获取头部的朝向。 国际惯例&#xff0c;参考博客&#xff1a; opencv:Camera Calibration and 3D Reconstruction opencv:…

卡通角色表情驱动系列二

前言 之前介绍了使用传统算法求解BS系数的表情驱动方法&#xff0c;其中提到过的三种方法之一是基于网格形变迁移做的&#xff0c;那么这篇文章就是对《Deformation Transfer for Triangle Meshes》做表情驱动的解析。 国际惯例&#xff0c;参考博客&#xff1a; 论文原文《…

UE自带重定向原理

UE自带重定向方法验证 核心源码在VS的解决方案中的位置&#xff1a; UE4\Source\Developer\AssetTools\Private\AssetTypeActions\AnimSequence.cpp中第3237行RemapTracksToNewSkeleton函数 跳转方法 AssetTypeActions_AnimationAsset.cpp的RetargetNonSkeletonAnimationHa…