导读:本期聚焦于花满楼创作的《如何用MiDaS将2D视频转为3D视差效果并生成Z-depth深度图?》,敬请观看详情。想把普通2D实拍视频改成左右格式立体画面,或给合成软件输出Z-depth通道,不一定需要双摄像头或深度传感器。MiDaS采用单目深度估计策略,从单帧RGB图像预测稠密深度图,将视频帧逐帧送入模型后即可得到连续的深度序列。深度图经过伪彩色映射能直接生成Z-depth预览,按深度反比换算成视差后,还能用重映射生成左右眼画面,实现3D视差效果。本文围绕模型选型、视频逐帧推理、深度尺度统一、视差生成与Z-depth导出展开,给出完整Python实现,并讨论深度抖动、分辨率一致性、16位深度保存等实际落地细节。与双目匹配或结构光方案相比,这套管线成本低、对设备要求不高,适合视频后期、虚拟制作以及轻量3D展示场景快速验证。

从一段普通手机视频生成左右格式立体画面,或输出Z-depth通道,过去通常依赖双摄像头标定或深度传感器。现在借助MiDaS这类单目深度估计模型,只需要一张张RGB帧就能得到稠密深度图。通过把深度值映射为视差,再对原始画面做像素偏移,可以快速得到左右眼画面;把深度图做伪彩色或数值导出,则能得到合成软件常用的Z-depth素材。整个流程不需要相机内参,也不要求视频来源是固定机位,适合快速验证和轻量后期制作。

如何用MiDaS将2D视频转为3D视差效果并生成Z-depth深度图?

MiDaS的输出并不是真实物理尺度下的深度,而是相对深度。也就是说,它能够准确判断画面中哪些区域更近、哪些更远,但不会直接给出厘米或米级别的距离值。这一点对视差生成和Z-depth预览影响不大,因为视差本身只需要相对关系,Z-depth在多数合成软件中也会重新映射到0到1范围。真正需要注意的是,如果视频场景中物体距离变化很大,或者镜头运动明显,相对深度在不同帧之间可能发生尺度漂移,后面必须做归一化和平滑处理,否则深度视频会出现明显闪烁。

MiDaS模型选型与推理前准备

MiDaS提供了多个模型版本,比较常用的是DPT_Hybrid、DPT_Large和MiDaS_small。DPT_Hybrid在精度和速度之间平衡较好,DPT_Large细节更丰富但推理更慢,MiDaS_small则适合CPU环境或长视频快速处理。对于视频逐帧处理来说,如果使用GPU,DPT_Hybrid通常可以在几十毫秒内完成一帧;如果只有CPU,MiDaS_small是更现实的选择。

模型加载可以通过PyTorch Hub直接完成,官方仓库会缓存权重文件。需要注意的是,不同模型对应的预处理transform不同,DPT系列使用dpt_transform,MiDaS_small使用small_transform。如果混用,输入张量的尺寸和归一化方式会不一致,推理结果会出现明显异常。

import cv2
import torch
import numpy as np

model_type = "DPT_Hybrid"
midas = torch.hub.load("intel-isl/MiDaS", model_type)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
midas.to(device)
midas.eval()

midas_transforms = torch.hub.load("intel-isl/MiDaS", "transforms")
if model_type in ["DPT_Large", "DPT_Hybrid"]:
    transform = midas_transforms.dpt_transform
else:
    transform = midas_transforms.small_transform

print("模型已加载到", device)

transform会把输入图像转换成归一化后的张量,并自动调整到模型需要的输入尺寸。调用midas(input_batch)得到的是缩放到模型输出分辨率的深度图,因此还需要用双三次插值恢复到原始视频帧尺寸。推理时务必使用torch.no_grad()上下文,并调用midas.eval()关闭Dropout等训练层,这样可以降低显存占用并加速计算。

视频逐帧推理与深度抖动控制

视频深度估计最容易出现的问题不是单帧精度,而是连续播放时的深度闪烁。由于模型对每一帧独立推理,输出深度图的数值范围并不稳定。第一帧的近处像素可能被映射到0.9,第二帧同样位置的像素可能变成0.7,这会导致伪彩色画面不断跳动。解决思路是先统计一个稳定的数值范围,然后用统一的最小值和最大值做归一化,而不是每一帧单独使用自己的极值。

下面的代码使用第一帧的2%和98%分位数作为全局范围,后续所有帧都使用这个范围做归一化。这种方式适合场景深度变化不剧烈的视频。如果视频前半段是室内近景、后半段是户外远景,固定范围会失效,此时更适合使用滑动窗口动态更新范围,但更新速度要慢,避免重新引入闪烁。

cap = cv2.VideoCapture("input.mp4")
fps = cap.get(cv2.CAP_PROP_FPS)
w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
out = cv2.VideoWriter(
    "depth_color.mp4",
    cv2.VideoWriter_fourcc(*"mp4v"),
    fps,
    (w, h),
)

global_min = None
global_max = None

while True:
    ret, frame = cap.read()
    if not ret:
        break

    rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    input_batch = transform(rgb).to(device)

    with torch.no_grad():
        prediction = midas(input_batch)
        prediction = torch.nn.functional.interpolate(
            prediction.unsqueeze(1),
            size=(h, w),
            mode="bicubic",
            align_corners=False,
        ).squeeze()

    depth = prediction.cpu().numpy().astype(np.float32)

    if global_min is None:
        global_min = float(np.percentile(depth, 2))
        global_max = float(np.percentile(depth, 98))

    depth_norm = (depth - global_min) / (global_max - global_min)
    depth_norm = np.clip(depth_norm, 0.0, 1.0)
    depth_u8 = (depth_norm * 255).astype(np.uint8)
    depth_color = cv2.applyColorMap(depth_u8, cv2.COLORMAP_INFERNO)

    out.write(depth_color)

cap.release()
out.release()

如果仍然存在轻微闪烁,可以对连续深度图做时间平滑。常见做法是维护上一帧深度,并用加权平均更新当前帧,例如smoothed = 0.7 * previous + 0.3 * current。但平滑必须在同一尺寸、同一场景结构下进行,如果镜头快速切换或物体大幅运动,简单混合会留下残影。对于固定机位视频,这种平滑非常有效;对于运动镜头,更稳妥的方案是降低伪彩色映射的敏感度,而不是直接修改深度数值。

性能方面,DPT_Hybrid在主流GPU上处理1080p视频通常每秒可以跑10到20帧,MiDaS_small在CPU上可能只有几帧每秒。实际项目中建议先把视频缩放到960×540或1280×720,生成深度后再放大回原始分辨率。这样既能减少推理时间,又不会明显损失深度边缘,因为深度信息本身是低频信号。

从深度图到视差图与左右眼画面

2D视频转3D视差效果的核心是把深度关系转换为像素平移。近处物体应该产生更大的左右眼位移,远处物体位移接近零。对于MiDaS输出的相对深度,可以直接使用反比关系近似视差:disparity = 1.0 / (depth + epsilon),然后归一化到0到1范围。最大平移像素通常设置在16到32之间,过大容易造成重影和视觉疲劳,过小则立体感不明显。

得到视差图后,可以用cv2.remap对原始帧做像素偏移。左眼视图需要把像素向右偏移,右眼视图需要把像素向左偏移,偏移量由视差图控制。下面代码使用网格坐标加减位移图,并利用边界复制减少边缘空洞。对于轻度视差效果,这个方法足够稳定;如果视差较大,物体边缘会出现明显拉伸,需要进一步做遮挡填充或深度图边缘平滑。

depth_norm = (depth - global_min) / (global_max - global_min)
depth_norm = np.clip(depth_norm, 0.0, 1.0)
disparity = 1.0 / (depth_norm + 1e-5)
disparity_min = disparity.min()
disparity_max = disparity.max()
if disparity_max > disparity_min:
    disparity_norm = (disparity - disparity_min) / (disparity_max - disparity_min)
else:
    disparity_norm = np.zeros_like(disparity)

max_shift_px = 24.0
shift_map = disparity_norm * max_shift_px

h, w = frame.shape[:2]
x, y = np.meshgrid(np.arange(w, dtype=np.float32), np.arange(h, dtype=np.float32))
map_x_left = (x - shift_map).astype(np.float32)
map_y_left = y.astype(np.float32)
map_x_right = (x + shift_map).astype(np.float32)
map_y_right = y.astype(np.float32)

left_view = cv2.remap(frame, map_x_left, map_y_left, cv2.INTER_LINEAR, borderMode=cv2.BORDER_REPLICATE)
right_view = cv2.remap(frame, map_x_right, map_y_right, cv2.INTER_LINEAR, borderMode=cv2.BORDER_REPLICATE)

side_by_side = np.hstack([left_view, right_view])

除了左右并排格式,也可以生成红蓝立体画面。红蓝模式把左眼视图的红色通道保留,右眼视图的绿色和蓝色通道保留,然后合成到一张图。这种格式在普通屏幕上配合红蓝眼镜就能看到立体效果,但色彩会有损失。左右并排格式更适合在VR设备、3D电视或后期软件中使用。合成时要注意两路视图的分辨率一致,否则直接拼接会导致画面错位。

最终立体视频写入时,编码器选择和输入视频保持一致即可。OpenCV的mp4v编码兼容性较好,但如果要保留更高画质,可以输出为图像序列再交给FFmpeg处理。无论哪种方式,都建议先输出一小段测试视频,确认视差强度和边缘质量后再跑完整视频。

Z-depth图生成与后期合成应用

Z-depth图本质上是把深度信息编码成灰度或伪彩色图像,用来表示画面元素的前后关系。在After Effects、Nuke、DaVinci Resolve等工具中,Z-depth常用于雾效、景深、遮挡关系以及粒子发射范围控制。MiDaS输出的浮点深度需要先归一化,再根据用途决定输出8位预览图还是16位、32位浮点文件。

如果只是用于预览或快速确认深度关系,8位伪彩色图足够直观。使用cv2.applyColorMap把0到255的灰度值映射到颜色带,近处物体通常用暖色、远处物体用冷色。但伪彩色图不适合作为精确的Z通道参与合成,因为颜色映射会丢失数值线性关系。更严谨的做法是保存16位PNG或32位浮点EXR,让后期软件读取原始深度值。

depth_u16 = (depth_norm * 65535).astype(np.uint16)
cv2.imwrite("depth_z.png", depth_u16)

# 保存浮点深度供合成软件使用
depth_float32 = depth.astype(np.float32)
cv2.imwrite("depth_z.exr", depth_float32)

16位PNG适合大部分常规合成需求,0表示最远、65535表示最近。EXR文件能够保留浮点数值范围,适合需要深度合成、三维重建或自定义着色器的场景。需要注意的是,不同软件对Z通道的朝向定义可能不同,有的以黑为近、白为远,有的相反,使用前要在具体软件中确认映射方向。

MiDaS生成Z-depth时,透明物体、反射表面和重复纹理区域容易产生错误估计。比如玻璃窗、水面、光滑地面可能被误判为不同的深度平面。对于这些区域,可以在后期使用模糊、手动遮罩或边缘保护滤波进行局部修正。虽然单目深度估计不能完全替代真实深度传感器,但作为视频后期的辅助通道,它已经能覆盖大量2D转3D、背景替换和视觉特效需求。

MiDaS深度估计视差效果修改时间:2026-09-26 08:10:57

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0926/62057.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。