修正 mini_npz 深度数据职责

This commit is contained in:
2026-08-12 18:27:40 +08:00
parent c3a151ad2c
commit e3d023cac5
4 changed files with 32 additions and 119 deletions

View File

@@ -9,7 +9,6 @@ DEPTH_KEY_CANDIDATES = ("depth", "depths")
INTRINSICS_KEY_CANDIDATES = ("intrinsics", "ixts")
DISPLACEMENT_STRENGTH = 1.0
DISPLACEMENT_MIDLEVEL = 0.0
REFERENCE_DEPTH_KEY = "snapform_reference_depth"
ORIGINAL_DISPLACEMENT_STRENGTH = 1.0
ORIGINAL_DISPLACEMENT_MIDLEVEL = 0.0
BUMP_BASE_DISTANCE = 0.001
@@ -695,25 +694,6 @@ def extract_intrinsics_array(npz_path):
return intrinsics
def extract_reference_depth(npz_path):
try:
import numpy as np
except ImportError as error:
raise RuntimeError("Blender Python does not provide NumPy") from error
try:
with np.load(npz_path, allow_pickle=False) as archive:
if REFERENCE_DEPTH_KEY not in archive.files:
return None
reference_depth = float(archive[REFERENCE_DEPTH_KEY])
except OSError as error:
raise ValueError(f"Unable to read Mini NPZ: {npz_path}") from error
if not np.isfinite(reference_depth) or reference_depth <= 0.0:
raise ValueError("Mini NPZ has an invalid reference depth")
return reference_depth
def prepare_depth_values(depth):
import numpy as np
@@ -725,6 +705,13 @@ def prepare_depth_values(depth):
return values
def depth_displacement_values(depth):
"""Convert raw DA3 camera distance to positive outward displacement."""
import numpy as np
return 1.0 - np.asarray(depth, dtype=np.float32)
def camera_plane_bounds(
source_object,
depth,
@@ -1407,18 +1394,19 @@ def build_depth_plane(
depth_frames_directory=None,
):
source_name = source_object.name
depth_arrays = extract_depth_arrays(npz_path)
depth = depth_arrays[0]
raw_depth_arrays = extract_depth_arrays(npz_path)
raw_depth = raw_depth_arrays[0]
intrinsics = extract_intrinsics_array(npz_path)
stored_reference_depth = extract_reference_depth(npz_path)
depth_values = prepare_depth_values(depth)
raw_depth_values = prepare_depth_values(raw_depth)
bounds, reference_depth = camera_plane_bounds(
source_object,
depth_values,
raw_depth_values,
intrinsics,
DISPLACEMENT_STRENGTH,
reference_depth=stored_reference_depth,
)
depth_arrays = [
depth_displacement_values(frame) for frame in raw_depth_arrays
]
depth_name = depth_name or depth_data_name(source_object)
frame_count = len(depth_arrays)
foreground_image = (

View File

@@ -36,7 +36,6 @@ HF_ENDPOINT = "https://huggingface.co"
MODELSCOPE_ENDPOINT = "https://modelscope.cn"
MODELSCOPE_REVISION = "master"
MODELSCOPE_TIMEOUT = 60.0
REFERENCE_DEPTH_KEY = "snapform_reference_depth"
class TaskCancelled(RuntimeError):
@@ -48,37 +47,6 @@ def check_cancelled(cancel_check):
raise TaskCancelled("Task cancelled")
def convert_ai_depth_for_displacement(prediction):
"""Convert DA3 distance into Blender's positive displacement direction."""
import numpy as np
prediction.depth = 1.0 - np.asarray(prediction.depth)
def depth_reference_value(depth):
"""Return the unmasked depth used to size the imported camera plane."""
import numpy as np
values = np.asarray(depth)
valid = np.isfinite(values) & (values > 0.0)
if not valid.any():
raise ValueError("Depth array contains no positive finite values")
return float(np.median(values[valid]))
def write_npz_reference_depth(npz_path, reference_depth):
"""Add SnapForm metadata without changing exported prediction arrays."""
import numpy as np
npz_path = Path(npz_path)
temporary_path = npz_path.with_name(f"{npz_path.stem}.tmp.npz")
with np.load(npz_path, allow_pickle=False) as archive:
values = {key: archive[key] for key in archive.files}
values[REFERENCE_DEPTH_KEY] = np.asarray(reference_depth, dtype=np.float32)
np.savez_compressed(temporary_path, **values)
temporary_path.replace(npz_path)
def write_status(
path,
progress,
@@ -1640,10 +1608,6 @@ def reconstruct(args, model_cache=None, cancel_check=None):
ref_view_strategy=args.ref_view_strategy,
)
check_cancelled(cancel_check)
reference_depth = None
if args.export_format == "mini_npz":
reference_depth = depth_reference_value(prediction.depth[0])
convert_ai_depth_for_displacement(prediction)
write_status(args.status, 0.88, f"Exporting {args.export_format}")
if args.export_format == "mesh":
export_textured_mesh_glb(
@@ -1701,10 +1665,6 @@ def reconstruct(args, model_cache=None, cancel_check=None):
result_paths,
cancel_check=cancel_check,
)
if args.export_format == "mini_npz":
result_path = output_directory / "results.npz"
if reference_depth is not None:
write_npz_reference_depth(result_path, reference_depth)
check_cancelled(cancel_check)
copy_frames(images, output_directory)
write_status(

View File

@@ -87,21 +87,6 @@ class ImageDepthTest(unittest.TestCase):
self.assertEqual(depth.shape, (2, 3))
def test_extracts_optional_reference_depth(self):
with tempfile.TemporaryDirectory() as directory:
npz_path = Path(directory) / "results.npz"
np.savez_compressed(
npz_path,
depth=np.ones((1, 2, 2), dtype=np.float32),
snapform_reference_depth=np.float32(2.5),
)
reference_depth = self.image_depth.extract_reference_depth(
npz_path
)
self.assertEqual(reference_depth, 2.5)
def test_is_animated_image_accepts_movie_and_sequence(self):
self.assertTrue(
self.image_depth.is_animated_image(
@@ -384,6 +369,23 @@ class ImageDepthTest(unittest.TestCase):
self.assertEqual(depth.shape, (2, 3))
def test_raw_da3_depth_is_converted_only_for_displacement(self):
raw_depth = np.array(
((0.25, 1.0), (1.5, 2.0)),
dtype=np.float32,
)
displacement = self.image_depth.depth_displacement_values(raw_depth)
np.testing.assert_array_equal(
displacement,
np.array(((0.75, 0.0), (-0.5, -1.0)), dtype=np.float32),
)
np.testing.assert_array_equal(
raw_depth,
np.array(((0.25, 1.0), (1.5, 2.0)), dtype=np.float32),
)
def test_extracts_camera_intrinsics(self):
expected = np.array(
((100.0, 0.0, 50.0), (0.0, 120.0, 40.0), (0.0, 0.0, 1.0)),

View File

@@ -157,40 +157,6 @@ class WorkerTest(unittest.TestCase):
)
self.assertEqual(args.remove_background_model_dir, "ben2")
def test_ai_depth_is_converted_for_positive_displacement(self):
prediction = SimpleNamespace(
depth=np.array([[[0.25, 1.0], [1.5, 2.0]]]),
conf=np.array([[[0.1, 0.2], [0.3, 0.4]]]),
)
self.worker.convert_ai_depth_for_displacement(prediction)
np.testing.assert_array_equal(
prediction.depth,
[[[0.75, 0.0], [-0.5, -1.0]]],
)
np.testing.assert_array_equal(
prediction.conf,
[[[0.1, 0.2], [0.3, 0.4]]],
)
def test_npz_reference_depth_preserves_prediction_arrays(self):
with tempfile.TemporaryDirectory() as directory:
npz_path = Path(directory) / "results.npz"
depth = np.array([[[1.0, 2.0]]], dtype=np.float32)
confidence = np.array([[[0.2, 0.8]]], dtype=np.float32)
np.savez_compressed(npz_path, depth=depth, conf=confidence)
self.worker.write_npz_reference_depth(npz_path, 1.5)
with np.load(npz_path, allow_pickle=False) as archive:
np.testing.assert_array_equal(archive["depth"], depth)
np.testing.assert_array_equal(archive["conf"], confidence)
self.assertEqual(
float(archive[self.worker.REFERENCE_DEPTH_KEY]),
1.5,
)
def test_prepares_a_single_image_file(self):
with tempfile.TemporaryDirectory() as directory:
image_path = Path(directory) / "input.png"
@@ -1013,12 +979,9 @@ class WorkerTest(unittest.TestCase):
_export_dir,
options,
) in export_calls:
expected_depth = (
-1.0 if export_format == "mini_npz" else 2.0
)
np.testing.assert_array_equal(
_prediction.depth,
np.full((1, 2, 2), expected_depth, dtype=np.float32),
np.full((1, 2, 2), 2.0, dtype=np.float32),
)
if export_format == "glb":
self.assertEqual(