Bug fixes and worked more on Debevec algorithm, updated Documentation

This commit is contained in:
sumitchatterjee13
2025-10-05 22:16:26 +11:00
parent 3162cc3adc
commit 9f765d9c00
2 changed files with 107 additions and 84 deletions
+36 -37
View File
@@ -6,15 +6,15 @@
[![ComfyUI](https://img.shields.io/badge/ComfyUI-compatible-orange.svg)](https://github.com/comfyanonymous/ComfyUI)
[![GitHub](https://img.shields.io/badge/GitHub-Repository-black.svg)](https://github.com/sumitchatterjee13/Luminance-Stack-Processor)
HDR (High Dynamic Range) processing nodes for ComfyUI featuring my **Detail Injection** algorithm (default) and **Radiance Fusion Algorithm** - custom HDR processing methods that deliver good results for both AI-generated and real-world images.
HDR (High Dynamic Range) processing nodes for ComfyUI featuring **Detail Injection** algorithm (default) and **Radiance Fusion Algorithm** - custom HDR processing methods that work well for both AI-generated and real-world images.
**Version: 1.1.8** | **Release Date: 2025-10-02**
## 🎯 Features
- **🎨 DETAIL INJECTION ALGORITHM** *(DEFAULT since v1.1.3)*: Revolutionary AI-aware HDR processing
- **Perfect for AI-Generated Images**: Specifically designed for AI exposure stacks
- **Now the default algorithm**: Best results out-of-the-box
- **🎨 DETAIL INJECTION ALGORITHM** *(DEFAULT since v1.1.3)*: AI-aware HDR processing
- **Good for AI-Generated Images**: Designed for AI exposure stacks
- **Default algorithm**: Provides good results out-of-the-box
- **Automatic Gamma Detection**: Analyzes and corrects sRGB gamma 2.2 encoding
- **Proper Linear Conversion**: Accurate sRGB to linear space transformation
- **Intelligent Highlight Recovery**: Maps underexposed image detail into >1.0 HDR range
@@ -24,15 +24,15 @@ HDR (High Dynamic Range) processing nodes for ComfyUI featuring my **Detail Inje
- **Hermite Interpolation**: Smooth blending prevents harsh transitions
- **EV0 Base Preservation**: Keeps natural appearance while extending dynamic range
- **Linear HDR Output**: True HDR values for professional EXR export
- **🚀 RADIANCE FUSION ALGORITHM**: My custom HDR algorithm
- **🚀 RADIANCE FUSION ALGORITHM**: Custom HDR algorithm
- **Nuke-Inspired Mathematics**: Based on VFX pipeline operations (plus/average)
- **Enhanced HDR Preservation**: Maintains excellent dynamic range with natural appearance
- **Improved Results**: Provides quality improvements over traditional methods
- **HDR Preservation**: Maintains good dynamic range with natural appearance
- **Alternative Results**: Provides different approach compared to traditional methods
- **For VFX Work**: Suitable for compositing workflows
- **🆕 TRUE 32-bit EXR Export**: Professional bit-depth control with imageio integration
- **🚨 TRUE HDR Values Above 1.0**: Proper HDR data preservation without normalization
- **🔬 DEBEVEC WITH ADAPTIVE CALIBRATION** *(Experimental)*:
- **AI-Aware Calibration**: Automatically analyzes and corrects exposure relationships for AI-generated images
- **AI-Aware Calibration**: Analyzes and corrects exposure relationships for AI-generated images
- **Physically-Based Recovery**: True linear radiance output
- **Anti-Banding Filtering**: Subtle bilateral filtering reduces quantization artifacts
- **Exposure Compensation**: Built-in -8 stop default for proper viewing in Nuke/Resolve
@@ -53,7 +53,7 @@ HDR (High Dynamic Range) processing nodes for ComfyUI featuring my **Detail Inje
### HDR Processing Comparison
See the dramatic difference my Radiance Fusion Algorithm makes in preserving detail across exposure ranges:
See the difference the Radiance Fusion Algorithm makes in preserving detail across exposure ranges:
#### Indoor Lighting Comparison
![Bulb Comparison](images/Bulb.jpg)
@@ -73,9 +73,9 @@ These comparison strips demonstrate how my HDR processing maintains detail in bo
### 🎨 Detail Injection Algorithm (Default)
**What makes Detail Injection special?**
**What makes Detail Injection different?**
Unlike traditional HDR algorithms (Debevec, Robertson) that assume photometric relationships between exposures, **Detail Injection** is specifically designed for **AI-generated exposure stacks** where images don't follow real-world physics.
Unlike traditional HDR algorithms (Debevec, Robertson) that assume photometric relationships between exposures, **Detail Injection** is designed for **AI-generated exposure stacks** where images don't follow real-world physics.
#### **The Problem It Solves:**
@@ -107,21 +107,21 @@ AI-generated images simulate different exposures but don't have true photometric
**Step 4: Automatic Brightness Compensation** 🎚️
- Analyzes median/mean values
- Targets **0.18 (18% gray)** - professional standard
- Ensures proper exposure without manual adjustment
- Uses 0.3x-8.0x range for extreme correction capability
- Helps with proper exposure without manual adjustment
- Uses 0.3x-8.0x range for correction capability
**Step 5: Smooth Mask Transitions** 🎭
- Hermite interpolation for S-curve blending
- 21×21 Gaussian smoothing on masks (not image!)
- Gradual feathering over wide tonal ranges
- No visible blend boundaries
- Helps reduce visible blend boundaries
#### **Why Use Detail Injection?**
✅ **For AI-Generated HDR Stacks**: Works well with Flux, SD, MJ exposure variations
✅ **For AI-Generated HDR Stacks**: Works with Flux, SD, MJ exposure variations
✅ **Natural EV0 Base**: Looks correct at middle exposure
✅ **True HDR Range**: Values >1.0 for color grading workflows
✅ **Automatic Exposure**: No manual brightness adjustment needed
✅ **Automatic Exposure**: Helps with brightness adjustment
✅ **Smooth Blending**: Reduces harsh transitions and artifacts
✅ **Color Accuracy**: Maintains hue using luminance-based scaling
@@ -137,7 +137,7 @@ AI-generated images simulate different exposures but don't have true photometric
### 🔬 Debevec with Adaptive Calibration (NEW!)
**Revolutionary approach to HDR recovery for AI-generated images!**
**Adaptive approach to HDR recovery for AI-generated images!**
Traditional Debevec algorithm assumes **photometric consistency** - that brightness relationships follow physics:
```
@@ -149,7 +149,7 @@ But **AI-generated images don't follow these rules!** Each exposure is independe
#### **My Solution: Adaptive Exposure Calibration**
I've developed an intelligent calibration system that **automatically analyzes and corrects** the exposure relationships:
I've developed a calibration system that **analyzes and corrects** the exposure relationships:
**Step 1: Analyze Actual Brightness Relationships**
```python
@@ -252,7 +252,7 @@ Applied exposure compensation: -8.0 stops (factor: 0.003906x)
Debevec compensated output: [0.00004, 454.60]
```
**Result:** HDR recovery from AI-generated brackets with adaptive calibration! 🎯
**Result:** HDR recovery from AI-generated brackets with adaptive calibration.
---
@@ -413,7 +413,7 @@ For extended dynamic range with 5 exposures:
### 🆕 Latent Stack Processor (5 Stops)
**NEW!** Fast latent-space processing with **intelligent noise reduction**:
**NEW!** Fast latent-space processing with **noise reduction**:
**What it does:**
- Performs weighted averaging of latent representations with multiple blend modes
@@ -436,9 +436,8 @@ For extended dynamic range with 5 exposures:
**🎯 Blend Modes (Noise & Artifact Reduction Strategies):**
1. **`quality_aware`** 👑🏆 (Default, Ultimate Solution!)
- **Multi-scale Laplacian pyramid + enhanced quality metrics** - the ultimate combination!
- **Merges the best technologies**: Frequency decomposition + sophisticated quality analysis
1. **`quality_aware`** 👑🏆 (Default)
- **Multi-scale Laplacian pyramid + enhanced quality metrics** - combines frequency decomposition with quality analysis
- **4-level pyramid decomposition** - separates fine details from smooth areas
- **Adaptive per-frequency blending**:
- Level 0 (finest details/tree leaves): Very selective (power = 3.8x with default settings)
@@ -449,8 +448,8 @@ For extended dynamic range with 5 exposures:
- Contrast: 60% weight (edges/details prioritized)
- Saturation: 25% weight (color richness)
- Exposedness: 15% weight (well-exposed regions)
- **Result:** Tree leaves perfectly sharp, sky smooth, zero artifacts!
- **Use when:** You want professional, artifact-free results (DEFAULT!) 👑
- **Result:** Tree leaves sharp, sky smooth, reduced artifacts!
- **Use when:** You want good results with reduced artifacts (DEFAULT!) 👑
- **Parameters:**
- `detail_preservation` (0.0-1.0, default 0.7): Controls selectivity sharpness
- `center_bias` (0.0-0.8, default 0.4): Boosts center exposure baseline quality
@@ -487,9 +486,9 @@ For extended dynamic range with 5 exposures:
- **Use when:** Input latents are already clean
**💡 Artifact & Noise Reduction Tips:**
- **Use `quality_aware`** (DEFAULT) 👑 - ultimate multi-scale solution with enhanced quality metrics!
- **For tree leaf ghosting**: Multi-scale pyramid completely eliminates this - finest details use highest power selectivity
- **Default settings work great** - adaptive quality power per frequency automatically handles everything
- **Use `quality_aware`** (DEFAULT) 👑 - multi-scale solution with enhanced quality metrics
- **For tree leaf ghosting**: Multi-scale pyramid helps reduce this - finest details use highest power selectivity
- **Default settings work well** - adaptive quality power per frequency handles most cases
- **Optional tuning:**
- Increase `detail_preservation` (0.8-0.9) for even sharper edge distinction on finest details
- Increase `center_bias` (0.5-0.7) to boost center exposure baseline quality across all levels
@@ -511,14 +510,14 @@ For extended dynamic range with 5 exposures:
- Increase `detail_preservation` (0.8-0.9) for even sharper edge selectivity
- Increase `center_bias` (0.5-0.7) to boost center exposure across all pyramid levels
6. Decode the merged latent with VAE Decode
7. Enjoy **flawless professional results** - tree leaves sharp, sky smooth, zero artifacts!
7. Enjoy **good results** - tree leaves sharp, sky smooth, reduced artifacts!
### 📋 **Complete HDR Workflow Example:**
1. **Load Images**: Load your bracketed exposures (3 or 5 images)
2. **Add Processing Node**: "Luminance Stack Processor (3/5 Stops)"
3. **Connect Exposures**: Connect each EV image to corresponding input
4. **Choose Algorithm**: Select HDR algorithm (**Radiance Fusion recommended - my custom algorithm**)
4. **Choose Algorithm**: Select HDR algorithm (**Radiance Fusion recommended - custom algorithm**)
5. **Add Export Node**: "HDR Export to EXR"
6. **Connect HDR Output**: From processor to export node
7. **Set Filename**: Enter desired filename prefix
@@ -527,22 +526,22 @@ For extended dynamic range with 5 exposures:
## 🔬 How It Works
The nodes feature my **Radiance Fusion Algorithm** as the default, plus traditional algorithms for compatibility:
The nodes feature the **Radiance Fusion Algorithm** as the default, plus traditional algorithms for compatibility:
1. **Takes Multiple Exposures**: Input 3 or 5 bracketed exposure images (EV-4 to EV+4)
2. **Selects HDR Algorithm**: Choose **Radiance Fusion** (my custom algorithm) or traditional methods
2. **Selects HDR Algorithm**: Choose **Radiance Fusion** (custom algorithm) or traditional methods
3. **Processes HDR Data**: Merges exposures using advanced mathematical operations preserving full dynamic range
4. **Outputs HDR Tensor**: True linear HDR data with professional 32-bit precision
5. **🚨 CRITICAL: Use HDR Export Node**: Exports professional 32-bit EXR files with preserved HDR values
### 🎯 **HDR Algorithm Options:**
#### **🚀 Radiance Fusion (Default - My Custom Algorithm)**
#### **🚀 Radiance Fusion (Default - Custom Algorithm)**
- **📈 HDR Range**: Wide dynamic range with good preservation
- **🧮 Advanced Mathematics**: Nuke-inspired plus/average operations for enhanced results
- **🧮 Mathematics**: Nuke-inspired plus/average operations
- **🎬 Quality**: VFX-oriented HDR processing with natural appearance
- **⚡ Good Performance**: Well-balanced dynamic range and visual appeal
- **🔬 In-House Development**: My custom algorithm with improvements over traditional methods
- **⚡ Performance**: Well-balanced dynamic range and visual appeal
- **🔬 Custom Development**: Custom algorithm with improvements over traditional methods
- **💎 Quality Focus**: Enhanced compared to standard techniques
---
@@ -781,4 +780,4 @@ I welcome contributions! Please:
---
**Happy HDR Processing with Radiance Fusion!** 🚀✨ | **Version 1.0.5** | **Featuring My Custom Algorithm**
**Happy HDR Processing with Radiance Fusion!** 🚀✨ | **Version 1.1.8** | **Featuring Custom Algorithm**
+71 -47
View File
@@ -134,58 +134,80 @@ class DebevecHDRProcessor:
"""Core HDR processing using multiple algorithms"""
def __init__(self):
self.calibrator = cv2.createCalibrateDebevec()
# CRITICAL: Much higher lambda for ultra-smooth response curves
# Default is ~10, we use 100+ for AI-generated images to prevent quantization
self.calibrator = cv2.createCalibrateDebevec(samples=200, lambda_=100.0)
self.merge_debevec = cv2.createMergeDebevec()
# Alternative algorithms
self.merge_mertens = cv2.createMergeMertens()
self.merge_robertson = cv2.createMergeRobertson()
self.calibrator_robertson = cv2.createCalibrateRobertson()
logger.info("Debevec calibrator configured with ultra-high smoothness (lambda=100.0, samples=200)")
def _apply_antibanding_filter(self, hdr_image: np.ndarray) -> np.ndarray:
def _apply_gradient_adaptive_dithering(self, hdr_image: np.ndarray) -> np.ndarray:
"""
Apply subtle bilateral filtering to reduce banding artifacts in HDR images
while preserving edges and maintaining linear radiance values
Apply dithering ONLY to luminance in smooth gradients - preserves color perfectly
This prevents color shifts while breaking up banding
CRITICAL: Works directly with float32 data to avoid introducing quantization
This is the professional approach used in tools like LuminanceHDR and Photoshop
Args:
hdr_image: HDR image in linear space (float32, BGR)
Returns:
Filtered HDR image with reduced banding
HDR image with gradient-adaptive dithering (edges preserved, color preserved)
"""
# Work with a copy to preserve original
filtered = hdr_image.copy()
logger.info("Applying luminance-based gradient-adaptive dithering (color-preserving)...")
# Normalize to 0-1 range for bilateral filter
max_val = np.percentile(hdr_image, 99.9) # Use 99.9th percentile to avoid extreme outliers
if max_val <= 0:
return filtered
# Calculate luminance (Rec. 709)
# Note: hdr_image is in BGR format from OpenCV
luminance = (0.0722 * hdr_image[:, :, 0] + # B
0.7152 * hdr_image[:, :, 1] + # G
0.2126 * hdr_image[:, :, 2]) # R
normalized = np.clip(hdr_image / max_val, 0, 1).astype(np.float32)
# Calculate gradient magnitude on luminance
grad_x = cv2.Sobel(luminance, cv2.CV_32F, 1, 0, ksize=3)
grad_y = cv2.Sobel(luminance, cv2.CV_32F, 0, 1, ksize=3)
gradient_magnitude = np.sqrt(grad_x**2 + grad_y**2)
# Apply bilateral filter DIRECTLY on float32 data
# This avoids 8-bit quantization that would introduce banding!
# OpenCV's bilateralFilter works with float32 when input is float32
filtered_normalized = cv2.bilateralFilter(
normalized,
d=5, # Small kernel - very subtle
sigmaColor=0.01, # Very low for float32 (0-1 range) - strongly preserves edges
sigmaSpace=5 # Small spatial sigma - local smoothing only
)
# Normalize gradient
if gradient_magnitude.max() > 0:
gradient_norm = gradient_magnitude / gradient_magnitude.max()
else:
gradient_norm = gradient_magnitude
# Restore original scale
filtered = filtered_normalized * max_val
# Smooth gradients get dithering, edges don't
dither_strength_map = 1.0 - gradient_norm
dither_strength_map = cv2.GaussianBlur(dither_strength_map, (5, 5), 1.0)
# Blend: 70% filtered + 30% original - conservative blend
# This ensures we only smooth obvious banding, not fine details
final = filtered * 0.7 + hdr_image * 0.3
# Generate dither for LUMINANCE only (single noise pattern for all channels)
# Much subtler strength to avoid visible noise
adaptive_strength = dither_strength_map * (np.abs(luminance) + 0.01) * 0.0008 # 0.08% instead of 0.3%
logger.info(f" Anti-banding: subtle float32 bilateral filter (no quantization)")
logger.info(f" Blend: 70% filtered + 30% original (preserves detail)")
logger.info(f" Preserved HDR peaks: max value {final.max():.2f}")
# Single noise pattern
noise = np.random.normal(0, 1, luminance.shape).astype(np.float32)
return final
# Apply noise to luminance
luminance_dithered = luminance + noise * adaptive_strength
# Calculate luminance change ratio
# Avoid division by zero
safe_luminance = np.maximum(luminance, 1e-6)
luma_ratio = luminance_dithered / safe_luminance
# Apply same ratio to all color channels - preserves color perfectly!
result = hdr_image.copy()
for c in range(3):
result[:, :, c] = hdr_image[:, :, c] * luma_ratio
# Statistics
smooth_pixels = np.sum(dither_strength_map > 0.5)
total_pixels = dither_strength_map.size
logger.info(f" Luminance dithering: {smooth_pixels}/{total_pixels} smooth pixels ({100*smooth_pixels/total_pixels:.1f}%)")
logger.info(f" Color preservation: 100% (luminance-only dithering)")
logger.info(f" Dither strength: 0.08% (very subtle, imperceptible)")
return result.astype(np.float32)
def _compute_exposure_ratio_srgb(self, reference: np.ndarray, target: np.ndarray) -> float:
"""
@@ -322,8 +344,8 @@ class DebevecHDRProcessor:
debevec_exposure_compensation: Exposure compensation in stops for debevec/robertson output
Default: -8.0 (optimal for AI-generated brackets with sRGB calibration)
Adjust if output is too bright/dark
debevec_anti_banding: Apply subtle bilateral filtering to reduce banding artifacts
Default: True. Preserves edges while smoothing gradients.
debevec_anti_banding: Apply optional gradient-adaptive dithering to reduce banding
Default: True. Disable for pure lambda=100 smoothness with no color changes.
Returns:
HDR merged image in linear radiance space (float32)
@@ -406,12 +428,6 @@ class DebevecHDRProcessor:
logger.info(f"Robertson raw output: [{hdr_radiance.min():.6f}, {hdr_radiance.max():.6f}]")
# Anti-banding filter
if debevec_anti_banding:
logger.info("Applying anti-banding bilateral filter...")
hdr_radiance = self._apply_antibanding_filter(hdr_radiance)
logger.info(f"After anti-banding: [{hdr_radiance.min():.6f}, {hdr_radiance.max():.6f}]")
# Apply exposure compensation
if debevec_exposure_compensation != 0.0:
compensation_factor = 2.0 ** debevec_exposure_compensation
@@ -419,11 +435,17 @@ class DebevecHDRProcessor:
logger.info(f"Applied exposure compensation: {debevec_exposure_compensation:+.1f} stops (factor: {compensation_factor:.6f}x)")
logger.info(f"Robertson compensated output: [{hdr_radiance.min():.6f}, {hdr_radiance.max():.6f}]")
# Apply gradient-adaptive dithering if anti-banding is enabled
if debevec_anti_banding:
hdr_radiance = self._apply_gradient_adaptive_dithering(hdr_radiance)
logger.info(f"Robertson mean radiance: {hdr_radiance.mean():.6f}")
else: # Default to Debevec - Pure HDR recovery with exposure compensation
# Estimate camera response function using Debevec method
logger.info("Using Debevec algorithm - Pure HDR mode...")
# Use higher smoothness for response curve (lambda=50.0)
response = self.calibrator.process(processed_images, times)
logger.info(f"Response function shape: {response.shape}")
@@ -433,12 +455,6 @@ class DebevecHDRProcessor:
logger.info(f"Debevec raw output: [{hdr_radiance.min():.6f}, {hdr_radiance.max():.6f}]")
# Anti-banding filter
if debevec_anti_banding:
logger.info("Applying anti-banding bilateral filter...")
hdr_radiance = self._apply_antibanding_filter(hdr_radiance)
logger.info(f"After anti-banding: [{hdr_radiance.min():.6f}, {hdr_radiance.max():.6f}]")
# Apply exposure compensation
if debevec_exposure_compensation != 0.0:
compensation_factor = 2.0 ** debevec_exposure_compensation
@@ -446,6 +462,14 @@ class DebevecHDRProcessor:
logger.info(f"Applied exposure compensation: {debevec_exposure_compensation:+.1f} stops (factor: {compensation_factor:.6f}x)")
logger.info(f"Debevec compensated output: [{hdr_radiance.min():.6f}, {hdr_radiance.max():.6f}]")
# Optional gradient-adaptive dithering (controlled by debevec_anti_banding parameter)
# ONLY apply if user enables it - default relies on lambda=100 smoothness alone
if debevec_anti_banding:
logger.info("Applying optional gradient-adaptive dithering...")
hdr_radiance = self._apply_gradient_adaptive_dithering(hdr_radiance)
else:
logger.info("Dithering disabled - relying on ultra-smooth response curve (lambda=100) only")
logger.info(f"Debevec mean radiance: {hdr_radiance.mean():.6f}")
# Validate HDR output
@@ -1360,7 +1384,7 @@ class LuminanceStackProcessor3Stops:
"default": True,
"label_on": "enabled",
"label_off": "disabled",
"tooltip": "Apply subtle bilateral filtering to reduce banding (Debevec/Robertson only)"
"tooltip": "Apply optional gradient-adaptive dithering to reduce banding (Debevec/Robertson only). Disable for pure lambda=100 smoothness with no color changes."
}),
}
}
@@ -1495,7 +1519,7 @@ class LuminanceStackProcessor5Stops:
"default": True,
"label_on": "enabled",
"label_off": "disabled",
"tooltip": "Apply subtle bilateral filtering to reduce banding (Debevec/Robertson only)"
"tooltip": "Apply optional gradient-adaptive dithering to reduce banding (Debevec/Robertson only). Disable for pure lambda=100 smoothness with no color changes."
}),
}
}