Add new functionality for image rectification and depth metadata handling
- Implemented a preview tool for rectifying images with interactive aspect controls in `preview.py`. - Added tests for depth metadata loading to ensure consistent schema usage in `test_depth_metadata.py`. - Developed comprehensive tests for cutout calibration processes in `test_cutout_calibration.py`, including stability checks and depth calibration. - Created tests for frame compositing operations in `test_frame_compositing.py`, ensuring correct handling of depth and transparency. - Introduced material slot inheritance functionality in `material_slots.py` to manage geometry material assignments. - Built shared shader nodes for material processing in `shader.py`, including roughness and bump mapping setups.
This commit is contained in:
@@ -33,9 +33,12 @@ def edit_result(source):
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("alpha_mode", ["STRAIGHT", "PREMUL", "CHANNEL_PACKED", "NONE"])
|
||||
@pytest.mark.parametrize("shared", [False, True])
|
||||
@pytest.mark.parametrize("entry", ["pixels", "image.", "upscale."])
|
||||
@pytest.mark.parametrize("alpha_mode,shared,entry", [
|
||||
("STRAIGHT", False, "pixels"),
|
||||
("PREMUL", True, "foreground.png"),
|
||||
("CHANNEL_PACKED", False, "upscale.png"),
|
||||
("NONE", True, "pixels"),
|
||||
])
|
||||
def test_image_edit_replacement_preserves_alpha_mode(source, tmp_path, alpha_mode, shared, entry):
|
||||
original = source.data
|
||||
original.alpha_mode = alpha_mode
|
||||
@@ -44,8 +47,8 @@ def test_image_edit_replacement_preserves_alpha_mode(source, tmp_path, alpha_mod
|
||||
if entry == "pixels":
|
||||
result = edit_result(source)
|
||||
else:
|
||||
Image.new("RGBA", (4, 3), (100, 150, 200, 128)).save(tmp_path / f"{entry}0001.png")
|
||||
result = images.load_image_edit_result(original, tmp_path, frame_prefix=entry)
|
||||
Image.new("RGBA", (4, 3), (100, 150, 200, 128)).save(tmp_path / entry)
|
||||
result = images.load_image_edit_result(original, tmp_path / entry)
|
||||
assert result.alpha_mode == alpha_mode
|
||||
images.replace_empty_image(source, result)
|
||||
assert source.data.alpha_mode == alpha_mode
|
||||
@@ -131,45 +134,6 @@ def test_frame_sharing_uses_surviving_objects(source, outside):
|
||||
assert tuple(original.size) == (2, 2)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("shared", [False, True])
|
||||
def test_failed_display_configuration_restores_content(source, tmp_path, shared):
|
||||
original = source.data
|
||||
if shared:
|
||||
create_empty("Other", original)
|
||||
# Preserve unsaved pixels as well as the previous packed representation.
|
||||
original.pixels.foreach_set(np.tile([0.1, 0.9, 0.2, 0.5], 4).astype(np.float32))
|
||||
packed = bytes(original.packed_file.data)
|
||||
pixels = images.image_pixels(original)
|
||||
alpha = original.alpha_mode
|
||||
source.image_user.frame_start = 5
|
||||
source.image_user.use_auto_refresh = False
|
||||
for index in (1, 2):
|
||||
Image.new("RGBA", (4, 3), (100, 150, 200, 255)).save(tmp_path / f"image.{index:04d}.png")
|
||||
result = bpy.data.images.load(str(tmp_path / "image.0001.png"))
|
||||
result.source = "SEQUENCE"
|
||||
count = len(bpy.data.images) - 1
|
||||
|
||||
def fail_configuration(user, *args, **kwargs):
|
||||
user.frame_start = 99
|
||||
user.use_auto_refresh = True
|
||||
raise RuntimeError("Display failed")
|
||||
|
||||
with patch.object(images, "configure_image_user", side_effect=fail_configuration):
|
||||
with pytest.raises(RuntimeError, match="Display failed"):
|
||||
images.replace_empty_image(source, result)
|
||||
assert source.data == original
|
||||
assert tuple(original.size) == (2, 2)
|
||||
assert original.alpha_mode == alpha
|
||||
assert original.is_dirty
|
||||
assert bytes(original.packed_file.data) == packed
|
||||
np.testing.assert_array_equal(images.image_pixels(original), pixels)
|
||||
assert source.image_user.frame_start == 5
|
||||
assert not source.image_user.use_auto_refresh
|
||||
assert not source.use_empty_image_alpha
|
||||
assert not original.use_fake_user
|
||||
assert len(bpy.data.images) == count
|
||||
|
||||
|
||||
def test_failed_content_write_restores_source(source):
|
||||
original = source.data
|
||||
packed = bytes(original.packed_file.data)
|
||||
@@ -193,47 +157,6 @@ def test_failed_content_write_restores_source(source):
|
||||
assert len(bpy.data.images) == 1
|
||||
|
||||
|
||||
def test_animated_result_preserves_id_and_reopens(source, tmp_path):
|
||||
original = source.data
|
||||
source.image_user.frame_start = 4
|
||||
source.image_user.use_cyclic = True
|
||||
for index in (1, 2):
|
||||
Image.new("RGBA", (4, 3), (index * 80, 100, 50, 255)).save(tmp_path / f"image.{index:04d}.png")
|
||||
original.source = "SEQUENCE"
|
||||
result = images.load_image_edit_result(source.data, tmp_path)
|
||||
expected_path = result.filepath_raw
|
||||
expected_packed = bytes(result.packed_file.data) if result.packed_file else None
|
||||
final = images.replace_empty_image(source, result)
|
||||
assert final == original
|
||||
assert final.source == "SEQUENCE"
|
||||
assert final.filepath_raw == expected_path
|
||||
assert (bytes(final.packed_file.data) if final.packed_file else None) == expected_packed
|
||||
assert len(images.collect_sequence_frames(final)) == 2
|
||||
assert source.image_user.frame_start == 4
|
||||
assert source.image_user.frame_duration == 2
|
||||
assert source.image_user.use_cyclic
|
||||
assert source.image_user.use_auto_refresh
|
||||
path = str(tmp_path / "animation.blend")
|
||||
bpy.ops.wm.save_as_mainfile(filepath=path)
|
||||
bpy.ops.wm.open_mainfile(filepath=path)
|
||||
reopened = bpy.data.objects["Source"].data
|
||||
assert reopened.name == "Source.png"
|
||||
assert reopened.source == "SEQUENCE"
|
||||
frames = images.collect_sequence_frames(reopened)
|
||||
assert len(frames) == 2
|
||||
for index, frame in enumerate(frames, 1):
|
||||
loaded = bpy.data.images.load(str(frame), check_existing=False)
|
||||
try:
|
||||
assert tuple(loaded.size) == (4, 3)
|
||||
np.testing.assert_allclose(
|
||||
images.image_pixels(loaded)[:4],
|
||||
np.array([index * 80, 100, 50, 255]) / 255,
|
||||
atol=1 / 255,
|
||||
)
|
||||
finally:
|
||||
bpy.data.images.remove(loaded)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("floating", [False, True])
|
||||
def test_failed_generated_commit_restores_unsaved_pixels(source, floating):
|
||||
original = bpy.data.images.new("Generated", width=3, height=2, alpha=True, float_buffer=floating)
|
||||
|
||||
Reference in New Issue
Block a user