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:
@@ -13,6 +13,7 @@ from ...preferences import (
|
||||
from ...properties import (
|
||||
cutout_alpha_threshold_property,
|
||||
cutout_fine_outline_property,
|
||||
cutout_gesture_property,
|
||||
)
|
||||
from ...runtime import JobOperatorBase
|
||||
from ...server.model_catalog import DEFAULT_GEOMETRY_MODEL_KEY
|
||||
@@ -35,7 +36,7 @@ from ...common.image import (
|
||||
image_rgba,
|
||||
load_normal_result_image,
|
||||
require_image_empty,
|
||||
video_total_frames,
|
||||
is_animated_image,
|
||||
)
|
||||
from ...common.depth import load_depth_metadata, load_depth_result_image
|
||||
from ...common.color_image import (
|
||||
@@ -47,7 +48,7 @@ from ...common.color_image import (
|
||||
)
|
||||
from ...common.viewport import (
|
||||
ImageGesture,
|
||||
image_edit_drag_keymap,
|
||||
image_edit_point_keymap,
|
||||
report_image_edit_exception,
|
||||
)
|
||||
from ...common.selection import (
|
||||
@@ -62,10 +63,8 @@ from .geometry import (
|
||||
)
|
||||
from .interaction import open_shape_pie
|
||||
from .object import (
|
||||
CutoutInwardAxis,
|
||||
NormalMode,
|
||||
create_shape_object,
|
||||
orient_object_normal_image,
|
||||
)
|
||||
from .shape import CUTOUT_SHAPES, DEPTH_CUTOUT_SHAPES
|
||||
|
||||
@@ -100,8 +99,8 @@ def create_cutout_shape(
|
||||
depth_metadata=None,
|
||||
normal_image=None,
|
||||
normal_mode=NormalMode.NONE,
|
||||
inward_axis=CutoutInwardAxis.NEGATIVE_Z,
|
||||
color_image,
|
||||
gesture="LASSO",
|
||||
):
|
||||
base_shape = build_base_shape(
|
||||
context,
|
||||
@@ -125,8 +124,8 @@ def create_cutout_shape(
|
||||
depth_metadata=depth_metadata,
|
||||
normal_image=normal_image,
|
||||
normal_mode=normal_mode,
|
||||
inward_axis=inward_axis,
|
||||
color_image=color_image,
|
||||
gesture=gesture,
|
||||
)
|
||||
undo_message = f"Convert to {shape.replace('_', ' ').title()}"
|
||||
if "FINISHED" not in bpy.ops.ed.undo_push(message=undo_message):
|
||||
@@ -191,6 +190,7 @@ class CutoutSelectionToShape(JobOperatorBase, bpy.types.Operator):
|
||||
selection_path_json: bpy.props.StringProperty(options=_HIDDEN)
|
||||
selection_bounds_json: bpy.props.StringProperty(options=_HIDDEN)
|
||||
shape: bpy.props.StringProperty(default="SOLID", options=_HIDDEN)
|
||||
gesture: bpy.props.StringProperty(default="LASSO", options=_HIDDEN)
|
||||
point_spacing: bpy.props.FloatProperty(
|
||||
default=32.0,
|
||||
min=0.01,
|
||||
@@ -215,23 +215,13 @@ class CutoutSelectionToShape(JobOperatorBase, bpy.types.Operator):
|
||||
generate_normal: bpy.props.BoolProperty(default=False, options=_HIDDEN)
|
||||
alpha_threshold: cutout_alpha_threshold_property()
|
||||
fine_outline: cutout_fine_outline_property()
|
||||
inward_axis: bpy.props.StringProperty(
|
||||
default=CutoutInwardAxis.NEGATIVE_Z.value,
|
||||
options=_HIDDEN,
|
||||
)
|
||||
|
||||
def _needs_generation(self):
|
||||
return self.shape in DEPTH_CUTOUT_SHAPES or self.generate_normal
|
||||
|
||||
def execute(self, context):
|
||||
try:
|
||||
self.inward_axis = getattr(
|
||||
self,
|
||||
"inward_axis",
|
||||
CutoutInwardAxis.NEGATIVE_Z.value,
|
||||
)
|
||||
source_object = require_image_empty(self.source_object_name)
|
||||
if video_total_frames(source_object.data) != 1:
|
||||
if is_animated_image(source_object.data):
|
||||
raise ValueError("Cutout currently supports still images")
|
||||
if self.shape not in CUTOUT_SHAPES:
|
||||
raise ValueError(f"Unsupported Cutout shape: {self.shape}")
|
||||
@@ -263,8 +253,9 @@ class CutoutSelectionToShape(JobOperatorBase, bpy.types.Operator):
|
||||
create_cutout_shape(
|
||||
context, source_object, self.shape, self.point_spacing,
|
||||
content_values, selection_mask.bounds,
|
||||
inward_axis=self.inward_axis, alpha_threshold=self.alpha_threshold,
|
||||
alpha_threshold=self.alpha_threshold,
|
||||
fine_outline=self.fine_outline, color_image=color_image,
|
||||
gesture=self.gesture,
|
||||
)
|
||||
return {"FINISHED"}
|
||||
setup_result = invoke_ai_setup_if_needed(self)
|
||||
@@ -317,21 +308,14 @@ class CutoutSelectionToShape(JobOperatorBase, bpy.types.Operator):
|
||||
)
|
||||
parameters.update(
|
||||
moge2_parameters(
|
||||
context.scene.anyimage_settings,
|
||||
source_path,
|
||||
self.model,
|
||||
self.resolution_level,
|
||||
1,
|
||||
)
|
||||
)
|
||||
return parameters
|
||||
|
||||
def response(self, context, result):
|
||||
inward_axis = getattr(
|
||||
self,
|
||||
"inward_axis",
|
||||
CutoutInwardAxis.NEGATIVE_Z.value,
|
||||
)
|
||||
source_object = require_image_empty(self.source_object_name)
|
||||
source_bounds = tuple(json.loads(self.selection_bounds_json))
|
||||
color_image, source_alpha, color_bounds = _generated_color_result(
|
||||
@@ -358,8 +342,6 @@ class CutoutSelectionToShape(JobOperatorBase, bpy.types.Operator):
|
||||
result.file(normal_key),
|
||||
source_object,
|
||||
)
|
||||
if normal_mode == NormalMode.OBJECT:
|
||||
orient_object_normal_image(normal_image, inward_axis)
|
||||
depth_metadata = None
|
||||
if shape_uses_depth(self.shape):
|
||||
depth_metadata = load_depth_metadata(result.file("depth_metadata"))
|
||||
@@ -379,10 +361,10 @@ class CutoutSelectionToShape(JobOperatorBase, bpy.types.Operator):
|
||||
depth_metadata=depth_metadata,
|
||||
normal_image=normal_image,
|
||||
normal_mode=normal_mode,
|
||||
inward_axis=inward_axis,
|
||||
alpha_threshold=self.alpha_threshold,
|
||||
fine_outline=self.fine_outline,
|
||||
color_image=color_image,
|
||||
gesture=self.gesture,
|
||||
)
|
||||
except Exception:
|
||||
if normal_image is not None and normal_image.users == 0:
|
||||
@@ -401,22 +383,32 @@ class CutoutSelectionToShape(JobOperatorBase, bpy.types.Operator):
|
||||
class SelectCutoutSelection(ImageGesture, bpy.types.Operator):
|
||||
bl_idname = "anyimage.select_cutout_selection"
|
||||
bl_label = "Start Cutout Selection"
|
||||
bl_description = "Lasso an Image Empty selection, then choose its mesh shape"
|
||||
bl_description = "Select an Image Empty area with a lasso or polyline, then choose its mesh shape"
|
||||
bl_options = {"INTERNAL"}
|
||||
active_tool_id = "anyimage.cutout_lasso"
|
||||
gesture = "LASSO"
|
||||
resolve_on_press = True
|
||||
gesture: cutout_gesture_property()
|
||||
|
||||
source_object_name: bpy.props.StringProperty(options={"HIDDEN", "SKIP_SAVE"})
|
||||
source_matrix_data: bpy.props.StringProperty(options={"HIDDEN", "SKIP_SAVE"})
|
||||
|
||||
_pie_event = None
|
||||
_polyline_confirm_type = None
|
||||
|
||||
def _read_gesture_settings(self, context):
|
||||
self.gesture = context.scene.anyimage_settings.cutout_gesture
|
||||
|
||||
def _set_status(self, context):
|
||||
action = (
|
||||
"click points; click start, double-click, or Enter for shapes; Backspace removes"
|
||||
if self.gesture == "POLYLINE" else "drag and release for shapes"
|
||||
)
|
||||
context.workspace.status_text_set(
|
||||
"Cutout Lasso: drag and release for shapes; RMB or Esc cancels"
|
||||
f"Cutout {self.gesture.title()}: {action}; RMB or Esc cancels"
|
||||
)
|
||||
|
||||
def invoke(self, context, event):
|
||||
self._polyline_confirm_type = None
|
||||
self._pie_event = event
|
||||
return super().invoke(context, event)
|
||||
|
||||
@@ -424,6 +416,19 @@ class SelectCutoutSelection(ImageGesture, bpy.types.Operator):
|
||||
self._pie_event = event
|
||||
return ImageGesture.modal(self, context, event)
|
||||
|
||||
def _confirm_polyline(self, context):
|
||||
if len(self._path) >= 3:
|
||||
self._polyline_confirm_type = self._pie_event.type
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
def _modal_polyline(self, context, event):
|
||||
if self._polyline_confirm_type is not None:
|
||||
if event.type == self._polyline_confirm_type and event.value == "RELEASE":
|
||||
self._polyline_confirm_type = None
|
||||
return ImageGesture._confirm_polyline(self, context)
|
||||
return {"RUNNING_MODAL"}
|
||||
return ImageGesture._modal_polyline(self, context, event)
|
||||
|
||||
def _submit_selection_path(self, context, selection_path):
|
||||
try:
|
||||
source_object = require_image_empty(self.source_object_name)
|
||||
@@ -435,6 +440,7 @@ class SelectCutoutSelection(ImageGesture, bpy.types.Operator):
|
||||
self._pie_event,
|
||||
source_object,
|
||||
selection_path,
|
||||
gesture=self.gesture,
|
||||
)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -444,19 +450,18 @@ class CutoutTool(bpy.types.WorkSpaceTool):
|
||||
bl_context_mode = "OBJECT"
|
||||
bl_idname = "anyimage.cutout_lasso"
|
||||
bl_label = "Cutout"
|
||||
bl_description = "Lasso an Image Empty and choose a mesh shape"
|
||||
bl_description = "Select an Image Empty area with a lasso or polyline and choose a mesh shape"
|
||||
bl_icon = "ops.mesh.primitive_sphere_add_gizmo"
|
||||
bl_operator = SelectCutoutSelection.bl_idname
|
||||
bl_keymap = image_edit_drag_keymap(
|
||||
bl_keymap = image_edit_point_keymap(
|
||||
SelectCutoutSelection.bl_idname,
|
||||
deselect_on_empty=False,
|
||||
)
|
||||
|
||||
def draw_settings(context, layout, _tool):
|
||||
settings = context.scene.anyimage_settings
|
||||
status = draw_ai_setup(layout)
|
||||
draw_ai_property(layout, settings, "cutout_generate_normal", status)
|
||||
layout.prop(settings, "cutout_gesture")
|
||||
layout.prop(settings, "cutout_mesh_detail")
|
||||
layout.prop(settings, "cutout_fine_outline")
|
||||
layout.prop(settings, "cutout_alpha_threshold")
|
||||
layout.prop(settings, "cutout_inward_axis")
|
||||
status = draw_ai_setup(layout)
|
||||
draw_ai_property(layout, settings, "cutout_generate_normal", status)
|
||||
|
||||
Reference in New Issue
Block a user