AI备忘录 - Matting
type: channel
link: https://aiuai.cn/tag/Matting/
items: Github 项目 - BackgroundMattingV2 之模型定义
items: Github 项目 - BackgroundMattingV2 之预测函数
items: Github 项目 - BackgroundMattingV2 之数据集函数
items: 论文阅读 - Real-Time High-Resolution Background Matting
items: Deep Image Matting 之 Pytorch 实现[转]
items: 贝塞尔曲线数学原理及Python实现
items: Matting - 生成三分图(trimap)及前景背景融合
items: Matting - 更换主体背景的实现
items: Matting - Background Matting 简单理解
items: 阿里达摩院视觉算法-透明抠图[转]

Github 项目 - BackgroundMattingV2 之模型定义
type: item
date: 2020-12-27T22:01:00+08:00
description: 正如论文里所给出的 BackgroundMattingV2 网络结构图如下：网路结构包含两个网络组成：[1] - base 网络在将采样后的分辨率上输出粗略抠图结果;[2] - refine 网络仅对容易出错(error-prone)的图像部分进行处理，输出全分辨率的结果. 其能够节省冗余计算量，有助于调整耗时.1. 总体模型定义model.pyimport torch from torch import nn from torch.nn import functional as F from torchvision.models.segmentation.deeplabv3 import ASPP from .decoder import Decoder from .mobilenet import MobileNetV2Encoder from .refiner import Refiner from .resnet import ResNetEncoder from .utils import load_matched_state_dict class Base(nn.Module): """ A generic implementation of the base encoder-decoder network inspired by DeepLab. Accepts arbitrary channels for input and output. """ def __init__(self, backbone: str, in_channels: int, out_channels: int): super().__init__() #支持的 Encoder-Decoder Base 网络 assert backbone in ["resnet50", "resnet101", "mobilenetv2"] if backbone in ['resnet50', 'resnet101']: self.backbone = ResNetEncoder(in_channels, variant=backbone) self.aspp = ASPP(2048, [3, 6, 9]) self.decoder = Decoder([256, 128, 64, 48, out_channels], [512, 256, 64, in_channels]) else: self.backbone = MobileNetV2Encoder(in_channels) self.aspp = ASPP(320, [3, 6, 9]) self.decoder = Decoder([256, 128, 64, 48, out_channels], [32, 24, 16, in_channels]) def forward(self, x): x, *shortcuts = self.backbone(x) x = self.aspp(x) x = self.decoder(x, *shortcuts) return x def load_pretrained_deeplabv3_state_dict(self, state_dict, print_stats=True): # Pretrained DeepLabV3 models are provided by <https://github.com/VainF/DeepLabV3Plus-Pytorch>. # This method converts and loads their pretrained state_dict to match with our model structure. # This method is not needed if you are not planning to train from deeplab weights. # Use load_state_dict() for normal weight loading. # Convert state_dict naming for aspp module state_dict = {k.replace('classifier.classifier.0', 'aspp'): v for k, v in state_dict.items()} if isinstance(self.backbone, ResNetEncoder): # ResNet backbone does not need change. load_matched_state_dict(self, state_dict, p…

Github 项目 - BackgroundMattingV2 之预测函数
type: item
date: 2020-12-27T21:21:00+08:00
description: BackgroundMattingV2 提供了对图像、视频、网络摄像头的抠图实现，以及支持导出 ONNX 和 Torchscript.1. HomographicAlignment 图像对齐图像对齐函数，用于将背景与源图的匹配.inference_utils.pyimport numpy as np import cv2 from PIL import Image class HomographicAlignment: """ Apply homographic alignment on background to match with the source image. """ def __init__(self): self.detector = cv2.ORB_create() # ''' ORB 采用FAST(features from accelerated segment test)算法来检测特征点. FAST核心思想是, 找出那些卓尔不群的点，即拿一个点跟它周围的点比较， 如果它和其中大部分的点都不一样就可以认为它是一个特征点. ''' self.matcher = cv2.DescriptorMatcher_create(cv2.DESCRIPTOR_MATCHER_BRUTEFORCE) ''' 描述符匹配，特征匹配 ''' def __call__(self, src, bgr): src = np.asarray(src) bgr = np.asarray(bgr) keypoints_src, descriptors_src = self.detector.detectAndCompute(src, None) keypoints_bgr, descriptors_bgr = self.detector.detectAndCompute(bgr, None) matches = self.matcher.match(descriptors_bgr, descriptors_src, None) matches.sort(key=lambda x: x.distance, reverse=False) num_good_matches = int(len(matches) * 0.15) matches = matches[:num_good_matches] points_src = np.zeros((len(matches), 2), dtype=np.float32) points_bgr = np.zeros((len(matches), 2), dtype=np.float32) for i, match in enumerate(matches): points_src[i, :] = keypoints_src[match.trainIdx].pt points_bgr[i, :] = keypoints_bgr[match.queryIdx].pt # H, _ = cv2.findHomography(points_bgr, points_src, cv2.RANSAC) h, w = src.shape[:2] bgr = cv2.warpPerspective(bgr, H, (w, h)) #透视变换 msk = cv2.warpPerspective(np.ones((h, w)), H, (w, h)) # For areas that is outside of the background, # We just copy pixels from the source. bgr[msk != 1] = src[msk != 1] src = Image.fromarray(src) bgr = Image.fromarray(bgr) return src, bgr关于图像对齐可参考：[1] - Image Alignment (Feature Based) using OpenCV (C++/Python)[2] - 使用OpenCV实现基于特征的图像对齐2. 图像抠图inference_images.py对图像进行抠图(多线程).import argparse import torch import os import shutil from torch.nn import functional as F from torch.utils.data import DataLoader from torchvision import transforms as T from torchvision.transforms.functional import to_pil_i…

Github 项目 - BackgroundMattingV2 之数据集函数
type: item
date: 2020-12-27T17:17:00+08:00
description: 主要集中汇总学习下 BackgroundMattingV2 中的一些关于数据集、数据增强等辅助函数.1. ImagesDatasetimport os import glob from torch.utils.data import Dataset from PIL import Image class ImagesDataset(Dataset): def __init__(self, root, mode='RGB', transforms=None): self.transforms = transforms self.mode = mode self.filenames = sorted( [*glob.glob(os.path.join(root, '**', '*.jpg'), recursive=True), *glob.glob(os.path.join(root, '**', '*.png'), recursive=True)]) def __len__(self): return len(self.filenames) def __getitem__(self, idx): with Image.open(self.filenames[idx]) as img: img = img.convert(self.mode) if self.transforms: img = self.transforms(img) return img2. VideoDatasetimport cv2 import numpy as np from torch.utils.data import Dataset from PIL import Image class VideoDataset(Dataset): def __init__(self, path: str, transforms: any = None): self.cap = cv2.VideoCapture(path) self.transforms = transforms self.width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH)) self.height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) self.frame_rate = self.cap.get(cv2.CAP_PROP_FPS) self.frame_count = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT)) def __len__(self): return self.frame_count def __getitem__(self, idx): if isinstance(idx, slice): return [self[i] for i in range(*idx.indices(len(self)))] if self.cap.get(cv2.CAP_PROP_POS_FRAMES) != idx: self.cap.set(cv2.CAP_PROP_POS_FRAMES, idx) ret, img = self.cap.read() if not ret: raise IndexError(f'Idx: {idx} out of length: {len(self)}') img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img = Image.fromarray(img) if self.transforms: img = self.transforms(img) return img def __enter__(self): return self def __exit__(self, exc_type, exc_value, exc_traceback): self.cap.release()3. ZipDatasetfrom torch.utils.data import Dataset from typing import List class ZipDataset(Dataset): def __init__(self, datasets: List[Dataset], transforms=None, assert_equal_length=False): self.datasets = datasets self.transforms = transforms if assert_equal_length: for i in range(1, len(datasets)): ass…

论文阅读 - Real-Time High-Resolution Background Matting
type: item
date: 2020-12-26T21:39:00+08:00
description: 论文：Real-Time High-Resolution Background Matting作者：Shanchuan Lin Andrey Ryabtsev Soumyadip Sengupta团队：cs.washington.eduHomepage - Real-Time High Resolution Background MattingGithub - PeterL1n/BackgroundMattingV2针对视频背景抠图，该论文的几个着重点：[1] - 实时且高分辨率, 在 GPU 上，4K 分辨率(3840x2160) 30fps; HD 分辨率(1920x1080) 60 fps.[2] - 基于背景抠图(background matting)，其需要单独捕捉一帧背景图，以用于重建 alpha matte 和前景层.[3] - 最关键的挑战是，高分辨率 alpha matte 的计算，在实时处理高分辨率图像的同时，保持发丝级细节.[4] - 采用两个神经网络，base 网络计算低分辨率结果; refine 网络对高分辨率图像所选择的图片块( selective patches) 上进一步处理.[5] - 两个视频和图像抠图数据集：VideoMatte240K 和 PhotoMatte 13K/85.1. 抠图数据集获取大量、高分辨率、高质量的、由人工标注的抠图数据集是非常难的.1.1. 公开数据集Adobe Deep Image Matting 数据集提供了 269 张人物训练样本和 11 张测试图像，分辨率平均为 1000x1000.Attention-Guided Hierarchical Structure Aggregation for Image Matting 中362 张人物相关的训练样本和 11 张测试样本，分辨率平均为 1700x2000.以上两个数据集共 631 张训练样本，是远远不够的.1.2. VideoMatte240KVideoMatte240K 中采集了 484 段高分辨率绿幕(green screen) 视频，并采用色度键抠图(Chroma-key)软件 Adobe After Effects 生成了 240709 张 alpha mattes 和 foregrounds 的视频帧.其中，384 段视频的分辨率为 4K, 100 段视频的分辨率为 HD. 根据 479:5 的比例将视频划分为 tran 和 validataion 数据集. 如图：1.3. PhotoMatte13K/85PhotoMatte13K/85 中采集了 13665 张图像集合，这些图像是采用摄影棚质量的光照，在绿幕前的相机所拍摄的，并采用色度键抠图算法(Chroma-key) 和手工调整以及错误修正所提取的 mattes.根据 13165:500 的比例划分为 train 和 validation 数据集. 数据集图像分辨率平均为 2000x2500，包括了一些头发.2. 算法细节给定图像 I 以及捕捉的背景 B, 算法会预测输出 alpha matte $\alpha$ 和前景 F, 基于如下公式，其可以被任何背景来组合，$$ I' = \alpha F + (1 - \alpha) B' $$其中，$B'$ 为新背景.相比于直接求解前景，这里求解的是前景残差 $F^{R} = F - I$. 因此，F 可以通过添加适当的约束将 $F^R$ 和输入图像 I 相加:$$ F = max(min( F^{R} + I, 1), 0) $$高分辨率抠图是很有挑战的，因为直接进行深度网络计算，其计算量和内存消耗是不切实际的. 如图所示：人物 mattes 往往是非常稀疏的，大面积的像素要么是背景($\alpha=0$)，要么是前景($\alpha = 1$)，仅有少量的区域包含更多的细节，如头发、眼镜、人物轮廓等周围.对此，相比于设计直接在高分辨率图像直接操作的一个网络，这里采用了两个网络：[1] - base 网络 $G_{base}$ - 对低分辨率图像进行处理;[2] - refinement 网络 $G_{refine}$ - 基于低分辨率预测结果，从原始图像分辨率选择特定图像块进行处理.网络结构如图：给定原始输入图像 I 和捕捉的背景图 B，首先采用缩放因子 $c$ 进行将采样到 $I_c$ 和 $B_c$. $G_{base}$ 采用 $I_c$ 和 $B_c$ 作为输入，并预测输出粗略的 alpha matte $\alpha_c$、foreground residual $F_c^R$、error prediction map $E_c$ 以及 hidden feature $H_c$. $G…

Deep Image Matting 之 Pytorch 实现[转]
type: item
date: 2020-11-26T20:06:00+08:00
description: 原文：Pytorch 抠图算法 Deep Image Matting 模型实现 - 2019.07.21GitHub: deep_image_matting_pytorch作者：公输睚信 - 简书本文旨在实现抠图算法 Semantic Human Matting 的第二阶段模型 M-Net，也即 Deep Image Matting. 值得说明的是，本文实现的模型与原始论文略有出入，除了模型的输入层有细微差别之外，损失函数也作了简化(但无本质差别). 本文训练数据来源于爱分割公司开源的 aisegmentcn/matting_human_datasets，总共包含 34426 张图片和对应的 alpha 通道，数据量非常大，能公开特别值得点赞. 但同时因为标注数据的 alpha 通道精度不高，导致训练后测试效果较差. 建议使用 Deep Image Matting 的数据集训练. 图：Semantic Human Matting 抠图模型总的来说，Semantic Human Matting 论文提出的自动抠图的思路特别清晰明了(如上图)，对于一张待抠图像，首先通过语义分割模型(即 T-Net)分割出前景 Fs、背景 Bs 和未知区域 Us，Fs+Bs+Us=1. 广义的认为前景Fs和未知区域Us组成一个三分图Trimap. 此时，再利用 Deep Image Matting(即 M-Net) 即可高质量的完成抠图. 本文只关注该模型的第二阶段(M-Net). M-Net 接受待抠图像(前景与背景的 RGB 3通道合成)以及语义分割模型输出的3通道预测(Fs，Bs，Us) 拼接而成的 6 通道输入，经过编码器提取图像特征之后，由解码器得到预测Alpha($\alpha _r$). 如果语义分割模型分割的精度较高，那么可以认为 Fs 和 Bs 对应的区域已经很好的抠出了大部分的前景和背景，唯一需要提升准确率的是待抠对象的边缘区域，所以模型的第二阶段 M-Net 的目的就是细化的预测边缘区域(这正是 Deep Image Matting 要干的事情)，两部分结合即得到最终的预测：$$ F_s + U_s \alpha _r $$公式可以这样理解：$$ 预测的前景 = 确定区域上的前景 + 未知区域上的前景 $$即：$$ P(前景) = P(确定区域上前景) + P(未知区域上前景) $$根据全概率公式，符号化表达为：$$ \alpha = P(F) = P(F|known) P(known) + P(F|unknown) P(unknown) $$$$ \alpha = \frac{F_s}{F_s + B_s}(F_s + B_s) + \alpha _r U_s = F_s + U_s \alpha _r $$但上述公式存在一个缺陷，即如果待抠目标外有大块噪声，则最终的预测也消除不了这个噪声，如下图：图：语义分割之后的前景带有外部噪声(衣服左侧的小照片)为了消除第一阶段可能包含的外部噪声，本文的在实现 M-Net 的时候做了一个小的改动：第二阶段的输入改为由待抠图像 + Fs 组成的 4通道图片(此时，相当于将 Fs 看成是三分图 trimap)，并且将第二阶段的预测作为最终的预测. 另外，第二阶段损失函数简化为只用 alpha 通道的损失更好.1. DIM 模型实现Deep Image Matting 原文的模型如下：图：Deep Image Matting 论文模型模型先通过一个编码器提取特征，之后经过一个解码器预测一个初始的 alpha 通道，这个预测值效果已经很好，但作者为了进一步提升抠图的精度，又额外的接了几层细化的小网络，然后将细化后的输出作为整个模型的最终输出. 具体来说，首先将待抠图像(3 通道)以及事先准备好的三分图(trimap)合成一个 4 通道图像，然后经过 VGG16 的前 13 个卷积层以及之后的 1 个全连接层(看成是 1x1 的卷积层)，总共 14 个卷积层提取图像特征(此时已做了 5 个最大池化，因此图像分辨率下降了 32 倍，如果输入是 320x320，那么特征映射的分辨率就变成了 10x10)，这是模型的编码器阶段. 接下来对图像特征进行解码，即开始解码器阶段. 解码器使用 6 个卷积层(5x5 的卷积核)和 5 个 反池化层，每个反池化层将特征映射的分辨率提升 2 倍，因此解码器的输出与模型输入的大小一样. 这里，使用反池化层的效果要比直接使用转置卷积(deconvolution)的效果要好. 虽然他们都是为了提升图像分辨率，但使用转置卷积并不能很好的抠出细节，而使用反池化层却可以抠图头发丝等非常细的前景. 为了最求极致效果，作者又接了一个小网络，将待抠图像和编码器预测的 alpha 通…

贝塞尔曲线数学原理及Python实现
type: item
date: 2020-11-24T20:15:00+08:00
description: 贝赛尔工具在 photoshop 中叫“钢笔工具”；在 CorelDraw 中翻译成“贝赛尔工具”；而在 Fireworks 中叫“画笔”. 它是用来“画线”造型的一种专业工具. 当然还有很多工具也可以完成画线的工作，例如常用的photoshop里的直线、喷枪、画笔工具，Fireworks里的直线、铅笔和笔刷工具，CorelDraw里的自由笔，手绘工具等等.很多设计师应该都知道贝塞尔曲线，习惯用 PS 的会用钢笔工具，习惯用AI 的会用贝塞尔，因为它所绘制出来的曲线很容易受控制，也很美观. 下面深入了解下贝塞尔曲线的数学原理和公式.在数学中，贝塞尔曲线又分为很多种，如，一阶贝塞尔曲线、二阶贝塞尔曲线、三阶贝塞尔曲线，等等. 除了一阶贝塞尔曲线是直线外，剩下的多阶贝塞尔曲线都是抛物线. 贝塞尔曲线由起点、终点和控制点组成，根据控制点的个数和位置决定了这个曲线的最终样式.1. 原理贝塞尔曲线的数学原理 - 2017.01.26先在一个平面内任选 3 个不共线的点，依次用线段连接，如图：在第一条线段上任选一个点 D，计算该点到线段起点的距离 AD，与该线段总长 AB 的比例.根据上一步得到的比例，从第二条线段上找出对应的点 E，使得 AD:AB = BE:BC.连接这两点 DE.从新的线段 DE 上再次找出相同比例的点 F，使得 DF:DE = AD:AB=BE:BC.到这里，就确定了贝塞尔曲线上的一个点 F. 接下来，稍微回想一下中学所学的极限知识，让选取的点 D 在第一条线段上从起点 A 移动到终点 B，找出所有的贝塞尔曲线上的点 F. 所有的点找出来以后，也就得到了这条贝塞尔曲线.如果想象不出来，可以看下面的动画.到这里，已经大概了解贝塞尔曲线绘制的过程了.下面看公式.2. 公式以下公式中：$B(t)$ 为 $t$ 时间下点的坐标； $P_0$ 为起点；$P_n$ 为终点，$P_i$ 为控制点.2.1. 一阶贝塞尔曲线一阶贝塞尔曲线只有起点和终点，并没有控制点，所以绘制出来的图形仅仅只是一条直线，那么在时间 t 为 1 秒的情况下，其公式为：$$ B(t) = (1 - t) P_0 + tP_1, t \in [0, 1] $$意义：由 $P_0$ 至 $P_1$ 的连续点， 描述的一条线段.2.2. 二阶贝塞尔曲线二阶贝塞尔曲线只存在一个控制点，此时从起点到终点的线段发生变化，具体的变化是由控制点的位置而改变的. 图中，绿色的线段为红色曲线的切线.$$ B(t) = (1 - t)^2 P_0 + 2t(1 - t)P_1 + t^2 P_2, t \in [0, 1] $$仅仅只是简单的一元二次方程式.原理：由 $P_0$ 至 $P_1$ 的连续点 $Q_0$，描述一条线段；由 $P_1$ 至 $P_2$ 的连续点 $Q_1$，描述一条线段；由 $Q_0$ 至 $Q_1$ 的连续点 $B(t)$，描述一条二次贝塞尔曲线.Python 示例如：#!/usr/bin/python3 #!--*-- coding: utf-8 --*-- import numpy as np import matplotlib.pyplot as plt P0, P1, P2 = np.array([[0, 0], [2, 4], [5, 3]]) # 定义贝塞尔曲线 P = lambda t: (1 - t)**2 * P0 + 2 * t * (1 - t) * P1 + t**2 * P2 # 在 [0, 1] 范围内的 50 个点上验证贝塞尔曲线 points = np.array([P(t) for t in np.linspace(0, 1, 50)]) # 分别获取点的 x 坐标和 y 坐标 x, y = points[:, 0], points[:, 1] # plt.plot(x, y, 'b-') plt.plot(*P0, 'r.') plt.plot(*P1, 'r.') plt.plot(*P2, 'r.') plt.show()如：可以看到，曲线从第一个控制点处开始，到最后一个控制点处结束. 这个结果对任意数量的点都成立.从P0到P2，P1 完全决定了曲线的形状。移动 P1：贝塞尔曲线总是包含在控制点形成的多边形中. 这个多边形因此被称为控制多边形，或者贝塞尔多边形. 这个属性也适用于任意数量的控制点，这使得它们在使用软件时的操作非常直观.控制多边形还具有以下特性：包含曲线的面积最小，称为凸包.2.3. 高阶贝塞尔曲线[1] - 三阶[2] - 四阶[3] - 五阶从三阶开始贝塞尔曲线就开始显得复杂了，高阶的通用公式如下：$$ P_i^k = (1 - t) P_i^{k-1} + t P_{i+1}^{k-1} …

Matting - 生成三分图(trimap)及前景背景融合
type: item
date: 2020-11-20T20:10:00+08:00
description: 抠图技术中三分图(trimap) 被普遍用到，这里记录下基于图像分割技术得到的粗分割结果生成三分图的 python 实现及图片合成实现.1. 生成三分图trimapimport random import numpy as np import cv2 def gen_trimap(alpha): k_size = random.choice(range(1, 5)) # 随机选择核大小 iterations = np.random.randint(1, 20) kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (k_size, k_size)) dilated = cv2.dilate(alpha, kernel, iterations) eroded = cv2.erode(alpha, kernel, iterations) trimap = np.zeros(alpha.shape) trimap.fill(128) trimap[eroded >= 255] = 255 trimap[dilated <= 0] = 0 return trimap if __name__ == "__main__": alpha = xxxx assert (np.unique(alpha_np) == [0, 255]).any() alpha = alpha * 255 gen_trimap(alpha)2. 合成图片composite4由背景、前景、前景alpha 合成新图像.Matting - 更换主体背景的实现 - AIUAIimport math import cv2 import numpy as np def composite4(fg, bg, a, w, h): fg = np.array(fg, np.float32) bg_h, bg_w = bg.shape[:2] x = 0 if bg_w > w: x = np.random.randint(0, bg_w - w) y = 0 if bg_h > h: y = np.random.randint(0, bg_h - h) bg = np.array(bg[y:y + h, x:x + w], np.float32) alpha = np.zeros((h, w, 1), np.float32) alpha[:, :, 0] = a / 255. im = alpha * fg + (1 - alpha) * bg im = im.astype(np.uint8) return im, a, fg, bg def process(fg_file, alpha_file, bg_file): im = cv2.imread(fg_file) a = cv2.imread(alpha_file, 0) h, w = im.shape[:2] bg = cv2.imread(bg_file) bh, bw = bg.shape[:2] wratio = w / bw hratio = h / bh ratio = wratio if wratio > hratio else hratio if ratio > 1: bg = cv2.resize(src=bg, dsize=(math.ceil(bw * ratio), math.ceil(bh * ratio)), interpolation=cv2.INTER_CUBIC) return composite4(im, bg, a, w, h) if __name__ == "__main__": # fg_file = 'fg.jpg' alpha_file = 'fg_alpha.png' bg_file = 'bg_file.jpg' img, alpha, fg, bg = process(fg_file, alpha_file, bg_file) # trimap trimap = gen_trimap(alpha) # 翻转 if np.random.random_sample() > 0.5: img = np.fliplr(img) trimap = np.fliplr(trimap) alpha = np.fliplr(alpha)3. 相关[1] - Python - OpenCV 之图像形态学(膨胀与腐蚀) - AIUAI[2] - Matting - 更换主体背景的实现 - AIUAI

Matting - 更换主体背景的实现
type: item
date: 2020-11-15T17:58:00+08:00
description: 1. 颜色背景实现如下：#!/usr/bin/python3 #!--*-- coding: utf-8 --*-- import numpy as np from PIL import Image import matplotlib.pyplot as plt img_file = 'test.jpg' png_file = './test.png' img_pil = Image.open(img_file) png_pil = Image.open(png_file) alpha = png_pil.split()[-1] ## pillow #im = Image.new('RGBA', png_pil.size, (255, 0, 255)) #im.paste(im, mask=im) # opencv bg = Image.new("RGB", png_pil.size, (255, 0, 255)) fg = np.array(img_pil, np.float32) alpha= np.expand_dims(np.array(alpha) / 255,axis=2) im = alpha * fg + (1 - alpha) * bg im = im.astype(np.uint8) # plt.figure(figsize=(12, 10)) plt.subplot(131) plt.imshow(img_pil) plt.subplot(132) plt.imshow(alpha) plt.subplot(133) plt.imshow(im) plt.show()如，左原图，中alpha图，右新背景图.2. 图片背景#!/usr/bin/python3 #!--*-- coding: utf-8 --*-- from PIL import Image img_file = 'test.jpg' png_file = './test.png' # bg = Image.open(bg_file) png_pil = Image.open(png_file) if png_pil.size != bg.size: png_pil = png_pil.resize(bg.size) r, g, b, alpha = png_pil.split() fg = Image.merge('RGB', (r, g, b)) out = Image.composite(fg, bg, png_pil)

Matting - Background Matting 简单理解
type: item
date: 2020-09-20T10:24:29+08:00
description: 论文： Background Matting: The World is Your Green Screen - CVPR2020Projects：Background Matting: The World is Your Green ScreenGithub: senguptaumd/Background-MattingBlog: Background Matting: The World is Your Green Screen - MediumBackground Matting 示例：图：手持智能手机相机，拍摄同一场景的两张照片 - 一张有目标(如，人物)和一张无目标；采用对抗损失(adversarial loss) 的神经网络来重建 alpha matte 和前景(foreground)；最后合并结果到新的背景(backgroud).1. Matting 问题Matting 是将图像分解为前景和背景的处理，以便于将前景合并到新的背景上. 这也是绿幕(green screen) 的关键技术，被广泛应用于视频生产、图形和APPs 中.Matting 问题的建模，将图像的每个像素表示为前景和背景的组合：$$ C = F * \alpha + B * (1 - \alpha) $$Matting 问题的求解是，给定图像 C，计算每个像素的前景F、背景B 和透明度alpha.由于图像有 RGB 三个通道，该公式需要基于 3 个已知量来求解 7 个未知量，问题是难以求解的.1.1. Matting 与 Segmentation一种可行方式是，采用分割(segmentation) 将前景从图像中分离出来. 但是，尽管分割技术近年取得了很大进步，其并没有解决 matting 公式里的问题.分割是对图像中每个像素分配一个二值标签(0, 1)，以表示背景和前景，而不是求解连续的 alpha 值. Segmention 和 Matting 的区别可以如下图简单例示：图：Segmentation 采用 DeepLabv3+ 得到. 图中，边缘区域，尤其是头发，是由 0-1 之间的真实 alpha 值的. 因此，分割的二值性导致前景边界周围出现严格边界，留下可见的不好效果. 解决了部分透明度和前景问题，对第二帧的合成更有利.1.2. Matting 与背景提取Is This Method Like Background Subtraction?Matting 问题和另一个接近的问题是，是否和背景提取类似呢？首先，如果使用任意背景组合这么简单，电影产业就不会每年花费大量美元投资在绿幕(green screens) 上.图：背景提取并不能对于任意拍摄背景取得较好效果.此外，背景提取并不能解决 alpha 值的问题，与分割一样存在相同的硬边缘问题. 且，当背景和前景比较相似、背景存在运动时，背景提取也不能很好的解决.2. 使用任意拍摄的背景Using A Casually Captured Background由于 matting 是比 segmentation 更难得问题，即使是采用深度学习技术，往往需要再利用其他信息来解决 matting 无约束问题.比如，很多算法需要采用 trimap 或手工标注的已知前景、背景和未知区域. 尽管其对于单张图片是可行的，但视频的标注是极其费时的，因此并不是视频抠图的一个可行研究方向.而这里，采用拍摄的背景作为真实背景的一种估计. 其使得前景和 alpha 值得求解更容易. 称之为“随意拍摄(casually captured)”背景，因为其包含轻微的抖动、颜色差一、轻微阴影、或者与前景相似的颜色.图：拍摄过程. 当目标离开场景后，再拍摄的一张背景有助于算法.图：拍摄的输入、背景和新背景组合后的结果.需要注意的是，这张图片是很有挑战的，因为其前景颜色和背景颜色非常相似(尤其是的头发周围). 而且，其是采用手持设备拍摄的，会包含轻微的背景抖动.“We call it a casually captured background because it can contain slight movements, color differences, slight shadows, or similar colors as the foreground.”2.1. 拍摄建议尽管本文算法能够处理一定的背景干扰，但如果背景是固定的且最好是室内，其效果会更好. 例如，算法对于由于目标所导致的存在高度可见阴影、背景移动(如水流、车流、树木) 或大曝光变化的情况时，是效果不好的. 图：失败情况. 人物在移动的喷泉前面.此外，还推荐在视频结束时，让人物离开场景；然后从连续的视频中拉出那一帧画面. 许多智能手机对于视频模式切换到照片…

阿里达摩院视觉算法-透明抠图[转]
type: item
date: 2020-09-06T19:07:00+08:00
description: 原文：达摩院视觉算法黑科技之透明抠图 -2020.07.13出处：阿里云视觉智能开放平台论文：Segmenting Transparent Objects in the Wild - 2020Github - Segment_Transparent_Objects简介： 透明抠图问题作为抠图问题的一种，其采用的方法和模型构建与通常的模型有所不同，透明抠图需要将环境光、折射率的影响纳入计算，而一般的折射光图又很难获得，因此透明抠图的模型在过去一直难以建立，或者说很难达到令人满意的效果，达摩院视觉算法团队通过双分支解码器（Object Mask获取，Opacity预测），颜色纠正模块，对图像实现高精度透明抠图.1. 透明抠图 vs 非透明抠图物体的抠图问题可以定义为求解以下的公式，即给定图像I，求解前景颜色F、背景颜色B和Alpha matte的线性组合:$$ I = \alpha F + (1 - \alpha)B, \alpha \in [0, 1] $$对于透明物体而言，它展现在观察者眼中的颜色是由其前景颜色，背景颜色以及环境光线经过前景物体自我反射折射混合而成的，因此，它的公式会更加复杂一些：$$ I = \alpha F + (1 - \alpha)B + \Phi, \alpha \in [0, 1] $$$$ \Phi = \iint R(\omega \rightarrow p) E(\omega) d \omega dp $$Φ表示的是环境光的影响, 它是所有光线E(w)与反射率方程R乘积在所有点上的二重积分，求解很复杂[1]，导致实现精确的透明抠图是一个非常困难的问题. 因此，现有透明抠图研究的目标也是实现视觉感受“真实”的抠图而已，并非追求完全真实的抠图结果.2. 透明抠图-现有研究SOTA的Matting算法在同时提供原图和对应trimap的情况下，可以的实现对半透物体的处理（如下图为GCA-Matting[2]的效果），但tripmap在实际的图像的处理中难以获取，限制了这类算法其在业务中的使用. TOM-Net[3] 将透明抠图问题视为折射流的估计问题，网络支持对单图输入,经过三分支的编解码器网络，分别预测图像的Object Mask，attenuative mask，flow mask(折射流图)，并可以通过折射流信息进一步在新的背景进行合成. 该方法的局限性在于其假设物体必须全部为无色透明物体，并且在训练过程中需要折射流图作为label, 而折射流图在真实世界是非常难以获取的，因此该方法的训练数只能依赖于图形学合成，与真实透明图像的分布无法一致（图像的语义合理性存疑，例如玻璃杯在山前悬浮）. 经过我们在实际数据上的测试，该方法在实际图像的表现并不理想. Segmenting Transparent Objects in the Wild[4]提出了基于语义分支和边缘分支结构的真实世界透明物体分割网络，通过边界注意力模块（Boundary Attention Modeule）增强对透明物体的分割精度，并发布了目前数量最大的透明物体分割标注数据集Trans10K. 然而，文章提出的算法和发布的数据集都是处理到语义分割层面，并没有对物体的透明度做进一步处理. 3. 透明抠图-问题简化考虑到透明抠图问题本身难以求解，而且数据构建也非常困难，在实际的应用场景中，为保证同时保证算法的泛化能力和抠图效果，我们对问题进行了简化， 我们假设所需处理的物体的透明部分是无色的，且所在环境的背景颜色分布相对均匀. 在这样的条件下，背景的自发光或反射光的颜色可以认为是全局一致的颜色，不会出现多种颜色叠加的情况，Φ的估计就只是和背景颜色相关了. 特别的，如果预知背景的颜色，可以通过将其作为先验引入Φ中，对结果进行背景杂色的抑制及去除.4. 透明抠图-模型设计我们的模型输入为单张图像，首先提取其深层特征. 然后进行语义级别的分割和提取，力求完整准确地获得物体所在图像区域（Mask）. 同步进行对图像物体不透明度(Opacity)的预测. 而不透明度(Opacity)的预测由于在训练的时候没有进行语义的约束，容易存在非主体区域的噪声影响，因此，将两者进行融合可以将透明信息约束在主体范围内. 最后，对于已知背景颜色先验的场景（如已知是绿幕），我们可以引入颜色纠正模块，实现对背景透出的杂色进行去除. 对于背景颜色未知，但饱和度低的场景，抠图结果也依然可用. 5. 透明抠图-结果与应用在已知背景颜色先验的情况下，可以通过颜色纠正模块对背景透出的杂色进行去除（左到右：实拍图，Opacity, 直接抠图结果，色偏纠正结果）对于背景颜色未知，但饱和度低的场景，抠图结果也依然可用，例如下面几组展示图. 目前在车辆分割算法上，我们已经实现了基于透…