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

@@ -13,6 +13,9 @@ from .server.model_catalog import (
)
DEFAULT_MOGE2_MODEL_KEY = "MOGE2_VITS_NORMAL"
DEFAULT_MAX_CUTOUT_MESH_RESOLUTION = 1024
MIN_CUTOUT_MESH_RESOLUTION = 4
MAX_CUTOUT_MESH_RESOLUTION = 1024
ADDON_PACKAGE = __package__
DEFAULT_STORAGE_ROOT = Path.home() / ".anyimage"
@@ -46,11 +49,24 @@ def configured_storage_root():
return DEFAULT_STORAGE_ROOT
def configured_max_ai_input_size(context=None):
def configured_max_cutout_mesh_resolution(context=None):
preferences = addon_preferences(context)
value = int(
getattr(preferences, "max_ai_input_size", DEFAULT_MAX_AI_INPUT_SIZE)
getattr(
preferences,
"max_cutout_mesh_resolution",
DEFAULT_MAX_CUTOUT_MESH_RESOLUTION,
)
)
return min(
max(value, MIN_CUTOUT_MESH_RESOLUTION),
MAX_CUTOUT_MESH_RESOLUTION,
)
def configured_max_ai_input_size(context=None):
preferences = addon_preferences(context)
value = int(getattr(preferences, "max_ai_input_size", DEFAULT_MAX_AI_INPUT_SIZE))
return min(max(value, MIN_MAX_AI_INPUT_SIZE), MAX_MAX_AI_INPUT_SIZE)
@@ -60,10 +76,10 @@ def device_items(owner, context):
return device_enum_items(owner, context)
def mesh_depth_model_items(owner, context):
from .properties import mesh_depth_model_enum_items
def geometry_model_items(owner, context):
from .properties import geometry_model_enum_items
return mesh_depth_model_enum_items(owner, context)
return geometry_model_enum_items(owner, context)
class AnyImagePreferences(bpy.types.AddonPreferences):
@@ -97,7 +113,7 @@ class AnyImagePreferences(bpy.types.AddonPreferences):
name="Maximum AI Input Size",
description=(
"Maximum source long edge eligible for Upscale and maximum long edge "
"used by Depth and Normal analysis"
"used by Depth, Normal and Region refinement"
),
default=DEFAULT_MAX_AI_INPUT_SIZE,
min=MIN_MAX_AI_INPUT_SIZE,
@@ -105,31 +121,30 @@ class AnyImagePreferences(bpy.types.AddonPreferences):
step=256,
subtype="PIXEL",
)
max_cutout_mesh_resolution: bpy.props.IntProperty(
name="Maximum Cutout Mesh Resolution",
description="Maximum sampling resolution along the longest Cutout edge",
default=DEFAULT_MAX_CUTOUT_MESH_RESOLUTION,
min=MIN_CUTOUT_MESH_RESOLUTION,
max=MAX_CUTOUT_MESH_RESOLUTION,
step=64,
subtype="PIXEL",
)
balloon_profile_algorithm: bpy.props.EnumProperty(
name="Balloon Profile",
description="Algorithm used to generate o_balloon for every Cutout shape",
items=BALLOON_PROFILE_ALGORITHM_ITEMS,
default="POISSON",
)
mesh_depth_model: bpy.props.EnumProperty(
name="Mesh Depth Model",
description="Depth model used by Depth Plane, Depth Balloon and Depth Surface",
items=mesh_depth_model_items,
geometry_model: bpy.props.EnumProperty(
name="Depth & Normal Model",
description="MoGe-2 model used for geometry and normal generation",
items=geometry_model_items,
default=0,
)
mesh_process_res_factor: bpy.props.FloatProperty(
name="DA3 Resolution Scale",
description="Source image scale used for Depth Anything 3 mesh conversion",
default=100.0,
min=1.0,
max=100.0,
step=100,
precision=0,
subtype="PERCENTAGE",
)
mesh_resolution_level: bpy.props.IntProperty(
geometry_resolution_level: bpy.props.IntProperty(
name="MoGe Resolution Level",
description="MoGe-2 inference resolution used for mesh conversion",
description="MoGe-2 inference resolution used for geometry and normal generation",
default=5,
min=0,
max=9,
@@ -205,34 +220,24 @@ class AnyImagePreferences(bpy.types.AddonPreferences):
section = self._draw_panel(layout, "cutout", "Cutout")
if section is not None:
section.prop(self, "max_cutout_mesh_resolution")
section.prop(self, "balloon_profile_algorithm")
def _draw_depth_normal(self, layout, models, environment_ready):
layout.prop(self, "mesh_depth_model")
mesh_depth_model = getattr(
self, "mesh_depth_model", DEFAULT_MOGE2_MODEL_KEY
)
if mesh_depth_model not in models:
mesh_depth_model = DEFAULT_MOGE2_MODEL_KEY
layout.prop(self, "geometry_model")
geometry_model = getattr(self, "geometry_model", DEFAULT_MOGE2_MODEL_KEY)
if geometry_model not in models:
geometry_model = DEFAULT_MOGE2_MODEL_KEY
self._draw_model(
layout,
models.get(mesh_depth_model),
models.get(geometry_model),
environment_ready,
)
if mesh_depth_model.startswith("MOGE2_"):
layout.prop(self, "mesh_resolution_level")
else:
layout.prop(self, "mesh_process_res_factor")
layout.label(
text="DA3 models cannot generate normals",
icon="INFO",
)
layout.prop(self, "geometry_resolution_level")
def _draw_upscale(self, layout, models, environment_ready):
layout.prop(self, "upscale_model")
upscale_model = getattr(
self, "upscale_model", DEFAULT_UPSCALE_MODEL_KEY
)
upscale_model = getattr(self, "upscale_model", DEFAULT_UPSCALE_MODEL_KEY)
if upscale_model not in models:
upscale_model = DEFAULT_UPSCALE_MODEL_KEY
self._draw_model(