手把手 | 初学者如何用Chainer为漫画上色 深度学习帮你逆袭漫画家(附代码)

本文授权转自知乎@李嘉铭

本文为(不完全)翻译版本。以下为正文:

最近一直有人说深度学习(Deep Learning)的附加价值高,于是我也在一两个月前开始学习chainer了。机会难得就想试着用chainer做一些各种各样的尝试,比如写个给线描上色的小程序之类的。

线描上色这个任务的性质是监督式学习(supervised learning),因此需要大量的线稿和上完色的图片,越多越好。

这次是用opencv用角色的画像把线稿生成了出来。生成例子如下:

收集了角色们的画像,将其转化为线稿之后数据集(dataset)就完成了。这次用了约60万张图片。

关于神经网络(neural network)的结构,我用了一种叫做U-net的网络。它的特点是会把卷积(convolution)和反卷积(deconvolution)的层混合着连接在一起。这样就可以做到参照着一开始的线稿来上色。(译者注:有兴趣的可以看一下unet的论文以及文末提供的神经网络代码。)

这样生成出来的图像和原来上完色的图像对比然后取平方差,神经网络的训练让平方差最小即可。

只要保证网络每一层的输入和下一层的输出相匹配就大体ok,但是自己定义的数据如何制作这方面,因为没有什么例子所以可能对各位来说有些难懂。

花了整整一晚上时间使劲用data喂饱了neural net(掩面)的结果↓

嗯~神经网络这边大概是想说“肌肤的颜色我大概能搞懂但是除此之外的实在不知道啊,发色啊衣服颜色啊我当然不可能知道吧。”

这里要登场的是叫对抗网络(adversarial net)的神经网络,简称“怼”。

“怼”要做的是学习真正的图像和被神经网络生成出来的图像之间的颜色差别,然后找出两个图像中的那个叛徒。

所以如果神经网络一直生成老照片那样颜色的图像的话“怼”只要学一会儿就能准确的找出哪一张是神经网络生成的。

但是如果“怼”太用力的话上色的神经网络会拼命反抗导致上色失败请多加注意。

这样的颜色都已经说不上是线稿上色后的东西而更加接近艺术了。(嘛,顺着这条道走,把上色用神经网络怼成艺术生也不是不可以。。。现在暂时还是回到学习和原画之间的差别上面吧)

呼,上色终于完成了!照着这个势头下去再接着干吧。

第一阶段是学习了128x128的图像,第二阶段是给512x512的图像学习上色。以下是没“怼”过的训练结果↓

还不错哟

不错不错。

把实际的线稿拿来喂给神经网络如何?我从pixiv上借用来了线稿类的画

(因为神经网络大部分都是卷积神经网络(cnn),宽高比有一些变化也没关系)

好棒!

变成了彩色的怪物了,嘛这种怪物也是有的。

平安收工。

说回来,果然还是会想亲手在线稿上上一些色吧?于是稍微改变一下输入,和一阶段不同的是在原来的线稿之外多加了三个输入层(rgb),给神经网络一些用色上的提示吧。

总之:

茶色的头发淡蓝的水手服和藏青的裙子,之类的要求也可以提了。

稍微霸气的像这样画上一笔也是可以的。

不管是大概的提示也好非常用心的每个细节都提示也好效果都不错。(可能有些难懂,就是用不同的颜色在各种地方点一下来提示,比如下面)

这样我也从工程师升职成画师了!

至此,我觉得线稿的自动上色和带提示的上色已经做的还不错了。

虽然还不如画师们认真画出来的,如果想随随便便涂个色还是非常方便的(译者:比如在经费不够的情况下。。。)。

漫画之类的也是,比起用网点贴纸(Screen tone)还是大致的上一个色比较快速方便的。(这次的神经网络非常擅长给肤色上色。。。我想说的各位懂得吧~)

顺便补充一下,弱点还是有几个的。

例如同时用对抗网络和上色提示一起训练的时候,上色提示会干涉到对抗网络,有时会导致训练结果不稳定。

↑明明只是想给泳装上一个不同的颜色,结果其他部分的颜色也跟着变了。

如果只是作为一个简单的上色工具的话,只加提示来训练神经网络可能会更加稳定。

另外,如果线稿的线太粗或者太细的情况下,线会崩坏掉导致结果不怎么样的情况也有,仔细的给了上色提示但是没有反应在结果上的情况也有。

不同的细节都用同一个神经网络来对付虽然比较厉害,但是作为工具使用的时候需要根据用途来做一些调整。

借鉴的线稿原画:

「【プリンセスロワイヤル】パンドラ」/「鉛筆工房【IRITH】」[pixiv] (http://www.pixiv.net/member_illust.php?mode=medium&illust_id=31274285)

線画詰め(http://www.pixiv.net/member_illust.php?mode=manga&illust_id=43369404)

「改弐」/「炬燵魂」[pixiv](http://www.pixiv.net/member_illust.php?mode=medium&illust_id=56689287)

「めりくり線画」/「タマコ」[pixiv](http://www.pixiv.net/member_illust.php?mode=medium&illust_id=40487409)

「泰1」/「20100301」[pixiv](http://www.pixiv.net/member_illust.php?mode=medium&illust_id=10552795)

※生成的线稿的训练材料和原画一时半会儿找不到,实在抱歉,还请多包涵。

顺便,这次的神经网络第一阶段和第二阶段的构造都是一样的,基本上感觉如下:

unet.py

class UNET(chainer.Chain): def __init__(self): super(UNET, self).__init__( c0 = L.Convolution2D(4, 32, 3, 1, 1), c1 = L.Convolution2D(32, 64, 4, 2, 1), c2 = L.Convolution2D(64, 64, 3, 1, 1), c3 = L.Convolution2D(64, 128, 4, 2, 1), c4 = L.Convolution2D(128, 128, 3, 1, 1), c5 = L.Convolution2D(128, 256, 4, 2, 1), c6 = L.Convolution2D(256, 256, 3, 1, 1), c7 = L.Convolution2D(256, 512, 4, 2, 1), c8 = L.Convolution2D(512, 512, 3, 1, 1),

dc8 = L.Deconvolution2D(1024, 512, 4, 2, 1), dc7 = L.Convolution2D(512, 256, 3, 1, 1), dc6 = L.Deconvolution2D(512, 256, 4, 2, 1), dc5 = L.Convolution2D(256, 128, 3, 1, 1), dc4 = L.Deconvolution2D(256, 128, 4, 2, 1), dc3 = L.Convolution2D(128, 64, 3, 1, 1), dc2 = L.Deconvolution2D(128, 64, 4, 2, 1), dc1 = L.Convolution2D(64, 32, 3, 1, 1), dc0 = L.Convolution2D(64, 3, 3, 1, 1),

bnc0 = L.BatchNormalization(32), bnc1 = L.BatchNormalization(64), bnc2 = L.BatchNormalization(64), bnc3 = L.BatchNormalization(128), bnc4 = L.BatchNormalization(128), bnc5 = L.BatchNormalization(256), bnc6 = L.BatchNormalization(256), bnc7 = L.BatchNormalization(512), bnc8 = L.BatchNormalization(512),

bnd8 = L.BatchNormalization(512), bnd7 = L.BatchNormalization(256), bnd6 = L.BatchNormalization(256), bnd5 = L.BatchNormalization(128), bnd4 = L.BatchNormalization(128), bnd3 = L.BatchNormalization(64), bnd2 = L.BatchNormalization(64), bnd1 = L.BatchNormalization(32) )

def calc(self,x, test = False): e0 = F.relu(self.bnc0(self.c0(x), test=test)) e1 = F.relu(self.bnc1(self.c1(e0), test=test)) e2 = F.relu(self.bnc2(self.c2(e1), test=test)) e3 = F.relu(self.bnc3(self.c3(e2), test=test)) e4 = F.relu(self.bnc4(self.c4(e3), test=test)) e5 = F.relu(self.bnc5(self.c5(e4), test=test)) e6 = F.relu(self.bnc6(self.c6(e5), test=test)) e7 = F.relu(self.bnc7(self.c7(e6), test=test)) e8 = F.relu(self.bnc8(self.c8(e7), test=test))

d8 = F.relu(self.bnd8(self.dc8(F.concat([e7, e8])), test=test)) d7 = F.relu(self.bnd7(self.dc7(d8), test=test)) d6 = F.relu(self.bnd6(self.dc6(F.concat([e6, d7])), test=test)) d5 = F.relu(self.bnd5(self.dc5(d6), test=test)) d4 = F.relu(self.bnd4(self.dc4(F.concat([e4, d5])), test=test)) d3 = F.relu(self.bnd3(self.dc3(d4), test=test)) d2 = F.relu(self.bnd2(self.dc2(F.concat([e2, d3])), test=test)) d1 = F.relu(self.bnd1(self.dc1(d2), test=test)) d0 = self.dc0(F.concat([e0, d1]))

return d0

“怼”

adv.py

class DIS(chainer.Chain): def __init__(self): super(DIS, self).__init__( c1 = L.Convolution2D(3, 32, 4, 2, 1), c2 = L.Convolution2D(32, 32, 3, 1, 1), c3 = L.Convolution2D(32, 64, 4, 2, 1), c4 = L.Convolution2D(64, 64, 3, 1, 1), c5 = L.Convolution2D(64, 128, 4, 2, 1), c6 = L.Convolution2D(128, 128, 3, 1, 1), c7 = L.Convolution2D(128, 256, 4, 2, 1), l8l = L.Linear(None, 2, wscale=0.02*math.sqrt(8*8*256)),

bnc1 = L.BatchNormalization(32), bnc2 = L.BatchNormalization(32), bnc3 = L.BatchNormalization(64), bnc4 = L.BatchNormalization(64), bnc5 = L.BatchNormalization(128), bnc6 = L.BatchNormalization(128), bnc7 = L.BatchNormalization(256), )

def calc(self,x, test = False): h = F.relu(self.bnc1(self.c1(x), test=test)) h = F.relu(self.bnc2(self.c2(h), test=test)) h = F.relu(self.bnc3(self.c3(h), test=test)) h = F.relu(self.bnc4(self.c4(h), test=test)) h = F.relu(self.bnc5(self.c5(h), test=test)) h = F.relu(self.bnc6(self.c6(h), test=test)) h = F.relu(self.bnc7(self.c7(h), test=test)) return self.l8l(h)

来自: <初心者がchainerで線画着色してみた。わりとできた。 - Qiita>

最近自己也在做类似的东西,但总做不出好的结果,偶尔看到这个帖子被吓到了。看上去效果非常厉害。最近在用tensorflow把程序写出来。写的过程中觉得这篇blog描述的还是不够详细,我自己怎么做都做不出他的效果,不知道是训练的数据问题还是其他问题。写完了的话会把结果一并发到这里。

日语业余也不会画画所以有些翻译不太准确,如果有日语大神请务必指出翻译不正确的地方。专有名词的中文翻译也有可能有问题,如果有不准的地方请参照括号内的英文。如果有问题欢迎批评指正。

译者注:

今天得知了作者终于公开了源代码的消息,以及提供了一个测试这个上色网络的网站。在此提供一下地址:

taizan/PaintsChainer (https://github.com/pfnet/PaintsChainer)

painter (http://paintschainer.preferred.tech)

顺手用英文也翻译了一下,地址如下:

最近在用tensorflow在没有源代码的情况下尝试重新根据这个blog写一个,写了快一个月还是失败了。。。不过现在有了源代码应该很快就会用tensorflow重现出来。到时候结果和训练集会一并放在这里。

编译作者:李嘉铭

【知乎网址】https://zhuanlan.zhihu.com/p/24712438?utm_medium=social&utm_source=wechat_timeline&from=groupmessage&isappinstalled=0

日语原文:初心者がchainerで線画着色してみた。わりとできた。 - Qiita(http://qiita.com/taizan/items/cf77fd37ec3a0bef5d9d)原作者:taizan

原文发布于微信公众号 - 大数据文摘(BigDataDigest)

原文发表时间:2017-03-04

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏大数据挖掘DT机器学习

详细步骤:用R语言做文本挖掘

目录 Part1 安装依赖包 Part2 分词处理 Part3文本聚类 Part4 文本分类 Part5情感分析 Part1 安装依赖包 R语言中中文分析的...

850120
来自专栏PPV课数据科学社区

R语言与数据分析之二:绘图

? 图形展示是最高效且形象的数据描述手段,因此巧妙的图像展示是高质量数据分析报告的必备内容,因此强大的图形展示功能也是统计分析软件的必备功能。R语言提供了强大...

465150
来自专栏深度学习入门与实践

【深度学习系列】PaddlePaddle垃圾邮件处理实战(一)

44240
来自专栏PPV课数据科学社区

数据挖掘比赛通用框架

作者|穆文 报名啦CDA数据分析师认证培训Level 1 国内权威的数据分析师系统 培养学员超过上千人理论结合实际 更有多重福利提供 点击文末“阅读原文”...

54460
来自专栏应兆康的专栏

谷歌发布PhotoScan:拍摄无炫光的图片

前天(4月19日),谷歌发布了一个适用于IOS和Android应用程序PhotoScan(中文名: 照片扫描仪),它可让您仅使用智能手机将照片打印数字化。 Ph...

63730
来自专栏机器之心

教程 | AI玩微信跳一跳的正确姿势:跳一跳Auto-Jump算法详解

402110
来自专栏机器学习之旅

python开发:特征工程代码模版(二)

正题开始: 这篇文章是入门级的特征处理的打包解决方案的python实现汇总,如果想get一些新鲜血液的朋友可以叉了,只是方便玩数据的人进行数据特征筛选的代码集...

19830
来自专栏CreateAMind

Keras和DDPG玩赛车游戏(自动驾驶)

这里,s是状态,a是行为/动作,θ是策略网络的模型参数,π是常见的表示策略的符号。我们可以设想策略是我们行为的代理人,即一个从状态到动作的映射函数。

49420
来自专栏量子位

OpenAI发布新强化学习算法:近端策略优化

允中 编译自 blog.openai.com 量子位 出品 | 公众号 QbitAI OpenAI今天发布一类新的强化学习算法:近端策略优化(Proximal ...

34850
来自专栏IT派

干货 | 机器学习在web攻击检测中的应用实践

岳良, 携程信息安全部高级安全工程师。2015年加入携程,主要负责渗透测试,安全评审,安全产品设计。 一、背景 在web应用攻击检测的发展历史中,到目前为止...

579110

扫码关注云+社区

领取腾讯云代金券