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:
117
effects/cartoon.py
Normal file
117
effects/cartoon.py
Normal file
@@ -0,0 +1,117 @@
|
||||
# /// script
|
||||
# requires-python = ">=3.10"
|
||||
# dependencies = ["numpy", "scipy"]
|
||||
# ///
|
||||
"""
|
||||
@effect cartoon
|
||||
@version 1.0.0
|
||||
@author artdag
|
||||
|
||||
@description
|
||||
Cartoon / Cel-shaded effect. Simplifies colors into flat regions
|
||||
and adds dark edge outlines for a hand-drawn cartoon appearance.
|
||||
|
||||
@param detail float
|
||||
@range 0.1 1.0
|
||||
@default 0.5
|
||||
Edge detection sensitivity. Higher = more edges detected.
|
||||
|
||||
@param edge_thickness int
|
||||
@range 1 5
|
||||
@default 1
|
||||
Outline thickness in pixels.
|
||||
|
||||
@param color_levels int
|
||||
@range 2 32
|
||||
@default 6
|
||||
Number of color levels per channel.
|
||||
|
||||
@param edge_color list
|
||||
@default [0, 0, 0]
|
||||
RGB color for edges (default black).
|
||||
|
||||
@param blur_size int
|
||||
@range 0 10
|
||||
@default 2
|
||||
Pre-blur for smoother color regions.
|
||||
|
||||
@example
|
||||
(effect cartoon :detail 0.6 :color_levels 4)
|
||||
|
||||
@example
|
||||
;; Thick outlines, fewer colors
|
||||
(effect cartoon :edge_thickness 3 :color_levels 3 :blur_size 4)
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
from scipy import ndimage
|
||||
|
||||
|
||||
def process_frame(frame: np.ndarray, params: dict, state: dict) -> tuple:
|
||||
"""
|
||||
Apply cartoon effect to a video frame.
|
||||
|
||||
Args:
|
||||
frame: Input frame as numpy array (H, W, 3) RGB uint8
|
||||
params: Effect parameters
|
||||
- detail: edge sensitivity 0.1-1.0 (default 0.5)
|
||||
- edge_thickness: outline thickness (default 1)
|
||||
- color_levels: posterization levels (default 6)
|
||||
- edge_color: RGB tuple (default [0,0,0])
|
||||
- blur_size: pre-blur amount (default 2)
|
||||
state: Persistent state dict
|
||||
|
||||
Returns:
|
||||
Tuple of (processed_frame, new_state)
|
||||
"""
|
||||
detail = np.clip(params.get("detail", 0.5), 0.1, 1.0)
|
||||
edge_thickness = max(1, min(int(params.get("edge_thickness", 1)), 5))
|
||||
color_levels = max(2, min(int(params.get("color_levels", 6)), 32))
|
||||
edge_color = params.get("edge_color", [0, 0, 0])
|
||||
blur_size = max(0, int(params.get("blur_size", 2)))
|
||||
|
||||
if state is None:
|
||||
state = {}
|
||||
|
||||
h, w = frame.shape[:2]
|
||||
result = frame.copy().astype(np.float32)
|
||||
|
||||
# Step 1: Blur to reduce noise and create smoother regions
|
||||
if blur_size > 0:
|
||||
for c in range(3):
|
||||
result[:, :, c] = ndimage.uniform_filter(result[:, :, c], size=blur_size)
|
||||
|
||||
# Step 2: Posterize colors (reduce to N levels)
|
||||
step = 256 / color_levels
|
||||
result = (np.floor(result / step) * step).astype(np.uint8)
|
||||
|
||||
# Step 3: Detect edges using Sobel
|
||||
gray = np.mean(frame, axis=2).astype(np.float32)
|
||||
|
||||
sobel_x = ndimage.sobel(gray, axis=1)
|
||||
sobel_y = ndimage.sobel(gray, axis=0)
|
||||
edges = np.sqrt(sobel_x**2 + sobel_y**2)
|
||||
|
||||
# Normalize and threshold
|
||||
edge_max = edges.max()
|
||||
if edge_max > 0:
|
||||
edges = edges / edge_max
|
||||
|
||||
edge_threshold = 1.0 - detail
|
||||
edge_mask = edges > edge_threshold
|
||||
|
||||
# Dilate edges for thickness
|
||||
if edge_thickness > 1:
|
||||
struct = ndimage.generate_binary_structure(2, 1)
|
||||
for _ in range(edge_thickness - 1):
|
||||
edge_mask = ndimage.binary_dilation(edge_mask, structure=struct)
|
||||
|
||||
# Step 4: Apply edge color
|
||||
if isinstance(edge_color, (list, tuple)) and len(edge_color) >= 3:
|
||||
color = np.array(edge_color[:3], dtype=np.uint8)
|
||||
else:
|
||||
color = np.array([0, 0, 0], dtype=np.uint8)
|
||||
|
||||
result[edge_mask] = color
|
||||
|
||||
return result, state
|
||||
Reference in New Issue
Block a user