
1. 项目概览:一套会“看深度”的智能抠图小助手 🧑💻
本项目来自华南理工小课题“抠图算法小组”,目标是构建一个面向图片与视频的人物图像抠取系统。它并不满足于普通 2D 图像里的“凭颜色猜边界”,而是尝试引入 Astra+ 深度相机采集到的深度信息,让系统在复杂背景、人物边缘、背景替换等场景中获得更稳定的判断依据。
换句话说,它想做的事情很直接:用户上传视频或图片,系统识别人物前景,生成透明度遮罩,再把人物自然地合成到新背景里。背景负责换装,人物负责出片,算法负责认真干活。😎
| 项目条目 | 内容 |
|---|---|
| 主题方向 | 人物图像抠取、视频抠像、背景替换 |
| 团队成员 | 王实依、方润泽、张辰旭 |
| 相机型号 | Astra+ |
| 支持设备 | Zora P1 开发板 |
| 核心能力 | 图片/视频上传、相机采集、深度辅助抠像、背景替换、结果下载 |
| 源码链接 | Portrait-extraction-algorithm |
| 教程链接 | 人物图像扣取教程 |
2. 为什么要用深度相机做人物抠取?📷
传统人物抠图通常只依赖 RGB 图像。这个思路在背景干净、光照稳定、人物衣物与背景颜色差异明显时效果很好;但一旦遇到头发丝、透明边缘、复杂室内背景、低对比服装,算法就容易开始“犹豫”:这块到底是人,还是背景?边界要不要保留?阴影算不算前景?
Astra+ 这类深度相机可以额外提供距离信息。对于人物抠取而言,深度图像相当于给系统补了一条线索:如果某些像素明显远离人物所在深度层,它们就更可能属于背景。项目中的预处理脚本正是利用这一点,将 RGB 视频与深度视频结合起来,为后续 matting 模型提供更干净的输入。
| 对比维度 | 仅 RGB 抠图 | 深度辅助抠图 |
|---|---|---|
| 输入信息 | 颜色、纹理、边缘 | 颜色、纹理、边缘、距离 |
| 背景复杂度适应 | 容易受颜色相似影响 | 可利用深度层次排除部分背景 |
| 视频稳定性 | 易出现边缘闪烁 | 可通过深度预处理减少干扰 |
| 设备要求 | 普通摄像头即可 | 需要 Astra+ 等深度相机 |
| 适用场景 | 轻量图像处理 | 视频会议、虚拟拍摄、AR 展示、人机交互 |
3. 系统总体架构:从采集到换背景,一条龙服务 🧩

从项目文件结构看,系统可以拆成五个层次:
| 层级 | 主要文件 | 作用 |
|---|---|---|
| 数据采集层 | 01fixingvideo.py |
调用 Orbbec SDK,同步采集 RGB 与深度视频 |
| 深度预处理层 | 01preprocess.py |
根据深度阈值削弱远处背景干扰 |
| 视频抠像层 | model/、inference.py |
基于 Robust Video Matting 思路输出前景、透明度与合成视频 |
| 合成调用层 | invoking3.py、evaluation/ |
调用模型并完成前景、alpha、背景的融合 |
| Web 交互层 | app.py、templates/、static/ |
提供登录、上传、展示、下载等页面功能 |
简化后的处理链路如下:
| 阶段 | 输入 | 输出 |
|---|---|---|
| 1. 相机采集 | Astra+ RGB 流、深度流 | rgb.mp4、depth.mp4 |
| 2. 深度预处理 | RGB 视频、深度视频 | rgb_processed.mp4 |
| 3. 抠像推理 | 预处理 RGB 视频、模型权重 | 前景 foreground.mp4、透明度 alpha.mp4 |
| 4. 背景合成 | 前景、alpha、新背景 | 合成结果 composition.mp4 或 output.mp4 |
| 5. 网页展示 | 处理后视频 | 在线预览、下载、清理缓存 |
4. RGB-D 视频采集:先把“看见”和“测距”同步起来 🛰️

项目的 01fixingvideo.py 使用 pyorbbecsdk 连接深度相机,启用彩色流与深度流,并通过帧同步机制保证两个视频序列在时间上尽量对齐。对于视频抠像来说,这一步非常重要:RGB 帧和深度帧如果错位,人物边界就会像没对齐的贴纸一样,边缘处理会变得很“迷惑”。
下面是采集逻辑的核心片段,整理自项目代码:
pipeline = Pipeline()
config = Config()
color_profile = pipeline.get_stream_profile_list(OBSensorType.COLOR_SENSOR)
.get_default_video_stream_profile()
depth_profile = pipeline.get_stream_profile_list(OBSensorType.DEPTH_SENSOR)
.get_default_video_stream_profile()
config.enable_stream(color_profile)
config.enable_stream(depth_profile)
pipeline.enable_frame_sync()
pipeline.start(config)
frames = pipeline.wait_for_frames(100)
color_frame = frames.get_color_frame()
depth_frame = frames.get_depth_frame()
采集后,系统将彩色视频写入 RGB 目录,将深度数据归一化为可视化视频写入 D 目录。这样做有两个好处:
- 便于后续算法读取统一格式的视频文件。
- 便于人工检查 RGB 与深度图是否匹配,调试时不用盲猜。
5. 深度预处理:给背景先做一次“体检”🩺
项目中的 01preprocess.py 使用了一个简洁但有效的思路:读取 RGB 帧和深度帧,如果某个像素的深度值超过阈值,就把该 RGB 像素置黑。它的作用不是直接完成精细抠图,而是提前压制一部分明显属于背景的区域,让后续模型更专注于人物边缘。
depth_threshold = 100
ret_rgb, frame_rgb = cap_rgb.read()
ret_depth, frame_depth = cap_depth.read()
scalar_depth = frame_depth[:, :, 0]
depth_mask = scalar_depth >= depth_threshold
frame_rgb[depth_mask] = [0, 0, 0]
out.write(frame_rgb)
可以把它理解成一个深度引导的粗筛选:
若 D(u, v) < threshold:
该像素更可能处于人物所在深度范围,保留 RGB 信息
否则:
该像素更可能属于远处背景,削弱或置黑
这种方法的学术意义在于,它把单纯依赖颜色分布的抠像问题,转化为“颜色 + 空间距离”的联合判断问题。深度信息不一定能完全替代语义分割或 matting 网络,但它能在复杂背景中提供额外约束,尤其适合人物与背景颜色相近、纹理混杂的场景。
| 处理对象 | 技术动作 | 对抠图的帮助 |
|---|---|---|
| RGB 帧 | 保留人物邻近区域颜色 | 给模型提供纹理与边缘信息 |
| 深度帧 | 按阈值生成背景掩码 | 快速排除远距离干扰 |
| 预处理视频 | 背景区域置黑 | 降低模型误判概率 |
6. 核心抠像模型:RVM 思路下的时序人物 Matting 🧠

项目模型主要位于 model/ 目录,整体思路借鉴 Robust Video Matting:网络不只看单帧图像,还通过循环记忆结构利用前后帧信息,从而减少视频抠像中常见的边缘闪烁问题。
模型可以概括为:
输入 RGB 视频帧
↓
Backbone 提取多尺度特征
↓
LR-ASPP 聚合上下文
↓
Recurrent Decoder 结合历史帧记忆
↓
Projection 输出前景残差与透明度 alpha
↓
Guided Filter 恢复高分辨率细节
项目中的 MattingNetwork 支持 mobilenetv3 与 resnet50 两类骨干网络。前者更轻量,适合边缘端部署;后者表达能力更强,但计算负担也更高。
class MattingNetwork(nn.Module):
def __init__(self, variant='mobilenetv3', refiner='deep_guided_filter'):
super().__init__()
assert variant in ['mobilenetv3', 'resnet50']
if variant == 'mobilenetv3':
self.backbone = MobileNetV3LargeEncoder()
self.aspp = LRASPP(960, 128)
self.decoder = RecurrentDecoder([16, 24, 40, 128], [80, 40, 32, 16])
else:
self.backbone = ResNet50Encoder()
self.aspp = LRASPP(2048, 256)
self.decoder = RecurrentDecoder([64, 256, 512, 256], [128, 64, 32, 16])
self.project_mat = Projection(16, 4)
在前向推理阶段,模型会输出前景 fgr 与透明度 pha。其中 pha 是抠图任务的关键:它不是简单的 0/1 二值掩码,而是一个连续透明度图。头发丝、衣服边缘、半透明区域都可以通过 0 到 1 之间的 alpha 值表达出来。
fgr_residual, pha = self.project_mat(hid).split([3, 1], dim=-3)
fgr = fgr_residual + src_sm
if downsample_ratio != 1:
fgr, pha = self.refiner(src, src_sm, fgr, pha, hid)
fgr = fgr.clamp(0., 1.)
pha = pha.clamp(0., 1.)
return [fgr, pha, r1, r2, r3, r4]
7. 时序记忆:视频抠像不能只看一帧 🎞️
单帧抠图像拍证件照,视频抠图像连拍一百张证件照还要求每张边缘都稳定。为了让输出不闪,项目使用了 ConvGRU 结构,让模型保留上一帧的记忆状态。
class ConvGRU(nn.Module):
def forward_single_frame(self, x, h):
if h is None:
h = torch.zeros((x.shape[0], self.out_channels, x.shape[2], x.shape[3]),
device=x.device, dtype=x.dtype)
xh = torch.cat([x, h], dim=1)
r, z = self.ih(xh).split([self.out_channels, self.out_channels], dim=1)
r = torch.sigmoid(r)
z = torch.sigmoid(z)
xrh = torch.cat([x, r * h], dim=1)
n = torch.tanh(self.hh(xrh))
h = (1 - z) * h + z * n
return h
这段逻辑的直观解释如下:
| 门控变量 | 含义 | 对视频抠像的作用 |
|---|---|---|
r |
重置门 | 控制上一帧记忆是否参与当前判断 |
z |
更新门 | 控制新信息写入多少 |
h |
隐状态 | 保存人物边界、姿态、运动等时序线索 |
有了这些记忆,模型处理连续视频时不会每一帧都“重新认识一个人”,而是会把前后帧联系起来。这对手臂晃动、发丝边缘、衣服褶皱等细节尤其重要。
8. Guided Filter:把边缘细节再精修一下 🔍
模型中的 fast_guided_filter.py 与 deep_guided_filter.py 用于把低分辨率预测结果细化回高分辨率图像。其核心思想是:先在低分辨率上快速估计前景和透明度,再借助原始高分辨率图像的边缘结构进行引导恢复。
mean_x = self.boxfilter(lr_x)
mean_y = self.boxfilter(lr_y)
cov_xy = self.boxfilter(lr_x * lr_y) - mean_x * mean_y
var_x = self.boxfilter(lr_x * lr_x) - mean_x * mean_x
A = cov_xy / (var_x + self.eps)
b = mean_y - A * mean_x
A = F.interpolate(A, hr_x.shape[2:], mode='bilinear', align_corners=False)
b = F.interpolate(b, hr_x.shape[2:], mode='bilinear', align_corners=False)
return A * hr_x + b
如果说深度预处理是在前面帮算法“扫清背景杂物”,那么 guided filter 就是在后面给边缘“补妆”:让轮廓更贴近真实图像结构,避免人物边缘糊成一团。
9. 背景替换:alpha 负责温柔地“混合”前景与背景 🌈

背景替换的数学形式很清楚:
Composite = Foreground * Alpha + Background * (1 - Alpha)
项目的 inference.py 和 invoking3.py 都体现了这一合成逻辑。模型输出前景 fgr 和透明度 pha 后,只需要准备一张背景图或背景视频,就能得到最终合成结果。
fgr, pha, *rec = model(src, *rec, downsample_ratio)
if writer_com is not None:
writer_com.write(fgr * pha + bgr * (1 - pha))
evaluation/ 目录中的脚本还进一步支持把前景、alpha、背景和合成结果分别保存,便于定量评估和可视化对比。
| 输出类型 | 文件含义 | 可视化价值 |
|---|---|---|
foreground |
抠出的人物前景 | 检查人物纹理是否完整 |
alpha |
透明度遮罩 | 检查边缘、头发、半透明区域 |
background |
替换背景 | 检查合成场景是否匹配 |
composition |
最终合成视频 | 直接展示系统效果 |
10. Web 端:让算法从代码走到可操作界面 🖥️

项目使用 Flask 构建了一个原型 Web 端。templates/login.html 与 register.html 提供账号入口,charts.html 提供视频上传、深度视频展示、背景图片展示、进度条与结果视频预览区域。整体上,这使项目不再只是命令行脚本,而是具备“用户上传、系统处理、网页查看结果”的应用雏形。
后端核心路由位于 app.py:
@app.route('/upload', methods=['POST'])
def upload():
file = request.files['file']
if file and allowed_file(file.filename):
filename = str(uuid.uuid4()) + '.' + file.filename.rsplit('.', 1)[1].lower()
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return filename
@app.route('/download/<filename>')
def download(filename):
path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
return send_file(path, as_attachment=True)
从工程化角度看,当前 Web 端更接近“可演示原型”。例如,上传、处理、进度查询、清理缓存等接口已经有基本结构,但个别依赖导入、模型调用封装和异常处理仍需要补齐。这个状态很正常:科研课题原型往往先验证链路,再逐步把接口打磨成稳定产品。
| Web 功能 | 已有基础 | 后续建议 |
|---|---|---|
| 用户入口 | 登录页、注册页 | 接入真实用户数据库与密码加密 |
| 文件上传 | AJAX 上传、进度条 | 区分 RGB、深度、背景文件类型 |
| 算法调用 | 预留 /process 接口 |
统一调用采集、预处理、推理和合成流程 |
| 结果展示 | 视频预览区域 | 支持前景、alpha、合成结果多标签查看 |
| 结果下载 | /download 路由 |
增加任务编号与历史记录管理 |
11. 数据集与评估:不是“看起来还行”就完事 📊
项目保留了 dataset/ 与 evaluation/ 两类脚本,这说明系统不仅考虑演示,也考虑训练与评估。
数据集部分支持 ImageMatte、VideoMatte、COCO Panoptic、YouTube-VIS、Supervisely Person 等来源,覆盖静态图像、视频序列、人体分割和前景透明度等不同训练信号。评估脚本则关注 alpha 与前景质量,例如 MAD、MSE、梯度误差、连通性误差、时序差异等指标。
| 模块 | 代表文件 | 说明 |
|---|---|---|
| 图像抠像数据 | imagematte.py |
读取前景、alpha、背景并构造序列 |
| 视频抠像数据 | videomatte.py |
读取视频前景与透明度序列 |
| 人体语义数据 | coco.py、youtubevis.py |
关注 person 类分割信息 |
| 数据增强 | augmentation.py |
模拟运动、模糊、噪声、颜色变化 |
| 低分辨率评估 | evaluate_lr.py |
适合快速评估 |
| 高分辨率评估 | evaluate_hr.py |
使用 CUDA 加速高分辨率指标计算 |
其中数据增强尤其值得一提。视频抠像最怕“现实世界突然不按训练集剧本演”:人物移动、背景晃动、画面模糊、光照变化都会影响稳定性。项目通过运动仿射、颜色扰动、噪声、模糊、水平翻转等方式增加训练样本多样性。
if random.random() < self.prob_fgr_affine:
fgrs, phas = self._motion_affine(fgrs, phas)
if random.random() < self.prob_noise:
fgrs, bgrs = self._motion_noise(fgrs, bgrs)
if random.random() < self.prob_hflip:
fgrs = F.hflip(fgrs)
phas = F.hflip(phas)
12. 技术亮点总结:它不只是“会换背景”✨
| 技术亮点 | 具体体现 | 价值 |
|---|---|---|
| RGB-D 融合 | Astra+ 同步采集 RGB 与深度流 | 增强复杂背景下的人物定位能力 |
| 深度预处理 | 根据深度阈值削弱远处背景 | 降低后续模型误分割压力 |
| 视频时序建模 | ConvGRU 保存多尺度历史状态 | 减少视频边缘闪烁 |
| 轻量骨干网络 | 支持 MobileNetV3 | 更适合 Zora P1 等边缘设备 |
| 高分辨率细化 | Fast/Deep Guided Filter | 改善人物轮廓与发丝细节 |
| Web 可视化 | Flask + Bootstrap 页面 | 降低使用门槛,便于展示与测试 |
13. 典型应用场景:背景换了,生产力也来了 🚀

14. 可继续完善的方向:让原型更像产品 🛠️
这个项目已经具备完整链路雏形,但如果要进一步走向稳定应用,可以重点优化以下方面:
| 方向 | 当前状态 | 建议 |
|---|---|---|
| 深度阈值 | 使用固定阈值 | 改为自适应阈值或人体深度聚类 |
| 相机标定 | 依赖同步采集 | 增加 RGB-D 对齐校验与异常提示 |
| 后端接口 | Flask 原型路由 | 补齐任务队列、异常处理、日志记录 |
| 模型部署 | 支持 MobileNetV3 | 针对 Zora P1 做量化、裁剪和推理加速 |
| 评估实验 | 保留指标脚本 | 增加对比实验、消融实验和用户场景测试 |
| Web 体验 | 页面可演示 | 优化文件类型区分、结果多视图、历史任务管理 |
15. 结语:从一张图里“请出主角”🎯
人物图像抠取看起来像一个简单功能:把人抠出来,把背景换掉。但真正做起来,它涉及相机采集、深度感知、视频时序建模、alpha matting、前后景合成、Web 工程化等多项技术。这个项目的亮点在于,它没有把抠图只当成一个纯 2D 视觉问题,而是借助 Astra+ 深度相机把三维空间信息引入处理流程,让系统更接近真实应用环境。
如果把普通抠图比作“用眼睛看边界”,那么这个系统更像是“既看颜色,也量距离”。主角站在画面里,背景可以优雅退场;算法站在幕后,认真把每一帧安排明白。🎬
