Files
test/effects/zoom.py
gilesb 406cc7c0c7 Initial commit: video effects processing system
Add S-expression based video effects pipeline with modular effect
definitions, constructs, and recipe files.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 12:34:45 +00:00

92 lines
2.2 KiB
Python

# /// script
# requires-python = ">=3.10"
# dependencies = ["numpy", "opencv-python"]
# ///
"""
@effect zoom
@version 1.0.0
@author artdag
@description
Zooms into the center of the frame. Values > 1 zoom in (magnify),
values < 1 zoom out (shrink with black borders).
@param factor float
@range 0.1 5
@default 1.0
Zoom factor. 1 = unchanged, 2 = 2x magnification, 0.5 = half size.
@param center_x float
@range 0 1
@default 0.5
Horizontal center of zoom (0 = left, 1 = right).
@param center_y float
@range 0 1
@default 0.5
Vertical center of zoom (0 = top, 1 = bottom).
@example
(effect zoom :factor 1.5)
@example
;; Pulse zoom on bass
(effect zoom :factor (bind bass :range [1.0 1.5] :transform sqrt))
@example
;; Zoom to corner
(effect zoom :factor 2 :center_x 0 :center_y 0)
"""
import numpy as np
import cv2
def process_frame(frame: np.ndarray, params: dict, state: dict) -> tuple:
"""
Zoom a video frame.
Args:
frame: Input frame as numpy array (H, W, 3) RGB uint8
params: Effect parameters
- factor: zoom multiplier (default 1.0)
- center_x: horizontal center 0-1 (default 0.5)
- center_y: vertical center 0-1 (default 0.5)
state: Persistent state dict (unused)
Returns:
Tuple of (processed_frame, new_state)
"""
factor = params.get("factor", 1.0)
center_x = params.get("center_x", 0.5)
center_y = params.get("center_y", 0.5)
if factor is None or factor <= 0.01:
factor = 1.0
if factor == 1.0:
return frame, state
h, w = frame.shape[:2]
# Calculate crop region for zoom in
new_w = int(w / factor)
new_h = int(h / factor)
if new_w <= 0 or new_h <= 0:
return frame, state
# Calculate crop offset based on center
x_start = int((w - new_w) * center_x)
y_start = int((h - new_h) * center_y)
# Clamp to valid range
x_start = max(0, min(x_start, w - new_w))
y_start = max(0, min(y_start, h - new_h))
# Crop and resize back to original dimensions
cropped = frame[y_start:y_start + new_h, x_start:x_start + new_w]
result = cv2.resize(cropped, (w, h), interpolation=cv2.INTER_LINEAR)
return result, state