Implement Omoo DA3 functionality with property group, runtime, worker, and status bar
- Added properties for Omoo DA3 settings in properties.py, including input path, video frames, model selection, and export format. - Created runtime management in runtime.py, handling model downloads, job paths, and command execution for reconstruction. - Developed status bar functionality in status_bar.py to display job progress and status. - Implemented worker.py for image and video processing, including frame extraction and model inference. - Added comprehensive unit tests covering addon registration, model handling, export processes, and UI interactions. - Included a lock file for dependency management with uv.
This commit is contained in:
322
tests/test_addon.py
Normal file
322
tests/test_addon.py
Normal file
@@ -0,0 +1,322 @@
|
||||
import importlib
|
||||
import inspect
|
||||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from tempfile import TemporaryDirectory
|
||||
from types import ModuleType, SimpleNamespace
|
||||
from unittest.mock import patch
|
||||
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||||
SOURCE_ROOT = PROJECT_ROOT / "src"
|
||||
|
||||
|
||||
class AddonTest(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.remove_modules()
|
||||
fake_bpy = ModuleType("bpy")
|
||||
fake_bpy.props = SimpleNamespace(
|
||||
BoolProperty=lambda **options: options,
|
||||
EnumProperty=lambda **options: options,
|
||||
FloatProperty=lambda **options: options,
|
||||
IntProperty=lambda **options: options,
|
||||
PointerProperty=lambda **options: options,
|
||||
StringProperty=lambda **options: options,
|
||||
)
|
||||
fake_bpy.types = SimpleNamespace(
|
||||
AddonPreferences=object,
|
||||
Operator=object,
|
||||
Panel=object,
|
||||
PropertyGroup=object,
|
||||
Scene=type("Scene", (), {}),
|
||||
STATUSBAR_HT_header=SimpleNamespace(
|
||||
prepend=lambda draw: cls.status_bar_draws.append(draw),
|
||||
remove=lambda draw: cls.status_bar_draws.remove(draw),
|
||||
),
|
||||
)
|
||||
cls.registered = []
|
||||
cls.status_bar_draws = []
|
||||
fake_bpy.utils = SimpleNamespace(
|
||||
register_class=cls.registered.append,
|
||||
unregister_class=cls.registered.remove,
|
||||
)
|
||||
fake_bpy.context = SimpleNamespace(
|
||||
preferences=SimpleNamespace(addons={}),
|
||||
)
|
||||
fake_bpy.app = SimpleNamespace(online_access=True)
|
||||
fake_bpy.path = SimpleNamespace(abspath=lambda path: path)
|
||||
sys.modules["bpy"] = fake_bpy
|
||||
sys.path.insert(0, str(SOURCE_ROOT))
|
||||
cls.addon = importlib.import_module("omoo_da3")
|
||||
cls.fake_bpy = fake_bpy
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
sys.path.remove(str(SOURCE_ROOT))
|
||||
sys.modules.pop("bpy", None)
|
||||
cls.remove_modules()
|
||||
|
||||
@staticmethod
|
||||
def remove_modules():
|
||||
for module_name in tuple(sys.modules):
|
||||
if module_name == "omoo_da3" or module_name.startswith("omoo_da3."):
|
||||
sys.modules.pop(module_name)
|
||||
|
||||
def tearDown(self):
|
||||
self.registered.clear()
|
||||
self.status_bar_draws.clear()
|
||||
self.addon.operators._active_job = None
|
||||
if hasattr(self.fake_bpy.types.Scene, "omoo_da3_settings"):
|
||||
del self.fake_bpy.types.Scene.omoo_da3_settings
|
||||
|
||||
def test_uses_extension_manifest_instead_of_legacy_bl_info(self):
|
||||
self.assertFalse(hasattr(self.addon, "bl_info"))
|
||||
|
||||
def test_registers_and_unregisters_extension_types(self):
|
||||
self.addon.register()
|
||||
|
||||
self.assertEqual(self.registered, list(self.addon.CLASSES))
|
||||
self.assertTrue(
|
||||
hasattr(self.fake_bpy.types.Scene, "omoo_da3_settings")
|
||||
)
|
||||
self.assertEqual(
|
||||
self.status_bar_draws,
|
||||
[self.addon.draw_status_bar],
|
||||
)
|
||||
|
||||
self.addon.unregister()
|
||||
|
||||
self.assertEqual(self.registered, [])
|
||||
self.assertFalse(
|
||||
hasattr(self.fake_bpy.types.Scene, "omoo_da3_settings")
|
||||
)
|
||||
self.assertEqual(self.status_bar_draws, [])
|
||||
|
||||
def test_preferences_use_package_identifier(self):
|
||||
self.assertEqual(
|
||||
self.addon.OMOO_DA3_preferences.bl_idname,
|
||||
"omoo_da3",
|
||||
)
|
||||
|
||||
def test_default_storage_is_shared_in_user_home(self):
|
||||
runtime = importlib.import_module("omoo_da3.runtime")
|
||||
|
||||
self.assertEqual(runtime.DEFAULT_STORAGE_ROOT.name, ".omoo_da3")
|
||||
self.assertEqual(
|
||||
self.addon.OMOO_DA3_preferences.__annotations__["storage_root"][
|
||||
"default"
|
||||
],
|
||||
str(runtime.DEFAULT_STORAGE_ROOT),
|
||||
)
|
||||
|
||||
def test_reconstruct_command_preserves_da3_parameter_names(self):
|
||||
runtime = importlib.import_module("omoo_da3.runtime")
|
||||
settings = SimpleNamespace(
|
||||
input_path="input.png",
|
||||
model="GIANT_1_1",
|
||||
gs_model="NESTED_GIANT_LARGE_1_1",
|
||||
process_res=504,
|
||||
process_res_method="upper_bound_resize",
|
||||
conf_thresh_percentile=40.0,
|
||||
num_max_points=1_000_000,
|
||||
show_cameras=True,
|
||||
gs_views_interval=1,
|
||||
video_frames=16,
|
||||
device="CUDA",
|
||||
use_ray_pose=False,
|
||||
ref_view_strategy="saddle_balanced",
|
||||
)
|
||||
with patch.object(
|
||||
runtime,
|
||||
"runtime_python",
|
||||
return_value=Path("python"),
|
||||
), patch.object(
|
||||
runtime,
|
||||
"model_directory",
|
||||
return_value=Path("model"),
|
||||
):
|
||||
command = runtime.reconstruct_command(
|
||||
settings,
|
||||
"gs_ply",
|
||||
Path("status.json"),
|
||||
Path("result"),
|
||||
)
|
||||
|
||||
self.assertIn("--export-format", command)
|
||||
self.assertIn("gs_ply", command)
|
||||
for parameter in (
|
||||
"--process-res",
|
||||
"--process-res-method",
|
||||
"--conf-thresh-percentile",
|
||||
"--num-max-points",
|
||||
"--show-cameras",
|
||||
"--gs-views-interval",
|
||||
"--use-ray-pose",
|
||||
"--ref-view-strategy",
|
||||
):
|
||||
with self.subTest(parameter=parameter):
|
||||
self.assertIn(parameter, command)
|
||||
|
||||
def test_export_format_property_includes_all_da3_formats(self):
|
||||
export_format = self.addon.OMOO_DA3_PG_settings.__annotations__[
|
||||
"export_format"
|
||||
]
|
||||
identifiers = [item[0] for item in export_format["items"]]
|
||||
|
||||
self.assertEqual(
|
||||
identifiers,
|
||||
["glb", "gs_ply", "depth_vis", "mini_npz"],
|
||||
)
|
||||
self.assertEqual(export_format["default"], "glb")
|
||||
|
||||
def test_gs_model_property_only_includes_gaussian_models(self):
|
||||
gs_model = self.addon.OMOO_DA3_PG_settings.__annotations__[
|
||||
"gs_model"
|
||||
]
|
||||
identifiers = [item[0] for item in gs_model["items"]]
|
||||
|
||||
self.assertEqual(
|
||||
identifiers,
|
||||
["GIANT_1_1", "NESTED_GIANT_LARGE_1_1"],
|
||||
)
|
||||
|
||||
def test_debug_panel_is_closed_by_default(self):
|
||||
self.assertIn(
|
||||
"DEFAULT_CLOSED",
|
||||
self.addon.OMOO_DA3_PT_debug.bl_options,
|
||||
)
|
||||
|
||||
def test_debug_panel_uses_one_export_button_and_dynamic_settings(self):
|
||||
draw_source = inspect.getsource(
|
||||
self.addon.OMOO_DA3_PT_debug.draw
|
||||
)
|
||||
|
||||
self.assertEqual(draw_source.count("layout.operator("), 1)
|
||||
self.assertIn('settings.export_format == "glb"', draw_source)
|
||||
self.assertIn('settings.export_format == "gs_ply"', draw_source)
|
||||
self.assertIn('"gs_model" if settings.export_format', draw_source)
|
||||
self.assertIn("model_row.prop(settings, model_property)", draw_source)
|
||||
self.assertIn('model_row.label(text="", icon="CHECKMARK")', draw_source)
|
||||
self.assertIn('model_row.label(text="", icon="ERROR")', draw_source)
|
||||
self.assertNotIn('text="Ready"', draw_source)
|
||||
self.assertNotIn('text="Missing"', draw_source)
|
||||
self.assertNotIn("License:", draw_source)
|
||||
self.assertIn('operator.job_type = "RECONSTRUCT"', draw_source)
|
||||
|
||||
def test_runtime_models_panel_assigns_each_download_target(self):
|
||||
draw_source = inspect.getsource(
|
||||
self.addon.OMOO_DA3_PT_runtime_models.draw
|
||||
)
|
||||
model_source = inspect.getsource(
|
||||
self.addon.OMOO_DA3_PT_runtime_models._draw_model
|
||||
)
|
||||
|
||||
self.assertIn("for model in MODELS.values()", draw_source)
|
||||
self.assertIn("operator.model_key = model.key", model_source)
|
||||
self.assertNotIn("Isolated Runtime ready", draw_source)
|
||||
self.assertNotIn("License:", model_source)
|
||||
self.assertEqual(
|
||||
self.addon.OMOO_DA3_PT_runtime_models.bl_label,
|
||||
"Setup",
|
||||
)
|
||||
|
||||
def test_completed_export_opens_job_directory(self):
|
||||
calls = []
|
||||
self.fake_bpy.ops = SimpleNamespace(
|
||||
wm=SimpleNamespace(
|
||||
path_open=lambda **options: calls.append(options) or {"FINISHED"},
|
||||
),
|
||||
)
|
||||
with TemporaryDirectory() as directory:
|
||||
self.addon.operators.open_job_directory(directory)
|
||||
|
||||
self.assertEqual(len(calls), 1)
|
||||
self.assertIn("filepath", calls[0])
|
||||
|
||||
def test_status_bar_draws_progress_and_cancel_button(self):
|
||||
progress_calls = []
|
||||
operator_calls = []
|
||||
|
||||
class Layout:
|
||||
ui_units_x = 0.0
|
||||
|
||||
def row(self, align=False):
|
||||
return self
|
||||
|
||||
def label(self, **_options):
|
||||
return None
|
||||
|
||||
def progress(self, **options):
|
||||
progress_calls.append(options)
|
||||
|
||||
def operator(self, operator, **options):
|
||||
operator_calls.append((operator, options))
|
||||
|
||||
self.addon.operators._active_job = SimpleNamespace()
|
||||
context = SimpleNamespace(
|
||||
scene=SimpleNamespace(
|
||||
omoo_da3_settings=SimpleNamespace(
|
||||
status="Processing",
|
||||
progress=0.25,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
self.addon.draw_status_bar(
|
||||
SimpleNamespace(layout=Layout()),
|
||||
context,
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
progress_calls,
|
||||
[{"factor": 0.25, "type": "BAR", "text": "25%"}],
|
||||
)
|
||||
self.assertEqual(
|
||||
operator_calls,
|
||||
[("omoo_da3.cancel_job", {"text": "", "icon": "X"})],
|
||||
)
|
||||
|
||||
def test_ui_redraw_targets_status_bar_and_view_3d_areas(self):
|
||||
redrawn = []
|
||||
areas = (
|
||||
SimpleNamespace(
|
||||
type="STATUSBAR",
|
||||
tag_redraw=lambda: redrawn.append("status"),
|
||||
),
|
||||
SimpleNamespace(
|
||||
type="VIEW_3D",
|
||||
tag_redraw=lambda: redrawn.append("view"),
|
||||
),
|
||||
)
|
||||
context = SimpleNamespace(
|
||||
window_manager=SimpleNamespace(
|
||||
windows=(
|
||||
SimpleNamespace(screen=SimpleNamespace(areas=areas)),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
self.addon.status_bar.redraw_ui(context)
|
||||
|
||||
self.assertEqual(redrawn, ["status", "view"])
|
||||
|
||||
def test_forced_ui_redraw_swaps_window_immediately(self):
|
||||
calls = []
|
||||
self.fake_bpy.ops = SimpleNamespace(
|
||||
wm=SimpleNamespace(
|
||||
redraw_timer=lambda **options: calls.append(options),
|
||||
),
|
||||
)
|
||||
context = SimpleNamespace(
|
||||
window=SimpleNamespace(),
|
||||
window_manager=SimpleNamespace(windows=()),
|
||||
)
|
||||
|
||||
self.addon.status_bar.redraw_ui(context, force=True)
|
||||
|
||||
self.assertEqual(
|
||||
calls,
|
||||
[{"type": "DRAW_WIN_SWAP", "iterations": 1}],
|
||||
)
|
||||
63
tests/test_bootstrap_runtime.py
Normal file
63
tests/test_bootstrap_runtime.py
Normal file
@@ -0,0 +1,63 @@
|
||||
import importlib.util
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import patch
|
||||
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def load_bootstrap_runtime():
|
||||
specification = importlib.util.spec_from_file_location(
|
||||
"omoo_da3_bootstrap_runtime",
|
||||
PROJECT_ROOT / "src" / "omoo_da3" / "bootstrap_runtime.py",
|
||||
)
|
||||
module = importlib.util.module_from_spec(specification)
|
||||
specification.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
class BootstrapRuntimeTest(unittest.TestCase):
|
||||
def test_installs_da3_and_non_compiled_gsplat(self):
|
||||
bootstrap = load_bootstrap_runtime()
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
runtime_dir = root / "runtime"
|
||||
runtime_dir.mkdir()
|
||||
args = SimpleNamespace(
|
||||
uv="uv",
|
||||
runtime_dir=str(runtime_dir),
|
||||
backend="cu128",
|
||||
da3_commit="test-commit",
|
||||
status=str(root / "status.json"),
|
||||
)
|
||||
with patch.object(bootstrap, "run") as mocked_run:
|
||||
bootstrap.install(args)
|
||||
|
||||
commands = [call.args[0] for call in mocked_run.call_args_list]
|
||||
da3_command = next(
|
||||
command
|
||||
for command in commands
|
||||
if any("depth-anything-3 @" in str(value) for value in command)
|
||||
)
|
||||
self.assertIn(
|
||||
f"addict=={bootstrap.ADDICT_VERSION}",
|
||||
da3_command,
|
||||
)
|
||||
gsplat_call = next(
|
||||
call
|
||||
for call in mocked_run.call_args_list
|
||||
if any("gsplat @" in str(value) for value in call.args[0])
|
||||
)
|
||||
gsplat_command = gsplat_call.args[0]
|
||||
self.assertIn("--no-build-isolation", gsplat_command)
|
||||
self.assertTrue(
|
||||
any(bootstrap.GSPLAT_COMMIT in str(value) for value in gsplat_command)
|
||||
)
|
||||
self.assertEqual(gsplat_call.args[1], {"BUILD_NO_CUDA": "1"})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
25
tests/test_manifest.py
Normal file
25
tests/test_manifest.py
Normal file
@@ -0,0 +1,25 @@
|
||||
import tomllib
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||||
MANIFEST_PATH = PROJECT_ROOT / "src" / "omoo_da3" / "blender_manifest.toml"
|
||||
|
||||
|
||||
class ManifestTest(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.manifest = tomllib.loads(MANIFEST_PATH.read_text(encoding="utf-8"))
|
||||
|
||||
def test_declares_modern_extension_metadata(self):
|
||||
self.assertEqual(self.manifest["schema_version"], "1.0.0")
|
||||
self.assertEqual(self.manifest["id"], "omoo_da3")
|
||||
self.assertEqual(self.manifest["type"], "add-on")
|
||||
self.assertNotIn("bl_info", self.manifest)
|
||||
|
||||
def test_declares_required_runtime_permissions(self):
|
||||
self.assertEqual(
|
||||
set(self.manifest["permissions"]),
|
||||
{"network", "files"},
|
||||
)
|
||||
45
tests/test_models.py
Normal file
45
tests/test_models.py
Normal file
@@ -0,0 +1,45 @@
|
||||
import importlib.util
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def load_module(name, relative_path):
|
||||
specification = importlib.util.spec_from_file_location(
|
||||
name,
|
||||
PROJECT_ROOT / relative_path,
|
||||
)
|
||||
module = importlib.util.module_from_spec(specification)
|
||||
specification.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
class ModelTest(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.models = load_module(
|
||||
"omoo_da3_models",
|
||||
"src/omoo_da3/models.py",
|
||||
)
|
||||
|
||||
def test_base_model_is_default_friendly_apache_model(self):
|
||||
model = self.models.get_model("BASE")
|
||||
|
||||
self.assertEqual(model.repository, "depth-anything/DA3-BASE")
|
||||
self.assertEqual(model.license_name, "Apache 2.0")
|
||||
|
||||
def test_large_model_exposes_noncommercial_license(self):
|
||||
model = self.models.get_model("LARGE_1_1")
|
||||
|
||||
self.assertEqual(model.license_name, "CC BY-NC 4.0")
|
||||
|
||||
def test_giant_models_expose_gaussian_head(self):
|
||||
for model_key in ("GIANT_1_1", "NESTED_GIANT_LARGE_1_1"):
|
||||
with self.subTest(model_key=model_key):
|
||||
self.assertTrue(self.models.get_model(model_key).supports_gs)
|
||||
|
||||
def test_unknown_model_is_rejected(self):
|
||||
with self.assertRaisesRegex(ValueError, "Unknown DA3 model"):
|
||||
self.models.get_model("UNKNOWN")
|
||||
39
tests/test_pack.py
Normal file
39
tests/test_pack.py
Normal file
@@ -0,0 +1,39 @@
|
||||
import importlib.util
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from zipfile import ZipFile
|
||||
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def load_pack():
|
||||
specification = importlib.util.spec_from_file_location(
|
||||
"omoo_da3_pack",
|
||||
PROJECT_ROOT / "pack.py",
|
||||
)
|
||||
module = importlib.util.module_from_spec(specification)
|
||||
specification.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
class PackTest(unittest.TestCase):
|
||||
def test_builds_extension_root_layout(self):
|
||||
pack = load_pack()
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
original_dist = pack.DIST_DIR
|
||||
pack.DIST_DIR = Path(directory)
|
||||
try:
|
||||
output_path = pack.build_extension()
|
||||
finally:
|
||||
pack.DIST_DIR = original_dist
|
||||
|
||||
with ZipFile(output_path) as archive:
|
||||
names = set(archive.namelist())
|
||||
|
||||
self.assertIn("__init__.py", names)
|
||||
self.assertIn("blender_manifest.toml", names)
|
||||
self.assertIn("worker.py", names)
|
||||
self.assertNotIn("importer.py", names)
|
||||
self.assertNotIn("omoo_da3/__init__.py", names)
|
||||
194
tests/test_worker.py
Normal file
194
tests/test_worker.py
Normal file
@@ -0,0 +1,194 @@
|
||||
import importlib.util
|
||||
import json
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from contextlib import nullcontext
|
||||
from pathlib import Path
|
||||
from types import ModuleType, SimpleNamespace
|
||||
from unittest.mock import patch
|
||||
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def load_worker():
|
||||
specification = importlib.util.spec_from_file_location(
|
||||
"omoo_da3_worker",
|
||||
PROJECT_ROOT / "src" / "omoo_da3" / "worker.py",
|
||||
)
|
||||
module = importlib.util.module_from_spec(specification)
|
||||
specification.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
class WorkerTest(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.worker = load_worker()
|
||||
|
||||
def test_collect_images_is_sorted_and_filters_suffixes(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
(root / "b.PNG").touch()
|
||||
(root / "a.jpg").touch()
|
||||
(root / "notes.txt").touch()
|
||||
|
||||
images = self.worker.collect_images(root)
|
||||
|
||||
self.assertEqual([path.name for path in images], ["a.jpg", "b.PNG"])
|
||||
|
||||
def test_prepares_a_single_image_file(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
image_path = Path(directory) / "input.png"
|
||||
image_path.touch()
|
||||
|
||||
images, temporary_directory = self.worker.prepare_input(
|
||||
image_path,
|
||||
16,
|
||||
directory,
|
||||
)
|
||||
|
||||
self.assertEqual(images, [image_path])
|
||||
self.assertIsNone(temporary_directory)
|
||||
|
||||
def test_rejects_unsupported_input_file(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
input_path = Path(directory) / "input.txt"
|
||||
input_path.touch()
|
||||
|
||||
with self.assertRaisesRegex(RuntimeError, "Unsupported input type"):
|
||||
self.worker.prepare_input(input_path, 16, directory)
|
||||
|
||||
def test_uses_official_da3_export_parameters_for_all_four_formats(self):
|
||||
inference_options = []
|
||||
export_calls = []
|
||||
|
||||
class FakeModel:
|
||||
@classmethod
|
||||
def from_pretrained(cls, model_dir):
|
||||
if model_dir != "model":
|
||||
raise AssertionError(f"Unexpected model directory: {model_dir}")
|
||||
return cls()
|
||||
|
||||
def to(self, device):
|
||||
if device.type != "cpu":
|
||||
raise AssertionError(f"Unexpected device: {device.type}")
|
||||
return self
|
||||
|
||||
def eval(self):
|
||||
return None
|
||||
|
||||
def inference(self, images, **options):
|
||||
inference_options.append((images, options))
|
||||
return SimpleNamespace()
|
||||
|
||||
def fake_export(prediction, export_format, export_dir, **options):
|
||||
export_calls.append((prediction, export_format, export_dir, options))
|
||||
output_directory = Path(export_dir)
|
||||
if export_format == "glb":
|
||||
(output_directory / "scene.glb").touch()
|
||||
elif export_format == "gs_ply":
|
||||
ply_directory = output_directory / "gs_ply"
|
||||
ply_directory.mkdir()
|
||||
(ply_directory / "0000.ply").touch()
|
||||
elif export_format == "depth_vis":
|
||||
depth_directory = output_directory / "depth_vis"
|
||||
depth_directory.mkdir()
|
||||
(depth_directory / "0000.jpg").touch()
|
||||
else:
|
||||
mini_directory = output_directory / "exports" / "mini_npz"
|
||||
mini_directory.mkdir(parents=True)
|
||||
(mini_directory / "results.npz").touch()
|
||||
|
||||
fake_torch = ModuleType("torch")
|
||||
fake_torch.device = lambda device_type: SimpleNamespace(
|
||||
type=device_type,
|
||||
)
|
||||
fake_torch.inference_mode = nullcontext
|
||||
fake_api = ModuleType("depth_anything_3.api")
|
||||
fake_api.DepthAnything3 = FakeModel
|
||||
fake_export_module = ModuleType("depth_anything_3.utils.export")
|
||||
fake_export_module.export = fake_export
|
||||
fake_utils = ModuleType("depth_anything_3.utils")
|
||||
fake_utils.__path__ = []
|
||||
fake_package = ModuleType("depth_anything_3")
|
||||
fake_package.__path__ = []
|
||||
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
input_path = root / "input.png"
|
||||
input_path.touch()
|
||||
with patch.dict(
|
||||
sys.modules,
|
||||
{
|
||||
"torch": fake_torch,
|
||||
"depth_anything_3": fake_package,
|
||||
"depth_anything_3.api": fake_api,
|
||||
"depth_anything_3.utils": fake_utils,
|
||||
"depth_anything_3.utils.export": fake_export_module,
|
||||
},
|
||||
):
|
||||
for export_format in self.worker.EXPORT_FORMATS:
|
||||
args = SimpleNamespace(
|
||||
input=str(input_path),
|
||||
video_frames=16,
|
||||
output_dir=str(root / export_format),
|
||||
export_format=export_format,
|
||||
status=str(root / f"{export_format}.json"),
|
||||
device="cpu",
|
||||
model_dir="model",
|
||||
process_res=518,
|
||||
process_res_method="lower_bound_resize",
|
||||
use_ray_pose=1,
|
||||
ref_view_strategy="middle",
|
||||
conf_thresh_percentile=35.0,
|
||||
num_max_points=12345,
|
||||
show_cameras=0,
|
||||
gs_views_interval=2,
|
||||
)
|
||||
self.worker.reconstruct(args)
|
||||
status = json.loads(
|
||||
Path(args.status).read_text(encoding="utf-8")
|
||||
)
|
||||
self.assertEqual(status["progress"], 1.0)
|
||||
self.assertIn(export_format, status["message"])
|
||||
|
||||
self.assertEqual(len(inference_options), 4)
|
||||
self.assertEqual(len(export_calls), 4)
|
||||
for images, options in inference_options:
|
||||
self.assertEqual(images, [str(input_path)])
|
||||
self.assertEqual(options["process_res"], 518)
|
||||
self.assertEqual(
|
||||
options["process_res_method"],
|
||||
"lower_bound_resize",
|
||||
)
|
||||
self.assertTrue(options["use_ray_pose"])
|
||||
self.assertEqual(options["ref_view_strategy"], "middle")
|
||||
for _prediction, export_format, _export_dir, options in export_calls:
|
||||
if export_format == "glb":
|
||||
self.assertEqual(
|
||||
options,
|
||||
{
|
||||
"glb": {
|
||||
"conf_thresh_percentile": 35.0,
|
||||
"num_max_points": 12345,
|
||||
"show_cameras": False,
|
||||
"export_depth_vis": False,
|
||||
}
|
||||
},
|
||||
)
|
||||
elif export_format == "gs_ply":
|
||||
self.assertEqual(
|
||||
options,
|
||||
{"gs_ply": {"gs_views_interval": 2}},
|
||||
)
|
||||
else:
|
||||
self.assertEqual(options, {})
|
||||
|
||||
infer_gs_values = [options["infer_gs"] for _, options in inference_options]
|
||||
self.assertEqual(infer_gs_values, [False, True, False, False])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user