本脚本 run_cleaning.py 整合了完整的高斯模型清理流程,包括以下四个步骤:
- 二值化mask:将原始mask图片转换为纯黑白二值图像
- 调整尺寸:将mask图片调整为与colmap数据集images相同的尺寸
- 第一次过滤:结合mask对高斯模型进行投影过滤
- 第二次过滤:使用DBSCAN聚类算法过滤离群点
确保已安装所需依赖:
pip install opencv-python numpy tqdm plyfile open3d pillowpython run_cleaning.py \
--mask_input <原始mask文件夹路径> \
--colmap_images <colmap数据集的images文件夹路径> \
--ply_file <原始高斯模型ply文件路径> \
--cameras_json <cameras.json文件路径>--mask_input 原始mask文件夹路径(必填)
--colmap_images colmap数据集的images文件夹路径(必填)
--ply_file 原始高斯模型ply文件路径(必填)
--cameras_json cameras.json文件路径(必填)
--output_dir 输出目录,默认当前目录下的output
--threshold 二值化阈值,默认127
--invert 是否反转mask颜色(黑白互换)
--workers 并行处理的进程数,默认8
--clean_gs_threshold 第一次过滤阈值,默认0.5
--eps DBSCAN聚类半径,默认0.2
--min_points DBSCAN最小点数,默认20
--top_k 保留最大的k个簇,默认1
python run_cleaning.py \
--mask_input ./original_masks \
--colmap_images ./colmap/images \
--ply_file ./original_model.ply \
--cameras_json ./cameras.json \
--output_dir ./cleaned_result \
--threshold 127 \
--workers 12 \
--clean_gs_threshold 0.6 \
--eps 0.3 \
--min_points 30脚本执行完成后,会在指定的输出目录中生成以下结果:
binary_masks/:二值化后的mask图片resized_masks/:调整尺寸后的mask图片first_cleaned.ply:第一次过滤(mask投影)后的高斯模型final_cleaned.ply:第二次过滤(DBSCAN聚类)后的最终高斯模型
使用 process_mask.py 脚本将原始mask图片转换为纯黑白二值图像。通过调整 --threshold 参数可以控制二值化的阈值,--invert 参数可以反转黑白颜色。
使用 resize_data.py 脚本将mask图片调整为与colmap数据集images相同的尺寸。脚本会自动检测colmap images的尺寸,并将mask图片调整为相同大小。
使用 clean_gs.py 脚本结合mask对高斯模型进行投影过滤。将3D点投影到2D图像上,检查是否在mask范围内,过滤掉不在mask内的点。
使用 cleanply.py 脚本进行DBSCAN聚类过滤。通过聚类算法找出离群点并过滤,保留最大的连通分量。
- 确保mask图片的文件名与colmap images的文件名相对应,这样才能正确匹配
- 对于不同的场景,可能需要调整
--eps和--min_points参数以获得最佳效果 - 建议先使用少量数据测试,调整好参数后再处理完整数据集
- 处理大型数据集时,建议适当增加
--workers参数以提高处理速度
run_cleaning_bbox.py(多目标 bbox 流程)会在输出目录里生成若干个 object{id}.ply(DBSCAN 去噪后的对象子集)。bbox_3d_xyxy.py 用于把这些子集 PLY 的 3D AABB 转换到 cameras.json 的世界坐标系下,以 3D xyxy 格式 [x_min, y_min, z_min, x_max, y_max, z_max] 输出,单位为米。
关键说明:3DGS PLY 的
x, y, z与cameras.json的position处于完全相同的世界坐标系(米级尺度),本脚本不进行任何坐标变换——直接读 PLY 顶点计算 AABB,结果就是现实世界坐标(米)。
与 run_cleaning.py 相同,需要 plyfile 与 numpy:
pip install plyfile numpy如果系统默认
python3报libblas.so.3缺失,运行前先设置LD_LIBRARY_PATH,例如:LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu/blas:/usr/lib/x86_64-linux-gnu/lapack \ python3 bbox_3d_xyxy.py <input>
# 默认输出到同目录同名 .json
python3 bbox_3d_xyxy.py <...>/object12.ply
# 指定输出路径
python3 bbox_3d_xyxy.py <...>/object12.ply -o <...>/object12.json# 就地输出(与 PLY 同名 .json)
python3 bbox_3d_xyxy.py <...>/output_v2/pass2/
# 输出到独立目录(推荐,避免污染 pass2)
python3 bbox_3d_xyxy.py <...>/output_v2/pass2/ \
--output-dir <...>/output_v2/bbox_3d_xyxy/每个对象生成一份 JSON:
{
"source_ply": "object325.ply",
"id": 325,
"xyxy_3d": [-2.2016, 0.4037, 1.7274, -2.1208, 0.4883, 1.8190],
"center": [-2.1612, 0.4460, 1.7732],
"size": [0.0808, 0.0846, 0.0916],
"total_vertices": 136
}字段说明:
| 字段 | 含义 | 单位 |
|---|---|---|
source_ply |
源 PLY 文件名(仅作来源标记) | — |
id |
对象 ID(解析自 object{id}.ply 文件名;非 object*.ply 命名时为 null) |
— |
xyxy_3d |
[x_min, y_min, z_min, x_max, y_max, z_max] |
米 |
center |
[cx, cy, cz],即 AABB 中心 |
米 |
size |
[w, h, d],即 AABB 三边长度 |
米 |
total_vertices |
PLY 顶点数(参与 AABB 计算的高斯点数) | — |
input_path PLY 文件路径或包含 PLY 的文件夹(必填)
-o, --output 单文件时的输出 JSON 路径
--output-dir 批量处理时的输出目录(默认就地输出)
对 test2biandianzhan 数据集 output_v2/pass2/ 下的 31 个对象批量计算 AABB:
python3 bbox_3d_xyxy.py \
<...>/output_v2/pass2/ \
--output-dir <...>/output_v2_bbox_3d_xyxy/输出示例(已验证数值与项目内 Bounding_box.py 完全一致):
[12] object12.ply center=(-1.969, +0.666, +0.643) size=(0.110 x 0.104 x 0.133) m
[143] object143.ply center=(-1.046, +0.893, -0.011) size=(0.181 x 0.237 x 0.207) m
[146] object146.ply center=(+0.179, +1.317, -0.749) size=(0.279 x 0.250 x 0.311) m
[325] object325.ply center=(-2.161, +0.446, +1.773) size=(0.081 x 0.085 x 0.092) m
...
批量处理完成!成功: 31/31
可以通过以下方式确认输出确实是"米"且无意外坐标偏移:
import json, numpy as np
# 1) 读 cameras.json
cams = json.load(open("cameras.json"))
cam_pos = np.array([c["position"] for c in cams])
# 2) 读 31 个对象的 xyxy_3d,合并整体 min/max
import glob
mins = np.array([json.load(open(p))["xyxy_3d"][:3]
for p in sorted(glob.glob("output_v2_bbox_3d_xyxy/object*.json"))])
maxs = np.array([json.load(open(p))["xyxy_3d"][3:]
for p in sorted(glob.glob("output_v2_bbox_3d_xyxy/object*.json"))])
print("对象 AABB 范围:", mins.min(0), maxs.max(0))
print("相机中心范围: ", cam_pos.min(0), cam_pos.max(0))
# 期望:所有对象 AABB 都落在相机中心包围盒内(±0.5m 容差)项目内另有 Bounding_box.py(含 center / bounds_min / bounds_max / 8 个角点 / 维度等字段),两者的 bounds_min / bounds_max 与本工具的 xyxy_3d[0:3] / xyxy_3d[3:6] 数值完全一致。区别仅在输出形式:
| 工具 | 输出 | 适用场景 |
|---|---|---|
Bounding_box.py |
center / min / max / 8 corners / dimensions | 需要 8 角点、单点调试、可视化 |
bbox_3d_xyxy.py |
6 元素 xyxy_3d + center + size | 下游程序化处理、与 2D 检测框概念对齐 |