写在前面
今天打算写一篇跟Unity基本无关的文章。起因是我上个星期不知怎么的搜到了一个网站 ,里面实现的效果感觉挺好的,后来发现是2012年的NPAR会议的最佳论文。看了下文章,觉得不是很难,就想着实现一下。后来发现国内有两个博主有相关的文章,一个是风吹夏天的文章,一个是Imageshop的文章。通过读论文和两位博主的文章基本对算法就走通了,剩下的就是编码了。原论文作者给出了算法第一个部分的代码,风吹夏天的文章在其基础上又给出了笔画部分的代码,但都不全。
我这篇文章就不赘述算法了,有兴趣的可以看论文或上面两个博主的文章,讲得都比较清楚。我把我实现的代码上传到了github的PencilDrawing项目中,包含了全部代码,基本可以实现论文中的效果,只是由于没有论文中使用的铅笔纹理,因此做不到100%一样。有兴趣的可以去下载下来玩玩看 :)
代码:我的github的PencilDrawing项目(Matlab代码)。
相关论文: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, 2012: 65-73.
概述
这篇文章想要做的事情就是,输入一张图像,输出一张铅笔画风格的图像。你可能会说,这不是PS的滤镜么?恩,的确PS有类似的功能,但效果不够好,当然技术好的童鞋可以通过多步加工得到更好的效果,但这篇文章的算法可以对输入图像一次就生成效果比较的铅笔画效果。比如下面的图像:
仔细观察的话,会发现这篇论文实现的效果有几个特点,一个是图像轮廓会有手绘的线条感,而不是像之前很多算法那样仅仅是进行边缘检测;另一个特点是它的图像整体色调更符合铅笔画的特征,比如有一些铅笔的笔触感,色调也更倾向于真实的铅笔画。
算法
非常详细的算法我就不再说明了,有兴趣的可以去看原论文和之前提到的两篇文章。这里大致讲一下算法流程:
首先需要生成笔画图像:
这是通过对图像计算梯度后,对像素进行方向分类,再对每个方向的像素分别和对应的算子进行滤波后得到的。
生成色调图像:
这一步是使用了论文里所谓的“基于模型的色调转换”,实际就是对真实的铅笔画的直方图进行分析,把整个直方图分成了三块:最亮的部分,中间灰度过渡部分和最暗的部分。作者使用了三个方程分别对这三个部分进行建模,再使用一组权重混合它们,得到目标直方图。这些权重是实验学习得到的,论文里提到了三组不同的权重,我在github的项目页也有所说明。最后,根据这个目标直方图对图像进行直方图匹配即可得到需要的色调图像。这一步总是无法得到和论文里完全一样的结果,没办法,论文总会有一些参数和小修改不会写进去,我猜想大概是进行了一些图像平滑的操作。
生成铅笔画风格的图像:
这一步会使用一张铅笔画纹理,例如:
然后使用这张纹理来模拟上一步得到的色调图,这是通过对铅笔画纹理进行指数运算+保证局部平滑求最优解来得到的。这一步使用的纹理对最后得到的画面效果很重要,无奈作者没有给出他们论文里使用的各个铅笔画纹理,因此我就只能从网上找喽,所以效果也不完全一样。这一步局部平滑度越低,得到的结果图像的铅笔笔触越明显。
最后,我们只需要把第1步得到的笔画图像和第3步得到的图像进行相乘,即可得到最终的图像:
对彩色图像的处理需要先把图像从RGB空间变换到YUV空间,然后只对Y分量进行上述处理,最后再重新变换会RGB空间即可。
结果
这个算法健壮性还是不错的,作者在他们的网站上给出了非常多的例子!不过通过我的实践,我认为很多图像要想得到比较好的效果还是要不断调整参数的,比较重要的参数有生成色调图时使用的三个混合权重(我一般使用最亮的那一组);笔画的长度,一些小图(例如400x400)要使用比较小的算子,例如6或7,大图可以适当使用10以上大小的算子,这会影响生成的图像中轮廓的铅笔笔画长度;还有就是尽管作者没有说明,但我觉得一些比较暗的图像需要最后有一个类似伽马的计算,把画面整体提亮。
下面给出一些效果:
这个是作者给出的众多测试图之一:
下面这个是武汉大学的落叶(来自于昨天我的朋友圈中某个武大美铝的照片,虽然一张落叶看不出什么,但不得不说武大真的是太美了…):
当当当,最后当然是我的母校,交大啦~下面这个是上海交大徐汇小区的图书馆:
其他的就不贴了,有兴趣的可以直接去github上下载。最后,have fun~