- Add stream_sexp_generic.py: fully generic sexp interpreter - Add streaming primitives for video sources and audio analysis - Add config system for external sources and audio files - Add templates for reusable scans and macros - Fix video/audio stream mapping in file output - Add dynamic source cycling based on sources array length - Remove old Python effect files (migrated to sexp) - Update sexp effects to use namespaced primitives Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
59 lines
2.8 KiB
Common Lisp
59 lines
2.8 KiB
Common Lisp
;; N-way weighted blend effect
|
|
;; Streaming-compatible: pass inputs as a list of frames
|
|
;; Usage: (blend_multi :inputs [(read a) (read b) (read c)] :weights [0.3 0.4 0.3])
|
|
;;
|
|
;; Parameters:
|
|
;; inputs - list of N frames to blend
|
|
;; weights - list of N floats, one per input (resolved per-frame)
|
|
;; mode - blend mode applied when folding each frame in:
|
|
;; "alpha" — pure weighted average (default)
|
|
;; "multiply" — darken by multiplication
|
|
;; "screen" — lighten (inverse multiply)
|
|
;; "overlay" — contrast-boosting midtone blend
|
|
;; "soft-light" — gentle dodge/burn
|
|
;; "hard-light" — strong dodge/burn
|
|
;; "color-dodge" — brightens towards white
|
|
;; "color-burn" — darkens towards black
|
|
;; "difference" — absolute pixel difference
|
|
;; "exclusion" — softer difference
|
|
;; "add" — additive (clamped)
|
|
;; "subtract" — subtractive (clamped)
|
|
;; "darken" — per-pixel minimum
|
|
;; "lighten" — per-pixel maximum
|
|
;; resize_mode - how to match frame dimensions (fit, crop, stretch)
|
|
;;
|
|
;; Uses a left-fold over inputs[1..N-1]. At each step the running
|
|
;; opacity is: w[i] / (w[0] + w[1] + ... + w[i])
|
|
;; which produces the correct normalised weighted result.
|
|
|
|
(require-primitives "image" "blending")
|
|
|
|
(define-effect blend_multi
|
|
:params (
|
|
(inputs :type list :default [])
|
|
(weights :type list :default [])
|
|
(mode :type string :default "alpha")
|
|
(resize_mode :type string :default "fit")
|
|
)
|
|
(let [n (len inputs)
|
|
;; Target dimensions from first frame
|
|
target-w (image:width (nth inputs 0))
|
|
target-h (image:height (nth inputs 0))
|
|
;; Fold over indices 1..n-1
|
|
;; Accumulator is (list blended-frame running-weight-sum)
|
|
seed (list (nth inputs 0) (nth weights 0))
|
|
result (reduce (range 1 n) seed
|
|
(lambda (pair i)
|
|
(let [acc (nth pair 0)
|
|
running (nth pair 1)
|
|
w (nth weights i)
|
|
new-running (+ running w)
|
|
opacity (/ w (max new-running 0.001))
|
|
f (image:resize (nth inputs i) target-w target-h "linear")
|
|
;; Apply blend mode then mix with opacity
|
|
blended (if (= mode "alpha")
|
|
(blending:blend-images acc f opacity)
|
|
(blending:blend-images acc (blending:blend-mode acc f mode) opacity))]
|
|
(list blended new-running))))]
|
|
(nth result 0)))
|