1. 基于中值滤波的ToF深度图去噪
任务说明
本案例处理ToF(Time-of-Flight)相机输出的原始深度图,目标是通过中值滤波去除因多路径反射、边缘混叠和低信噪比区域产生的孤立噪点,同时保留深度边缘的清晰度。输入为单通道16位深度图(单位:毫米),输出为滤波后的同尺寸深度图。有效像素值为正值,无效像素(测量失败)标记为0,滤波时需跳过无效像素。
核心实现
import numpy as np
from scipy import ndimage
import cv2
def median_filter_depth(depth_raw, kernel_size=5, max_valid_range=(0, 8000)):
"""
对ToF深度图执行中值滤波,自动处理无效像素(值为0)。
参数:
depth_raw: np.uint16数组, 原始深度图 (H, W), 单位mm
kernel_size: int, 滤波窗口边长(奇数)
max_valid_range: tuple, 有效深度范围(最小, 最大), 超出视为无效
返回:
depth_filtered: np.uint16数组, 滤波后深度图
"""
if kernel_size % 2 == 0:
kernel_size += 1
# 构建有效掩码: 深度值在有效范围内且非0
min_val, max_val = max_valid_range
valid_mask = (depth_raw >= min_val) & (depth_raw <= max_val) & (depth_raw > 0)
# 将无效像素替换为极大值,使其在排序时被排除
depth_work = depth_raw.astype(np.float32)
depth_work[~valid_mask] = np.inf
# 使用scipy的generic_filter实现中值滤波(忽略inf)
from scipy.ndimage import generic_filter
def _median_ignore_inf(window):
valid_vals = window[~np.isinf(window)]
if len(valid_vals) == 0:
return 0.0
return np.median(valid_vals)
depth_filtered = generic_filter(
depth_work,
_median_ignore_inf,
size=kernel_size,
mode='constant',
cval=np.inf
)
# 恢复原始无效像素位置为0
depth_filtered[~valid_mask] = 0
return depth_filtered.astype(np.uint16)
def generate_sample_depth_map():
"""生成带噪点的模拟ToF深度图 (H=8, W=8)"""
# 真实深度平面: 左侧2000mm, 右侧3000mm, 中间垂直边缘
depth = np.zeros((8, 8), dtype=np.uint16)
depth[:, :4] = 2000
depth[:, 4:] = 3000
# 添加孤立噪点(模拟多路径/飞点)
noise_positions = [(1, 1), (3, 6), (5, 2), (6, 5), (2, 7)]
for r, c in noise_positions:
depth[r, c] = 500 # 明显偏离真实值的噪点
# 添加一个无效像素
depth[4, 4] = 0
return depth
if __name__ == "__main__":
# 生成样例数据
sample = generate_sample_depth_map()
print("原始深度图:\n", sample)
# 执行中值滤波
filtered = median_filter_depth(sample, kernel_size=3)
print("\n滤波后深度图:\n", filtered)
# 验证: 检查噪点是否被消除
noise_positions = [(1, 1), (3, 6), (5, 2), (6, 5), (2, 7)]
all_removed = all(filtered[r, c] in (2000, 3000) for r, c in noise_positions)
invalid_preserved = (filtered[4, 4] == 0)
print(f"\n噪点全部消除: {all_removed}")
print(f"无效像素保持为0: {invalid_preserved}")
print(f"边缘保留(第4列仍为2000, 第5列仍为3000): "
f"{np.all(filtered[:, 3] == 2000) and np.all(filtered[:, 4] == 3000)}")
关键设计说明
上述实现的核心在于无效像素感知的中值计算:将无效像素(值为0或超出有效范围)替换为无穷大,在滑动窗口内计算中值时忽略这些无效值,仅对有效深度值排序取中。若窗口内全部为无效像素,则输出0。这种设计避免了无效像素对中值结果的污染,同时保留了深度边缘——因为中值滤波本身对边缘附近的异常值具有天然鲁棒性。
落地与运行方式
# 安装依赖
pip install numpy scipy opencv-python
# 运行案例
python median_filter_tof.py
验证方法与判定标准
运行上述脚本后,观察输出:
- 原始深度图中5个噪点位置(深度值500)在滤波后应变为所在区域的真实深度(2000或3000),即噪点被消除;
- 无效像素位置(4,4)滤波后仍为0,未被周围有效值填充;
- 深度边缘(第3列与第4列之间)保持清晰,左侧2000mm与右侧3000mm区域内部无串扰。
满足以上三点即判定滤波算法实现正确。若需处理实际ToF相机数据,将depth_raw替换为相机输出的uint16深度图即可,无需修改核心函数。