Add new tests and functionality for image processing and geometry handling
- Introduced color_image.py for preparing standalone color images for tests. - Added hdr_image.py to create HDR textures for testing. - Implemented test_image_depth_panorama.py to evaluate panorama topology, UVs, and depth discontinuities. - Created test_shadeless.py to validate the opacity and HDR color endpoints of the Shadeless group. - Developed moge3_export.py for exporting dynamic MoGe-3 backbone and ONNX graphs. - Added material.py to inherit material slots onto generated geometry. - Built image_depth_panorama.py to construct a quad panorama with depth separation and sampling.
This commit is contained in:
@@ -31,17 +31,19 @@ from ...common.ai import (
|
||||
require_model,
|
||||
)
|
||||
from ...common.image import (
|
||||
cleanup_image_input,
|
||||
color_data_name,
|
||||
image_rgba,
|
||||
load_normal_result_image,
|
||||
load_packed_rgba_image,
|
||||
prepare_image_bounds_input,
|
||||
require_image_empty,
|
||||
source_start_frame,
|
||||
video_total_frames,
|
||||
)
|
||||
from ...common.depth import load_depth_metadata, load_depth_result_image
|
||||
from ...common.color_image import (
|
||||
cleanup_material_color_input,
|
||||
load_material_color_image,
|
||||
material_analysis_input,
|
||||
material_color_image,
|
||||
prepare_material_color_input,
|
||||
)
|
||||
from ...common.viewport import (
|
||||
ImageGesture,
|
||||
image_edit_drag_keymap,
|
||||
@@ -61,7 +63,6 @@ from .interaction import open_shape_pie
|
||||
from .object import (
|
||||
CutoutInwardAxis,
|
||||
NormalMode,
|
||||
create_cropped_color_image,
|
||||
create_shape_object,
|
||||
orient_object_normal_image,
|
||||
)
|
||||
@@ -101,36 +102,31 @@ def create_cutout_shape(
|
||||
inward_axis=CutoutInwardAxis.NEGATIVE_Z,
|
||||
color_image,
|
||||
):
|
||||
try:
|
||||
base_shape = build_base_shape(
|
||||
context,
|
||||
source_object,
|
||||
point_spacing,
|
||||
content_values,
|
||||
content_bounds,
|
||||
alpha_threshold=alpha_threshold,
|
||||
fine_outline=fine_outline,
|
||||
)
|
||||
result = create_shape_object(
|
||||
context,
|
||||
source_object,
|
||||
shape,
|
||||
point_spacing,
|
||||
content_values,
|
||||
content_bounds,
|
||||
base_shape,
|
||||
alpha_threshold=alpha_threshold,
|
||||
depth_image=depth_image,
|
||||
depth_metadata=depth_metadata,
|
||||
normal_image=normal_image,
|
||||
normal_mode=normal_mode,
|
||||
inward_axis=inward_axis,
|
||||
color_image=color_image,
|
||||
)
|
||||
except Exception:
|
||||
if color_image.users == 0:
|
||||
bpy.data.images.remove(color_image, do_unlink=True)
|
||||
raise
|
||||
base_shape = build_base_shape(
|
||||
context,
|
||||
source_object,
|
||||
point_spacing,
|
||||
content_values,
|
||||
content_bounds,
|
||||
alpha_threshold=alpha_threshold,
|
||||
fine_outline=fine_outline,
|
||||
)
|
||||
result = create_shape_object(
|
||||
context,
|
||||
source_object,
|
||||
shape,
|
||||
point_spacing,
|
||||
content_values,
|
||||
content_bounds,
|
||||
base_shape,
|
||||
alpha_threshold=alpha_threshold,
|
||||
depth_image=depth_image,
|
||||
depth_metadata=depth_metadata,
|
||||
normal_image=normal_image,
|
||||
normal_mode=normal_mode,
|
||||
inward_axis=inward_axis,
|
||||
color_image=color_image,
|
||||
)
|
||||
undo_message = f"Convert to {shape.replace('_', ' ').title()}"
|
||||
if "FINISHED" not in bpy.ops.ed.undo_push(message=undo_message):
|
||||
raise RuntimeError(f"Unable to create the {undo_message} Undo step")
|
||||
@@ -160,12 +156,8 @@ def _cutout_content_values(
|
||||
return values
|
||||
|
||||
|
||||
def _generated_color_result(input_path, source_object, source_bounds):
|
||||
image = load_packed_rgba_image(
|
||||
input_path,
|
||||
source_object.data,
|
||||
color_data_name(source_object),
|
||||
)
|
||||
def _generated_color_result(input_path, source_bounds):
|
||||
image = load_material_color_image(input_path)
|
||||
try:
|
||||
alpha = image_rgba(image)[:, :, 3]
|
||||
except Exception:
|
||||
@@ -193,6 +185,7 @@ class CutoutSelectionToShape(JobOperatorBase, bpy.types.Operator):
|
||||
starting_message = "Generating Cutout artifacts"
|
||||
|
||||
input_path: bpy.props.StringProperty(options=_HIDDEN)
|
||||
color_path: bpy.props.StringProperty(options=_HIDDEN)
|
||||
source_object_name: bpy.props.StringProperty(options=_HIDDEN)
|
||||
selection_path_json: bpy.props.StringProperty(options=_HIDDEN)
|
||||
selection_bounds_json: bpy.props.StringProperty(options=_HIDDEN)
|
||||
@@ -265,25 +258,15 @@ class CutoutSelectionToShape(JobOperatorBase, bpy.types.Operator):
|
||||
selection_mask.values,
|
||||
alpha_threshold=self.alpha_threshold,
|
||||
)
|
||||
color_image = create_cropped_color_image(
|
||||
source_object,
|
||||
selection_mask.bounds,
|
||||
source_rgba,
|
||||
)
|
||||
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,
|
||||
fine_outline=self.fine_outline,
|
||||
color_image=color_image,
|
||||
)
|
||||
with material_color_image(source_object.data, bounds=selection_mask.bounds, rgba=source_rgba) as color_image:
|
||||
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,
|
||||
fine_outline=self.fine_outline, color_image=color_image,
|
||||
)
|
||||
return {"FINISHED"}
|
||||
setup_result = invoke_ai_setup_if_needed()
|
||||
setup_result = invoke_ai_setup_if_needed(self)
|
||||
if setup_result is not None:
|
||||
return setup_result
|
||||
self.model, self.resolution_level = cutout_geometry_settings(context)
|
||||
@@ -292,13 +275,10 @@ class CutoutSelectionToShape(JobOperatorBase, bpy.types.Operator):
|
||||
separators=(",", ":"),
|
||||
)
|
||||
self._selection_mask = selection_mask
|
||||
self.input_path = str(
|
||||
prepare_image_bounds_input(
|
||||
source_object.data,
|
||||
source_rgba,
|
||||
selection_mask.bounds,
|
||||
)
|
||||
)
|
||||
self.color_path = str(prepare_material_color_input(
|
||||
source_object.data, selection_mask.bounds, source_rgba,
|
||||
))
|
||||
self.input_path = str(material_analysis_input(self.color_path))
|
||||
except (
|
||||
ImageEditWarning,
|
||||
OSError,
|
||||
@@ -306,11 +286,12 @@ class CutoutSelectionToShape(JobOperatorBase, bpy.types.Operator):
|
||||
TypeError,
|
||||
ValueError,
|
||||
) as error:
|
||||
cleanup_material_color_input(getattr(self, "color_path", None))
|
||||
report_image_edit_exception(self, error)
|
||||
return {"CANCELLED"}
|
||||
result = super().execute(context)
|
||||
if "CANCELLED" in result:
|
||||
cleanup_image_input(self.input_path, True)
|
||||
cleanup_material_color_input(self.color_path)
|
||||
return result
|
||||
|
||||
def request(self, context):
|
||||
@@ -329,7 +310,6 @@ class CutoutSelectionToShape(JobOperatorBase, bpy.types.Operator):
|
||||
"alpha_threshold": float(self.alpha_threshold),
|
||||
}
|
||||
if generate_depth or normal_mode != NormalMode.NONE:
|
||||
start_frame = source_start_frame(self.source_object_name)
|
||||
parameters.update(
|
||||
model=self.model,
|
||||
resolution_level=self.resolution_level,
|
||||
@@ -340,7 +320,7 @@ class CutoutSelectionToShape(JobOperatorBase, bpy.types.Operator):
|
||||
source_path,
|
||||
self.model,
|
||||
self.resolution_level,
|
||||
start_frame,
|
||||
1,
|
||||
)
|
||||
)
|
||||
return parameters
|
||||
@@ -354,14 +334,12 @@ class CutoutSelectionToShape(JobOperatorBase, bpy.types.Operator):
|
||||
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(
|
||||
self.input_path,
|
||||
source_object,
|
||||
self.color_path,
|
||||
source_bounds,
|
||||
)
|
||||
value = result.value if isinstance(result.value, dict) else {}
|
||||
normal_image = None
|
||||
depth_image = None
|
||||
shape_started = False
|
||||
try:
|
||||
selection_mask = getattr(self, "_selection_mask", None)
|
||||
if selection_mask is None:
|
||||
@@ -389,7 +367,6 @@ class CutoutSelectionToShape(JobOperatorBase, bpy.types.Operator):
|
||||
source_object,
|
||||
depth_metadata,
|
||||
)
|
||||
shape_started = True
|
||||
create_cutout_shape(
|
||||
context,
|
||||
source_object,
|
||||
@@ -411,13 +388,13 @@ class CutoutSelectionToShape(JobOperatorBase, bpy.types.Operator):
|
||||
bpy.data.images.remove(normal_image, do_unlink=True)
|
||||
if depth_image is not None and depth_image.users == 0:
|
||||
bpy.data.images.remove(depth_image, do_unlink=True)
|
||||
if not shape_started and color_image.users == 0:
|
||||
if color_image.users == 0:
|
||||
bpy.data.images.remove(color_image, do_unlink=True)
|
||||
raise
|
||||
return f"{self.shape.replace('_', ' ').title()} is ready"
|
||||
|
||||
def cleanup(self):
|
||||
cleanup_image_input(self.input_path, True)
|
||||
cleanup_material_color_input(self.color_path)
|
||||
|
||||
|
||||
class SelectCutoutSelection(ImageGesture, bpy.types.Operator):
|
||||
|
||||
Reference in New Issue
Block a user