Enhance image editing tests and functionality

- Added parameterized tests for image edit replacement to ensure alpha mode preservation.
- Refactored existing tests to utilize new image editing methods and improve clarity.
- Introduced new tests for image target functionality, ensuring proper handling of shared textures and context.
- Updated various tests to validate alpha mode handling across different scenarios.
- Improved undo/redo functionality in image operators to maintain state consistency.
- Added support for texture node actions, enhancing integration with Blender's node system.
- Ensured that image properties are preserved during operations and that invalid contexts are handled gracefully.
This commit is contained in:
2026-09-10 00:00:13 +08:00
parent f48bbb3d05
commit 1622afa7a2
28 changed files with 1068 additions and 236 deletions

View File

@@ -10,14 +10,8 @@ from ..common.ai import (
require_model,
)
from ..common.image import (
cleanup_image_input,
default_extract_frames,
is_image_empty,
prepare_image_input,
source_start_frame,
video_total_frames,
)
from ..common.image import cleanup_image_input
from ..common.image_target import ImageEditTarget, image_edit_owner
_HIDDEN = {"HIDDEN", "SKIP_SAVE"}
@@ -26,14 +20,14 @@ _HIDDEN = {"HIDDEN", "SKIP_SAVE"}
class RemoveImageBackground(bpy.types.Operator):
bl_idname = "anyimage.remove_image_background"
bl_label = "Remove Background"
bl_description = "Remove the background from this Image Empty with BEN2"
bl_description = "Remove the image background with BEN2"
bl_options = {"REGISTER"}
@classmethod
def poll(cls, context):
from ..runtime import runtime
return is_image_empty(context.object) and not runtime.server_busy()
return image_edit_owner(context) is not None and not runtime.server_busy()
def invoke(self, _context, _event):
setup_result = invoke_ai_setup_if_needed()
@@ -41,58 +35,35 @@ class RemoveImageBackground(bpy.types.Operator):
return setup_result
return self.execute(_context)
def execute(self, context):
source_object = context.object
total_frames = video_total_frames(source_object.data)
if total_frames > 1:
context.scene.anyimage_settings.video_frames = (
default_extract_frames(source_object)
)
try:
source_path, delete_input = prepare_image_input(
source_object.data,
context.scene,
)
except RuntimeError as error:
self.report({"ERROR"}, str(error))
return {"CANCELLED"}
result = bpy.ops.anyimage.run_background_removal(
input_path=str(source_path),
source_object_name=source_object.name,
delete_input=delete_input,
)
def execute(self, _context):
result = bpy.ops.anyimage.run_background_removal()
if "CANCELLED" not in result:
return {"FINISHED"}
if delete_input:
cleanup_image_input(source_path, True)
return {"CANCELLED"}
def apply_background_response(result, source_object_name):
from ..common.image import (
load_image_edit_result,
replace_empty_image,
require_image_empty,
)
source_object = require_image_empty(source_object_name)
result_image = load_image_edit_result(source_object, result.directory)
replace_empty_image(source_object, result_image)
if "FINISHED" not in bpy.ops.ed.undo_push(message="Remove Background"):
raise RuntimeError("Unable to create the Remove Background Undo step")
class RunBackgroundRemoval(JobOperatorBase, bpy.types.Operator):
bl_idname = "anyimage.run_background_removal"
bl_label = "Run Background Removal"
job_type = "remove-background"
starting_message = "Starting Image Empty background removal"
starting_message = "Starting image background removal"
input_path: bpy.props.StringProperty(options=_HIDDEN)
source_object_name: bpy.props.StringProperty(options=_HIDDEN)
delete_input: bpy.props.BoolProperty(default=False, options=_HIDDEN)
def execute(self, context):
try:
self._image_target = ImageEditTarget.capture(context)
source_path, self.delete_input = self._image_target.prepare(context)
except RuntimeError as error:
self.report({"ERROR"}, str(error))
return {"CANCELLED"}
self.input_path = str(source_path)
result = super().execute(context)
if "CANCELLED" in result:
self.cleanup()
return result
def request(self, context):
require_environment()
require_model(BEN2_MODEL_KEY)
@@ -102,12 +73,15 @@ class RunBackgroundRemoval(JobOperatorBase, bpy.types.Operator):
"model": BEN2_MODEL_KEY,
"device": production_device().lower(),
"video_frames": settings.video_frames,
"start_frame": source_start_frame(self.source_object_name),
"start_frame": self._image_target.start_frame,
}
def response(self, _context, result):
apply_background_response(result, self.source_object_name)
self._image_target.apply(result.directory)
if "FINISHED" not in bpy.ops.ed.undo_push(message="Remove Background"):
raise RuntimeError("Unable to create the Remove Background Undo step")
return "Image background is removed"
def cleanup(self):
cleanup_image_input(self.input_path, self.delete_input)
self._image_target = None