You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于深度掩码的类散景模糊实现(Pillow/CV2)

基于深度图的伪景深(散景效果)实现方案

核心思路

伪景深的关键是根据深度图灰度值映射连续变化的模糊半径:近景(深度图中亮度较高区域,对应MiDaS生成的近距离物体)使用小半径模糊甚至保留清晰,远景(亮度较低区域)使用大半径模糊。同时用圆盘形状卷积核模拟真实散景的圆形光斑效果,替代普通的高斯/盒状模糊。


PIL 实现方案

PIL原生没有内置圆盘模糊,需自定义卷积核实现,再结合深度图做多尺度融合:

步骤1:自定义圆盘模糊函数

import numpy as np
from PIL import Image, ImageFilter

def disc_blur(image, radius):
    # 生成圆盘形状的卷积核
    kernel_size = 2 * radius + 1
    kernel = np.zeros((kernel_size, kernel_size), dtype=np.float32)
    center = radius
    y, x = np.ogrid[:kernel_size, :kernel_size]
    dist_from_center = np.sqrt((x - center)**2 + (y - center)**2)
    kernel[dist_from_center <= radius] = 1
    # 归一化核权重
    kernel /= kernel.sum()
    
    # 将PIL图像转为numpy数组进行卷积
    img_array = np.array(image)
    channels = img_array.shape[2] if len(img_array.shape) == 3 else 1
    result = np.zeros_like(img_array)
    
    for c in range(channels):
        channel = img_array[..., c]
        # 边缘填充避免黑边
        padded = np.pad(channel, radius, mode='reflect')
        # 滑动窗口卷积
        for i in range(img_array.shape[0]):
            for j in range(img_array.shape[1]):
                result[i, j, c] = np.sum(padded[i:i+kernel_size, j:j+kernel_size] * kernel)
    
    return Image.fromarray(result.astype(np.uint8))

步骤2:基于深度图的多尺度融合

# 加载原图与深度图
oimg = Image.open('2.png').convert('RGB')
width, height = oimg.size
mimg = Image.open('2_depth.png').resize((width, height)).convert('L')

# 预处理深度图:归一化到0-1范围,反转(亮部为近景保留清晰,暗部为远景模糊)
depth_array = np.array(mimg) / 255.0
depth_array = 1 - depth_array  # 反转后值越大对应越远,模糊半径越大

# 定义模糊半径范围:近景半径0,远景最大半径10
min_radius = 0
max_radius = 10

# 生成多尺度模糊图像(分5个尺度,可按需调整)
scales = 5
blur_images = []
for i in range(scales):
    radius = min_radius + (max_radius - min_radius) * (i / (scales - 1))
    blur_img = disc_blur(oimg, int(radius))
    blur_images.append(blur_img)

# 将模糊图像转为numpy数组
blur_arrays = [np.array(img) for img in blur_images]

# 根据深度值加权融合多尺度图像
result_array = np.zeros_like(np.array(oimg), dtype=np.float32)
for i in range(scales):
    if i == scales - 1:
        weight = np.where(depth_array >= (i / (scales - 1)), 1.0, 0.0)
    else:
        lower = i / (scales - 1)
        upper = (i + 1) / (scales - 1)
        weight = np.where((depth_array >= lower) & (depth_array < upper), 
                          (depth_array - lower) / (upper - lower), 0.0)
    # 扩展权重到3通道
    weight_3ch = np.stack([weight]*3, axis=-1)
    result_array += blur_arrays[i] * weight_3ch

# 转为PIL图像并保存
rimg = Image.fromarray(result_array.astype(np.uint8))
rimg.save('pseudo_depth_of_field.png')

OpenCV 实现方案

OpenCV可高效生成圆盘核,结合高斯金字塔实现多尺度模糊融合:

import cv2
import numpy as np

# 加载原图与深度图
oimg = cv2.imread('2.png')
mimg = cv2.imread('2_depth.png', cv2.IMREAD_GRAYSCALE)
mimg = cv2.resize(mimg, (oimg.shape[1], oimg.shape[0]))

# 预处理深度图:归一化并反转
depth_array = mimg / 255.0
depth_array = 1 - depth_array

# 定义模糊半径范围
min_radius = 0
max_radius = 10

# 生成高斯金字塔(多尺度模糊)
pyr = [oimg]
for i in range(5):
    pyr.append(cv2.pyrDown(pyr[-1]))

# 将金字塔图像放大回原尺寸
scaled_pyr = [pyr[0]]
for i in range(1, len(pyr)):
    scaled = cv2.resize(pyr[i], (oimg.shape[1], oimg.shape[0]), interpolation=cv2.INTER_LINEAR)
    scaled_pyr.append(scaled)

# 生成圆盘模糊核并应用
def apply_disc_blur(img, radius):
    if radius == 0:
        return img
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (2*radius+1, 2*radius+1))
    kernel = kernel / kernel.sum()
    return cv2.filter2D(img, -1, kernel)

# 对每个尺度应用圆盘模糊
blur_scales = [apply_disc_blur(img, int(min_radius + (max_radius - min_radius)*(i/(len(scaled_pyr)-1)))) 
               for i, img in enumerate(scaled_pyr)]

# 加权融合多尺度图像
result = np.zeros_like(oimg, dtype=np.float32)
scales = len(blur_scales)
for i in range(scales):
    if i == scales -1:
        weight = np.where(depth_array >= (i/(scales-1)), 1.0, 0.0)
    else:
        lower = i/(scales-1)
        upper = (i+1)/(scales-1)
        weight = np.where((depth_array >= lower) & (depth_array < upper), 
                          (depth_array - lower)/(upper - lower), 0.0)
    weight_3ch = np.stack([weight]*3, axis=-1)
    result += blur_scales[i] * weight_3ch

# 转为uint8格式并保存
result = cv2.convertScaleAbs(result)
cv2.imwrite('pseudo_depth_of_field_cv2.png', result)

关键改进说明

  1. 连续模糊半径:替代原方案的"清晰/固定模糊"二元合成,实现从近到远的平滑模糊过渡
  2. 圆盘模糊核:模拟真实散景的圆形光斑,比高斯/盒状模糊更贴近相机拍摄效果
  3. 多尺度融合:通过分层模糊+加权融合,平衡视觉效果与计算效率

内容的提问来源于stack exchange,提问作者WASasquatch

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 11:01:31