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>
73 lines
1.9 KiB
Python
73 lines
1.9 KiB
Python
# /// script
|
|
# requires-python = ">=3.10"
|
|
# dependencies = ["numpy", "opencv-python"]
|
|
# ///
|
|
"""
|
|
@effect pixelate
|
|
@version 1.0.0
|
|
@author artdag
|
|
|
|
@description
|
|
Pixelate effect. Reduces resolution to create blocky, retro pixel art
|
|
look. Great for 8-bit aesthetics.
|
|
|
|
@param block_size int
|
|
@range 2 64
|
|
@default 8
|
|
Size of pixel blocks. Larger = more pixelated.
|
|
|
|
@param maintain_edges bool
|
|
@default false
|
|
Try to preserve edges while pixelating.
|
|
|
|
@example
|
|
(effect pixelate :block_size 16)
|
|
|
|
@example
|
|
;; Beat-reactive pixelation
|
|
(effect pixelate :block_size (bind bass :range [4 32]))
|
|
"""
|
|
|
|
import numpy as np
|
|
import cv2
|
|
|
|
|
|
def process_frame(frame: np.ndarray, params: dict, state: dict) -> tuple:
|
|
"""
|
|
Apply pixelate effect to a video frame.
|
|
|
|
Args:
|
|
frame: Input frame as numpy array (H, W, 3) RGB uint8
|
|
params: Effect parameters
|
|
- block_size: pixel block size (default 8)
|
|
- maintain_edges: preserve edges (default False)
|
|
state: Persistent state dict
|
|
|
|
Returns:
|
|
Tuple of (processed_frame, new_state)
|
|
"""
|
|
block_size = max(2, min(int(params.get("block_size", 8)), 64))
|
|
maintain_edges = params.get("maintain_edges", False)
|
|
|
|
if state is None:
|
|
state = {}
|
|
|
|
h, w = frame.shape[:2]
|
|
|
|
# Scale down then up to create pixelation
|
|
small_h = max(1, h // block_size)
|
|
small_w = max(1, w // block_size)
|
|
|
|
small = cv2.resize(frame, (small_w, small_h), interpolation=cv2.INTER_AREA)
|
|
result = cv2.resize(small, (w, h), interpolation=cv2.INTER_NEAREST)
|
|
|
|
if maintain_edges:
|
|
# Detect edges in original and overlay
|
|
gray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
|
|
edges = cv2.Canny(gray, 50, 150)
|
|
edges_dilated = cv2.dilate(edges, np.ones((2, 2), np.uint8))
|
|
edge_mask = edges_dilated > 0
|
|
result[edge_mask] = frame[edge_mask]
|
|
|
|
return result, state
|