diff --git a/README.md b/README.md index e789970..10af496 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ A comprehensive collection of ComfyUI custom nodes organized by category for enh ### 🔸 Post Process Tools - **Realistic Film Grain**: Apply photographic film grain simulation with ISO control +- **ARRI Halation Effect**: Simulate film bloom around highlights with red-orange tint - **Spatially Correlated Noise**: Advanced noise generation using scipy for realistic grain patterns - **Color Channel Control**: Option for monochrome or colored grain effects @@ -96,12 +97,20 @@ Find the nodes under the **post_process** category: - **Strength**: Overall grain intensity (0.0 to 1.0) - **ISO**: Simulated film sensitivity (100-6400) - **Grain Size**: Particle size control (1.0-10.0) - - **Colored**: Toggle between monochrome and colored grain + - **Colored**: Toggle between monochrome or colored grain -#### Example Workflow: +- **🎬 CYH Post Process | ARRI Halation** - Apply ARRI-style film bloom effect + - **Threshold**: Highlight detection threshold (0-255) + - **Blur Size**: Glow effect size (1-101, odd numbers) + - **Intensity**: Halation strength (0.0 to 1.0) + +#### Example Workflows: ``` [KSampler] → [VAE Decode] → [🎬 CYH Post Process | Film Grain] ``` +``` +[KSampler] → [VAE Decode] → [🎬 CYH Post Process | ARRI Halation] +``` ## 📱 Phone Screen Resolutions diff --git a/categories/post_process.py b/categories/post_process.py index 1c3207e..54434b1 100644 --- a/categories/post_process.py +++ b/categories/post_process.py @@ -143,11 +143,114 @@ class CYHFilmGrainNode: return (result,) +def arri_halation_effect(image_tensor, threshold=220, blur_size=25, intensity=0.6): + """ + Apply ARRI-style halation effect to simulate film bloom around highlights + + Args: + image_tensor: Input image tensor (B, H, W, C) + threshold: Highlight threshold (0-255) + blur_size: Gaussian blur kernel size (odd number) + intensity: Halation effect strength (0.0 to 1.0) + + Returns: + Tensor with applied halation effect + """ + import cv2 + import numpy as np + + # Convert to numpy + image_np = image_tensor.cpu().numpy() + + # Convert from (B, H, W, C) to (H, W, C) for single image processing + if len(image_np.shape) == 4: + image_np = image_np[0] # Take first image in batch + + # Convert from float [0,1] to uint8 [0,255] + image_uint8 = (image_np * 255).astype(np.uint8) + + # Convert BGR to RGB (OpenCV uses BGR, ComfyUI uses RGB) + image_bgr = cv2.cvtColor(image_uint8, cv2.COLOR_RGB2BGR) + + # Convert to float32 for precision + img_float = image_bgr.astype(np.float32) / 255.0 + + # Step 1: Extract Highlights (luminance) + lum = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2GRAY) + _, highlights = cv2.threshold(lum, threshold, 255, cv2.THRESH_BINARY) + highlights = highlights.astype(np.float32) / 255.0 + + # Step 2: Create halation color mask (red-orange tint) + # Start with zero image, add shifted values in R and G channels + halation_mask = np.zeros_like(img_float) + halation_mask[:, :, 2] = highlights * 1.0 # enhance red channel (BGR order: B=0, G=1, R=2) + halation_mask[:, :, 1] = highlights * 0.5 # add some green for orange tint + + # Step 3: Blur the mask to create glow + # Ensure blur_size is odd + blur_size = max(1, blur_size) + if blur_size % 2 == 0: + blur_size += 1 + + glow = cv2.GaussianBlur(halation_mask, (blur_size, blur_size), 0) + + # Step 4: Additive blend glow onto original + result = np.clip(img_float + glow * intensity, 0, 1) + + # Convert back to RGB and uint8 + result_uint8 = (result * 255).astype(np.uint8) + result_rgb = cv2.cvtColor(result_uint8, cv2.COLOR_BGR2RGB) + + # Convert back to float [0,1] and restore batch dimension + result_float = result_rgb.astype(np.float32) / 255.0 + result_float = np.expand_dims(result_float, axis=0) # Add batch dimension + + # Convert back to tensor + return torch.from_numpy(result_float).to(image_tensor.device) + + +class CYHARRHalationNode: + """ + A post-processing node that applies ARRI-style halation effect to images. + Simulates the film bloom around highlights with customizable parameters. + """ + + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "image": ("IMAGE",), + "threshold": ("INT", {"default": 220, "min": 0, "max": 255, "step": 1}), + "blur_size": ("INT", {"default": 25, "min": 1, "max": 101, "step": 2}), + "intensity": ("FLOAT", {"default": 0.6, "min": 0.0, "max": 1.0, "step": 0.01}), + }, + } + + RETURN_TYPES = ("IMAGE",) + FUNCTION = "apply_halation" + CATEGORY = POST_PROCESS_CATEGORY + + @classmethod + def IS_CHANGED(cls, **kwargs): + return "" + + @classmethod + def VALIDATE_INPUTS(cls, **kwargs): + return True + + def apply_halation(self, image, threshold, blur_size, intensity): + # Apply ARRI halation effect to the image + result = arri_halation_effect(image, threshold, blur_size, intensity) + return (result,) + + # Node registration for this category NODE_CLASS_MAPPINGS = { "CYHFilmGrainNode": CYHFilmGrainNode, + "CYHARRHalationNode": CYHARRHalationNode, } NODE_DISPLAY_NAME_MAPPINGS = { "CYHFilmGrainNode": "🎬 CYH Post Process | Film Grain", + "CYHARRHalationNode": "🎬 CYH Post Process | ARRI Halation", } \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index fab42af..37a88a1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,5 @@ torch>=2.0.0 comfyui>=1.0.0 numpy>=1.21.0 -scipy>=1.7.0 \ No newline at end of file +scipy>=1.7.0 +opencv-python>=4.5.0 \ No newline at end of file