# /// script # requires-python = ">=3.10" # dependencies = ["numpy", "opencv-python"] # /// """ @effect color_cycle @version 1.0.0 @author artdag @description Color cycle effect. Shifts all hues over time creating psychedelic rainbow cycling. Great for trippy visuals. @param speed float @range 0 10 @default 1 Cycle speed (rotations per second). @param offset float @range 0 360 @default 0 Initial hue offset in degrees. @param saturation_boost float @range 0 2 @default 1 Saturation multiplier. @param mode string @enum all highlights shadows midtones @default all Which tones to affect. @example (effect color_cycle :speed 0.5) @example ;; Beat-synced color shift (effect color_cycle :offset (bind beat_position :range [0 360])) """ import numpy as np import cv2 def process_frame(frame: np.ndarray, params: dict, state: dict) -> tuple: """ Apply color cycle effect to a video frame. Args: frame: Input frame as numpy array (H, W, 3) RGB uint8 params: Effect parameters - speed: rotations per second (default 1) - offset: initial hue offset (default 0) - saturation_boost: saturation multiplier (default 1) - mode: which tones to affect (default all) state: Persistent state dict Returns: Tuple of (processed_frame, new_state) """ speed = params.get("speed", 1) offset = params.get("offset", 0) saturation_boost = max(0, min(params.get("saturation_boost", 1), 2)) mode = params.get("mode", "all") t = params.get("_time", 0) if state is None: state = {} # Calculate hue shift hue_shift = int((offset + speed * t * 360) % 360) # Convert to HSV (OpenCV uses BGR, our frame is RGB) frame_bgr = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR) hsv = cv2.cvtColor(frame_bgr, cv2.COLOR_BGR2HSV).astype(np.float32) if mode == "all": # Shift all hues hsv[:, :, 0] = (hsv[:, :, 0] + hue_shift / 2) % 180 hsv[:, :, 1] = np.clip(hsv[:, :, 1] * saturation_boost, 0, 255) else: # Calculate luminance mask lum = hsv[:, :, 2] / 255.0 if mode == "highlights": mask = np.clip((lum - 0.67) * 3, 0, 1) elif mode == "shadows": mask = np.clip(1 - lum * 3, 0, 1) else: # midtones shadow_mask = np.clip(1 - lum * 3, 0, 1) highlight_mask = np.clip((lum - 0.67) * 3, 0, 1) mask = 1 - shadow_mask - highlight_mask # Apply selective hue shift shifted_hue = (hsv[:, :, 0] + hue_shift / 2) % 180 hsv[:, :, 0] = hsv[:, :, 0] * (1 - mask) + shifted_hue * mask # Convert back hsv = np.clip(hsv, 0, 255).astype(np.uint8) result_bgr = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR) result = cv2.cvtColor(result_bgr, cv2.COLOR_BGR2RGB) return result, state