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:
@@ -18,21 +18,19 @@ def test_target_uses_edit_tree_and_survives_renaming(texture, tmp_path):
|
||||
texture.id_data.nodes.active = other
|
||||
context.space_data.edit_tree = None
|
||||
write_result(tmp_path)
|
||||
target.apply(tmp_path)
|
||||
target.apply(tmp_path / "foreground.png")
|
||||
assert tuple(texture.image.size) == (8, 6)
|
||||
assert other.image is None
|
||||
|
||||
|
||||
@pytest.mark.parametrize("color_space,alpha_mode", [
|
||||
("sRGB", "PREMUL"),
|
||||
("AgX Base sRGB", "STRAIGHT"),
|
||||
("Filmic sRGB", "STRAIGHT"),
|
||||
("sRGB", "STRAIGHT"),
|
||||
("AgX Base sRGB", "PREMUL"),
|
||||
@pytest.mark.parametrize("color_space,alpha_mode,filename,shared", [
|
||||
("sRGB", "PREMUL", "foreground.png", True),
|
||||
("AgX Base sRGB", "STRAIGHT", "upscale.png", False),
|
||||
("Filmic sRGB", "STRAIGHT", "foreground.png", False),
|
||||
("sRGB", "STRAIGHT", "upscale.png", True),
|
||||
("AgX Base sRGB", "PREMUL", "foreground.png", True),
|
||||
])
|
||||
@pytest.mark.parametrize("prefix", ["image.", "upscale."])
|
||||
@pytest.mark.parametrize("shared", [False, True])
|
||||
def test_image_actions_preserve_source_color_space_and_alpha(texture, tmp_path, color_space, alpha_mode, prefix, shared):
|
||||
def test_image_actions_preserve_source_color_space_and_alpha(texture, tmp_path, color_space, alpha_mode, filename, shared):
|
||||
texture.image.colorspace_settings.name = color_space
|
||||
texture.image.alpha_mode = alpha_mode
|
||||
original = texture.image
|
||||
@@ -41,8 +39,8 @@ def test_image_actions_preserve_source_color_space_and_alpha(texture, tmp_path,
|
||||
other = texture.id_data.nodes.new("ShaderNodeTexImage")
|
||||
other.image = original
|
||||
target = targets.ImageEditTarget.capture(texture_context(texture))
|
||||
write_result(tmp_path, prefix)
|
||||
target.apply(tmp_path, frame_prefix=prefix)
|
||||
write_result(tmp_path, filename)
|
||||
target.apply(tmp_path / filename)
|
||||
result = texture.image
|
||||
assert result.colorspace_settings.name == color_space
|
||||
assert result.alpha_mode == alpha_mode
|
||||
@@ -102,7 +100,7 @@ def test_target_rejects_changed_identity_before_loading_result(texture, tmp_path
|
||||
bpy.data.images.remove(texture.image)
|
||||
count = len(bpy.data.images)
|
||||
with pytest.raises(RuntimeError, match="no longer available"):
|
||||
target.apply(tmp_path)
|
||||
target.apply(tmp_path / "foreground.png")
|
||||
assert len(bpy.data.images) == count
|
||||
|
||||
|
||||
@@ -137,7 +135,7 @@ def test_texture_commit_preserves_structure_and_isolates_users(texture, tmp_path
|
||||
shared = reference in {"same_tree", "other_material", "empty", "brush"}
|
||||
links = [(link.from_socket, link.to_socket) for link in texture.id_data.links]
|
||||
write_result(tmp_path)
|
||||
target.apply(tmp_path)
|
||||
target.apply(tmp_path / "foreground.png")
|
||||
assert (texture.image == original) == (not shared)
|
||||
assert tuple(texture.image.size) == (8, 6)
|
||||
assert texture.image.colorspace_settings.name == "Non-Color"
|
||||
@@ -163,63 +161,46 @@ def test_texture_commit_preserves_structure_and_isolates_users(texture, tmp_path
|
||||
assert len(reopened.id_data.links) == len(links)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("shared", [False, True])
|
||||
def test_animated_texture_preserves_playback_and_reopens(texture, tmp_path, shared):
|
||||
write_result(tmp_path, count=4)
|
||||
texture.image.source = "SEQUENCE"
|
||||
texture.image.filepath_raw = str(tmp_path / "image.0001.png")
|
||||
texture.image_user.frame_start = 9
|
||||
texture.image_user.frame_offset = 1
|
||||
texture.image_user.frame_duration = 2
|
||||
texture.image_user.use_cyclic = True
|
||||
context = texture_context(texture)
|
||||
target = targets.ImageEditTarget.capture(context)
|
||||
assert (target.start_frame, target.frame_count) == (2, 2)
|
||||
if shared:
|
||||
other = texture.id_data.nodes.new("ShaderNodeTexImage")
|
||||
other.image = texture.image
|
||||
with patch.object(targets, "prepare_image_input", return_value=(tmp_path, False)):
|
||||
target.prepare(context)
|
||||
assert context.scene.anyimage_settings.video_frames == 2
|
||||
target.apply(tmp_path)
|
||||
assert images.image_user_settings(texture) == (9, 0, 4, True)
|
||||
assert texture.image_user.use_auto_refresh
|
||||
assert texture.image.source == "SEQUENCE"
|
||||
path = str(tmp_path / "animated.blend")
|
||||
bpy.ops.wm.save_as_mainfile(filepath=path)
|
||||
bpy.ops.wm.open_mainfile(filepath=path)
|
||||
reopened = bpy.data.materials["Texture material"].node_tree.nodes["Source texture"]
|
||||
assert images.image_user_settings(reopened) == (9, 0, 4, True)
|
||||
assert len(images.collect_sequence_frames(reopened.image)) == 4
|
||||
|
||||
|
||||
@pytest.mark.parametrize("source", ["MOVIE", "SEQUENCE"])
|
||||
def test_target_rejects_animated_image(texture, source):
|
||||
texture.image.source = source
|
||||
with pytest.raises(RuntimeError, match="Only still images"):
|
||||
targets.ImageEditTarget.capture(texture_context(texture))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("shared", [False, True])
|
||||
def test_texture_failed_commit_restores_dirty_pixels_and_playback(texture, tmp_path, shared):
|
||||
def test_failed_texture_assignment_restores_source(texture, tmp_path, shared):
|
||||
original = texture.image
|
||||
if shared:
|
||||
other = texture.id_data.nodes.new("ShaderNodeTexImage")
|
||||
other.image = original
|
||||
original.pixels.foreach_set(np.tile([0.1, 0.3, 0.7, 0.5], 12).astype(np.float32))
|
||||
packed = bytes(original.packed_file.data)
|
||||
pixels = images.image_pixels(original)
|
||||
timing = images.image_user_settings(texture)
|
||||
write_result(tmp_path, count=2)
|
||||
result = bpy.data.images.load(str(tmp_path / "image.0001.png"))
|
||||
result.source = "SEQUENCE"
|
||||
result = images.load_image_edit_result(original, write_result(tmp_path))
|
||||
count = len(bpy.data.images) - 1
|
||||
|
||||
def fail_configuration(user, *args, **kwargs):
|
||||
user.frame_start = 80
|
||||
user.use_auto_refresh = True
|
||||
raise RuntimeError("Playback failed")
|
||||
class FailingTarget:
|
||||
failed = False
|
||||
|
||||
with patch.object(targets, "configure_image_user", side_effect=fail_configuration):
|
||||
with pytest.raises(RuntimeError, match="Playback failed"):
|
||||
targets.replace_texture_image(texture, result)
|
||||
def __getattr__(self, name):
|
||||
return getattr(texture, name)
|
||||
|
||||
@property
|
||||
def image(self):
|
||||
return texture.image
|
||||
|
||||
@image.setter
|
||||
def image(self, value):
|
||||
texture.image = value
|
||||
if not self.failed:
|
||||
self.failed = True
|
||||
raise RuntimeError("Assignment failed")
|
||||
|
||||
with patch.object(targets, "has_other_image_user", return_value=shared):
|
||||
with pytest.raises(RuntimeError, match="Assignment failed"):
|
||||
targets.replace_texture_image(FailingTarget(), result)
|
||||
assert texture.image == original
|
||||
assert tuple(original.size) == (4, 3)
|
||||
assert bytes(original.packed_file.data) == packed
|
||||
np.testing.assert_array_equal(images.image_pixels(original), pixels)
|
||||
assert images.image_user_settings(texture) == timing
|
||||
assert not texture.image_user.use_auto_refresh
|
||||
assert len(bpy.data.images) == count
|
||||
|
||||
Reference in New Issue
Block a user