做建筑设计、旧房改造或室内装修时,最费时间往往不是画图本身,而是前期的“量房”和“勘察”。拿着激光测距仪一间一间量、画手绘图、再录入 CAD,这个过程既枯燥又容易出错。尤其遇到结构复杂的户型,现场测量的误差会一路传到施工图里,后期返工成本很高。
HomeCat 这类项目给出了一条新思路:直接用手机绕着房间拍一段视频,利用计算机视觉和三维重建技术,自动生成可参考、可编辑、甚至可用于报批的建筑平面图。本文就围绕“手机视频 → 三维点云 → 平面图 → 合规化图纸”这条链路展开,拆解实现原理、演示可运行的技术原型,并说明真实项目落地时还需要补哪些工程环节。
无论你是做 AI 应用、建筑设计数字化,还是刚接触三维视觉的开发者,这条技术路线都值得完整走一遍。
1. 背景与核心概念
1.1 HomeCat 是什么
HomeCat 的核心理念,是降低建筑图纸的生产门槛。传统建筑平面图的生成流程,需要专业测量员、全站仪或激光扫描仪,设备和人力成本都很高。而 HomeCat 想做的事情是:普通用户拿手机拍一段视频,系统自动识别墙体、门窗、房间边界,输出一份接近“报批可用”的平面图。
从技术角度看,这属于“基于图像和视频的三维重建 + 语义理解 + 计算机辅助制图”三个领域的结合。
整个处理链路可以拆成四个阶段:
- 采集阶段:用手机摄像头拍摄建筑室内或室外环境。
- 重建阶段:从视频中恢复出三维几何信息,得到稀疏点云、稠密点云,甚至带纹理的网格模型。
- 理解阶段:识别哪些点属于墙体、门窗、楼板,提取出建筑结构边界。
- 成图阶段:把三维结构投影成二维平面图,输出 DXF、DWG、PDF 等标准格式。
HomeCat 这类产品的价值,不在于做出一张看得过去的示意图,而在于把误差控制和合规性放在整个流程里。报批图不是简单的“看起来像”,它需要尺寸准确、图层规范、标注完整,并且满足当地建筑规范。
1.2 手机视频为什么能做三维重建
很多人第一次听说“用视频重建建筑模型”时会觉得不现实,但它的数学基础其实已经很成熟,核心叫“运动恢复结构”。
简单解释:相机在移动过程中,从不同角度拍摄同一场景,画面中的同一个墙角、门框或地砖角,会出现在多帧画面中。算法通过匹配这些同名点,反推出两件事:一是相机在每一帧的位置和姿态,二是这些点在三维空间中的位置。
整个过程主要包含这几个步骤:
- 关键帧抽取:从视频中提取出清晰、有重叠区的帧。
- 特征提取与匹配:检测每帧图像中的角点、边缘等特征点,并在帧之间建立对应关系。
- 稀疏重建:恢复相机位姿和三维特征点,结果是一组稀疏点云。
- 稠密重建:在稀疏点云基础上,利用多视立体匹配生成更密集的点云,形成更完整的墙面、地面几何。
有了稠密点云,再配合平面检测、点云分割等算法,就能提取出房间轮廓。
1.3 与点云扫描、BIM 的区别
有建筑经验的人可能会问:这和激光扫描仪点云建模有什么区别?
区别主要在三个维度:
| 维度 | 手机视频重建 | 激光扫描仪 |
|---|---|---|
| 硬件成本 | 普通手机,几乎零额外成本 | 扫描仪设备价格高,贵则数十万 |
| 精度 | 厘米级到分米级,需要标定参考物 | 毫米级,可直接用于施工测量 |
| 操作门槛 | 普通用户拍视频即可 | 需要专业人员操作和布站 |
BIM 是另一条线。BIM 强调“信息模型”,不只是几何体,还要包含构件类型、材质、生产厂家、运维数据等。视频重建得到的通常是几何网格和点云,需要在后续步骤中通过识别算法转成“墙”“门”“窗”等 BIM 构件,再导出 IFC 等格式。所以 HomeCat 代表的路线不是取代 BIM 软件,而是给 BIM 建模提供一个低成本的“前处理入口”。
2. 从视频到图纸:整体流程拆解
2.1 主流程
完整流程可以写成下面这条链路:
手机视频录像 ↓ 关键帧抽取与质量筛选 ↓ 特征提取与匹配 ↓ 稀疏重建(相机位姿 + 三维特征点) ↓ 稠密重建(生成点云或网格) ↓ 平面点云投影与墙体边界提取 ↓ 矢量化为墙体、门窗图层 ↓ CAD/DXF 图纸输出 ↓ 人工复核与合规化调整每一步都有对应的开源工具或商业软件,这也是我们能够独立实现一个原型的基础。
2.2 各环节对应技术工具
- 视频处理:OpenCV 负责抽帧、缩放、去模糊。
- 三维重建:COLMAP、OpenMVG 负责运动恢复结构;OpenSplat、Nerfstudio 负责更高质量的新视角合成和稠密重建。
- 点云处理:Open3D 是首选,负责滤波、降采样、平面分割、法向量估计。
- 矢量化:OpenCV 的轮廓提取、RANSAC 直线拟合、Douglas-Peucker 多边形简化。
- CAD 导出:ezdxf 可以生成 DXF 文件,配合 AutoCAD、浩辰 CAD 等工具继续编辑。
2.3 为什么需要“报批可用”的合规化处理
“报批”和“看看效果图”完全不同。工程报批图在图层、线型、尺寸标注、比例尺、指北针、图签等方面有明确规范。一张 AI 跑出来的原始粗糙网格图,即使几何外形相似,也很难直接送审。
因此真正的产品级流程,会在生成图纸后增加大量后处理:
- 墙体中线提取:把点云轮廓转成标准双线墙或单线墙。
- 门窗图块替换:识别门洞、窗洞后,替换成标准图块。
- 尺寸链自动标注:根据点云标定比例尺,生成尺寸标注。
- 房间名称与面积标注:结合语义分割结果自动命名。
- 人工审图环节:注册建筑师或绘图员复核关键尺寸并签章。
3. 环境准备与项目结构
3.1 运行环境
本文示例以常见环境为例,具体版本请大家根据本机情况调整:
- 操作系统:Windows 10/11、Ubuntu 20.04/22.04 均可。
- Python:3.9 或 3.10。
- C++ 工具链:COLMAP 依赖 CMake、CUDA(可选)。
- 显存:如果做稠密重建,建议至少 4 GB;纯 CPU 跑小场景也可以,只是速度慢。
3.2 核心依赖
安装 Python 依赖:
pip install opencv-python open3d ezdxf numpy matplotlib安装 COLMAP:
- Windows:可以直接下载 COLMAP 预编译包,加入系统 PATH。
- macOS:
brew install colmap。 - Ubuntu:
sudo apt install colmap。
如果使用 GPU 版本,确保 CUDA 驱动和 PyTorch 环境一致,不过本文示例不强依赖 GPU。
3.3 示例项目结构
homecat-demo/ ├── input/ │ └── sample.mp4 # 手机拍摄的室内视频 ├── frames/ # 抽帧结果 ├── colmap/ │ ├── database.db │ ├── sparse/ │ └── dense/ ├── output/ │ └── floor_plan.dxf ├── extract_frames.py # 抽帧脚本 ├── build_pointcloud.py # 点云转平面占用图 └── export_dxf.py # DXF 导出脚本把视频放到input/目录后,按后面步骤依次执行即可。
4. 完整实战案例:从视频到平面图原型
为了让流程可见、可运行,我们把整个过程拆成四个脚本。先说明:这是一个技术原型,重点演示处理思路;真实项目中,墙体识别、尺度标定和 CAD 规范化还需要更多工程环节。
4.1 关键帧抽取
视频中相邻帧重叠度太高,直接全部送入三维重建会非常慢,而且容易造成冗余匹配。一般每 0.5 秒左右抽一帧,并统一缩放到较短边 720 像素以内。
创建extract_frames.py:
import cv2 import os video_path = "input/sample.mp4" frame_dir = "frames" os.makedirs(frame_dir, exist_ok=True) cap = cv2.VideoCapture(video_path) fps = cap.get(cv2.CAP_PROP_FPS) target_interval = max(1, int(fps * 0.5)) idx = 0 saved = 0 while True: ret, frame = cap.read() if not ret: break if idx % target_interval == 0: # 缩放到较短边 720,降低计算量 h, w = frame.shape[:2] scale = 720 / min(h, w) frame = cv2.resize(frame, (int(w * scale), int(h * scale))) cv2.imwrite(os.path.join(frame_dir, f"frame_{saved:04d}.jpg"), frame) saved += 1 idx += 1 cap.release() print(f"共保存 {saved} 帧到 {frame_dir}")运行:
python extract_frames.py如果保存的帧数太少,比如小于 10 帧,说明视频运动太慢或太短,建议重新拍摄。
4.2 使用 COLMAP 做稀疏与稠密重建
抽帧完成后,使用 COLMAP 依次执行特征提取、特征匹配、稀疏重建和稠密重建。
先建目录:
mkdir -p colmap/sparse特征提取:
colmap feature_extractor \ --database_path colmap/database.db \ --image_path frames \ --ImageReader.camera_model PINHOLE \ --ImageReader.single_camera 1这里PINHOLE表示针孔相机模型,适合大多数手机主摄;single_camera=1表示所有帧共用同一相机内参。
特征匹配:
colmap exhaustive_matcher \ --database_path colmap/database.db如果帧数很多,可以把exhaustive_matcher换成sequential_matcher,效率更高:
colmap sequential_matcher \ --database_path colmap/database.db \ --SequentialMatching.overlap 10稀疏重建:
colmap mapper \ --database_path colmap/database.db \ --image_path frames \ --output_path colmap/sparse稀疏重建结果位于colmap/sparse/0。下面进行稠密重建:
colmap image_undistorter \ --image_path frames \ --input_path colmap/sparse/0 \ --output_path colmap/dense colmap patch_match_stereo \ --workspace_path colmap/dense \ --workspace_format COLMAP \ --PatchMatchStereo.geom_consistency true colmap stereo_fusion \ --workspace_path colmap/dense \ --workspace_format COLMAP \ --input_type geometric \ --output_path colmap/dense/fused.ply如果电脑没有 GPU,patch_match_stereo会非常慢,建议先把视频场景控制在一个房间内,或者先只用稀疏点云做平面轮廓,后续再补稠密重建。
4.3 点云转平面占用图
稠密重建得到三维点云fused.ply。建筑平面图本质上是把三维点云投影到地面平面上,提取出“哪些区域被物体占据”。
创建build_pointcloud.py:
import open3d as o3d import numpy as np pcd = o3d.io.read_point_cloud("colmap/dense/fused.ply") print(f"加载点云,点数:{len(pcd.points)}") # 去除统计离群点,减少漂浮噪点 pcd, _ = pcd.remove_statistical_outlier(nb_neighbors=20, std_ratio=2.0) pts = np.asarray(pcd.points) x_min, x_max = np.percentile(pts[:, 0], [1, 99]) y_min, y_max = np.percentile(pts[:, 1], [1, 99]) grid_size = 0.02 # 每个网格 2cm,按精度需求调整 width = int((x_max - x_min) / grid_size) + 1 height = int((y_max - y_min) / grid_size) + 1 occupancy = np.zeros((height, width), dtype=np.int32) for x, y in pts[:, :2]: col = int((x - x_min) / grid_size) row = int((y - y_min) / grid_size) if 0 <= row < height and 0 <= col < width: occupancy[row, col] += 1 # 过滤掉只有少量点的噪声网格 density_map = (occupancy > 3).astype(np.uint8) * 255 np.save("output/density_map.npy", density_map) import cv2 cv2.imwrite("output/density_map.png", density_map) print(f"网格尺寸:{width} x {height}") print("占用图已保存到 output/density_map.png")这段代码把三维点云投影到 X-Y 平面,生成了一张类似“从上往下看”的占位图。墙面、家具顶面都会落入网格中,后续通过轮廓提取就能得到大致的房间边界。
4.4 导出 DXF 图纸
有了二维占用图,接下来把它转成 DXF 矢量图形。为了演示,我们用 OpenCV 查找外轮廓,并对每个连通区域生成外接矩形,写入墙体图层。
创建export_dxf.py:
import cv2 import numpy as np import ezdxf # 读取占用图 density_map = cv2.imread("output/density_map.png", cv2.IMREAD_GRAYSCALE) _, binary = cv2.threshold(density_map, 10, 255, cv2.THRESH_BINARY) # 形态学闭运算,把细小断裂连接起来 binary = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, np.ones((15, 15), np.uint8)) contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) doc = ezdxf.new("R2010") msp = doc.modelspace() grid_size = 0.02 # 与上一步保持一致 x_min = np.load("output/x_min.npy") y_min = np.load("output/y_min.npy") for c in contours: x, y, w, h = cv2.boundingRect(c) # 过滤过于细碎的轮廓 if w < 10 or h < 10: continue # 换算成真实坐标 x0 = x_min + x * grid_size y0 = y_min + y * grid_size msp.add_lwpolyline( [ (x0, y0), (x0 + w * grid_size, y0), (x0 + w * grid_size, y0 + h * grid_size), (x0, y0 + h * grid_size), ], close=True, dxfattribs={"layer": "WALL", "color": 1}, ) doc.saveas("output/floor_plan.dxf") print(f"DXF 已保存到 output/floor_plan.dxf,共 {len(contours)} 个轮廓")这里需要注意:x_min和y_min需要在上一步保存。给build_pointcloud.py补两行:
np.save("output/x_min.npy", x_min) np.save("output/y_min.npy", y_min)运行export_dxf.py之前,先执行build_pointcloud.py生成依赖文件。
4.5 运行与验证
按顺序执行:
python extract_frames.py # 然后执行 COLMAP 系列命令 python build_pointcloud.py python export_dxf.py验证 DXF 文件是否正常:
import ezdxf doc = ezdxf.readfile("output/floor_plan.dxf") entities = list(doc.modelspace()) print(f"DXF 中共有 {len(entities)} 个实体")用 CAD 软件打开output/floor_plan.dxf,看到的应该是若干个代表房间边界或家具轮廓的矩形。
5. 从“生成图”到“报批图”,还差哪几步
上面原型生成的 DXF 只能算“几何草图”,距离真正可用于报批的图纸还有明显差距。
5.1 尺度标定
单目视频重建的结果本身没有绝对尺度,重建出来的“1 米”不一定等于物理世界中的“1 米”。解决办法是放进已知尺寸的参考物。
比如已知一扇标准门宽 0.9 米,在点云中测量得到该门宽为 0.45 个单位,那么缩放系数就是:
real_width = 0.9 # 真实门宽,单位米 measured_width = 0.45 # 点云中量出的宽度,单位点云单位 scale = real_width / measured_width print(f"缩放系数:{scale},即每个点云单位代表 {scale} 米")得到缩放系数后,把点云整体乘以scale,再做投影和 DXF 导出,图纸尺寸才接近真实尺寸。
5.2 墙体识别与图层规范
真实墙体不是简单的矩形外轮廓,它由两层面线、填充、尺寸标注、门窗图块组成。工程级做法是:
- 用 RANSAC 算法拟合直线段,提取墙体的中心线。
- 通过点云密度和高度信息,区分墙面与地面、天花板。
- 对门洞、窗洞做空洞检测,识别门窗位置。
- 在 DXF 中建立
WALL、DOOR、WINDOW、DIMENSION等多个图层。
这一步是 HomeCat 这类产品最有技术壁垒的地方,也是决定图纸能否被设计院接受的关键。
5.3 报批合规性要求
报批图需要满足当地规划部门或审图机构的要求,通常包括:标准图框、比例尺、指北针、尺寸标注、房间面积、使用功能说明、责任人签章等。AI 生成图纸可以作为底图,但最终需要具备资质的设计人员核对、修改、签字。
因此,在工程落地时,不要把 AI 生成结果当作“终稿”,而是把它嵌入到“初步测绘 → 自动生成 → 人工精修 → 正式出图”的流水线中。
6. 常见问题与排查思路
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 重建出来的房间严重扭曲 | 拍摄路径太短,或镜头一直原地旋转 | 环绕拍摄,保持平移运动,确保帧之间有足够视差 |
| 白色墙面匹配不上,重建断裂 | 墙面纹理稀疏,特征点太少 | 在墙面贴临时标记物,或选择有踢脚线、挂画的场景 |
| 点云尺度不对,无法确定尺寸 | 单目重建没有真实尺度 | 在场景中放置标尺、已知尺寸的门或地砖作参考 |
| 点云噪声很大,出现飞点 | 玻璃、镜子、抛光砖反光干扰 | 拍摄时避开高反光物体;后期用统计离群点去除算法 |
| 生成的 DXF 只有大矩形,没有墙体细节 | 原型只做了连通域外接矩形 | 换成直线拟合、墙线提取算法,增加语义分割模型 |
| 报批时被退回,说图纸不规范 | 没有图层、标注、指北针 | 使用 CAD 标准模板,由专业绘图员规范化整理 |
| COLMAP 速度极慢 | 输入帧数过多,或 CPU 跑稠密重建 | 降低抽帧频率,先用稀疏点云验证流程 |
如果重建失败,可以先检查colmap/database.db里的特征点数:
colmap database_analyzer \ --database_path colmap/database.db关注每个图像的特征数是否在几百以上。如果是个位数,说明画面纹理太差或抽帧后图片太模糊,需要重新拍摄。
7. 最佳实践与工程建议
7.1 拍摄规范
手机视频重建对拍摄要求比较高,以下建议能显著提高成功率:
- 横屏拍摄,保持画面稳定,最好使用手持稳定器。
- 围绕房间走一圈,不要站在原地只转动手机。
- 与墙面保持 0.5 到 2 米的距离,避免过近导致运动模糊。
- 相邻帧重叠率保持在 70% 以上,移动速度慢一点。
- 避免逆光、强阴影和夜间拍摄,光照均匀最好。
7.2 数据合规与隐私
建筑平面图属于敏感的空间信息。拍摄前必须获得场所所有权人或管理人的明确授权;处理后的图纸如果涉及未公开建筑结构,不应上传到不受信任的第三方服务。在技术选型上,优先考虑支持私有化部署的开源方案,比如本地运行 COLMAP 和 Open3D,只把最终脱敏结果用于协作。
7.3 可追溯性与版本管理
AI 制图的公信力依赖可追溯性。建议对每次重建保存以下内容:
- 原始视频文件和抽帧结果。
- COLMAP 数据库和重建模型。
- 所使用的缩放系数、阈值参数。
- 算法版本和运行日志。
这样当图纸尺寸出现争议时,可以快速回溯到源头。
7.4 生产级架构建议
如果把原型包装成线上服务,建议这样分层:
用户上传视频 → 任务队列(Redis + Celery) → 抽帧服务 → 重建服务(GPU 机器跑 COLMAP) → 点云分析服务 → CAD 生成服务 → 人工审核工作台人工审核环节不能省,它既是质量保障,也是合规要求。
8. 总结与下一步学习路线
本文从 HomeCat 的产品理念出发,完整拆解了“手机视频 → 建筑平面图”的技术链路。我们动手完成了抽帧、COLMAP 三维重建、点云投影、DXF 导出四个关键步骤,形成一个可运行的简化原型。同时也明确了差距:真实报批图还需要尺度标定、墙体语义识别、CAD 图层规范化、设计人员签字确认等工程环节。
如果你想继续深入,可以从这几个方向入手:
- 学习 COLMAP 的论文和参数,理解相机构建和光束法平差原理。
- 学习 Open3D 的平面分割与 RANSAC 算法,实现更准确的墙线提取。
- 学习语义分割模型,自动识别门窗、家具和墙体。
- 学习 ezdxf 高级用法,生成带尺寸标注和图层的标准图纸。
- 尝试 NeRF 或 3D Gaussian Splatting,在纹理重建方面获得更高质量的结果。
这波 AI 与建筑行业的碰撞才刚刚开始,HomeCat 代表的方向值得持续关注。纸上得来终觉浅,建议你立刻拿起手机,对着办公室或家里拍一段视频,把这条流程完整跑一遍,你会对三维重建和 CAD 生成有更直观的理解。