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>
This commit is contained in:
88
effects/emboss.py
Normal file
88
effects/emboss.py
Normal file
@@ -0,0 +1,88 @@
|
||||
# /// script
|
||||
# requires-python = ">=3.10"
|
||||
# dependencies = ["numpy", "opencv-python"]
|
||||
# ///
|
||||
"""
|
||||
@effect emboss
|
||||
@version 1.0.0
|
||||
@author artdag
|
||||
|
||||
@description
|
||||
Emboss / relief effect. Creates a 3D raised appearance by highlighting
|
||||
edges from a simulated light direction. Great for sculptural looks.
|
||||
|
||||
@param strength float
|
||||
@range 0.5 3
|
||||
@default 1.0
|
||||
Emboss intensity.
|
||||
|
||||
@param direction float
|
||||
@range 0 360
|
||||
@default 135
|
||||
Light direction in degrees. Bind to beat for rotating light.
|
||||
|
||||
@param blend float
|
||||
@range 0 1
|
||||
@default 0.3
|
||||
Blend with original (0 = full emboss, 1 = original).
|
||||
|
||||
@example
|
||||
(effect emboss :strength 1.5)
|
||||
|
||||
@example
|
||||
;; Rotating light direction
|
||||
(effect emboss :direction (bind beat_position :range [0 360]))
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import cv2
|
||||
|
||||
|
||||
def process_frame(frame: np.ndarray, params: dict, state: dict) -> tuple:
|
||||
"""
|
||||
Apply emboss effect to a video frame.
|
||||
|
||||
Args:
|
||||
frame: Input frame as numpy array (H, W, 3) RGB uint8
|
||||
params: Effect parameters
|
||||
- strength: emboss intensity (default 1.0)
|
||||
- direction: light angle in degrees (default 135)
|
||||
- blend: mix with original (default 0.3)
|
||||
state: Persistent state dict (unused)
|
||||
|
||||
Returns:
|
||||
Tuple of (processed_frame, new_state)
|
||||
"""
|
||||
strength = params.get("strength", 1.0)
|
||||
direction = params.get("direction", 135)
|
||||
blend = params.get("blend", 0.3)
|
||||
|
||||
# Calculate kernel based on direction
|
||||
angle_rad = np.deg2rad(direction)
|
||||
dx = np.cos(angle_rad)
|
||||
dy = np.sin(angle_rad)
|
||||
|
||||
# Create emboss kernel
|
||||
kernel = np.array([
|
||||
[-strength * dy - strength * dx, -strength * dy, -strength * dy + strength * dx],
|
||||
[-strength * dx, 1, strength * dx],
|
||||
[strength * dy - strength * dx, strength * dy, strength * dy + strength * dx]
|
||||
], dtype=np.float32)
|
||||
|
||||
# Apply to grayscale
|
||||
gray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY).astype(np.float32)
|
||||
embossed = cv2.filter2D(gray, -1, kernel)
|
||||
|
||||
# Normalize
|
||||
embossed = embossed + 128
|
||||
embossed = np.clip(embossed, 0, 255)
|
||||
|
||||
# Convert to RGB
|
||||
embossed_rgb = cv2.cvtColor(embossed.astype(np.uint8), cv2.COLOR_GRAY2RGB)
|
||||
|
||||
# Blend with original
|
||||
if blend > 0:
|
||||
result = frame.astype(np.float32) * blend + embossed_rgb.astype(np.float32) * (1 - blend)
|
||||
return np.clip(result, 0, 255).astype(np.uint8), state
|
||||
|
||||
return embossed_rgb, state
|
||||
Reference in New Issue
Block a user