diff --git a/README.md b/README.md index 5a62c42..54b80ff 100644 --- a/README.md +++ b/README.md @@ -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`。 ### 视频与序列帧 diff --git a/docs/depth-plane.md b/docs/depth-plane.md index 750393a..e4ec7da 100644 --- a/docs/depth-plane.md +++ b/docs/depth-plane.md @@ -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` 数值, diff --git a/docs/development.md b/docs/development.md index 2e35b11..f6ae966 100644 --- a/docs/development.md +++ b/docs/development.md @@ -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 diff --git a/docs/features.md b/docs/features.md index a191f35..92e875f 100644 --- a/docs/features.md +++ b/docs/features.md @@ -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 是正式功能 diff --git a/docs/server.md b/docs/server.md index 990a4f2..8b3168a 100644 --- a/docs/server.md +++ b/docs/server.md @@ -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。 diff --git a/docs/surface.md b/docs/surface.md index eda5d67..c866007 100644 --- a/docs/surface.md +++ b/docs/surface.md @@ -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`, 与透视表面实际深度尺度一致;它允许负数以反转凹凸方向。 diff --git a/pyproject.toml b/pyproject.toml index 5c1eb8c..835e7a7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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" diff --git a/src/snapform/blender/depth_plane.py b/src/snapform/blender/depth_plane.py index 80af6e7..9fedbb4 100644 --- a/src/snapform/blender/depth_plane.py +++ b/src/snapform/blender/depth_plane.py @@ -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( diff --git a/src/snapform/blender/surface.py b/src/snapform/blender/surface.py index 29c5e12..9c13cee 100644 --- a/src/snapform/blender/surface.py +++ b/src/snapform/blender/surface.py @@ -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 diff --git a/src/snapform/blender_manifest.toml b/src/snapform/blender_manifest.toml index 04831d5..e446ecc 100644 --- a/src/snapform/blender_manifest.toml +++ b/src/snapform/blender_manifest.toml @@ -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" diff --git a/src/snapform/server/jobs/common.py b/src/snapform/server/jobs/common.py index c7693ea..b658c1d 100644 --- a/src/snapform/server/jobs/common.py +++ b/src/snapform/server/jobs/common.py @@ -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( diff --git a/src/snapform/server/jobs/depth.py b/src/snapform/server/jobs/depth.py index 528a3d9..547c18f 100644 --- a/src/snapform/server/jobs/depth.py +++ b/src/snapform/server/jobs/depth.py @@ -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, ) diff --git a/src/snapform/server/jobs/surface.py b/src/snapform/server/jobs/surface.py index 8571078..0da8a4b 100644 --- a/src/snapform/server/jobs/surface.py +++ b/src/snapform/server/jobs/surface.py @@ -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, ) diff --git a/src/snapform/server/models/onnx_runtime.py b/src/snapform/server/models/onnx_runtime.py index 109b8c6..d699202 100644 --- a/src/snapform/server/models/onnx_runtime.py +++ b/src/snapform/server/models/onnx_runtime.py @@ -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) diff --git a/tests/test_image_depth.py b/tests/test_image_depth.py index 76e274d..e40150d 100644 --- a/tests/test_image_depth.py +++ b/tests/test_image_depth.py @@ -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" diff --git a/tests/test_moge_onnx.py b/tests/test_moge_onnx.py index b1d28a5..17aba88 100644 --- a/tests/test_moge_onnx.py +++ b/tests/test_moge_onnx.py @@ -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", diff --git a/tests/test_worker.py b/tests/test_worker.py index deb9500..121764f 100644 --- a/tests/test_worker.py +++ b/tests/test_worker.py @@ -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)) diff --git a/tools/validate_depth_plane_asset.py b/tools/validate_depth_plane_asset.py index e53673b..de5ab8a 100644 --- a/tools/validate_depth_plane_asset.py +++ b/tools/validate_depth_plane_asset.py @@ -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 = [ diff --git a/tools/validate_moge_surface.py b/tools/validate_moge_surface.py index 9272280..ccde3c6 100644 --- a/tools/validate_moge_surface.py +++ b/tools/validate_moge_surface.py @@ -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" diff --git a/uv.lock b/uv.lock index d8a403b..b79a57e 100644 --- a/uv.lock +++ b/uv.lock @@ -305,7 +305,7 @@ wheels = [ [[package]] name = "snapform" -version = "0.12.11" +version = "0.12.13" source = { virtual = "." } dependencies = [ { name = "blendjob" },