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:
108
effects/bloom.py
Normal file
108
effects/bloom.py
Normal file
@@ -0,0 +1,108 @@
|
||||
# /// script
|
||||
# requires-python = ">=3.10"
|
||||
# dependencies = ["numpy", "opencv-python"]
|
||||
# ///
|
||||
"""
|
||||
@effect bloom
|
||||
@version 1.0.0
|
||||
@author artdag
|
||||
|
||||
@description
|
||||
Bloom effect. Creates a soft glow around bright areas, simulating
|
||||
camera lens bloom. Great for dreamy or ethereal looks.
|
||||
|
||||
@param intensity float
|
||||
@range 0 2
|
||||
@default 0.5
|
||||
Bloom brightness.
|
||||
|
||||
@param threshold int
|
||||
@range 0 255
|
||||
@default 200
|
||||
Brightness threshold for bloom (pixels above this glow).
|
||||
|
||||
@param radius int
|
||||
@range 1 50
|
||||
@default 15
|
||||
Blur radius for the glow.
|
||||
|
||||
@param soft_threshold bool
|
||||
@default true
|
||||
Use soft threshold (gradual) vs hard threshold.
|
||||
|
||||
@param color_tint list
|
||||
@default [255, 255, 255]
|
||||
Tint color for the bloom.
|
||||
|
||||
@example
|
||||
(effect bloom :intensity 0.7 :threshold 180)
|
||||
|
||||
@example
|
||||
;; Warm bloom
|
||||
(effect bloom :intensity 0.6 :color_tint [255 200 150])
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import cv2
|
||||
|
||||
|
||||
def process_frame(frame: np.ndarray, params: dict, state: dict) -> tuple:
|
||||
"""
|
||||
Apply bloom effect to a video frame.
|
||||
|
||||
Args:
|
||||
frame: Input frame as numpy array (H, W, 3) RGB uint8
|
||||
params: Effect parameters
|
||||
- intensity: bloom brightness (default 0.5)
|
||||
- threshold: brightness cutoff (default 200)
|
||||
- radius: blur radius (default 15)
|
||||
- soft_threshold: gradual vs hard (default True)
|
||||
- color_tint: RGB tint (default white)
|
||||
state: Persistent state dict
|
||||
|
||||
Returns:
|
||||
Tuple of (processed_frame, new_state)
|
||||
"""
|
||||
intensity = params.get("intensity", 0.5)
|
||||
threshold = int(params.get("threshold", 200))
|
||||
radius = max(1, int(params.get("radius", 15)))
|
||||
soft_threshold = params.get("soft_threshold", True)
|
||||
color_tint = params.get("color_tint", [255, 255, 255])
|
||||
|
||||
if state is None:
|
||||
state = {}
|
||||
|
||||
if intensity <= 0:
|
||||
return frame, state
|
||||
|
||||
# Convert to float
|
||||
result = frame.astype(np.float32)
|
||||
|
||||
# Get brightness (luminance)
|
||||
lum = 0.299 * result[:, :, 0] + 0.587 * result[:, :, 1] + 0.114 * result[:, :, 2]
|
||||
|
||||
# Create bloom mask
|
||||
if soft_threshold:
|
||||
# Soft threshold - gradual falloff
|
||||
bloom_mask = np.clip((lum - threshold) / (255 - threshold + 1e-6), 0, 1)
|
||||
else:
|
||||
# Hard threshold
|
||||
bloom_mask = (lum > threshold).astype(np.float32)
|
||||
|
||||
# Extract bright areas
|
||||
bloom = result * bloom_mask[:, :, np.newaxis]
|
||||
|
||||
# Apply blur to create glow
|
||||
ksize = radius * 2 + 1
|
||||
bloom = cv2.GaussianBlur(bloom, (ksize, ksize), 0)
|
||||
|
||||
# Apply color tint
|
||||
if isinstance(color_tint, (list, tuple)) and len(color_tint) >= 3:
|
||||
tint = np.array(color_tint[:3], dtype=np.float32) / 255.0
|
||||
for c in range(3):
|
||||
bloom[:, :, c] *= tint[c]
|
||||
|
||||
# Add bloom to original (screen blend)
|
||||
result = result + bloom * intensity
|
||||
|
||||
return np.clip(result, 0, 255).astype(np.uint8), state
|
||||
Reference in New Issue
Block a user