Files
AnyImage/openspec/changes/unify-reference-depth/design.md
nan 2dfa9b2add Refactor depth reference handling and cleanup
- Unified reference depth calculation to use a single implementation in common/depth.py.
- Removed server-side reference depth computation and adjusted depth.json to only include image_size and intrinsics.
- Updated depth texture handling to ignore transparent, invalid, and non-positive pixels when calculating reference depth.
- Changed reference depth calculation to use the 95th percentile of valid depth values.
- Adjusted depth direction fitting to return a flat direction when no samples are available.
- Removed cleanup threshold parameters from Cutout and related nodes, simplifying the interface.
- Updated tests to verify new reference depth logic and ensure compatibility with existing functionality.
2026-09-15 22:50:16 +08:00

7.1 KiB
Raw Blame History

Context

参考深度是深度几何的零点,同时是 Uniform Scale 的归一化基准:depth_uniform_scale() 用 参考深度 × fx / (宽度 - 1) 把模型相机单位换算成 Image Empty 的局部距离。同一张深度图因此只能有一个基准,否则投影尺度与位移零点会互相矛盾。

当前有两个算法。服务端 write_depth_metadata() 以「模型有效性 ∧ 深度有限为正 ∧ 源图 Alpha ≥ 阈值」取中位数写入 depth.json;Cutout 的 metric_depth_reference() 以「选区 ∩ 源图 Alpha ≥ 阈值」取 95 百分位,并在选区透明像素不足 5% 时改用固定值 8.0。Depth Plane 与 Relief Plane 用前者,Depth Solid 与 Depth Symmetry 用后者,Relief Plane 又用 Depth Offset = 1 把中位数基准向后补偿。

depth.exr 的 Alpha 是「源图 Alpha × 模型有效性」,随深度纹理一起被 Blender 读入,camera_depth_values() 已经在读它的 Alpha 与相机 Z,服务端那次计算没有额外信息可用。

Goals / Non-Goals

Goals:

  • 参考深度只有一处实现、一条有效性判据、一个统计量。
  • 服务端只提供相机内参和图像尺寸,不再判断参考区域。
  • 删除 Cutout 的透明比例闸门与固定基准兜底。
  • 让 Relief Plane 的基准平面由参考深度决定,而不是由 Depth Offset 的默认值补偿。

Non-Goals:

  • 不改变 AI 推理、深度纹理协议、节点组接口与 Depth Symmetry 的方向拟合行为。
  • 不为旧 depth.json 字段保留兼容读取或转发函数。
  • 不把参考深度暴露成新的用户参数。

Decisions

参考深度在 Blender 侧由深度纹理计算

common/depth.py 新增 reference_depth(image, mask=None),读取 camera_depth_values() 的相机 Z 与 Alpha,在 Alpha > 0.5 且深度有限且为正 的像素上取 95 百分位,返回模型单位。调用方再乘 Uniform Scale。服务端删除参考深度写入,depth.json 只保留 image_size 与 intrinsics。

服务端没有选区信息,也无法在加载时知道几何目标的意图;把判据放在读取深度纹理的一侧,才能让「有效性」只有 Alpha 一个来源。备选是保留服务端字段作为默认值,但这会让同一张深度图继续存在两个基准。

有效性只由 Alpha 与深度值决定

Alpha 已经包含源图 Alpha 与模型有效性两个条件,因此不再需要 alpha_threshold 参与参考深度,也不再需要 validity 与 Alpha 双重过滤。有效性阈值取 0.95:模型的有效性信号接近二值,抬高阈值主要排除源图 Alpha 的半透明边缘,让参考深度只统计完全不透明且模型判定有效的像素。代价是毛发、玻璃等本身就半透明的主体可能没有可用像素,此时几何创建明确失败而不是回退到固定基准。非有限与非正深度仍被排除,因为它们无法参与深度换算。

统计量统一为 95 百分位

基准平面取有效深度的上分位,主体整体落在基准之前:Depth Solid、Depth Symmetry 与 Relief Plane 的位移与厚度归一化因此都从「内容后端」起算,Depth Plane 与 Relief Plane 不再需要靠 Depth Offset 人工后移。作为 Uniform Scale 的锚点,这个取值同时决定近处内容相对源图轮廓的缩进量,属于可见的行为变化,由几何测试与人工检查确认。

备选是统一取中位数:实现同样简单,但 Depth Solid 与 Depth Symmetry 会有一半内容被钳制在深度零点,厚度归一化也随之改变。

参考深度丢弃超出米制上限的样本

参考深度同时是 Uniform Scale 的锚点,Uniform Scale 与参考深度成反比,所以远景会把整个重建结果缩小并整体推向基准之前。风景图没有主体,中位数与上分位都落在远景上,这个效应最明显。因此统计前丢弃距离超过 REFERENCE_DEPTH_LIMIT(米)的样本,让基准由主要内容的距离决定;距离上限之外的内容仍保留在深度纹理里,只影响几何投影,不影响标定。

上限「选不出样本」和「没有有效像素」是同一个问题的两面——有效像素的选择多了一条距离条件——所以两者用同一条规则:没有样本剩下时,参考深度取上限本身。这样参考深度的取值域始终是 (0, REFERENCE_DEPTH_LIMIT],同一张图不会因为少量近景像素出现而跳到另一个尺度,也不会因为深度不可信而失败或弹出提示。上限取固定米数而不是相对中位数的比例,因为米制深度是这个工具与 Blender 场景的既有接口,固定上限可以直接说明「主要内容在多少米以内」。

参考域可选,Cutout 传入选区

reference_depth(image, mask=None) 的 mask 由目标自己决定:Cutout 传入当前选区,Plane 转换不传。选区只是参考深度的作用域,不再参与透明度比例判断。

全幅照片没有透明区域时,若忽略选区,参考深度会落在背景深度上,投影归一化会让被套索的主体缩小并与源图轮廓错开。备选是完全按整幅深度图计算,但会破坏套索全幅照片这一既有用法。

计算函数返回模型单位

depth_uniform_scale() 需要未缩放的参考深度才能求出 uniform_scale,因此 reference_depth() 返回模型单位,由调用方乘以尺度后写入 Modifier。

Relief Plane 的 Depth Offset 默认值改为 0

基准平面已由 95 百分位放到内容后端,Depth Offset 恢复为纯粹的微调参数。默认值属于节点组接口,修改构建脚本后重新生成 O_AnyImage.blend。

删除 Cutout 标定模块

depth_calibration.py 的参考深度计算与透明比例闸门被 reference_depth() 取代;其中的基础形状 UV 采样与平面方向拟合下沉到 common/depth.py 的 fit_symmetry_depth_direction(),与 fit_depth_direction() 同处。geometry.py 的 selection_calibration() 只服务于旧标定,随之删除。

方向拟合沿用同一条选择规则

fit_depth_direction() 在没有可用样本时返回无倾斜的规范方向 (0, 1, 0),而不是报错;Relief Plane 与 Depth Symmetry 因此都得到 (0, 0, 1) 的 Direction。兜底放在拟合函数里而不是调用点,是因为「有效像素选不出来」对参考深度和方向是同一个问题,放在一处可以删掉 Plane 转换里原有的 try/except,也让两个深度目标行为一致。

Risks / Trade-offs

  • [Depth Plane 与 Relief Plane 的基准从内容中位数移到上分位,几何尺度变化明显] → 这是本次明确要求的行为变化,用几何测试锁定 Reference Depth 与投影结果,不增加兼容分支。
  • [套索选区在源图全不透明时决定参考深度] → 保留可选 mask 参数,让 Cutout 继续以选区为参考域。
  • [节点组默认值与发布资产不一致] → 修改构建脚本后运行 uv run node-group build。
  • [已有场景保存的 Modifier 仍带旧 Reference Depth 与 Depth Offset] → 已保存数值按原样保留,只有重新生成深度几何时才使用新基准。