导
语
最近偶然翻到一篇来自港中文的paper,十分有意思,可以根据拍摄到的2D图像自动生成该图的铅笔素描画。随手复现了一下,在这里分享给大家。让我们愉快地开始吧~
开发工具
Python版本:3.6.4
相关模块:
opencv-python模块;
numpy模块;
argparse模块;
pillow模块;
scipy模块;
以及一些python自带的模块。
环境搭建
安装Python并添加到环境变量,pip安装需要的相关模块即可。关注公众号“Charles的皮卡丘”,公众号内回复‘铅笔素描画’获取。
原理介绍
paper(在相关文件里提供了)是:
Combining Sketch and Tone for Pencil Drawing Production
这里主要介绍一下paper提出的铅笔素描画生成算法步骤,其他诸如相关工作之类的就不介绍了。而且这里我假设想了解该算法原理的小伙伴是掌握图像处理基础知识的。
先放算法框架,然后再慢慢说:
该基于图像的铅笔素描画生成算法主要分为两个步骤:
pencil stroke generation;
pencil tone drawing.
前者的目标为表示图像场景的一般结构(主要是绘制一些线条),后者则更专注于图像内物体的形状和产生的阴影等内容。下面分别详细地介绍一下这两个步骤。
1.pencil stroke generation
这个步骤的目标主要是通过一些线条来表示图像场景的一般结构(也就是轮廓图)。首先假设我们有输入灰度图I,于是我们可以获得图像的梯度图G:
但显然计算图像梯度是很容易受到噪声干扰的,因此这样生成的图像轮廓图效果肯定不好。为了得到更加稳定的结果,作者首先对G进行8个方向的卷积(例如0,22.5,...,157.5):
每个方向的卷积核为沿着该方向的值为1,其他方向的值为0。当然,实际应用中,考虑抗锯齿问题,卷积核可以通过双线性插值得到(类似于指定角度的运动模糊处理)。同时,在论文中,作者根据经验认为卷积核大小一般取原图大小的1/30。
得到各个方向的卷积结果后,对每个像素点,我们都有8个卷积响应值,每个响应值对应一个方向。由此,我们可以得到每个方向的卷积响应图,其定义为(注:原文中给的公式是错的,显然是笔误了):
这里p代表像素点,G为之前获得的图像梯度图。对得到的每个方向的卷积响应图再次进行方向卷积:
然后对得到的S'进行像素值反转和映射到[0, 1]操作就可以得到最终的图像轮廓图S了。作者画了个图更加形象直观地说明了上述处理过程:
以及与直接使用G来生成图像轮廓图的对比:
2.pencil tone drawing
OK,接下来我们来介绍该算法的第二部分,也就是色调渲染,该部分主要包括以下两个步骤:
直方图匹配;
纹理渲染。
(1)直方图匹配
作者通过对真实的铅笔素描画的观察发现,铅笔画的色调可以由三部分组成,如下图所示:
可以看出,阴影部分很适合用正态分布来拟合,中间调很适合用均匀分布来拟合,而高光部分则很适合用拉普拉斯分布来拟合。具体而言,对于高光部分,使用以下函数来模拟(因为作图用的纸张一般是白色的,所以在接近色阶255时分布曲线很陡峭。):
对于中间调,则使用以下函数来模拟:
对于阴影部分,则使用以下函数来模拟:
由此,我们可以得到最终的铅笔画色调概率分布公式:
即对高光,阴影和中间调进行加权求和(权重系数可以根据不同的需要进行调节)并利用归一化因子Z以保证:
上述公式中,v均代表色调值,未知的参数均为控制参数,它们决定了铅笔画最终的色调直方图。这些未知的参数可以使用最大似然估计来求得。具体而言,这些控制参数的计算公式为:
其中m和s分别代表各层(高光,中间调,阴影)像素值的均值和方差。接下来的工作就是对每层进行直方图匹配(核心思想就是使输出图像具有规定的直方图形状。)并对他们进行重组。
假设该步骤最终输出的图像为J。
(2)纹理渲染
简单而言,就是模拟人反复用铅笔描的过程(在人类绘图时,一般会通过在相同位置重复绘制来实现纹理渲染)。作者使用笔画的乘法来模拟这一过程,表示成公式是这样的:
即使用H进行β次渲染来逼近J。显然,β越大,图像越黑:
同时我们要求β局部平滑,因此问题等价于求解:
作者取λ为0.2。因此上述方程可以转换为标准的线性方程并使用共轭梯度求解。于是我们获得最终的铅笔画纹理图为:
补充一下,H其实就是一张预先选好的真实场景下的铅笔画纹理图,代表绘画模式,网上可以找到很多,例如:
3.Overall Framework
通过前面两个步骤,我们分别获得图像轮廓图S和色调纹理图T。因此,我们可以获得最终的结果图R=S·T。由此,只要我们输入灰度图像I,便可以轻松地获得铅笔素描画R了。
4.Color Pencil Drawing
该算法可以很轻松地扩展到彩色图像上。具体而言,就是把彩色图像从RGB空间转到YUV空间,把Y单独拿出来看作灰度图像进行上述变换后获得新的YUV空间,最后将新的YUV空间重新映射到RGB空间即可。
效
果
展
示
运行方式:
pythonmain.py-m绘图模式(colororgray)-i待处理图片路径-s输出结果保存文件名
效果展示:
(1)第一组
原图:
color:
gray:
(2)第二组
原图:
color:
gray:
(3)第三组
原图:
color:
gray:
(4)第四组
原图:
color:
gray:
All done~
参
考
文
献
[1]/wiki/%E5%8F%8D%E9%8B%B8%E9%BD%92
[2]http://www.cse.cuhk.edu.hk/~leojia/projects/pencilsketch/pencil_drawing.htm
[3]Lu C, Xu L, Jia J. Combining sketch and tone for pencil drawing production[C]//Proceedings of the Symposium on Non-Photorealistic Animation and Rendering. Eurographics Association, : 65-73.
[4]/duduainankai/pencil-python
[5]/taldatech/image2pencil-drawing
[6]/candycat1992/PencilDrawing