论文连接:Combining Sketch and Tone for Pencil Drawing Productionjava
Matlab版本的代码,目前找到有两个:python
一、https://github.com/fumin/pencilgit
二、https://github.com/candycat1992/PencilDrawing github
效果看起来第二个要好,并且写的代码很是简洁。api
我实现了Scala的版本(有一小部分用到了python),基于第一个Matlab版本的代码:框架
https://github.com/Ldpe2G/Pencil-Drawing-Scala学习
实际上是差很少实现完了才发现了第二个版本的matlab代码,后面会看看可否做些改进。spa
首先看看从论文中截取的素描风格生成框架图:.net
主要是两大步组成,模拟画家画素描画的两个步骤:scala
1,Line Drawing,先画线,描轮廓;
2,Tone Drawing 再加上色调,好比阴影。
下面详细介绍两个步骤。
首先来看看一幅图:
左边是画家画的一幅素描画,右边是放大细节部分。经过观察咱们能够发现,画家在画
边的时候,都是用一段一段的线段组合起一幅画的。基于这个事实,文章提出了一种模拟
素描画边的方法。主要也是分两步走。
首先将输入图片转为灰度图,而后经过前向差分,分别计算x,y方向的梯度,再根据如下公式
计算大小:
公式中的 表示输入图片的灰度图,实现上我是用Prewitt’s operator来计算梯度的,也试过
Sobel operator,效果差很少。
在模拟素描画线的难点在于估计每一个像素点的画线方向,比较简单的方法是根据梯度方向,
可是会很容易受噪声的影响,而在文章中则提出了一个更加鲁棒的策略。
首先生成8个方向的线段(卷积核),:
而后分别和G做卷积:
而后经过获得的相应图Gi来分类像素点,i (1~8):
p表明原图像素点的索引。根据公式3,咱们能够知道。
文章中声称以上的方法能对抗各类的噪声。
得到Ci以后,首先和对应的线段卷积核做卷积,而后再加起来:
经过卷积操做能够汇集附近同一个方向的像素点,这样就能够把边原图上边上的像素点连起
来。最后的结果就是把S‘的像素值反转,而后再映射到 [0, 1] 区间。就获得结果了。
用scala代码跑的结果做演示:
这一步这要就是模拟画家用铅笔上色的过程,这须要利用道原始灰度图的信息。
咱们首先来看一张图:
左边是天然场景图片和对应的像素值直方图,右边是素描画和对应的直方图。
能够看到直方图的分布是很不同的。所以原图像的色调是不能直接用在色调生成上的。
而后文章中提出了一种参数化模型来解决这个问题。
文中提出了一个模型来表示色调分布:
v 表示色调值,而后 p(v)表示这个像素是用色调值v来表示的几率。Z是正则化因子。
三个 pi(v) 分别表明在素描画中的三个不一样的色调层,ω 表明权值,形象的理解能够看做是
对应的色调的像素值的个数。再来看一幅图:
(a)是一幅素描画,而后 (b),是把像素值分红三类的结果,绿,橙,蓝分别表明 深,中,浅
三种色调。(c)对应的三种色调的直方图。分析结果就是,天然图像和素描画的最大的区别
就是素描画空白的区域更大,亮度更高。
而后三中色调对应三个公式来表示:
而后就是如何求解公式中的参数了。
权值 ω 由每一个色调层的像素个数决定。而后对每层的参数采用最大似然估计的方式来求解。
每层的像素值的均值和标准差表示为m和s。参数能够近似的表示为:
xi表明像素值,N表示每层像素值的个数。
而后学习到的参数以下:
可是其实在matlab代码的实现上,对于权值 ω的设定和三个公式的实现,并非彻底呀按照
论文中的定义来实现的,我作了很多实验来调节参数而后看结果,发现仍是得按照matlab
代码的设定才能最大程度复现论文的结果,因此应该还有有哪里一些细节没处理好。
而后学习到参数以后,对于每一张新的输入图像,经过直方图匹配的方法来修正灰度图的
像素值,也就是用输入图像的灰度图的直方图去匹配素描画的直方图。
作完直方图匹配以后,原图的像素值分配就比较接近素描画的了,可是还不能直接就用这个
修正的灰度图和上一步生成的描边直接组合,还须要模拟一下素描画的纹理。如何生成
这个纹理是一个很难解决的问题。
文章中生成他们收集了20张左右的素描纹理图来作实验,matlab的代码中提供了3张:
每一个输入图片只须要一张便可。在画家做画的时候,色调的生成就是在某处重复的画。
模拟的方法直观的理解就是,经过将纹理图做乘法。。这个
就是咱们要
求解的。越大则获得的获得色调越深。经过求解如下公式能够获得
:
其实怎么在代码上去实现求解我是想不到的,可是好在matlab的代码实现了求解,
最后实际上是在求解一个很是大的线性方程组,不过矩阵都是很是稀疏的。至于怎么能那么
实现,到目前为止我仍是没看懂,不过直接把matlab的代码移植到scala仍是没问题的。个
人感受复现过程当中最难的部分也就是这里了,我尝试了不少java/scala的矩阵库
(la4j, mtj, colt等等),速度上都不满意,最终发现 breeze是速度上最接近matlab的,
可是在求解稀疏矩阵相关的线性方程组的时候,breeze还不支持,最后实在没办法了,
只能把这部分求解的实现放到python中去作,用scipy这个库来解决,由此能够看到,
scala在作科学计算上仍是,比不上python。最后用了一种比较low的方法,在代码中调用
python的脚本,而后再把脚本的结果读取上来。
ok,回到正题,获得以后,模拟素描画色调纹理的图就能够获得了:
最终的结果就是把色调和轮廓结合起来,用一个矩阵的点乘操做便可:
而后文章还作了一些拓展,好比如何给素描画上色,就是好比把RGB转换到YUV空间,
而后把Y通道拿出来,通过以前的步骤来生成素描画,再把它放回到Y通道上,最后再
从新转换成RGB便可。
而后文章剩下的部分就是一些结果展现和对比试验结果,想深刻了解的话能够去看看paper。
最后展现一下用scala代码生成的一些素描图,只能说勉强复现了论文的方法。
展现格式,左上角原图,左下角素描轮廓图,右下角,素描图或彩色素描图。
[1] http://stackoverflow.com/questions/12636896/how-to-solve-a-linear-system-of-matrices-in-scala-breeze
[2] http://statr.me/2015/09/an-overview-of-linear-algebra-libraries-in-scala-java/
[4] https://en.wikipedia.org/wiki/Edge_detection
[5] http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.301.927&rep=rep1&type=pdf