Deep Image Matting
还有一篇:深度抠图--Deep Image Matting也不错。不过,下面转载另一篇挺好的博文↓,对Deep Image Matting复现过程总结:
原文链接:想要快速了解论文的同学可以只看第一部分。想自己实现一遍这篇论文的同学,请仔细阅读第二部分论文复现,这部分涉及非常多的细节。
论文地址
https://arxiv.org/abs/1703.03872
目录
论文梳理
首先需要理清的是,image matting到底是用于解决什么样的问题?matting问题的核心是一个表达式: ———————————-(1)
即通过alpha控制透明度来使前景FG和背景BG融合的技术。有不少人把deep image matting理解为’深度抠图’,其实抠图用到的是backgournd removal技术[1],它关注的是如何寻找确定的前景和背景,即alpha等于0和1的部分,matting想解决的则像是如何完美的将两张图融入到一起,即alpha不等于1的部分(我的理解,可能有误)。认识这点对后面损失函数权重的配比至关重要。
创建新的训练集
要以深度学习的方式来解决这个问题,就需要寻找大量的训练数据。已经公开的matting领域的benchmark——alphamatting[2][3]的训练集只有27张训练样本,远远达不到深度学习的要求。作者从视频中采取了400多张图,并用PS人工抠出了它的前景图,然后将每一张图分别融合到100个不同的背景里,最后得到了49300个训练样本(在申请作者数据的时候,得到的文件中会包含一份纠错说明,提到论文的数据有误,实际上并没有那么多)。
想要训练什么样的网络
文章想要最终实现的效果如下图:输入原始的RGB图片和对应的trimap(确定的前景alpha=1,确定的背景alpha=0,和不确定的边缘——unknown region, alpha=0.5),输出预测的alpha,即没有unknown region的trimap。
输入
输出
模型结构和训练技巧
左边为典型的encoder和decoder结构,在segmentation和很多以图得图的GAN中比较常见。encoder用pre-trained的VGG16,把fc6从全连接换成了卷积,并在输入增加了第四通道channel4,用来存放输入的trimap,因为channel4而增加的weights全部初始化为0。decoder用简单的unpool和convolution的组合来做upsampling和空间结构推断。右边的refine network是为了解决第一阶段预测输出边缘blur的情况。
损失函数
为两个loss的加权组合:alpha loss和compositional loss。alpha loss是预测alpha和实际alpha的误差,conpositional loss是将预测的alpha通过公式(1)与相应的背景和前景融合后,与ground truth RGB图的误差。只有trimap中unknown region区域的预测误差才会被反向传播(这是matting与background removal的区别,matting需要trimap作为用户交互接口,只需预测matting中不确定的区域,而backgournd removal则需要将整张图的误差反响传播),即trimap中为纯背景和纯前景的部分不计入模型预测范围,只需要把这两部分直接复制到输出的alpha即可。(注意误差都是pixel-wise的)
作者在训练过程中的技巧
- 从原图中随机crop 320,480,640的训练patch,然后统一resize到320,作为网络输入(这里的操作必须对于你训练的那个样本的background,foreground,alpha同步操作)。
- 随机翻转(同上)
- trimap由alpha以随机大小kernel的dilation[4]操作生成。
Refinement很好理解,这里不展开解释了。
论文阅读阶段遗留的几个问题
- 训练mini batch的size
- crop的安全性是怎么解决的?因为以unknown region为中心随机crop,很可能会出现crop超出边界的情况。
- compositional值的scale是0~255,而alpha loss的scale是0~1,如果不对compositional loss除以255的话,会出现loss严重倾向于compositional loss的情况。
- 在网络结构上是否有其他技巧,比如PReLU,batch_normalization。
论文复现
数据准备和预处理阶段
由于有来自xx公司的数据,所以实验没有建立在作者数据的基础上。预处理阶段需要注意的很多很多,大致流程如下图:
能离线处理的数据就尽量离线处理,比如把所有的backgournd都提前整理好,具体文件夹结构的组织方式可以去看我在github[5]上上传的data_structure说明。对这个图简单解释一下:
我们有什么?
- 训练数据的alpha
- 训练数据的RGB(即图中的eps,这个数据是foreground的来源,需要在程序中与alpha组合产生foreground,再matting到背景上实现真正的输入RGB)
- background(来自pascal voc和coco)
如何整理数据?
- 把alpha和eps都resize到最长边为640,同时保留长宽比。
- 因为crop的最大size为640*640,所以把background都resize到最短边为1280,同时保留长宽比。
- 把alpha和eps都center padding到背景里,这样无论怎样crop,只要是以trimap的unknown region为中心,就不会出现crop出边界的情况(另一种解决方案,不resize背景,直接将alpha和eps center pad进背景,假设crop出边界,把出界的那条边拉回来即可。比如crop左边出界了,则设置crop左边的起始位置为0。作者应该采用的是这种方式,因为从作者分享的代码来看没有针对background做特殊操作)。
前三点都是离线准备好的,即不是在网络训练的时候进行,下面的步骤都是在网络训练的时候同时进行。
- 通过对alpha进行random dilation得到trimap。
- 以trimap中的unknown region任取一点为中心对alphap,eps,trimap进行crop(size也是从320,480,640中随机选择)
- 如果size选择到的不是320,则对所有数据resize到320作为网络输入。此时我们拥有了ground truth background, ground truth alpha, ground truth trimap, ground truth eps。还缺了什么?缺了ground truth foreground
- 通过公式1对此时的alpha和eps进行composition得到ground truth foregournd。
- 通过公式1对此时的alpha,eps和BGcomposition得到ground truth RGB。
- ground truth RGB减去global mean,然后concatenate trimap作为网络的4通道输入,剩余的作为计算Loss的原料。
至此,数据准备和预处理阶段完成。这里值得提的有四点(我犯过的错误)
1. 离线数据不到最后一步千万不要用jpg格式保存,用png,因为jpg是有损压缩,对于这种像素级精度都要求很高的项目来说,中间以jpg保存一次就非常致命了。可以在最后一步保存为jpg因为即便出现误差,也是ground truth的整体飘移,飘移后的数据依然保持一致性,不会对网络的训练造成影响。
2. 任何resize操作不能发生在用公式(1)之后,这也是为什么要组织上图那样精细的数据预处理的原因。这样会造成ground truth漂移,会产生训练数据的不一致性。下面有举例说明。
3. 其他resize可以采用默认的双线性插值。但是trimap最好用nearest插值的方式resize,因为其他方式几乎都会改变像素值,而对于trimap必须保证unknown region为128,而nearest插值不会生成新的像素值,只会在原始图像的像素中提取,而且trimap对nearest插值带来的锯齿不敏感(因为trimap本身就是非常语义模糊的),所以对于trimap,nearest是最理想的resize方式。
4. resize alpha的时候,先把数据类型转换成uint8,resize过后再转换回float32,因为misc.imresize操作会对float型的数据rescale,所以你输入图片最大的像素值是32,输出可能直接就被rescale到255了。
为什么先融合再resize会产生ground truth飘移
先融合,再resize 和 先resize,在融合
两张图看似一样,但是一作差就会发现
所以如果先融合,再resize,此时你得到的foreground对应的都不是当初原原本本的alpha了(resize产生的误差),这对于神经网络的训练极为不利,因为网络努力拟合的方向不是真正它应该去的方向(出现了些许飘移)。从adobe公司提供的组合compositional的代码可以发现,ground truth RGB是提前离线生成的,也就是说先mat了,而在后面的预处理阶段还有resize操作。假设作者真的犯了这个错误而没意识到,那可能是文章中模型需要第二阶段refinement的原因。也就是说如果不犯这个错误的话,可能就不需要第二阶段的refinement网络了(这是我个人的推测)。
训练阶段
训练阶段没有太多可以谈的。谈谈我实现过程中与作者不一致的地方。
1. 一开始我选择用deconvolution代替unpool操作,因为tensorflow没有现成的unpool函数,虽然有opensource的,但是都会对网络输入的batch size产生限制。即你训练用mini batch size为5,那么预测阶段也只能5个一起预测,非常不便。但是实验中发现,虽然用deconvolution能对单一的有样本过拟合,但是却很难在整个训练集上拟合,考虑到用deconvolution后,网络的复杂度其实是高于作者用的网络的,所以不能在整个网络上拟合是非常奇怪的事。我能想到唯一的解释就是训练时间不够。最终deconvolution的实验在学习率为1e-5的情况下跑了5天,模型能非常好的预测general shape,但是一直无法拟合边缘,后来放弃了deconvolution的做法。
2. 用了batch normalization。虽然原文没提到这一点,但是仅仅是因为不用batch norm,模型最后都会预测出全黑的结果,所以加上了这个正则化方法,相应的把学习率调整到了1e-4,拟合变得非常快。用了unpool之后拟合只需要7小时。
3. 之前说unpool有限制输入batch size的问题,后来为什么又用unpool了?因为参考了segmentation的论文发现,其实在这种以图预测图的任务中,可以认为每一个像素都是一个训练样本,这样即便batch size为一,模型在一次训练中也进行了大量的预测,这点是与classification任务不同的地方。所以最后索性设置batch size为1,将deconvolution换成了unpool.模型成功拟合了,泛化略微没有作者表现的那么好。具体请参考我关于这个项目的github页面[5]。
4. 出于某些原因,在github上的代码里对纯背景和纯前景的地方也进行了误差的反向传播,权重设为了0.5。实验证明,是否对bg和fg区域进行反向传播对模型的上限没有影响。如果不想那两部分的误差有影响,只需将权重设置为0。
公司出于某些原因,给我安排了新任务,虽然跟matting很像,但是这篇论文的复现恐怕要到此为止了,训练阶段的第一点,即“排除了作者预处理上的失误后,我们能不能不用refinement网络而达到原论文的水平”暂时没时间去验证。也因为公司接下来不会用这个项目,所以才能以博客和开源的方式在这里把复现过程分享给各位,欢迎大家发表自己的意见。
最后把这个项目的github地址单独拉出来:
https://github.com/Joker316701882/Deep-Image-Matting
References
[1]https://clippingmagic.com/
[2]http://alphamatting.com/
[3]http://www.juew.org/publication/CVPR09_evaluation_final_HQ.pdf
[4]http://docs.opencv.org/2.4/doc/tutorials/imgproc/erosion_dilatation/erosion_dilatation.html
[5]https://github.com/Joker316701882/Deep-Image-Matting