基于深度数据对图像进行背景模糊虚化

Jan 15,2019   1980 words   8 min


随着现在双摄手机的普及,相机大都有了“人像模式”的拍照功能,简而言之就是模仿单反相机的效果,将人像突出、背景虚化。 对手机而言,从理论上来说实现这个效果有两种思路。一种是基于图像处理的办法,即拍摄照片,然后运用图像算法对人像进行识别并圈出范围,将范围以外的影像虚化,达到效果。 另一种是基于深度信息的办法。由于具有双摄像头,所以理论上可以根据视差计算出深度图。在深度图中对深度进行过滤,识别出较近物体的范围,将距离较远的物体进行虚化。

而在实际过程中,手机更多使用的还是第一种方法。原因在于,虽然手机现在都有双摄,但两个镜头之间的基线距离太短了,根本无法准确估计出稍大场景的深度信息。若是基于这个不精确的深度图(如深度空洞等)进行模糊,效果不会很好。 同时,随着现在手机NPU的成熟,基于深度学习的图像识别算法越来越普及,效率越来越高。因此采用第一种方法既省时又省力。

但本文还是对第二种方法进行一些简单的探讨,旨在提供一种思路。下面直接上代码。

1.代码

# coding=utf-8
import cv2
from matplotlib import pyplot as plt
import numpy as np


def nothing(x):
    pass


if __name__ == '__main__':
    # depth = cv2.imread("depth.png", cv2.IMREAD_UNCHANGED)
    # rgb = cv2.imread("rgb.png")

    # 逐像素计算深度,模拟深度图
    depth = np.zeros([450, 800], np.int32)
    for i in range(depth.shape[1]):
        for j in range(depth.shape[0]):
            # 给小猪部分设置深度
            if (i - 300) * (i - 300) + (j - 228) * (j - 228) <= 115 * 115:
                depth[j, i] = (300 - i) * (300 - i) + (228 - j) * (228 - j)
            # 给小鸡部分设置深度
            elif (i - 563) * (i - 563) + (j - 216) * (j - 216) <= 124 * 124:
                depth[j, i] = (563 - i) * (563 - i) + (216 - j) * (216 - j)
            # 其它区域设置深度
            else:
                depth[j, i] = 0.5 * (i - 400) * (i - 400) + (j - 225) * (j - 225) + 100

    plt.imshow(depth)
    plt.show()

    rgb = cv2.imread("img.jpg")

    max_dis = int(np.max(depth))
    min_dis = int(np.min(depth))
    th_dis = int(np.mean(depth))
    print 'max dis:', max_dis
    print 'min dis:', min_dis
    print 'threshold dis:', th_dis

    rgb_res = np.zeros(rgb.shape, rgb.dtype)
    rgb_blur = cv2.GaussianBlur(rgb, (31, 31), 0)

    # 逐像素判断深度是否大于阈值
    for i in range(rgb.shape[0]):
        for j in range(rgb.shape[1]):
            if depth[i, j] >= th_dis:
                rgb_res[i, j] = rgb_blur[i, j]
            else:
                rgb_res[i, j] = rgb[i, j]

    cv2.namedWindow("BlurWithDepth")
    cv2.createTrackbar('Threshold', 'BlurWithDepth', th_dis, max_dis, nothing)

    while 1:
        cv2.imshow("BlurWithDepth", rgb_res)

        k = cv2.waitKey(1) & 0xff
        if k == 27:
            break

        th_dis = cv2.getTrackbarPos('Threshold', 'BlurWithDepth')

        # 逐像素判断深度是否大于阈值
        for i in range(rgb.shape[0]):
            for j in range(rgb.shape[1]):
                if depth[i, j] >= th_dis:
                    rgb_res[i, j] = rgb_blur[i, j]
                else:
                    rgb_res[i, j] = rgb[i, j]

    cv2.destroyAllWindows()

生成的深度图如下,颜色越暗表示距离越小。 完成动图效果如下。

使用TUM-RGBD数据集的真实数据效果如下。 但正如一开始提到的,由于深度图并不是很精确,而且经常会有空洞和缺失,所以效果并没有想象那么好,深度图如下所示。 测试数据、代码在Github,点击查看。 因此如果想基于深度图进行背景虚化,还需要对于细节进行一些处理,可以考虑用一些插值算法等将这些空洞、缺失补齐。

本文作者原创,未经许可不得转载,谢谢配合

返回顶部