聊聊人像抠图背后的算法技术

本文分享自华为云社区《人像抠图:算法概述及工程实现(一)》,原文做者:杜甫盖房子 。python

本文将从算法概述、工程实现、优化改进三个方面阐述如何实现一个实时、优雅、精确的视频人像抠图项目。linux

什么是抠图

对于一张图I, 咱们感兴趣的人像部分称为前景F,其他部分为背景B,则图像I能够视为F与B的加权融合:I = alpha F + (1 - alpha) BI=alpha∗F+(1−alpha)∗B,而抠图任务就是找到合适的权重alpha。值得一提的是,如图,查看抠图ground truth能够看到,alpha是[0, 1]之间的连续值,能够理解为像素属于前景的几率,这与人像分割是不一样的。如图,在人像分割任务中,alpha只能取0或1,本质上是分类任务,而抠图是回归任务。算法

抠图ground truth:
image.pngsegmentfault

分割ground truth:
image.png网络

相关工做

咱们主要关注比较有表明性的基于深度学习的抠图算法。目前流行的抠图算法大体能够分为两类,一种是须要先验信息的Trimap-based的方法,宽泛的先验信息包括Trimap、粗糙mask、无人的背景图像、Pose信息等,网络使用先验信息与图片信息共同预测alpha;另外一种则是Trimap-free的方法,仅根据图片信息预测alpha​,对实际应用更友好,但效果广泛不如Trimap-based的方法。dom

Trimap-based

Trimap是最经常使用的先验知识,顾名思义Trimap是一个三元图,每一个像素取值为{0,128,255}其中之一,分别表明前景、未知与背景,如图。
image.pngide

Deep Image Matting

多数抠图算法采用了Trimap做为先验知识。Adobe在17年提出了Deep Image Matting1,这是首个端到端预测alpha的算法,整个模型分Matting encoder-decoder stage与Matting refinement stage两个部分,Matting encoder-decoder stage是第一部分,根据输入图像与对应的Trimap,获得较为粗略的alpha matte。Matting refinement stage是一个小的卷积网络,用来提高alpha matte的精度与边缘表现。
image.png
image.png工具

本文在当时达到了state-of-the-art,后续不少文章都沿用了这种“粗略-精细”的抠图思路,此外,因为标注成本高,过去抠图任务的数据是很是有限的。本文还经过合成提出了一个大数据集Composition-1K,将精细标注的前景与不一样背景融合,获得了45500训练图像和1000测试图像,大大丰富了抠图任务的数据。学习

Background Matting

Background Matting2是华盛顿大学提出的抠图算法,后续发布了Backgroun MattingV2,方法比较有创新点,而且在实际工程应用中取得了不错的效果。
image.png测试

同时,因为Adobe的数据都是基于合成的,为了更好的适应真实输入,文中提出一个自监督网络训练G_{Real}GReal​来对未标注的真实输入进行学习。G_{Real}GReal​输入与G_{Adobe}GAdobe​相同,用G_{Adobe}GAdobe​输出的alpha matte与F来监督G_{Real}GReal​的输出获得loss,此外,G_{Real}GReal​的输出合成获得的RGB还将经过一个鉴别器来判断真伪获得第二个loss,共同训练G_{Real}GReal​。
image.png

文中列举了一些使用手机拍摄获得的测试结果,能够看到大部分状况结果仍是很不错的。
image.png

Background Matting V2

Background Matting获得了不错的效果,但该项目没法实时运行,也没法很好的处理高分辨率输入。因此项目团队又推出了Background Matting V23,该项目能够以30fps的速度在4k输入上获得不错的结果。
image.png

文章实现高效高分辨率抠图的一个重要想法是,alpha matte中大部分像素是0或1,只有少许的区域包含过渡像素。所以文章将网络分为base网络和refine网络,base网络对低分辨率图像进行处理,refine网络根据base网络的处理结果选择原始高分辨率图像上特定图像块进行处理。
image.png

base网络输入为c倍下采样的图像与背景,经过encoder-decoder输出粗略的alpha matte、F、error map与hidden features。将采样c倍获得的error map E_cEc​上采样到原始分辨率的\frac{1}{4}41​为E_4E4​,则E_4E4​每一个像素对应原图4x4图像块,从E_4E4​选择topk error像素,即为原始topk error 4x4图像块。在选择出的像素周围裁剪出多个8x8图像块送入refine网络。refine网络是一个two-stage网络,首先将输入经过部分CBR操做获得第一阶段输出,与原始输入中提取的8x8图像块cat后输入第二阶段,最后将refine后的图像块与base获得的结果交换获得最终的alpha matte和F。
image.png
image.png

此外文章还发布了两个数据集:视频抠图数据集VideoMatte240K与图像抠图数据集PhotoMatte13K/85。VideoMatte240K收集了484个高分辨率视频,使用Chroma-key软件生成了240000+前景和alpha matte对。PhotoMatte13K/85则是在良好光照下拍摄照片使用软件和手工调整的方法获得13000+前景与alpha matte数据对。大型数据集一样是本文的重要贡献之一。
image.png
image.png

此外还有一些文章如Inductive Guided Filter4、MGMatting5等,使用粗略的mask做为先验信息预测alpha matte,在应用时也比trimap友好不少。MGMatting同时也提出了一个有636张精确标注人像的抠图数据集RealWorldPortrait-636,能够经过合成等数据增广方法扩展使用。

Trimap-free

实际应用中先验信息获取起来是很不方便的,一些文章将先验信息获取的部分也放在网络中进行。

Semantic Human Matting

阿里巴巴提出的Semantic Human Matting6一样分解了抠图任务,网络分为三个部分,T-Net对像素三分类获得Trimap,与图像concat获得六通道输入送入M-Net,M-Net经过encoder-decoder获得较为粗糙的alpha matte,最后将T-Net与M-Net的输出送入融合模块Fusion Module,最终获得更精确的alpha matte。
image.png

网络训练时的alpha loss分为alpha loss与compositional loss,与DIM相似,此外还加入了像素分类lossL_tLt​,最终loss为:L = L_p + L_t=L_\alpha + L_c + L_tL=Lp​+Lt​=Lα​+Lc​+Lt​。文章实现了端到端Trimap-free的抠图算法,但较为臃肿。此外文章提出Fashion Model数据集,从电商网站收集整理了35000+标注的图片,但并无开放。

Modnet

modnet7认为神经网络更擅长学习单一任务,因此将抠图任务分为三个子任务,分别进行显式监督训练和同步优化,最终能够以63fps在512x512输入下达到soft结果,所以在后续的工程实现中我也选择了modnet做为Baseline。

网络的三个子任务分别是Semantic Estimation、Detail Prediction和Semantic-Detail Fusion,Semantic Estimation部分由backbone与decoder组成,输出相对于输入下采样16倍的semantics,用来提供语义信息,此任务的ground truth是标注的alpha通过下采样与高斯滤波获得的。 Detail Prediction任务输入有三个:原始图像、semantic分支的中间特征以及S分支的输出S_pSp​,D分支一样是encoder-decoder结构,值得留意的该分支的loss,因为D分支只关注细节特征,因此经过ground truth alpha生成trimap,只在trimap的unknown区域计算d_pdp​与\alpha_gαg​的L_1L1​损失。F分支对语义信息与细节预测进行融合,获得最终的alpha matte与ground truth计算L_1L1​损失,网络训练的总损失为:L=\lambda_sL_s + \lambda_dL_d+\lambda_{\alpha}L_{\alpha}L=λs​Ls​+λd​Ld​+λα​Lα​。
image.png
image.png

最后,文章还提出了一种使视频结果在时间上更平滑的后处理方式OFD,在先后两帧较为类似而中间帧与先后两帧距离较大时,使用先后帧的平均值平滑中间帧,但该方法会致使实际结果比输入延迟一帧。
image.png

此外,U^2U2-Net、SIM等网络能够对图像进行显著性抠图,感兴趣的话能够关注一下。

数据集

  • Adobe Composition-1K
  • matting_human_datasets
  • VideoMatte240K
  • PhotoMatte85
  • RealWorldPortrait-636

评价指标

经常使用的客观评价指标来自于2009年CVPR一篇论文8,主要有:
image.png

此外,能够在paperwithcode上查看Image Matting任务的相关文章,在Alpha Matting网站上查看一些算法的evaluation指标。

本项目的最终目的是在HiLens Kit硬件上落地实现实时视频读入与背景替换,开发环境为HiLens配套在线开发环境HiLens Studio,先上一下对比baseline的改进效果:

使用modnet预训练模型modnet_photographic_portrait_matting.ckpt进行测试结果以下:
image.png

能够看到因为场景较为陌生、逆光等缘由会致使抠图结果有些闪烁,虽然modnet能够针对特定视频进行自监督finetune,但咱们的目的是在广泛意义上效果更好,所以没有对本视频进行自监督学习。

优化后的模型效果以下:
image.png

本视频并无做为训练数据。能够看到,抠图的闪烁状况减小了不少,毛发等细节也基本没有损失。

工程落地

为了测试baseline效果,首先咱们要在使用场景下对baseline进行工程落地。根据文档导入/转换本地开发模型可知

昇腾310 AI处理器支持模型格式为".om",对于Pytorch模型来讲能够经过"Pytorch->Caffe->om"或"Pytorch->onnx->om"(新版本)的转换方式获得,这里我选择的是第一种。Pytorch->Caffe模型转换方法与注意事项在以前的博客中有具体阐述过,这里不赘述。转换获得Caffe模型后,能够在HiLens Studio中直接转为om模型,很是方便。

首先在HiLens Studio中新建一个技能,此处选择了空模板,只须要修改一下技能名称就能够。
image.png

将Caffe模型上传到model文件夹下:
image.png

在控制台中运行模型转换命令便可获得能够运行的om模型:

/opt/ddk/bin/aarch64-linux-gcc7.3.0/omg --model=./modnet_portrait_320.prototxt --weight=./modnet_portrait_320.caffemodel --framework=0 --output=./modnet_portrait_320 --insert_op_conf=./aipp.cfg

image.png

接下来完善demo代码。在测试时HiLens Studio能够在工具栏选择使用视频模拟摄像头输入,或链接手机使用手机进行测试:
image.png

具体的demo代码以下:

# -*- coding: utf-8 -*-
# !/usr/bin/python3
# HiLens Framework 0.2.2 python demo
​
import cv2
import os
import hilens
import numpy as np
from utils import preprocess
import time
​
​
def run(work_path):
    hilens.init("hello")  # 与建立技能时的校验值一致
​
    camera = hilens.VideoCapture('test/camera0_2.mp4')  # 模拟输入的视频路径
    display = hilens.Display(hilens.HDMI)
​
    # 初始化模型
    model_path = os.path.join(work_path, 'model/modnet_portrait_320.om') # 模型路径
    model = hilens.Model(model_path)
​
    while True:
        try:
            input_yuv = camera.read()
            input_rgb = cv2.cvtColor(input_yuv, cv2.COLOR_YUV2RGB_NV21)
            # 抠图后替换的背景
            bg_img = cv2.cvtColor(cv2.imread('data/tiantan.jpg'), cv2.COLOR_BGR2RGB) 
            crop_img, input_img = preprocess(input_rgb)  # 预处理
            s = time.time()
            matte_tensor = model.infer([input_img.flatten()])[0]
            print('infer time:', time.time() - s)
            matte_tensor = matte_tensor.reshape(1, 1, 384, 384)
​
            alpha_t = matte_tensor[0].transpose(1, 2, 0)
            matte_np = cv2.resize(np.tile(alpha_t, (1, 1, 3)), (640, 640))
            fg_np = matte_np * crop_img + (1 - matte_np) * bg_img  # 替换背景
            view_np = np.uint8(np.concatenate((crop_img, fg_np), axis=1))
            print('all time:', time.time() - s)
​
            output_nv21 = hilens.cvt_color(view_np, hilens.RGB2YUV_NV21)
            display.show(output_nv21)
​
        except Exception as e:
            print(e)
            break
​
    hilens.terminate()

其中预处理部分的代码为:

import cv2
import numpy as np
​
​
TARGET_SIZE = 640
MODEL_SIZE = 384
​
​
def preprocess(ori_img):
    ori_img = cv2.flip(ori_img, 1)
    H, W, C = ori_img.shape
    x_start = max((W - min(H, W)) // 2, 0)
    y_start = max((H - min(H, W)) // 2, 0)
    crop_img = ori_img[y_start: y_start + min(H, W), x_start: x_start + min(H, W)]
    crop_img = cv2.resize(crop_img, (TARGET_SIZE, TARGET_SIZE))
    input_img = cv2.resize(crop_img, (MODEL_SIZE, MODEL_SIZE))
​
    return crop_img, input_img

demo部分的代码很是简单,点击运行便可在模拟器中看到效果:
image.png

模型推理耗时44ms左右,端到端运行耗时60ms左右,达到了咱们想要的实时的效果。

效果改进

预训练模型在工程上存在着时序闪烁的问题,原论文中提出了一种使视频结果在时间上更平滑的后处理方式OFD,即用先后两帧平均偏差大的中间帧。但这种办法只适合慢速运动,同时会致使一帧延迟,而咱们但愿能够对摄像头输入进行实时、普适的时序处理,所以OFD不适合咱们的应用场景。

在Video Object Segmentation任务中有一些基于Memory Network的方法(如STM),抠图领域也有新论文如DVM考虑引入时序记忆单元使抠图结果在时序上更稳定,但这些方法广泛须要先后n帧信息,在资源占用、推理实时性、适用场景上都与咱们但愿的场景不符合。

考虑到资源消耗与效果的平衡,咱们采用将前一帧的alpha结果cat到当前帧RGB图像后共同做为输入的方法来使网络在时序上更稳定。

网络上的修改很是简单,只需在模型初始化时指定in_channels = 4:

modnet = MODNet(in_channels=4, backbone_pretrained=False)

训练数据方面,咱们选择一些VideoMatting的数据集:VideoMatte240K、ConferenceVideoSegmentationDataset。

最初,咱们尝试将前一帧alpha做为输入、缺失前帧时补零这种简单的策略对模型进行训练:

if os.path.exists(os.path.join(self.alpha_path, alpha_pre_path)):
    alpha_pre = cv2.imread(os.path.join(self.alpha_path, alpha_pre_path))
else:
    alpha_pre = np.zeros_like(alpha)
 
net_input = torch.cat([image, alpha_pre], dim=0)

收敛部署后发现,在场景比较稳定时模型效果提高较大,而在人进、出画面时模型适应较差,同时若是某一帧结果较差,将对后续帧产生很大影响。针对这些问题,考虑制定相应的数据加强的策略来解决问题。

  • 人进、出画面时模型适应较差:数据集中空白帧较少,对人物入画出画学习不够,所以在数据处理时增长空白帧几率:
if os.path.exists(os.path.join(self.alpha_path, alpha_pre_path)) and random.random() < 0.7:
    alpha_pre = cv2.imread(os.path.join(self.alpha_path, alpha_pre_path))
else:
    alpha_pre = np.zeros_like(alpha)
  • 某一帧结果较差,将对后续帧产生很大影响:目前的结果较为依赖前一帧alpha,没有学会抛弃错误结果,所以在数据处理时对alpha_pre进行必定几率的仿射变换,使网络学会忽略误差较大的结果;
  • 此外,光照问题仍然存在,在背光或光线较强处抠图效果较差:对图像进行光照加强,具体的,必定几率状况下模拟点光源或线光源叠加到原图中,使网络对光照更鲁棒。光照数据加强有两种比较经常使用的方式,一种是经过opencv进行简单的模拟,具体能够参考augmentation.py,另外还有经过GAN生成数据,咱们使用opencv进行模拟。

从新训练后,咱们的模型效果已经能够达到前文展现的效果,在16T算力的HiLens Kit上彻底达到了实时、优雅的效果。进一步的,我还想要模型成为耗时更少、效果更好的优秀模型~目前在作的提高方向是:

  • 更换backbone:针对应用硬件选择合适的backbone一贯是提高模型性价比最高的方法,直接根据耗时与资源消耗针对硬件搜一个模型出来最不错,目前搜出来的模型转为onnx测试结果(输入192x192):
GPU:
Average Performance excluding first iteration. Iterations 2 to 300. (Iterations greater than 1 only bind and evaluate)
  Average Bind: 0.124713 ms
  Average Evaluate: 16.0683 ms
​
  Average Working Set Memory usage (bind): 6.53219e-05 MB
  Average Working Set Memory usage (evaluate): 0.546117 MB
​
  Average Dedicated Memory usage (bind): 0 MB
  Average Dedicated Memory usage (evaluate): 0 MB
​
  Average Shared Memory usage (bind): 0 MB
  Average Shared Memory usage (evaluate): 0.000483382 MB
 
CPU:
Average Performance excluding first iteration. Iterations 2 to 300. (Iterations greater than 1 only bind and evaluate)
  Average Bind: 0.150212 ms
  Average Evaluate: 13.7656 ms
​
  Average Working Set Memory usage (bind): 9.14507e-05 MB
  Average Working Set Memory usage (evaluate): 0.566746 MB
​
  Average Dedicated Memory usage (bind): 0 MB
  Average Dedicated Memory usage (evaluate): 0 MB
​
  Average Shared Memory usage (bind): 0 MB
  Average Shared Memory usage (evaluate): 0 MB
  • 模型分支:在使用的观察中发现,大部分较为稳定的场景可使用较小的模型获得不错的结果,全部考虑finetune LRBranch处理简单场景,HRBranch与FusionBranch依旧用来处理复杂场景,这项工做还在进行中。

  1. Xu, Ning, et al. “Deep image matting.”Proceedings of the IEEE conference on computer vision and pattern recognition. 2017
  2. Sengupta, Soumyadip, et al. “Background matting: The world is your green screen.”Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition. 2020.
  3. Lin, Shanchuan, et al. “Real-Time High-Resolution Background Matting.”arXiv preprint arXiv:2012.07810(2020).
  4. Li, Yaoyi, et al. “Inductive Guided Filter: Real-Time Deep Matting with Weakly Annotated Masks on Mobile Devices.”2020 IEEE International Conference on Multimedia and Expo (ICME). IEEE, 2020.
  5. Yu, Qihang, et al. “Mask Guided Matting via Progressive Refinement Network.”arXiv e-prints(2020): arXiv-2012.
  6. Chen, Quan, et al. “Semantic human matting.”Proceedings of the 26th ACM international conference on Multimedia. 2018.
  7. Ke, Zhanghan, et al. “Is a Green Screen Really Necessary for Real-Time Human Matting?.”arXiv preprint arXiv:2011.11961(2020).
  8. Rhemann, Christoph, et al. “A perceptually motivated online benchmark for image matting.”2009 IEEE Conference on Computer Vision and Pattern Recognition. IEEE, 2009.