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:
@@ -136,11 +136,13 @@ class BlenderTestCase(unittest.TestCase):
|
||||
)
|
||||
cls.image_selection = importlib.import_module("anyimage.common.selection")
|
||||
cls.image_frame = importlib.import_module(
|
||||
"anyimage.operators.frame_tool"
|
||||
"anyimage.operators.frame_tool.operators"
|
||||
)
|
||||
cls.rectify = importlib.import_module(
|
||||
"anyimage.operators.rectify_tool"
|
||||
"anyimage.operators.rectify_tool.operators"
|
||||
)
|
||||
cls.rectify_geometry = importlib.import_module("anyimage.operators.rectify_tool.geometry")
|
||||
cls.rectify_preview = importlib.import_module("anyimage.operators.rectify_tool.preview")
|
||||
cls.cutout_geometry = importlib.import_module(
|
||||
"anyimage.operators.cutout_tool.geometry"
|
||||
)
|
||||
@@ -194,5 +196,3 @@ class BlenderTestCase(unittest.TestCase):
|
||||
)
|
||||
if hasattr(self.fake_bpy.types.Scene, "anyimage_settings"):
|
||||
del self.fake_bpy.types.Scene.anyimage_settings
|
||||
if hasattr(self.fake_bpy.types.Image, "anyimage_upscale_source"):
|
||||
del self.fake_bpy.types.Image.anyimage_upscale_source
|
||||
|
||||
@@ -65,7 +65,7 @@ def surface(*, step=6.0, size=1.0, resolution=2, wrapper=False):
|
||||
("Depth Scale", 1.0),
|
||||
("Thickness", 0.0),
|
||||
("Front Inflation", 0.0),
|
||||
("Split Threshold", 0.0),
|
||||
("Depth Split", 0.0),
|
||||
("Mode", 1),
|
||||
("Smooth", 0),
|
||||
("Boundary Smooth", 0),
|
||||
@@ -73,7 +73,7 @@ def surface(*, step=6.0, size=1.0, resolution=2, wrapper=False):
|
||||
set_value(name, value)
|
||||
if wrapper:
|
||||
set_value("Cleanup Threshold", 0.0)
|
||||
set_value("Inward Axis", 0)
|
||||
set_value("Depth Axis", 0)
|
||||
set_value("Smooth", 0)
|
||||
return obj, set_value
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ def _create_cutout_shape(
|
||||
depth_image=None,
|
||||
depth_metadata=None,
|
||||
selection_values=None,
|
||||
inward_axis=cutout_object.CutoutInwardAxis.NEGATIVE_Z,
|
||||
gesture="LASSO",
|
||||
):
|
||||
if bounds is None:
|
||||
bounds = (0, 0, *tuple(int(value) for value in source.data.size))
|
||||
@@ -66,7 +66,7 @@ def _create_cutout_shape(
|
||||
base_shape,
|
||||
depth_image=depth_image,
|
||||
depth_metadata=depth_metadata,
|
||||
inward_axis=inward_axis,
|
||||
gesture=gesture,
|
||||
color_image=color_image,
|
||||
)
|
||||
|
||||
@@ -117,7 +117,7 @@ def _add_cutout_modifier(obj, shape):
|
||||
object_data.set_modifier_input(modifier, object_data.modifier_input_identifier(group, "Mode"), 1)
|
||||
object_data.set_modifier_input(
|
||||
modifier,
|
||||
object_data.modifier_input_identifier(group, "Inward Axis"),
|
||||
object_data.modifier_input_identifier(group, "Depth Axis"),
|
||||
0,
|
||||
)
|
||||
object_data.set_modifier_input(
|
||||
@@ -171,7 +171,10 @@ def _depth_surface_step_object():
|
||||
):
|
||||
object_data.set_modifier_input(
|
||||
modifier,
|
||||
object_data.modifier_input_identifier(modifier.node_group, name),
|
||||
object_data.modifier_input_identifier(
|
||||
modifier.node_group, name,
|
||||
subtype="DISTANCE" if name == "Thickness" else None,
|
||||
),
|
||||
value,
|
||||
)
|
||||
return obj, modifier
|
||||
|
||||
@@ -35,11 +35,11 @@ def texture_context(node, **space_options):
|
||||
edit_tree=node.id_data, id=bpy.data.materials["Texture material"], **space_options),
|
||||
),
|
||||
object=None,
|
||||
scene=SimpleNamespace(anyimage_settings=SimpleNamespace(video_frames=16)),
|
||||
scene=SimpleNamespace(anyimage_settings=SimpleNamespace()),
|
||||
)
|
||||
|
||||
|
||||
def write_result(directory, prefix="image.", count=1):
|
||||
for index in range(1, count + 1):
|
||||
Image.new("RGBA", (8, 6), (128, 64, 32, 80)).save(directory / f"{prefix}{index:04d}.png")
|
||||
|
||||
def write_result(directory, filename="foreground.png"):
|
||||
path = directory / filename
|
||||
Image.new("RGBA", (8, 6), (128, 64, 32, 80)).save(path)
|
||||
return path
|
||||
|
||||
@@ -9,14 +9,3 @@ CUTOUT_BUILDERS = {
|
||||
"DEPTH_SOLID": build_image_depth_cutout_group,
|
||||
"DEPTH_BALLOON": build_image_depth_balloon_group,
|
||||
}
|
||||
|
||||
|
||||
def validate_node_structure(node_group):
|
||||
"""Check visible sockets, default titles, and paired execution endpoints."""
|
||||
nodes = [node for node in node_group.nodes if node.bl_idname not in {"NodeReroute", "NodeFrame"}]
|
||||
assert all(not node.label for node in nodes), node_group.name
|
||||
assert not any(node.bl_idname == "FunctionNodeInputString" for node in nodes), node_group.name
|
||||
assert all(socket.is_linked or socket.hide for node in node_group.nodes for socket in node.outputs), node_group.name
|
||||
for node in nodes:
|
||||
if node.bl_idname in {"GeometryNodeRepeatInput", "GeometryNodeSimulationInput", "GeometryNodeForeachGeometryElementInput"}:
|
||||
assert node.paired_output in node_group.nodes.values(), (node_group.name, node.name)
|
||||
|
||||
@@ -1,38 +1,29 @@
|
||||
"""Evaluate complete-image projection and rectangular surface topology."""
|
||||
from tests.support.paths import PROJECT_ROOT
|
||||
|
||||
import bpy
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
ROOT = PROJECT_ROOT
|
||||
from tools.nodes.groups.image_plane import build_image_plane_group
|
||||
from tools.nodes.groups.image_depth_plane import build_image_depth_plane_group
|
||||
from tools.nodes.groups.image_relief_plane import build_image_relief_plane_group
|
||||
|
||||
|
||||
@pytest.fixture(params=("definition", "asset"))
|
||||
def surface(request):
|
||||
return create_surface(request.param, "DEPTH")
|
||||
@pytest.fixture
|
||||
def surface():
|
||||
return create_surface("DEPTH")
|
||||
|
||||
|
||||
@pytest.fixture(params=("definition", "asset"))
|
||||
def relief(request):
|
||||
return create_surface(request.param, "RELIEF")
|
||||
@pytest.fixture
|
||||
def relief():
|
||||
return create_surface("RELIEF")
|
||||
|
||||
|
||||
def create_surface(source, plane_type):
|
||||
def create_surface(plane_type):
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
group_name = f"O Image {plane_type.title()} Plane"
|
||||
if source == "asset":
|
||||
with bpy.data.libraries.load(str(ROOT / "src/anyimage/assets/O_AnyImage.blend")) as (library, target):
|
||||
target.node_groups = [group_name]
|
||||
group = target.node_groups[0]
|
||||
plane = group.nodes["Group"].node_tree
|
||||
else:
|
||||
plane = build_image_plane_group()
|
||||
builder = build_image_depth_plane_group if plane_type == "DEPTH" else build_image_relief_plane_group
|
||||
group = builder(plane)
|
||||
plane = build_image_plane_group()
|
||||
builder = build_image_depth_plane_group if plane_type == "DEPTH" else build_image_relief_plane_group
|
||||
group = builder(plane)
|
||||
mesh = bpy.data.meshes.new("Rectangle")
|
||||
mesh.from_pydata([(-2, -1, 0), (2, -1, 0), (2, 1, 0), (-2, 1, 0)], [], [(0, 1, 2, 3)])
|
||||
material = bpy.data.materials.new("Image")
|
||||
@@ -65,8 +56,8 @@ def create_surface(source, plane_type):
|
||||
set_value(name, value)
|
||||
if "Boundary Smooth" in inputs:
|
||||
set_value("Boundary Smooth", 0)
|
||||
if "Split Threshold" in inputs:
|
||||
set_value("Split Threshold", 0.0)
|
||||
if "Depth Split" in inputs:
|
||||
set_value("Depth Split", 0.0)
|
||||
return obj, set_value, plane, inputs, image
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user