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>
60 lines
1.5 KiB
Python
60 lines
1.5 KiB
Python
# /// script
|
|
# requires-python = ">=3.10"
|
|
# dependencies = ["numpy", "opencv-python"]
|
|
# ///
|
|
"""
|
|
@effect hue_shift
|
|
@version 1.0.0
|
|
@author artdag
|
|
|
|
@description
|
|
Rotates the hue of all colors by a specified amount (0-360 degrees).
|
|
Creates rainbow cycling effects when animated over time.
|
|
|
|
@param shift float
|
|
@range 0 360
|
|
@default 0
|
|
Hue rotation in degrees. 0/360 = unchanged, 180 = complementary colors.
|
|
|
|
@example
|
|
(effect hue_shift :shift 180) ; complementary colors
|
|
|
|
@example
|
|
;; Rainbow cycling based on time (use with frame counter in state)
|
|
(effect hue_shift :shift (bind beat_position :range [0 360]))
|
|
"""
|
|
|
|
import numpy as np
|
|
import cv2
|
|
|
|
|
|
def process_frame(frame: np.ndarray, params: dict, state: dict) -> tuple:
|
|
"""
|
|
Shift hue of a video frame.
|
|
|
|
Args:
|
|
frame: Input frame as numpy array (H, W, 3) RGB uint8
|
|
params: Effect parameters
|
|
- shift: hue rotation in degrees (default 0)
|
|
state: Persistent state dict (unused)
|
|
|
|
Returns:
|
|
Tuple of (processed_frame, new_state)
|
|
"""
|
|
shift = params.get("shift", 0)
|
|
|
|
if shift == 0:
|
|
return frame, state
|
|
|
|
# Convert RGB to HSV (OpenCV uses H: 0-179, S: 0-255, V: 0-255)
|
|
hsv = cv2.cvtColor(frame, cv2.COLOR_RGB2HSV)
|
|
|
|
# Shift hue (OpenCV hue is 0-179, so divide by 2)
|
|
hue_shift = int((shift % 360) / 2)
|
|
hsv[:, :, 0] = (hsv[:, :, 0].astype(np.int16) + hue_shift) % 180
|
|
|
|
# Convert back to RGB
|
|
result = cv2.cvtColor(hsv, cv2.COLOR_HSV2RGB)
|
|
|
|
return result, state
|