# 三视图重建三维模型 · 实验记录

## 结论先行

从带尺寸标注的正交三视图中重建三维实体，在本次实验的三个案例里可以做到**几何等价**：完整标注下体素交并比（IoV）为 **100.0%**，体积误差 0.0%；把标注刻意删减到只剩 5 个、其余尺寸全部靠像素测量反推后，IoV 仍有 **99.8%**，体积误差 -0.2%。

但成立的前提同样明确，必须一并记住：

1. **必须有尺寸标注或已知比例尺**。纯轮廓、零标注时，绝对尺度在数学上不可知，只能还原拓扑与相对比例。
2. **三视图本身是欠定的**。圆柱与等径圆柱、圆角与倒角、真实孔与盲孔都可能给出相同投影；本次的成功依赖标注或几何关系唯一确定了这些点。
3. **必须有一个可验证的标准答案**。本次用程序化生成的精确 B-Rep 作 ground truth，重建过程严格只读 PNG，不允许回看建模参数——否则测出来的是抄写精度，不是读图精度。

## 三个案例

| 案例 | 零件 | 输入难度 | 体积误差 | 体素 IoV | precision / recall |
|---|---|---|---|---|---|
| P1 | D 形立板安装支架 | 完整标注 | 0.0% | 100.0% | 100% / 100% |
| P2 | 曲柄臂（两段圆弧 + 两条外公切线） | 完整标注，切点需解析反推 | 0.0% | 100.0% | 100% / 100% |
| P3 | 与 P1 同一几何，标注删至 5 个 | 仅 5 个标注 + 像素测量 | -0.2% | 99.8% | 99.9% / 99.9% |

Chamfer 距离分别为 0.45 / 0.83 / 0.68 mm，但这是**表面随机采样的离散化噪声**（8000 点覆盖约 29000 mm²，平均点间距约 1.9 mm），不是真实几何偏差。

## 读图精度实测（P3）

第三个案例刻意只保留总长 120、总高 80、底板厚 8、4×Ø9、2×Ø11 五个标注，其余全部由 `tools/measure.py` 在图纸像素上测得：

| 量 | 测量值 | 真值 | 偏差 |
|---|---|---|---|
| 比例尺（底板像素长 / 120mm） | 119.87 mm | 120 | -0.11% |
| 立板厚 | 7.965 | 8 | -0.035 |
| 加强筋斜边（front Z=20 处） | 32.908 | 33 | -0.092 |
| 加强筋 Y 边界 | 12.07 / 18.15 / 42.01 / 48.09 | 12 / 18 / 42 / 48 | ≤ 0.15 |
| 底板孔心 ×4 | (25.12,14.76) (24.87,45.06) (94.92,15.07) (94.92,45.09) | (25,15) (25,45) (95,15) (95,45) | ≤ 0.24 |
| 立板孔心 ×2 | (29.90,25.10) (29.60,55.16) | (30,25) (30,55) | ≤ 0.40 |

测量原理：扫描线上取深色像素簇定位棱边，ROI 内对深色像素做**代数圆拟合（Kasa）**定位孔心——注意"最暗点"法在孔被加强筋穿过时会失效，筋的直线像素会拉偏结果。比例尺由渲染时导出的像素↔毫米变换矩阵给出，并用已知标注交叉校验。

## 流程

1. **造标准答案**：参数化 CAD 写精确 B-Rep，体积/面积/包围盒可解析计算。
2. **渲染三视图**：OpenCascade `HLRBRep_Algo` 做隐藏线消除。可见线取 `VCompound`（硬边）+ `Rg1LineVCompound`（相切线）+ `OutLineVCompound`（轮廓线）；隐藏线取 `HCompound` + `OutLineHCompound`。同时导出每视图的**像素↔毫米变换矩阵**，供后续像素测量使用。
3. **只读图重建**：模型不看源码，只从图纸读特征、尺寸与拓扑关系。重建脚本刻意采用与 ground truth **不同的建模路径**（例如半圆柱用"圆柱 + 切除"而不是草图拉伸），以证明得到的是同一个几何而非同一段代码。
4. **像素级测量**：补齐缺失标注。
5. **量化评估**：体积、包围盒、体素 IoV（precision/recall）、双向 Chamfer；辅以三视图叠加图（红虚线 = GT，蓝实线 = 重建）。
6. **第三方复读**：FreeCAD 独立打开 STEP/IGES 自己算几何，Blender 渲染 STL。

## 第三方软件复读结果

安装并使用两个软件（均装在用户目录）：

- **FreeCAD 1.1.4**（arm64）：`freecadcmd` 无界面打开每个交付文件并独立计算。
- **Blender 4.2.9 LTS**（arm64）：`blender -b --python` 离屏渲染 STL，Cycles CPU + 96 samples。

```
[FreeCAD] p1_bracket_recon.step   solids=1 faces=22 volume= 93628.09 mm^3 bbox=120.0x60.0x80.0
[FreeCAD] p1_bracket_recon.iges   solids=1 faces=22 volume= 93628.09 mm^3 bbox=120.0x60.0x80.0
[FreeCAD] p2_crank_recon.step     solids=1 faces=15 volume=136808.90 mm^3 bbox=180.0x20.0x60.0
[FreeCAD] p2_crank_recon.iges     solids=1 faces=15 volume=136808.90 mm^3 bbox=180.0x20.0x60.0
```

与生成端（OpenCascade / cadquery）逐位一致 → STEP / IGES 交付物无拓扑丢失、无单位缩放偏差。

## 一次差分归因：重建对了，标准答案错了

P2 首次评估时重建比 ground truth **少 15.5% 体积**，precision 100% / recall 85.2%，即重建是 GT 的真子集。常规做法是去调重建，但叠加三视图 + 体积守恒反查后发现问题出在 ground truth 自己：

`cq.Workplane("XZ")` 的法向是 **−Y**，`workplane(offset=-5).extrude(t)` 实际只切到 `y ∈ [0, 5]`，减重槽和立板孔**并没有穿透板厚**，GT 因此"虚胖"。修正为 `offset(-(THICK+5))` 并 `extrude(2*(THICK+5))` 后，两边完全等价。

教训：差分归因优先于调参；解析体积是最快的裁决者；双方都可能是错的来源。

## 实现上踩过的坑

1. **跨枚举同名常量**：`OCP.GeomAbs.GeomAbs_C0` 实际是 `GeomAbs_Shape.GeomAbs_C0`（值 0），与 `GeomAbs_CurveType.GeomAbs_Line`（值也是 0）比较恒为真，导致直线边被误判跳过。必须用 `GeomAbs_CurveType.GeomAbs_XXX` 显式成员。
2. **`GCPnts_QuasiUniformDeflection` 对直线退化抛异常**，直线必须单独取两端点。
3. **cadquery 弧线参数用全局坐标**：`threePointArc` 的点被当作全局 3D 点，而 `moveTo/lineTo` 用 workplane 局部坐标，二者混用会得到非平面 wire。解法：在 XY 平面画草图后整体旋转到目标平面。
4. **OCC HLR 在"视线平行于圆柱轴"时退化**：正对观察者的圆弧边被丢弃，曲面轮廓被输出成端点之间的**弦**。需按"圆弧所在平面法向 ∥ 视线"补真实圆弧，并剔除对应的假弦。
5. **折线碎片合并**：在几何顶点处错接不同走向的棱边会画出横贯视图的假线。精确 B-Rep 的 HLR 输出本就是完整边，最终选择不做合并。
6. **Blender 导入 STL 会按 mm→m 缩放**：相机距离若用绝对魔数，模型会落在 `clip_end=100` 之外被整个裁掉，渲染出纯背景或纯黑。相机距离须取 `extent` 的相对倍数，`clip_end` 按模型尺度设置。
7. **面光源能量与尺度耦合**：内部米制下 `energy × extent²` 会算出几百万瓦导致整幅过曝纯白。改用太阳光（辐照度与距离无关）后稳定。
8. **FreeCAD 1.1 首次运行向导是模态的**，坐标点击被系统辅助功能权限拦截；改为启动时注入 Python，用 `QTimer` 延迟后关闭模态窗再打开文档。`saveImage` 首调还会因 OpenGL 上下文未就绪返回空白，需要强制 `processEvents()` 循环若干秒。

## 明确不能声称的部分

- **纯轮廓、零标注时无法求解绝对尺度**。
- **三视图固有歧义未被消除**，本次成功依赖标注或几何关系消歧。
- **评估分辨率存在下限**：体素 88³ 时，P3 的 0.2% 级差异已接近分辨力。
- **像素测量依赖等比图纸**，扫描件需先做比例校正。
- **渲染轮廓补偿只覆盖圆柱与锥台的轴向平行情形**，球面与环面未覆盖。
- 本次全部为**程序化自造零件**，未涉及任何真实商业图纸；结论不能外推到扫描件、残缺图纸或复杂曲面零件。

## 交付物

- `out/deliverables/`：三个重建模型的 STEP / STL / IGES，STEP 与 IGES 均已回读校验。
- `out/gt/`：ground truth 三视图与模型；`out/eval/`：三视图叠加对比图。
- `out/render/`、`out/render_from_cad/`：Blender 与 FreeCAD 渲染图。
- `tools/measure.py`：像素级尺寸测量器。
- `tools/blender_render.py`、`tools/freecad_open.py`、`tools/freecad_gui.py`：渲染与复读脚本。
