feat(cutout_tool): Implement cutout region to shape functionality
- Added interaction helpers for cutout region validation and shape menu interaction. - Created main operator for converting image regions into cutout shapes, including AI artifact generation. - Introduced object creation logic for cutout shapes, handling depth and normal textures. - Defined cutout shape identifiers and node groups for various shape types. - Implemented depth texture writing and prediction artifact generation for MoGe-2. - Developed server jobs for generating cutout artifacts, depth planes, and refining image regions. - Added region refinement logic to enhance gesture masks using BEN2.
This commit is contained in:
@@ -1,9 +1,8 @@
|
||||
from pathlib import Path
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
PROJECT_ROOT = Path(__file__).parents[2]
|
||||
sys.path.insert(0, str(PROJECT_ROOT / "src"))
|
||||
sys.path.insert(
|
||||
@@ -11,12 +10,12 @@ sys.path.insert(
|
||||
str(next((PROJECT_ROOT / "src" / "anyimage" / "wheels").glob("blendjob-*.whl"))),
|
||||
)
|
||||
|
||||
from anyimage.operators.cutout_tool import node_assets as cutout_nodes
|
||||
from anyimage.operators.cutout_tool import shape_types as shapes
|
||||
from anyimage.operators.common.mesh_result import (
|
||||
modifier_input_identifier,
|
||||
set_modifier_input,
|
||||
)
|
||||
from anyimage.operators.cutout_tool import object as cutout_objects
|
||||
from anyimage.operators.cutout_tool import shape as shapes
|
||||
|
||||
|
||||
def evaluated_mesh(obj):
|
||||
@@ -34,11 +33,7 @@ def input_names(group):
|
||||
|
||||
|
||||
def grid_object(name):
|
||||
vertices = [
|
||||
(0.0, float(y), float(z))
|
||||
for z in range(3)
|
||||
for y in range(3)
|
||||
]
|
||||
vertices = [(0.0, float(y), float(z)) for z in range(3) for y in range(3)]
|
||||
faces = []
|
||||
for z in range(2):
|
||||
for y in range(2):
|
||||
@@ -56,7 +51,7 @@ def grid_object(name):
|
||||
|
||||
|
||||
def add_modifier(obj, shape, thickness):
|
||||
group = cutout_nodes.cutout_node_group(shape)
|
||||
group = cutout_objects.load_cutout_node_group()
|
||||
modifier = obj.modifiers.new(group.name, "NODES")
|
||||
modifier.node_group = group
|
||||
for name, value in (
|
||||
@@ -76,9 +71,9 @@ def add_modifier(obj, shape, thickness):
|
||||
|
||||
def main():
|
||||
bpy.ops.wm.read_factory_settings(use_empty=True)
|
||||
wrapper = cutout_nodes.cutout_node_group("CUTOUT")
|
||||
wrapper = cutout_objects.load_cutout_node_group()
|
||||
assert all(
|
||||
cutout_nodes.cutout_node_group(shape) == wrapper
|
||||
cutout_objects.load_cutout_node_group() == wrapper
|
||||
for shape in shapes.CUTOUT_SHAPES
|
||||
)
|
||||
assert wrapper.name == shapes.CUTOUT_NODE_GROUP_NAME
|
||||
@@ -90,8 +85,7 @@ def main():
|
||||
assert all(not group.is_modifier for group in groups.values())
|
||||
|
||||
menu_switch = next(
|
||||
node for node in wrapper.nodes
|
||||
if node.bl_idname == "GeometryNodeMenuSwitch"
|
||||
node for node in wrapper.nodes if node.bl_idname == "GeometryNodeMenuSwitch"
|
||||
)
|
||||
assert [item.name for item in menu_switch.enum_items] == list(
|
||||
shapes.CUTOUT_SHAPE_LABELS.values()
|
||||
@@ -131,7 +125,7 @@ def main():
|
||||
"Depth Image",
|
||||
]
|
||||
expected_inputs = {
|
||||
"CUTOUT": ["Geometry", "Thickness"],
|
||||
"SURFACE": ["Geometry", "Thickness"],
|
||||
"BALLOON": ["Geometry", "Thickness"],
|
||||
"DEPTH_BALLOON": [
|
||||
"Geometry",
|
||||
@@ -162,9 +156,7 @@ def main():
|
||||
if link.to_node.bl_idname != "GeometryNodeGroup":
|
||||
continue
|
||||
shape = next(
|
||||
shape
|
||||
for shape, group in groups.items()
|
||||
if group == link.to_node.node_tree
|
||||
shape for shape, group in groups.items() if group == link.to_node.node_tree
|
||||
)
|
||||
dependencies.setdefault(link.from_socket.name, set()).add(shape)
|
||||
assert dependencies == {
|
||||
@@ -178,41 +170,30 @@ def main():
|
||||
}
|
||||
|
||||
repeat_counts = {
|
||||
shape: sum(
|
||||
node.bl_idname == "GeometryNodeRepeatInput"
|
||||
for node in group.nodes
|
||||
)
|
||||
shape: sum(node.bl_idname == "GeometryNodeRepeatInput" for node in group.nodes)
|
||||
for shape, group in groups.items()
|
||||
}
|
||||
assert repeat_counts == {
|
||||
"CUTOUT": 1,
|
||||
"SURFACE": 1,
|
||||
"BALLOON": 0,
|
||||
"DEPTH_BALLOON": 0,
|
||||
"DEPTH_SURFACE": 1,
|
||||
}
|
||||
assert sum(
|
||||
node.bl_idname == "GeometryNodeRepeatInput"
|
||||
for node in wrapper.nodes
|
||||
) == 1
|
||||
assert sum(
|
||||
node.bl_idname == "GeometryNodeInputMeshIsland"
|
||||
for node in wrapper.nodes
|
||||
) == 1
|
||||
assert (
|
||||
sum(node.bl_idname == "GeometryNodeRepeatInput" for node in wrapper.nodes) == 1
|
||||
)
|
||||
assert (
|
||||
sum(node.bl_idname == "GeometryNodeInputMeshIsland" for node in wrapper.nodes)
|
||||
== 1
|
||||
)
|
||||
assert not any(
|
||||
node.bl_idname == "GeometryNodeAttributeStatistic"
|
||||
for node in wrapper.nodes
|
||||
node.bl_idname == "GeometryNodeAttributeStatistic" for node in wrapper.nodes
|
||||
)
|
||||
assert all(
|
||||
not any(
|
||||
node.bl_idname == "GeometryNodeInputMeshIsland"
|
||||
for node in group.nodes
|
||||
)
|
||||
not any(node.bl_idname == "GeometryNodeInputMeshIsland" for node in group.nodes)
|
||||
for group in groups.values()
|
||||
)
|
||||
assert any(
|
||||
node.bl_idname == "GeometryNodeBlurAttribute"
|
||||
for node in wrapper.nodes
|
||||
)
|
||||
assert any(node.bl_idname == "GeometryNodeBlurAttribute" for node in wrapper.nodes)
|
||||
smooth = next(
|
||||
item
|
||||
for item in wrapper.interface.items_tree
|
||||
@@ -244,23 +225,19 @@ def main():
|
||||
assert iterations.min_value == 0
|
||||
assert getattr(iterations.parent, "name", "") == ""
|
||||
assert all(
|
||||
not any(
|
||||
node.bl_idname == "GeometryNodeBlurAttribute"
|
||||
for node in group.nodes
|
||||
)
|
||||
not any(node.bl_idname == "GeometryNodeBlurAttribute" for node in group.nodes)
|
||||
for group in groups.values()
|
||||
)
|
||||
|
||||
for group in (wrapper, *groups.values()):
|
||||
assert all(not node.label for node in group.nodes)
|
||||
assert all(
|
||||
link.from_node.location.x <= link.to_node.location.x
|
||||
for link in group.links
|
||||
link.from_node.location.x <= link.to_node.location.x for link in group.links
|
||||
)
|
||||
group_nodes = list(group.nodes)
|
||||
assert len({tuple(node.location) for node in group_nodes}) == len(group_nodes)
|
||||
for index, node in enumerate(group_nodes):
|
||||
for other in group_nodes[index + 1:]:
|
||||
for other in group_nodes[index + 1 :]:
|
||||
assert not (
|
||||
abs(node.location.x - other.location.x) < 150.0
|
||||
and abs(node.location.y - other.location.y) < 100.0
|
||||
@@ -277,14 +254,13 @@ def main():
|
||||
if item.item_type == "SOCKET" and item.in_out == "INPUT":
|
||||
assert item.structure_type == "SINGLE"
|
||||
surface = grid_object("Surface Validation")
|
||||
add_modifier(surface, "CUTOUT", 2.4)
|
||||
add_modifier(surface, "SURFACE", 2.4)
|
||||
evaluated, result = evaluated_mesh(surface)
|
||||
try:
|
||||
assert len(result.vertices) == 34
|
||||
assert len(result.polygons) == 40
|
||||
side_faces = [
|
||||
polygon for polygon in result.polygons
|
||||
if abs(polygon.normal.x) < 0.1
|
||||
polygon for polygon in result.polygons if abs(polygon.normal.x) < 0.1
|
||||
]
|
||||
assert len(side_faces) == 24
|
||||
finally:
|
||||
@@ -415,14 +391,12 @@ def main():
|
||||
try:
|
||||
coordinates = [tuple(vertex.co) for vertex in result.vertices]
|
||||
assert all(
|
||||
all(abs(actual - expected) < 1e-5 for actual, expected in zip(point, target))
|
||||
all(
|
||||
abs(actual - expected) < 1e-5 for actual, expected in zip(point, target)
|
||||
)
|
||||
for point, target in zip(
|
||||
coordinates,
|
||||
(
|
||||
(y * 0.25, y * 0.5, z * 0.5)
|
||||
for z in range(3)
|
||||
for y in range(3)
|
||||
),
|
||||
((y * 0.25, y * 0.5, z * 0.5) for z in range(3) for y in range(3)),
|
||||
)
|
||||
)
|
||||
finally:
|
||||
|
||||
Reference in New Issue
Block a user