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:
2026-08-31 11:36:27 +08:00
parent 08309f719d
commit 87d7721128
80 changed files with 4823 additions and 4297 deletions

View File

@@ -6,9 +6,6 @@ from pathlib import Path
from types import ModuleType, SimpleNamespace
from unittest.mock import patch
import numpy as np
PROJECT_ROOT = Path(__file__).resolve().parents[1]
@@ -44,15 +41,6 @@ def load_image_modules():
/ "common"
/ "mesh_result.py",
),
(
"anyimage.operators.convert_to_plane.resolution",
PROJECT_ROOT
/ "src"
/ "anyimage"
/ "operators"
/ "convert_to_plane"
/ "resolution.py",
),
)
packages = {
name: ModuleType(name)
@@ -85,6 +73,7 @@ def load_image_modules():
setattr(namespace, name, value)
return namespace
class ImageTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
@@ -95,6 +84,7 @@ class ImageTest(unittest.TestCase):
self.image_depth.node_asset_path(),
PROJECT_ROOT / "src" / "anyimage" / "assets" / "O_AnyImage.blend",
)
def test_material_color_space_matches_scene_view_transform(self):
mappings = {
"ACES 1.3": "ACES 1.3 sRGB",
@@ -143,19 +133,13 @@ class ImageTest(unittest.TestCase):
def test_is_animated_image_accepts_movie_and_sequence(self):
self.assertTrue(
self.image_depth.is_animated_image(
SimpleNamespace(source="MOVIE")
)
self.image_depth.is_animated_image(SimpleNamespace(source="MOVIE"))
)
self.assertTrue(
self.image_depth.is_animated_image(
SimpleNamespace(source="SEQUENCE")
)
self.image_depth.is_animated_image(SimpleNamespace(source="SEQUENCE"))
)
self.assertFalse(
self.image_depth.is_animated_image(
SimpleNamespace(source="FILE")
)
self.image_depth.is_animated_image(SimpleNamespace(source="FILE"))
)
def test_video_total_frames_reads_movie_duration(self):
@@ -327,9 +311,7 @@ class ImageTest(unittest.TestCase):
def test_image_user_settings_defaults_without_user(self):
self.assertEqual(
self.image_depth.image_user_settings(
SimpleNamespace(image_user=None)
),
self.image_depth.image_user_settings(SimpleNamespace(image_user=None)),
(1, 0, 0, False),
)
@@ -411,31 +393,6 @@ class ImageTest(unittest.TestCase):
["frame-1.jpg", "frame-2.jpg", "frame-10.jpg"],
)
def test_process_resolution_scales_long_edge_and_aligns_to_patch_size(self):
image = SimpleNamespace(size=(1080, 1920))
self.assertEqual(
self.image_depth.scaled_process_resolution(image, 25.0),
476,
)
self.assertEqual(
self.image_depth.scaled_process_resolution(image, 50.0),
966,
)
def test_process_resolution_clamps_to_supported_range(self):
large_image = SimpleNamespace(size=(4000, 3000))
small_image = SimpleNamespace(size=(200, 100))
self.assertEqual(
self.image_depth.scaled_process_resolution(large_image, 100.0),
1008,
)
self.assertEqual(
self.image_depth.scaled_process_resolution(small_image, 1.0),
252,
)
def test_depth_data_uses_source_image_name(self):
source_object = SimpleNamespace(
name="obj_img",