chore: Bump version to 0.12.13 and update documentation for color space handling
This commit is contained in:
@@ -64,19 +64,19 @@ Bump 合并;网格本身不写入 custom normal,因此重拓扑降低顶点
|
||||
当前此功能仅支持单张图片。
|
||||
|
||||
MoGe-2 ViT-S、ViT-B、ViT-L、DA3 Small、DA3 Base 与 BEN2 全部使用 ONNX。模型执行、相机与深度恢复、
|
||||
点云重投影和网格生成均不依赖 PyTorch。Windows 使用 DirectML 加速,provider 不可用时会回退到 ONNX Runtime CPU,
|
||||
点云重投影和网格生成均不依赖 PyTorch。Windows 使用 DirectML 加速,macOS 使用 CoreML 的 `MLProgram` 模型格式;provider 不可用时会回退到 ONNX Runtime CPU,
|
||||
并在任务状态中显示实际 provider。
|
||||
|
||||
转换对话框提供模型与 `Create Matching Camera`、`Remove Background`、`Shadeless`;DA3 显示
|
||||
`Resolution Scale`,MoGe-2 显示 `Resolution Level`,
|
||||
以及分割线下方的 `Keep Original`。
|
||||
界面会显示计算所得的实际 Process Res。剔除和重拓扑参数直接在
|
||||
Geometry Nodes 修改器中调整;`Shadeless` 与 Depth Plane 共用 `O Shadeless` 材质节点组。颜色贴图始终使用统一的 `.color` 转换产物;Remove Background 只决定是否由 BEN2 修改 Alpha,并使用 `AgX Base sRGB`。普通材质的
|
||||
Geometry Nodes 修改器中调整;`Shadeless` 与 Depth Plane 共用 `O Shadeless` 材质节点组。颜色贴图始终使用统一的 `.color` 转换产物;Remove Background 只决定是否由 BEN2 修改 Alpha。颜色贴图会按当前场景的 View Transform 选择对应输入色彩空间:AgX、ACES 1.3、ACES 2.0、Filmic、Khronos PBR Neutral 分别使用 `AgX Base sRGB`、`ACES 1.3 sRGB`、`ACES 2.0 sRGB`、`Filmic sRGB`、`Khronos PBR Neutral sRGB`;无法匹配当前 View Transform 时回退 `AgX Base sRGB`;当前 Blender 连该选项也不支持时才安全回退 `sRGB`。普通材质的
|
||||
Bump Scale 会在材质组内乘以 `0.001`,并按透视表面的深度空间缩放。Surface 与 Depth Plane 的
|
||||
Layer Color 同时驱动 Principled BSDF 的 Base Color、Subsurface Radius 和 Emission Color,Layer Normal
|
||||
同时驱动 Normal 与 Coat Normal;Subsurface、Emission、Coat 的其他设置保持默认。Surface attributes
|
||||
统一使用 `o_` 前缀;Server 准备过程显示
|
||||
`Preparing surface data n/6`,可看到当前阶段与总步骤;Surface 单帧模型推理不显示多余的 `1/1`。
|
||||
`Preparing surface data n/6`,可看到当前阶段与总步骤;Surface 与 Depth 单帧模型推理都不显示多余的 `1/1`。
|
||||
|
||||
### 视频与序列帧
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
`Keep Original` 默认关闭,并在分割线下方保持为最后一项。仅当开启 `Remove Background` 时才需要
|
||||
`BEN2 Base`,并把 `.color` 的 Alpha 作为独立 Mask,不在 Server 中改写深度值。无论颜色层是直接
|
||||
复制原图,还是由 BEN2 为单图或序列修改 Alpha,Server 都统一写为 `color.0001.*`;Blender 只导入
|
||||
这一份 `.color`,不复用源 Empty 的 Image,Color Space 固定为 `AgX Base sRGB`。
|
||||
这一份 `.color`,不复用源 Empty 的 Image。颜色贴图会按当前场景的 View Transform 选择对应输入色彩空间:AgX、ACES 1.3、ACES 2.0、Filmic、Khronos PBR Neutral 分别使用 `AgX Base sRGB`、`ACES 1.3 sRGB`、`ACES 2.0 sRGB`、`Filmic sRGB`、`Khronos PBR Neutral sRGB`;无法匹配当前 View Transform 时回退 `AgX Base sRGB`;当前 Blender 连该选项也不支持时才安全回退 `sRGB`。
|
||||
Image Empty 引用视频或序列帧时,DA3 与 MoGe-2 会按 Empty 的 Image Frames 数值逐帧估计
|
||||
深度(未设置时为完整帧数,显式设为 1 时只处理 1 帧),起始帧跟随 Empty 的 Offset,与背景蒙版范围一致;
|
||||
DA3 会复用同一个 ONNX Session 对每帧独立推理,再合并为原有序列输出,避免多帧 Attention 的平方级显存占用。
|
||||
@@ -30,7 +30,7 @@
|
||||
一致;Original 模式的视频或序列帧使用 `color.0001.*` 颜色层同时作为颜色与深度输入;开启
|
||||
Remove Background 时同名颜色层包含 BEN2 修改后的 Alpha。
|
||||
DA3 的深度仍以 EXR 序列帧(`depth.0001.exr` 起)保存在 job 目录根下;MoGe-2 分别使用 `moge_depth`、`moge_mask` 与 `moge_normal` EXR 序列。
|
||||
`Shadeless`、`Remove Background` 与 `Keep Original` 均默认关闭,因此默认删除原对象。`Shadeless` 会以 `O Shadeless` 节点组替代普通 Principled BSDF 路径,并继续通过 Alpha 与 Transparent BSDF 混合。
|
||||
DA3 与 MoGe-2 的单帧推理进度只显示模型与阶段名称,不追加 `1/1`;多帧时继续显示当前帧与总数。`Shadeless`、`Remove Background` 与 `Keep Original` 均默认关闭,因此默认删除原对象。`Shadeless` 会以 `O Shadeless` 节点组替代普通 Principled BSDF 路径,并继续通过 Alpha 与 Transparent BSDF 混合。
|
||||
3. DA3 或 MoGe-2 完成后会自动创建面片。DA3 面片几何先由 `mini_npz` 的相机内参与深度确定,再通过对象
|
||||
Transform 匹配原 Image Empty 的可见尺寸、位置、旋转和 Offset。Original 模式直接使用源 Empty
|
||||
的可见尺寸、Offset 与 Transform。DA3 会把用于匹配画面尺寸的缩放预乘到 `.depth` 数值,
|
||||
|
||||
@@ -109,7 +109,7 @@ uv run pytest tests/test_addon.py tests/test_manifest.py tests/test_pack.py -v
|
||||
uv run python pack.py
|
||||
```
|
||||
|
||||
`pack.py` 根据 `blender_manifest.toml` 分别生成 Windows、macOS 和 Linux 扩展包,例如 `dist/SnapForm.v0.12.11.windows-x64.zip`。更换 `src/snapform/wheels/` 中随扩展分发的 wheel 后,先刷新锁文件再测试:
|
||||
`pack.py` 根据 `blender_manifest.toml` 分别生成 Windows、macOS 和 Linux 扩展包,例如 `dist/SnapForm.v0.12.13.windows-x64.zip`。更换 `src/snapform/wheels/` 中随扩展分发的 wheel 后,先刷新锁文件再测试:
|
||||
|
||||
```bash
|
||||
uv lock --refresh
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
## 使用
|
||||
|
||||
1. 运行 `uv run python pack.py`,分别构建 `windows-x64`、`macos-arm64`、
|
||||
`linux-x64` 三个平台的 Extension ZIP(如 `SnapForm.v0.12.11.macos-arm64.zip`)。
|
||||
`linux-x64` 三个平台的 Extension ZIP(如 `SnapForm.v0.12.13.macos-arm64.zip`)。
|
||||
安装包不携带 uv 或 AI 项目源码;首次安装 Environment 时下载固定版本 uv,并校验固定 SHA-256。官方安装按钮使用 GitHub、uv 默认 Python 下载源和 PyPI;镜像按钮使用 CNB uv Release、CNB Python 解释器镜像和清华 PyPI。
|
||||
2. 在 Blender 的 `Preferences > Extensions` 中选择 `Install from Disk`。
|
||||
3. 在 Extension Preferences 中确认 Storage Root 与 Device。Device 是正式功能
|
||||
|
||||
@@ -63,7 +63,7 @@ ONNX 实现位于 `models/`;纯投影、网格与唯一 Surface Builder 位于
|
||||
|
||||
运行环境不安装 PyTorch、TorchVision、xFormers,也不安装 DA3、MoGe、BEN2 或 utils3d 的 Python 源码包。模型推理和 SnapForm 所需的相机、点云、网格后处理均由 ONNX Runtime、NumPy 与内部实现完成。
|
||||
|
||||
Windows 当前发行环境提供 DirectML 与 CPU;`Auto` 优先使用 `DmlExecutionProvider`。显式指定不可用设备时会安全回退到 CPU。其他平台安装标准 ONNX Runtime,并按可用 provider 选择 CoreML、OpenVINO 或 CPU。
|
||||
Windows 当前发行环境提供 DirectML 与 CPU;`Auto` 优先使用 `DmlExecutionProvider`。显式指定不可用设备时会安全回退到 CPU。其他平台安装标准 ONNX Runtime,并按可用 provider 选择 CoreML、OpenVINO 或 CPU。macOS 的 CoreML Session 使用 `MLProgram` 模型格式与全部可用计算单元,允许动态输入 Shape;未被 CoreML 接管的节点继续由 CPU 执行。
|
||||
|
||||
`environment.json` 保存规范化后的声明,`manifest.json` 保存该声明与基础 Server packages 的 Hash。Python、packages、平台 packages、基础 Server packages 或 Environment Schema 改变后需要重新执行 `Install Environment`,不再维护手工 Manifest Revision。BlendJob 直接随 Extension 更新,不参与 Environment Hash。
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
6. Server 在 job 根目录写出 `mesh.npz`。NPZ 保存 camera-space `vertices`、`faces`、`uv`、
|
||||
`intrinsics`、`image_size` 与有效深度中位数 `reference_depth`。颜色层无论来自原图复制还是 BEN2,
|
||||
Server 都统一写为 `color.0001.*`。Blender 读取网格数据后额外导入为 `.color`,材质不复用源
|
||||
Empty 的 Image,Color Space 固定为 `AgX Base sRGB`。Server 不生成 `texture.png`,材质只在
|
||||
Empty 的 Image。颜色贴图会按当前场景的 View Transform 选择对应输入色彩空间:AgX、ACES 1.3、ACES 2.0、Filmic、Khronos PBR Neutral 分别使用 `AgX Base sRGB`、`ACES 1.3 sRGB`、`ACES 2.0 sRGB`、`Filmic sRGB`、`Khronos PBR Neutral sRGB`;无法匹配当前 View Transform 时回退 `AgX Base sRGB`;当前 Blender 连该选项也不支持时才安全回退 `sRGB`。 Server 不生成 `texture.png`,材质只在
|
||||
Blender 端创建。
|
||||
DA3 Surface 只保留 Geometry Nodes 后处理实际使用的 attributes:点域 `o_depth`、面域
|
||||
`o_confidence` 与面域 `o_depth_discontinuity`。Remove Background 已在 Server
|
||||
@@ -67,7 +67,7 @@
|
||||
`Remove Background`、`Shadeless`,以及分割线下方的 `Keep Original`。背景移除默认关闭;开启后 Server
|
||||
使用 BEN2,并在写出 Surface 前直接删除背景三角面,不再由 Geometry Nodes 的 Background 开关处理。
|
||||
该预删除同时适用于 DA3 与 MoGe-2;因此 MoGe-2 转换后即使关闭 `Mask`,BEN2 已删除的背景也不会恢复。
|
||||
Surface 材质的 Color Image Texture 始终使用 `.color`;Remove Background 只决定该层是否带 BEN2 Alpha,原 Empty 始终保持原图。颜色层使用 `AgX Base sRGB`。
|
||||
Surface 材质的 Color Image Texture 始终使用 `.color`;Remove Background 只决定该层是否带 BEN2 Alpha,原 Empty 始终保持原图。颜色贴图会按当前场景的 View Transform 选择对应输入色彩空间:AgX、ACES 1.3、ACES 2.0、Filmic、Khronos PBR Neutral 分别使用 `AgX Base sRGB`、`ACES 1.3 sRGB`、`ACES 2.0 sRGB`、`Filmic sRGB`、`Khronos PBR Neutral sRGB`;无法匹配当前 View Transform 时回退 `AgX Base sRGB`;当前 Blender 连该选项也不支持时才安全回退 `sRGB`。
|
||||
`Shadeless` 使用与 Depth Plane 相同的 `O Shadeless` 节点组,并继续通过 Alpha 与 Transparent BSDF 混合;普通材质的
|
||||
Bump Scale 按 `abs(object.scale.z) × camera scale_z` 设置,并在材质组内乘以固定基准 `0.001`,
|
||||
与透视表面实际深度尺度一致;它允许负数以反转凹凸方向。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "snapform"
|
||||
version = "0.12.12"
|
||||
version = "0.12.13"
|
||||
description = "Fast image-guided structure building tools for Blender"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.11"
|
||||
|
||||
@@ -24,7 +24,13 @@ MOGE_SURFACE_MATERIAL_NODE_GROUP_NAME = "O MoGe2 Surface Layer"
|
||||
DEPTH_PLANE_MATERIAL_NODE_GROUP_NAME = "O DA3 Depth Plane Layer"
|
||||
MOGE_MATERIAL_NODE_GROUP_NAME = "O MoGe2 Depth Plane Layer"
|
||||
SHADELESS_NODE_GROUP_NAME = "O Shadeless"
|
||||
MATERIAL_COLOR_SPACE = "AgX Base sRGB"
|
||||
VIEW_TRANSFORM_COLOR_SPACES = (
|
||||
("ACES 1.3", "ACES 1.3 sRGB"),
|
||||
("ACES 2.0", "ACES 2.0 sRGB"),
|
||||
("AgX", "AgX Base sRGB"),
|
||||
("Filmic", "Filmic sRGB"),
|
||||
("Khronos PBR Neutral", "Khronos PBR Neutral sRGB"),
|
||||
)
|
||||
DA3_PATCH_SIZE = 14
|
||||
DA3_MIN_PROCESS_RES = 252
|
||||
DA3_MAX_PROCESS_RES = 1008
|
||||
@@ -70,6 +76,36 @@ def is_image_empty(candidate):
|
||||
)
|
||||
|
||||
|
||||
def material_color_space_name(scene):
|
||||
view_settings = getattr(scene, "view_settings", None)
|
||||
view_transform = getattr(view_settings, "view_transform", "")
|
||||
normalized = str(view_transform).strip().casefold()
|
||||
for view_name, color_space in VIEW_TRANSFORM_COLOR_SPACES:
|
||||
if normalized.startswith(view_name.casefold()):
|
||||
return color_space
|
||||
return "AgX Base sRGB"
|
||||
|
||||
|
||||
def set_material_color_space(image, scene=None):
|
||||
if scene is None:
|
||||
scene = getattr(bpy.context, "scene", None)
|
||||
settings = image.colorspace_settings
|
||||
preferred = material_color_space_name(scene)
|
||||
original = settings.name
|
||||
for color_space in dict.fromkeys(
|
||||
(preferred, "AgX Base sRGB", "sRGB", original)
|
||||
):
|
||||
if not color_space:
|
||||
continue
|
||||
try:
|
||||
settings.name = color_space
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
if settings.name == color_space:
|
||||
return color_space
|
||||
return settings.name
|
||||
|
||||
|
||||
def is_animated_image(image):
|
||||
return getattr(image, "source", None) in {"MOVIE", "SEQUENCE"}
|
||||
|
||||
@@ -978,6 +1014,7 @@ def create_moge_material(
|
||||
frame_count=1,
|
||||
frame_start=1,
|
||||
use_cyclic=False,
|
||||
scene=None,
|
||||
):
|
||||
material = bpy.data.materials.new(name)
|
||||
material.use_nodes = True
|
||||
@@ -990,7 +1027,7 @@ def create_moge_material(
|
||||
uv.uv_map = "UVMap"
|
||||
uv.location = (-900.0, 200.0)
|
||||
color = nodes.new("ShaderNodeTexImage")
|
||||
color_image.colorspace_settings.name = MATERIAL_COLOR_SPACE
|
||||
set_material_color_space(color_image, scene)
|
||||
color.image = color_image
|
||||
color.interpolation = "Linear"
|
||||
color.extension = "EXTEND"
|
||||
@@ -1303,6 +1340,7 @@ def create_material(
|
||||
use_cyclic=None,
|
||||
surface_group_name=None,
|
||||
normal_image=None,
|
||||
scene=None,
|
||||
):
|
||||
material = bpy.data.materials.new(name)
|
||||
material.use_nodes = True
|
||||
@@ -1318,7 +1356,7 @@ def create_material(
|
||||
uv_map.uv_map = "UVMap"
|
||||
uv_map.location = (-1250.0, 350.0)
|
||||
material_image = color_image
|
||||
material_image.colorspace_settings.name = MATERIAL_COLOR_SPACE
|
||||
set_material_color_space(material_image, scene)
|
||||
color_texture.image = material_image
|
||||
color_texture.interpolation = "Linear"
|
||||
color_texture.extension = "EXTEND"
|
||||
@@ -1730,6 +1768,7 @@ def build_image_depth_plane(
|
||||
depth_frame_offset=texture_frame_offset,
|
||||
depth_frame_duration=texture_frame_count,
|
||||
use_cyclic=source_use_cyclic,
|
||||
scene=context.scene,
|
||||
)
|
||||
mesh.materials.append(material)
|
||||
add_depth_plane_processing(
|
||||
@@ -1834,6 +1873,7 @@ def build_depth_plane(
|
||||
depth_frame_offset=0,
|
||||
depth_frame_duration=frame_count,
|
||||
use_cyclic=source_use_cyclic,
|
||||
scene=context.scene,
|
||||
)
|
||||
mesh.materials.append(material)
|
||||
add_depth_plane_processing(
|
||||
@@ -1939,6 +1979,7 @@ def build_moge2_depth_plane(
|
||||
frame_count=len(depth),
|
||||
frame_start=source_frame_start,
|
||||
use_cyclic=source_use_cyclic,
|
||||
scene=context.scene,
|
||||
)
|
||||
mesh.materials.append(material)
|
||||
modifier = add_moge_depth_plane_processing(
|
||||
|
||||
@@ -456,6 +456,7 @@ def build_image_mesh(
|
||||
MOGE_SURFACE_MATERIAL_NODE_GROUP_NAME if moge2 else None
|
||||
),
|
||||
normal_image=normal_image,
|
||||
scene=context.scene,
|
||||
)
|
||||
mesh.materials.append(material)
|
||||
mesh_object["snapform_reference_depth"] = reference_depth
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
schema_version = "1.0.0"
|
||||
|
||||
id = "snapform"
|
||||
version = "0.12.12"
|
||||
version = "0.12.13"
|
||||
name = "SnapForm"
|
||||
tagline = "Build spatial structures from images, fast"
|
||||
maintainer = "OmooLab"
|
||||
|
||||
@@ -4,6 +4,12 @@ from contextlib import contextmanager
|
||||
from ..media import prepare_input
|
||||
|
||||
|
||||
def frame_progress_message(message, index, total):
|
||||
if total > 1:
|
||||
return f"{message} ({index + 1}/{total})"
|
||||
return message
|
||||
|
||||
|
||||
@contextmanager
|
||||
def input_frames(context, parameters):
|
||||
images, temporary_directory = prepare_input(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from ..media import copy_color_images
|
||||
from ..models import da3, geometry_adapter, moge2
|
||||
from ..outputs.depth import write_da3_depth, write_moge_depth
|
||||
from .common import input_frames
|
||||
from .common import frame_progress_message, input_frames
|
||||
from .foreground import write_optional
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ def run(context, parameters):
|
||||
images,
|
||||
progress_callback=lambda index, total: context.progress(
|
||||
0.15 + 0.65 * index / total,
|
||||
f"Estimating DA3 depth ({index + 1}/{total})",
|
||||
frame_progress_message("Estimating DA3 depth", index, total),
|
||||
),
|
||||
cancel_check=context.check_cancelled,
|
||||
)
|
||||
@@ -31,7 +31,7 @@ def run(context, parameters):
|
||||
include_points=False,
|
||||
progress_callback=lambda index, total: context.progress(
|
||||
0.15 + 0.65 * index / total,
|
||||
f"Estimating MoGe-2 depth ({index + 1}/{total})",
|
||||
frame_progress_message("Estimating MoGe-2 depth", index, total),
|
||||
),
|
||||
cancel_check=context.check_cancelled,
|
||||
)
|
||||
|
||||
@@ -2,17 +2,9 @@ from ..geometry import build_surface, foreground_mesh_mask
|
||||
from ..media import copy_color_images
|
||||
from ..models import geometry_adapter, moge2
|
||||
from ..outputs.surface import write_normal_texture, write_surface_archive
|
||||
from .common import input_frames
|
||||
from .common import frame_progress_message, input_frames
|
||||
from .foreground import write_optional
|
||||
|
||||
|
||||
def inference_progress_message(model_name, index, total):
|
||||
message = f"Estimating {model_name} geometry"
|
||||
if total > 1:
|
||||
message += f" ({index + 1}/{total})"
|
||||
return message
|
||||
|
||||
|
||||
def run(context, parameters):
|
||||
context.progress(0.02, "Preparing surface input")
|
||||
context.directory.mkdir(parents=True, exist_ok=True)
|
||||
@@ -30,7 +22,7 @@ def run(context, parameters):
|
||||
include_points=True,
|
||||
progress_callback=lambda index, total: context.progress(
|
||||
0.12 + 0.58 * index / total,
|
||||
inference_progress_message("MoGe-2", index, total),
|
||||
frame_progress_message("Estimating MoGe-2 geometry", index, total),
|
||||
),
|
||||
cancel_check=context.check_cancelled,
|
||||
)
|
||||
@@ -41,7 +33,7 @@ def run(context, parameters):
|
||||
images,
|
||||
progress_callback=lambda index, total: context.progress(
|
||||
0.12 + 0.58 * index / total,
|
||||
inference_progress_message("DA3", index, total),
|
||||
frame_progress_message("Estimating DA3 geometry", index, total),
|
||||
),
|
||||
cancel_check=context.check_cancelled,
|
||||
)
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
"""Shared ONNX Runtime session helpers."""
|
||||
|
||||
COREML_PROVIDER_OPTIONS = {
|
||||
"ModelFormat": "MLProgram",
|
||||
"MLComputeUnits": "ALL",
|
||||
"RequireStaticInputShapes": "0",
|
||||
"EnableOnSubgraphs": "0",
|
||||
}
|
||||
|
||||
|
||||
def select_providers(requested, available):
|
||||
available = set(available)
|
||||
@@ -40,6 +47,12 @@ def create_session(path, requested_device="auto"):
|
||||
requested_device,
|
||||
onnxruntime.get_available_providers(),
|
||||
)
|
||||
providers = [
|
||||
(provider, dict(COREML_PROVIDER_OPTIONS))
|
||||
if provider == "CoreMLExecutionProvider"
|
||||
else provider
|
||||
for provider in providers
|
||||
]
|
||||
return onnxruntime.InferenceSession(str(path), providers=providers)
|
||||
|
||||
|
||||
|
||||
@@ -42,6 +42,52 @@ class ImageDepthTest(unittest.TestCase):
|
||||
def setUpClass(cls):
|
||||
cls.image_depth = load_image_depth()
|
||||
|
||||
def test_material_color_space_matches_scene_view_transform(self):
|
||||
mappings = {
|
||||
"ACES 1.3": "ACES 1.3 sRGB",
|
||||
"ACES 2.0": "ACES 2.0 sRGB",
|
||||
"AgX": "AgX Base sRGB",
|
||||
"Filmic": "Filmic sRGB",
|
||||
"Khronos PBR Neutral": "Khronos PBR Neutral sRGB",
|
||||
"Standard": "AgX Base sRGB",
|
||||
}
|
||||
for view_transform, expected in mappings.items():
|
||||
with self.subTest(view_transform=view_transform):
|
||||
scene = SimpleNamespace(
|
||||
view_settings=SimpleNamespace(
|
||||
view_transform=view_transform,
|
||||
)
|
||||
)
|
||||
self.assertEqual(
|
||||
self.image_depth.material_color_space_name(scene),
|
||||
expected,
|
||||
)
|
||||
|
||||
def test_material_color_space_falls_back_when_unsupported(self):
|
||||
class ColorSpaceSettings:
|
||||
def __init__(self):
|
||||
self._name = "sRGB"
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@name.setter
|
||||
def name(self, value):
|
||||
if value != "sRGB":
|
||||
raise TypeError("unsupported color space")
|
||||
self._name = value
|
||||
|
||||
image = SimpleNamespace(colorspace_settings=ColorSpaceSettings())
|
||||
scene = SimpleNamespace(
|
||||
view_settings=SimpleNamespace(view_transform="ACES 2.0")
|
||||
)
|
||||
|
||||
result = self.image_depth.set_material_color_space(image, scene)
|
||||
|
||||
self.assertEqual(result, "sRGB")
|
||||
self.assertEqual(image.colorspace_settings.name, "sRGB")
|
||||
|
||||
def test_extracts_depth_and_uses_first_view(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
npz_path = Path(directory) / "results.npz"
|
||||
|
||||
@@ -76,6 +76,34 @@ class MogeOnnxTest(unittest.TestCase):
|
||||
["DmlExecutionProvider", "CPUExecutionProvider"],
|
||||
)
|
||||
|
||||
def test_coreml_session_uses_mlprogram(self):
|
||||
calls = []
|
||||
fake_runtime = ModuleType("onnxruntime")
|
||||
fake_runtime.get_available_providers = lambda: [
|
||||
"CoreMLExecutionProvider",
|
||||
"CPUExecutionProvider",
|
||||
]
|
||||
fake_runtime.InferenceSession = lambda path, providers: calls.append(
|
||||
(path, providers)
|
||||
)
|
||||
|
||||
with patch.dict(sys.modules, {"onnxruntime": fake_runtime}):
|
||||
self.moge.create_runtime_session("model.onnx")
|
||||
|
||||
self.assertEqual(calls[0][0], "model.onnx")
|
||||
self.assertEqual(
|
||||
calls[0][1][0],
|
||||
(
|
||||
"CoreMLExecutionProvider",
|
||||
{
|
||||
"ModelFormat": "MLProgram",
|
||||
"MLComputeUnits": "ALL",
|
||||
"RequireStaticInputShapes": "0",
|
||||
"EnableOnSubgraphs": "0",
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
def test_explicit_unavailable_provider_falls_back_to_cpu(self):
|
||||
providers = self.moge.select_providers(
|
||||
"cuda",
|
||||
|
||||
@@ -118,6 +118,7 @@ class WorkerTest(unittest.TestCase):
|
||||
cls.surface_outputs = load_server_module("outputs.surface")
|
||||
cls.debug_da3_outputs = load_server_module("outputs.debug_da3")
|
||||
cls.debug_moge2_outputs = load_server_module("outputs.debug_moge2")
|
||||
cls.common_job = load_server_module("jobs.common")
|
||||
cls.depth_job = load_server_module("jobs.depth")
|
||||
cls.surface_job = load_server_module("jobs.surface")
|
||||
cls.debug_da3_job = load_server_module("jobs.debug_da3")
|
||||
@@ -127,20 +128,39 @@ class WorkerTest(unittest.TestCase):
|
||||
cls.foreground_job = load_server_module("jobs.foreground")
|
||||
cls.download = load_server_module("model_download")
|
||||
|
||||
def test_surface_inference_progress_omits_single_frame_count(self):
|
||||
def test_inference_progress_omits_single_frame_count(self):
|
||||
self.assertEqual(
|
||||
self.surface_job.inference_progress_message("MoGe-2", 0, 1),
|
||||
self.common_job.frame_progress_message(
|
||||
"Estimating MoGe-2 geometry", 0, 1
|
||||
),
|
||||
"Estimating MoGe-2 geometry",
|
||||
)
|
||||
self.assertEqual(
|
||||
self.surface_job.inference_progress_message("DA3", 0, 1),
|
||||
self.common_job.frame_progress_message(
|
||||
"Estimating DA3 geometry", 0, 1
|
||||
),
|
||||
"Estimating DA3 geometry",
|
||||
)
|
||||
self.assertEqual(
|
||||
self.surface_job.inference_progress_message("DA3", 1, 3),
|
||||
self.common_job.frame_progress_message(
|
||||
"Estimating DA3 geometry", 1, 3
|
||||
),
|
||||
"Estimating DA3 geometry (2/3)",
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
self.common_job.frame_progress_message(
|
||||
"Estimating MoGe-2 depth", 0, 1
|
||||
),
|
||||
"Estimating MoGe-2 depth",
|
||||
)
|
||||
self.assertEqual(
|
||||
self.common_job.frame_progress_message(
|
||||
"Estimating DA3 depth", 0, 1
|
||||
),
|
||||
"Estimating DA3 depth",
|
||||
)
|
||||
|
||||
def test_surface_has_one_model_independent_builder(self):
|
||||
self.assertTrue(callable(self.surface.build_surface))
|
||||
self.assertTrue(callable(self.surface_outputs.write_surface_archive))
|
||||
|
||||
@@ -13,7 +13,7 @@ sys.path.insert(
|
||||
|
||||
from snapform.blender.depth_plane import (
|
||||
DEPTH_PLANE_MATERIAL_NODE_GROUP_NAME,
|
||||
MATERIAL_COLOR_SPACE,
|
||||
material_color_space_name,
|
||||
SHADELESS_NODE_GROUP_NAME,
|
||||
SURFACE_MATERIAL_NODE_GROUP_NAME,
|
||||
add_depth_plane_processing,
|
||||
@@ -273,7 +273,9 @@ def main():
|
||||
"Shadow or Reflection?": "MAXIMUM",
|
||||
"Shadow, Reflect or Refract?": "MAXIMUM",
|
||||
}
|
||||
assert foreground.colorspace_settings.name == MATERIAL_COLOR_SPACE
|
||||
assert foreground.colorspace_settings.name == material_color_space_name(
|
||||
bpy.context.scene
|
||||
)
|
||||
surface_material = create_material("Surface", foreground, 1.0)
|
||||
surface_group = bpy.data.node_groups[SURFACE_MATERIAL_NODE_GROUP_NAME]
|
||||
surface_group_nodes = [
|
||||
|
||||
@@ -14,6 +14,7 @@ sys.path.insert(
|
||||
)
|
||||
|
||||
from snapform.blender.depth_plane import MOGE_SURFACE_MATERIAL_NODE_GROUP_NAME
|
||||
from snapform.blender.depth_plane import material_color_space_name
|
||||
from snapform.blender.surface import (
|
||||
MOGE_NODE_GROUP_NAME,
|
||||
build_image_mesh,
|
||||
@@ -198,7 +199,9 @@ def main():
|
||||
if node.bl_idname == "ShaderNodeTexImage" and node.image == foreground
|
||||
)
|
||||
assert color_texture.image.name == "MoGe Surface Test.color"
|
||||
assert color_texture.image.colorspace_settings.name == "AgX Base sRGB"
|
||||
assert color_texture.image.colorspace_settings.name == material_color_space_name(
|
||||
bpy.context.scene
|
||||
)
|
||||
normal_texture = next(
|
||||
node for node in surface.data.materials[0].node_tree.nodes
|
||||
if node.bl_idname == "ShaderNodeTexImage"
|
||||
|
||||
Reference in New Issue
Block a user