feat(selection): separate selection geometry from image content processing
- Introduced a new design for selection rasterization that decouples the selection geometry from the image content, ensuring that the SelectionMask is independent of the source image's alpha and color. - Updated the `rasterize_selection_path` function to only accept selection paths and image dimensions, removing parameters related to source alpha and alpha thresholds. - Implemented a new approach for handling selection paths using non-zero winding fill, ensuring overlapping areas are preserved. - Enhanced performance by utilizing a scanline algorithm for rasterization, significantly reducing processing time for high-resolution images. - Updated internal documentation and added comprehensive tests to validate the new behavior and performance improvements. - Ensured backward compatibility by maintaining existing interfaces where necessary while introducing breaking changes in the selection processing logic.
This commit is contained in:
@@ -4,10 +4,7 @@ from enum import StrEnum
|
||||
|
||||
import bpy
|
||||
|
||||
from ...common.image import (
|
||||
color_data_name,
|
||||
image_rgba,
|
||||
)
|
||||
from ...common.image import color_data_name
|
||||
from ...common.depth import camera_depth_values, depth_uniform_scale
|
||||
from ...common.material import (
|
||||
IMAGE_MATERIAL_NODE_GROUP_NAME,
|
||||
@@ -84,8 +81,8 @@ def _assign_float_point_attribute(mesh, name, values):
|
||||
attribute.data.foreach_set("value", np.asarray(values, dtype=np.float32))
|
||||
|
||||
|
||||
def _cropped_color_image(source_object, selection_mask, source_rgba):
|
||||
crop_bounds = selection_mask.bounds
|
||||
def create_cropped_color_image(source_object, crop_bounds, source_rgba):
|
||||
"""Create a packed color image for one local Cutout bounds rectangle."""
|
||||
import numpy as np
|
||||
|
||||
source = source_object.data
|
||||
@@ -95,17 +92,10 @@ def _cropped_color_image(source_object, selection_mask, source_rgba):
|
||||
height = bottom - top
|
||||
if width <= 0 or height <= 0:
|
||||
raise ValueError("The Cutout crop dimensions must be positive")
|
||||
cropped = np.zeros((height, width, 4), dtype=np.float32)
|
||||
source_height, source_width = rgba.shape[:2]
|
||||
source_left = max(0, left)
|
||||
source_top = max(0, top)
|
||||
source_right = min(source_width, right)
|
||||
source_bottom = min(source_height, bottom)
|
||||
if source_right > source_left and source_bottom > source_top:
|
||||
cropped[
|
||||
source_top - top : source_bottom - top,
|
||||
source_left - left : source_right - left,
|
||||
] = rgba[source_top:source_bottom, source_left:source_right]
|
||||
if left < 0 or top < 0 or right > source_width or bottom > source_height:
|
||||
raise ValueError("The Cutout crop is outside the source RGBA")
|
||||
cropped = rgba[top:bottom, left:right]
|
||||
image = bpy.data.images.new(
|
||||
color_data_name(source_object),
|
||||
width=width,
|
||||
@@ -151,21 +141,22 @@ def create_shape_object(
|
||||
source_object,
|
||||
shape,
|
||||
mesh_resolution,
|
||||
selection_mask,
|
||||
content_values,
|
||||
content_bounds,
|
||||
base_shape,
|
||||
*,
|
||||
depth_image=None,
|
||||
depth_metadata=None,
|
||||
normal_image=None,
|
||||
normal_mode=NormalMode.NONE,
|
||||
source_rgba=None,
|
||||
color_image,
|
||||
):
|
||||
import numpy as np
|
||||
|
||||
normal_mode = NormalMode(normal_mode)
|
||||
bounds = crop_plane_bounds(
|
||||
source_object,
|
||||
selection_mask.bounds,
|
||||
content_bounds,
|
||||
tuple(source_object.data.size),
|
||||
)
|
||||
crop_center = (
|
||||
@@ -177,11 +168,6 @@ def create_shape_object(
|
||||
cutout_sampling_spacing(source_object, mesh_resolution),
|
||||
world_metric,
|
||||
)
|
||||
color_image = _cropped_color_image(
|
||||
source_object,
|
||||
selection_mask,
|
||||
image_rgba(source_object.data) if source_rgba is None else source_rgba,
|
||||
)
|
||||
material = None
|
||||
mesh = None
|
||||
mesh_object = None
|
||||
@@ -209,7 +195,7 @@ def create_shape_object(
|
||||
raise ValueError("Depth Shape requires a Depth texture and metadata")
|
||||
camera_depth, valid = camera_depth_values(depth_image)
|
||||
calibration_valid, transparent_fraction = selection_calibration(
|
||||
selection_mask, valid.shape
|
||||
content_values, valid.shape
|
||||
)
|
||||
calibration_valid &= valid
|
||||
uniform_scale = depth_uniform_scale(
|
||||
@@ -311,6 +297,4 @@ def create_shape_object(
|
||||
bpy.data.meshes.remove(mesh)
|
||||
if material is not None and material.users == 0:
|
||||
bpy.data.materials.remove(material)
|
||||
if color_image.users == 0:
|
||||
bpy.data.images.remove(color_image, do_unlink=True)
|
||||
raise
|
||||
|
||||
Reference in New Issue
Block a user