使用深度图像实现照片虚化效果

使用深度图像实现照片虚化效果

语雀:https://www.yuque.com/lart/blog/vw6pcx

原理介绍

随着智能手机的发展,手机自带的相机功能也逐渐变得丰富起来。其中的“人像模式”可以模仿单反相机的效果,将特定目标突出并将其余背景虚化。这一效果可以获得良好的视觉效果,有效的突出感兴趣目标的主体。

在这里插入图片描述

从理论上来说,实现这一效果有两种思路。

  • 基于图像处理的办法:对主体目标进行分割后,获得前背景对应的区域。利用算法模糊背景区域的内容,即实现虚化的效果。
  • 基于深度信息的办法:部分手机利用硬件支持或者软件算法处理,可以得到近似的视差信息,即照片对应的深度图。基于得到的深度图数据,对深度进行过滤,从而依据相对深度或者绝对深度识别出感兴趣区域,为不同深度位置提供不同程度的虚化效果。

本文尝试对第二种方法进行一些简单的尝试。

需要说明的是,这里为了方便利用并行加速,所以代码都是基于 pytorch 实现的。

实现细节

对于第二种思路,一种直观的实现方式是使得模糊核与深度相关联,越远的位置对应的模糊程度越大。

这里我们基于均值滤波的形式进行扩展。让滤波器的权重分布在均值滤波器的基础上进行调整。

归一化深度数据

由于我们的 depth 图像中深度数据是 0~255 的数值,为了后面将其用作用于调整均值核和恒等核之间的变换参数,这里我们对 depth 图像进行 minmax 归一化:

depth = depth - depth.min()
depth = depth / depth.max()

折叠图像数据

在我们的处理中,可以注意到与传统的直接对整个背景整体模糊的形式最大的不同,即我们是根据不同位置自身对应的深度信息来自适应的调整模糊程度。所以这种位置自适应的处理形式就需要我们借助于 pytorch 提供的 img2col 函数 unfold 来实现了:

image = image.permute(2, 0, 1).unsqueeze(0)
image = F.pad(image, pad=(kernel_size//2, kernel_size//2, kernel_size//2, kernel_size//2), mode='replicate')
image = F.unfold(image, kernel_size=kernel_size)
image = image.reshape(num_channels, kernel_size*kernel_size, height, width)

这里同时利用 replicate 的模式来补齐边缘处的像素,以确保后期加权平均不会出现明显的暗边。

关于 unfold 的理解,可以参看我之前的文章中的介绍:https://www.yuque.com/lart/papers/frxyq3#sptFi。其就是用于将局部方形邻域的数据收集起来,堆叠到通道维度上。可以说其实现了卷积中的局部聚合操作中加权之前的行为。

生成权重

weight = get_log_kernel2d_pt(kernel_size=kernel_size, valid_ratio=depth, normalize=True)

这里将归一化后的 depth 传入了 valid_ratio 上。首先对这一参数进行约束,保证数值范围满足要求:

assert valid_ratio.min() >= 0 and valid_ratio.max() <= 1

这一参数用来调整核参数的形式:

  • 元素值为 1 的时候,对应的核形式为均值滤波器;
  • 元素值为 0 的时候,对应的核形式为恒等核,即仅有中心参数为 1,其余均为 0。

基于后面的设计,我们还需要将这一参数映射到 [ 0 , inf ⁡ ] [0, \inf] [0,inf] 区间上,其中 0 对应于恒等核,而正无穷对应于均值滤波器。这里基于负对数函数的形式:

valid_ratio = -torch.log(1 - valid_ratio)

为了对这些滤波器以均值滤波器为基础进行统一表示,我们引入了绝对值函数作为加权窗口中各点的权重分布形式。即 F.relu(1 - torch.abs(coords / valid_width)).nan_to_num(nan=1) 所表示的形式:

  • valid_width=0 的时候,对于相对坐标为 0 处,1 - torch.abs(coords / valid_width) 结果为 nan,而其余位置均为 -inf,通过 relu.nan_to_num() 处理后,可以得到恒等核,仅保留中心元素,其余元素的参数均为 0。
  • valid_width 趋向于无穷大的时候,此时开始从恒等核逐步向均值核变化。
title: PyTorch 中几个特殊值的除法

- inf / inf = nan
- num / inf = 0
- inf / num = inf
- \* / 0 = inf
- nan / \* = nan
- \* / nan = nan
>>> a = torch.tensor([-torch.inf, -1, 0, 1, torch.inf, torch.nan]) 
>>> a / -torch.inf 
tensor([nan, 0., -0., -0., nan, nan])
>>> a / -1        
tensor([inf,  1., -0., -1., -inf, nan])
>>> a / 0         
tensor([-inf, -inf, nan, inf, inf, nan])
>>> a / 1 
tensor([-inf, -1.,  0.,  1., inf, nan])
>>> a / torch.inf  
tensor([nan, -0., 0., 0., nan, nan])
>>> a / torch.nan
tensor([nan, nan, nan, nan, nan, nan])

二维权重可以通过一维权重矩阵相乘来获得。这里利用 einsum 来保留原始的形状,避免直接压缩维度后还需重新调整形状的麻烦。

kernel_1d = F.relu(1 - torch.abs(coords / valid_width)).nan_to_num(nan=1)
kernel_2d = torch.einsum("xhw,yhw->xyhw", kernel_1d, kernel_1d)
kernel_2d = kernel_2d.reshape(-1, *ori_shape)  # 压缩前两个维度

最终,我们可以根据需要来对权重进行归一化,以确保总和为 1:

if normalize:
    kernel_2d = kernel_2d / kernel_2d.sum(dim=0)

合成图像

直接对权重和原图相乘加和后得到结果:

tgt_image = (image * weight).sum(dim=1)
tgt_image = tgt_image.permute(1, 2, 0).numpy()

实验效果

原始RGB图像:

在这里插入图片描述
对应的Depth图像:

在这里插入图片描述
合成后的虚化图像示例:

在这里插入图片描述

链接

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值