Files
AnyImage/src/anyimage/operators/rectify_tool/preview.py
nan 938d95db0f 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.
2026-09-12 19:20:06 +08:00

169 lines
4.6 KiB
Python

"""Rectify preview images, overlays, and interactive aspect controls."""
import math
import bpy
from ...common.image import image_base_name
from .geometry import (
MAX_INTERACTIVE_ASPECT,
MIN_INTERACTIVE_ASPECT,
)
PREVIEW_TEXTURE_SIZE = 512
ASPECT_PIXELS_PER_DOUBLE = 240.0
ASPECT_PRESETS = (
("9 : 16", 9.0 / 16.0),
("3 : 4", 3.0 / 4.0),
("1 : 1", 1.0),
("4 : 3", 4.0 / 3.0),
("16 : 9", 16.0 / 9.0),
)
def preview_polygon(points):
"""Return a non-crossing preview hull for the current unordered clicks."""
import numpy as np
points = np.asarray(points, dtype=np.float64)
if len(points) < 3:
return points
center = points.mean(axis=0)
return points[np.argsort(np.arctan2(points[:, 1] - center[1], points[:, 0] - center[0]))]
def interactive_preview_polygon(points, cursor):
"""Return the clicked points plus one distinct moving cursor point."""
vertices = list(points)
if (
cursor is not None
and vertices
and len(vertices) < 4
and math.dist(vertices[-1], cursor) >= 2.0
):
vertices.append(cursor)
return preview_polygon(vertices)
def aspect_ratio_from_mouse(initial_ratio, horizontal_delta):
ratio = initial_ratio * math.pow(
2.0, float(horizontal_delta) / ASPECT_PIXELS_PER_DOUBLE
)
return min(max(ratio, MIN_INTERACTIVE_ASPECT), MAX_INTERACTIVE_ASPECT)
def snapped_aspect_ratio(aspect_ratio):
label, ratio = min(
ASPECT_PRESETS,
key=lambda preset: abs(math.log(aspect_ratio / preset[1])),
)
return ratio, label
def aspect_ratio_label(aspect_ratio, preset_label=None):
if preset_label is not None:
return preset_label
if aspect_ratio >= 1.0:
return f"{aspect_ratio:.2f} : 1"
return f"1 : {1.0 / aspect_ratio:.2f}"
def preview_draw_bounds(region_size, aspect_ratio):
region_width, region_height = (float(value) for value in region_size)
available_width = min(region_width * 0.65, 640.0)
available_height = min(region_height * 0.65, 640.0)
if aspect_ratio >= available_width / available_height:
width = available_width
height = width / aspect_ratio
else:
height = available_height
width = height * aspect_ratio
return (
(region_width - width) * 0.5,
(region_height - height) * 0.5,
width,
height,
)
def draw_preview_frame(bounds):
import gpu
from gpu_extras.batch import batch_for_shader
left, bottom, width, height = bounds
vertices = (
(left, bottom),
(left + width, bottom),
(left + width, bottom + height),
(left, bottom + height),
(left, bottom),
)
shader = gpu.shader.from_builtin("UNIFORM_COLOR")
batch = batch_for_shader(shader, "LINE_STRIP", {"pos": vertices})
gpu.state.blend_set("ALPHA")
gpu.state.line_width_set(2.0)
shader.bind()
shader.uniform_float("color", (0.12, 0.52, 1.0, 1.0))
batch.draw(shader)
gpu.state.line_width_set(1.0)
gpu.state.blend_set("NONE")
def draw_centered_text(text, center_x, baseline_y, size, color):
import blf
font_id = 0
blf.size(font_id, size)
width, _height = blf.dimensions(font_id, text)
x = center_x - width * 0.5
blf.position(font_id, x + 1.0, baseline_y - 1.0, 0.0)
blf.color(font_id, 0.0, 0.0, 0.0, 0.9)
blf.draw(font_id, text)
blf.position(font_id, x, baseline_y, 0.0)
blf.color(font_id, *color)
blf.draw(font_id, text)
def draw_aspect_hud(bounds, aspect_ratio, preset_label=None):
left, bottom, width, height = bounds
center_x = left + width * 0.5
draw_centered_text(
aspect_ratio_label(aspect_ratio, preset_label),
center_x,
bottom + height * 0.5 - 10.0,
22,
(0.25, 0.65, 1.0, 1.0),
)
draw_centered_text(
"Move horizontally • Ctrl snap • LMB confirm • Backspace edit",
center_x,
bottom - 26.0,
13,
(0.85, 0.9, 1.0, 1.0),
)
def create_perspective_preview_image(source_image, pixels):
image = bpy.data.images.new(
f"{image_base_name(source_image)}.rectify-preview",
width=PREVIEW_TEXTURE_SIZE,
height=PREVIEW_TEXTURE_SIZE,
alpha=True,
)
try:
image.colorspace_settings.name = "sRGB"
image.alpha_mode = "STRAIGHT"
image.pixels.foreach_set(pixels)
image.update()
except Exception:
bpy.data.images.remove(image, do_unlink=True)
raise
return image
def preview_texture_draw_options(blender_version):
if tuple(blender_version) >= (5, 0, 0):
return {"is_scene_linear_with_rec709_srgb_target": True}
return {}