Files
Limbicnation-ComfyUIDepthEs…/depth_estimation_node.py
T
limbicnation 778ea98dd2 feat: Depth estimation node with enhanced error handling and post-processing
adds input validation and error handling
implements auto-contrast and gamma correction options
fixes indentation and formatting issues
improves performance with conditional filters
2024-11-29 18:58:21 +01:00

135 lines
4.7 KiB
Python

"""
ComfyUI Depth Estimation Node - Verified and Enhanced Version
A custom node for depth map estimation using transformer models.
"""
import os
import numpy as np
import torch
from transformers import pipeline
from PIL import Image, ImageFilter, ImageOps
import folder_paths
from comfy.model_management import get_torch_device
def ensure_odd(value):
"""Ensure the value is an odd integer."""
value = int(value)
return value if value % 2 == 1 else value + 1
def convert_path(path):
"""Convert path for compatibility between Windows and WSL."""
if os.name == 'nt':
return path.replace('\\', '/')
return path
def gamma_correction(img, gamma=1.0):
"""Apply gamma correction to the image."""
if not isinstance(img, (Image.Image, np.ndarray)):
raise TypeError("Input must be PIL Image or numpy array")
inv_gamma = 1.0 / gamma
table = [((i / 255.0) ** inv_gamma) * 255 for i in range(256)]
table = np.array(table, np.uint8)
return Image.fromarray(np.array(img).astype(np.uint8)).point(lambda i: table[i])
def auto_gamma_correction(image):
"""Automatically adjust gamma correction for the image."""
image_array = np.array(image).astype(np.float32) / 255.0
mean_luminance = np.mean(image_array)
if mean_luminance <= 0:
return image
gamma = np.log(0.5) / np.log(mean_luminance)
return gamma_correction(image, gamma=gamma)
def auto_contrast(image):
"""Apply automatic contrast adjustment to the image."""
return ImageOps.autocontrast(image)
class DepthEstimationNode:
def __init__(self):
self.device = get_torch_device()
self.depth_estimator = None
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"image": ("IMAGE",),
"blur_radius": ("FLOAT", {"default": 2.0, "min": 0.0, "max": 10.0}),
"median_size": ("INT", {"default": 5, "min": 3, "max": 11, "step": 2}),
"apply_auto_contrast": ("BOOLEAN", {"default": True}),
"apply_gamma": ("BOOLEAN", {"default": True})
}
}
RETURN_TYPES = ("IMAGE",)
FUNCTION = "estimate_depth"
CATEGORY = "image/processing"
def ensure_model_loaded(self):
"""Ensure the depth estimation model is loaded."""
if self.depth_estimator is None:
try:
self.depth_estimator = pipeline("depth-estimation", device=self.device)
except Exception as e:
raise RuntimeError(f"Failed to load depth estimation model: {str(e)}")
def estimate_depth(self, image, blur_radius=2.0, median_size=5,
apply_auto_contrast=True, apply_gamma=True):
"""
Estimate depth from input image with optional post-processing.
Args:
image (torch.Tensor): Input image tensor (B,H,W,C)
blur_radius (float): Gaussian blur radius
median_size (int): Median filter kernel size
apply_auto_contrast (bool): Whether to apply automatic contrast
apply_gamma (bool): Whether to apply gamma correction
Returns:
tuple(torch.Tensor): Processed depth map tensor (B,H,W)
"""
self.ensure_model_loaded()
# Input validation
if not isinstance(image, np.ndarray) or image.ndim != 4:
raise ValueError("Input image must be 4D numpy array (B,H,W,C)")
# Convert image to PIL
image_pil = Image.fromarray((image[0] * 255).astype(np.uint8))
try:
# Generate depth map
depth_map = self.depth_estimator(image_pil)["depth"]
# Post-processing pipeline
median_size = ensure_odd(median_size) # Ensure odd kernel size
if blur_radius > 0:
depth_map = depth_map.filter(ImageFilter.GaussianBlur(radius=blur_radius))
depth_map = depth_map.filter(ImageFilter.MedianFilter(size=median_size))
if apply_auto_contrast:
depth_map = auto_contrast(depth_map)
if apply_gamma:
depth_map = auto_gamma_correction(depth_map)
# Convert to tensor format
depth_tensor = np.array(depth_map).astype(np.float32) / 255.0
depth_tensor = depth_tensor[None, ...] # Add batch dimension
return (depth_tensor,)
except Exception as e:
raise RuntimeError(f"Depth estimation failed: {str(e)}")
# Node registration
NODE_CLASS_MAPPINGS = {
"DepthEstimationNode": DepthEstimationNode
}
NODE_DISPLAY_NAME_MAPPINGS = {
"DepthEstimationNode": "Depth Estimation"
}