feat: unify reference depth calculation by using median-based range factor and baseline

This commit is contained in:
2026-09-16 01:04:59 +08:00
parent 474fc10f0f
commit cf512dc013
6 changed files with 45 additions and 36 deletions

View File

@@ -39,11 +39,11 @@ Alpha 已经包含源图 Alpha 与模型有效性两个条件,因此不再需
备选是统一取中位数:实现同样简单,但 Depth Solid 与 Depth Symmetry 会有一半内容被钳制在深度零点,厚度归一化也随之改变。
### 参考深度丢弃超出米制上限的样本
### 参考深度丢弃超出中位数倍数的样本
参考深度同时是 `Uniform Scale` 的锚点,`Uniform Scale` 与参考深度成反比,所以远景会把整个重建结果缩小并整体推向基准之前。风景图没有主体,中位数与上分位都落在远景上,这个效应最明显。因此统计前丢弃距离超过 `REFERENCE_DEPTH_LIMIT`(米)的样本,让基准由主要内容的距离决定;距离上限之外的内容仍保留在深度纹理里,只影响几何投影,不影响标定。
参考深度同时是 `Uniform Scale` 的锚点,`Uniform Scale` 与参考深度成反比,所以远景会把整个重建结果缩小并整体推向基准之前。风景图没有主体,中位数与上分位都落在远景上,这个效应最明显。因此统计前丢弃距离超过 `REFERENCE_DEPTH_RANGE_FACTOR`(`1.1`)倍中位数的样本,让基准由主要内容的距离决定;被丢弃的内容仍保留在深度纹理里,只影响几何投影,不影响标定。
上限「选不出样本」和「没有有效像素」是同一个问题的两面——有效像素的选择多了一条距离条件——所以两者用同一条规则:没有样本剩下时,参考深度取上限本身。这样参考深度的取值域始终是 `(0, REFERENCE_DEPTH_LIMIT]`,同一张图不会因为少量近景像素出现而跳到另一个尺度,也不会因为深度不可信而失败或弹出提示。上限取固定米数而不是相对中位数的比例,因为米制深度是这个工具与 Blender 场景的既有接口,固定上限可以直接说明「主要内容在多少米以内」。
上限取中位数的倍数而不是固定米数,因为固定米数假设「主要内容在多少米以内」,一旦主体整体在固定上限之外,基准会落到主体之前,`reference − depth` 全为负而被钳制,几何直接消失。倍数上限随内容尺度变化:中位数永远落在上限之内,所以只要存在有效像素,统计集合一定非空,也不会因为题材远近而失效。代价是风景图的上限随内容放大,远景被推后的力度比固定小上限时轻。
### 参考域可选,Cutout 传入选区

View File

@@ -6,7 +6,7 @@
- **BREAKING**:服务端不再计算参考深度,`depth.json` 只保留 `image_size` 与 `intrinsics`。
- 在 `common/depth.py` 提供唯一的参考深度计算:以 `depth.exr` 的 Alpha 大于 `0.95`、深度有限且为正为有效域,取相机 Z 的 95 百分位。
- 统计前丢弃距离超过 `REFERENCE_DEPTH_LIMIT`(`8` 米)的样本,避免远景把参考深度与整个重建尺度拖到远处;选择后没有样本时参考深度取该上限,不报错也不提示。
- 统计前丢弃距离超过中位数 `REFERENCE_DEPTH_RANGE_FACTOR`(`1.1`)倍的样本,避免远景把参考深度与整个重建尺度拖到远处;完全没有有效像素时参考深度取 `REFERENCE_DEPTH_BASELINE`(`8` 米),不报错也不提示。
- 深度方向拟合没有可用样本时返回无倾斜方向,Relief Plane 与 Depth Symmetry 的 `Direction` 一律回退为 `(0, 0, 1)`,不再报错,也不在调用点各自兜底。
- Depth Plane、Relief Plane、Depth Solid、Depth Symmetry 共用同一实现与同一统计量,不再区分「服务端基准」与「标定基准」;Cutout 把选区作为可选参考域传入,Plane 转换使用整幅深度图。
- **BREAKING**:参考深度从内容中位数改为上分位,基准平面移动到场景后端,Depth Plane 与 Relief Plane 的几何结果随之变化。

View File

@@ -26,20 +26,20 @@
#### Scenario: Exclude everything
- **WHEN** 没有任何像素满足有效条件
- **THEN** 参考深度取 `REFERENCE_DEPTH_LIMIT`
- **THEN** 参考深度取 `REFERENCE_DEPTH_BASELINE`
### Requirement: Reference depth uses the upper depth percentile
参考深度 SHALL 取有效像素相机 Z 的 95 百分位,并以模型单位返回。所有深度几何 SHALL 使用同一判据,MUST NOT 按目标区分统计量。
#### Scenario: Distant background is dropped before the percentile
- **WHEN** 部分有效像素的距离超过 `REFERENCE_DEPTH_LIMIT` 米
- **WHEN** 部分有效像素的距离超过有效像素中位数的 `REFERENCE_DEPTH_RANGE_FACTOR` 倍
- **THEN** 这些像素不参与百分位统计
- **AND** 参考深度只由上限之内的像素决定
- **AND** 参考深度只由该范围内的像素决定
#### Scenario: No sample survives the selection
- **WHEN** 有效像素的距离全部超过 `REFERENCE_DEPTH_LIMIT` 米
- **THEN** 参考深度取 `REFERENCE_DEPTH_LIMIT`
- **AND** 系统不报告错误,也不提示基准来源
#### Scenario: Keep a distant subject
- **WHEN** 全部有效像素都远于 `REFERENCE_DEPTH_BASELINE`,但仍在自身中位数的 `REFERENCE_DEPTH_RANGE_FACTOR` 倍以内
- **THEN** 参考深度按这些像素的 95 百分位计算
- **AND** 主体不会因为基准落在自身之前而被钳制
#### Scenario: Depth distribution is skewed
- **WHEN** 有效深度在前后景之间分布不均

View File

@@ -4,7 +4,7 @@
- [x] 1.2 Drop `reference_depth` from `load_depth_metadata()` and change `depth_uniform_scale()` to take the computed reference depth instead of reading it from metadata.
- [x] 1.3 Move the base-shape direction fit into `common/depth.py` as `fit_symmetry_depth_direction()` beside `fit_depth_direction()`, then delete `src/anyimage/operators/cutout_tool/depth_calibration.py`.
- [x] 1.4 Delete `selection_calibration()` from `src/anyimage/operators/cutout_tool/geometry.py` and remove its test.
- [x] 1.5 Drop reference samples farther than `REFERENCE_DEPTH_LIMIT` meters before the percentile and use that limit as the reference when the selection leaves no sample.
- [x] 1.5 Drop reference samples farther than `REFERENCE_DEPTH_RANGE_FACTOR` times the median before the percentile, and return `REFERENCE_DEPTH_BASELINE` when there is no valid pixel at all.
- [x] 1.6 Return the flat canonical direction from `fit_depth_direction()` when no sample is usable, and delete the caller-side `try/except` in plane conversion.
## 2. Server artifacts