- Fix scoping bug in __init__.py where module_version was undefined after ImportError, causing incorrect DA3_AVAILABLE check - Add defensive try/except guard around depth_anything_3 import in depth_estimation_node.py to handle edge cases - Move logger initialization before DA3 import to prevent NameError - Bump version to 1.3.6 Resolves node loading failures on RunPod with PyTorch nightly builds where the error node "Depth Estimation (Error)" was incorrectly shown even when DA3 was intentionally not installed.
162 lines
5.5 KiB
Python
162 lines
5.5 KiB
Python
"""
|
|
ComfyUI Depth Estimation Node
|
|
A custom node for depth map estimation using Depth-Anything models.
|
|
"""
|
|
|
|
import os
|
|
import logging
|
|
import importlib.util
|
|
|
|
# Setup logging
|
|
logging.basicConfig(level=logging.INFO)
|
|
logger = logging.getLogger("DepthEstimation")
|
|
|
|
# Version info
|
|
__version__ = "1.3.6"
|
|
|
|
# Node class mappings - will be populated based on dependency checks
|
|
NODE_CLASS_MAPPINGS = {}
|
|
NODE_DISPLAY_NAME_MAPPINGS = {}
|
|
|
|
# Web extension info for ComfyUI
|
|
WEB_DIRECTORY = "./js"
|
|
|
|
# Graceful dependency checking
|
|
required_dependencies = {
|
|
"torch": "2.0.0",
|
|
"transformers": "4.20.0",
|
|
"numpy": "1.23.0",
|
|
"PIL": "9.2.0", # Pillow is imported as PIL
|
|
"timm": "0.6.12",
|
|
"huggingface_hub": "0.16.0"
|
|
}
|
|
|
|
# Optional dependencies for DA3 (Depth Anything V3) support
|
|
# Node works without these but DA3 models will not be available
|
|
optional_dependencies = {
|
|
"depth_anything_3": "0.0.0" # For DA3 models
|
|
}
|
|
|
|
missing_dependencies = []
|
|
DA3_AVAILABLE = False
|
|
|
|
# Check each dependency
|
|
for module_name, min_version in required_dependencies.items():
|
|
try:
|
|
if module_name == "PIL":
|
|
# Special case for Pillow/PIL
|
|
import PIL
|
|
module_version = PIL.__version__
|
|
else:
|
|
module = __import__(module_name)
|
|
module_version = getattr(module, "__version__", "unknown")
|
|
|
|
logger.info(f"Found {module_name} version {module_version}")
|
|
except ImportError:
|
|
missing_dependencies.append(f"{module_name}>={min_version}")
|
|
logger.warning(f"Missing required dependency: {module_name}>={min_version}")
|
|
|
|
# Check optional DA3 dependencies
|
|
for module_name, min_version in optional_dependencies.items():
|
|
module_version = None # Initialize BEFORE try block to fix scoping issue
|
|
try:
|
|
module = __import__(module_name)
|
|
module_version = getattr(module, "__version__", "0.0.0")
|
|
|
|
logger.info(f"Found optional {module_name} version {module_version}")
|
|
|
|
except ImportError:
|
|
logger.info(f"Optional dependency {module_name} not installed. DA3 models will not be available.")
|
|
except Exception as e:
|
|
logger.warning(f"Error checking version for {module_name}: {e}. DA3 models might not work correctly.")
|
|
|
|
# Version comparison - now module_version is always defined
|
|
if module_name == "depth_anything_3" and module_version is not None:
|
|
try:
|
|
# Compare versions. This simple check works for "X.Y.Z" formats.
|
|
if tuple(map(int, module_version.split('.'))) >= tuple(map(int, min_version.split('.'))):
|
|
DA3_AVAILABLE = True
|
|
else:
|
|
logger.warning(f"Optional dependency {module_name} version {module_version} is older than required {min_version}. DA3 models may not be available or work correctly.")
|
|
except ValueError:
|
|
# Fallback for non-standard version strings (e.g. from git)
|
|
logger.info(f"Could not parse version {module_version} for {module_name}. Assuming compatible.")
|
|
DA3_AVAILABLE = True
|
|
|
|
if missing_dependencies:
|
|
# Create placeholder node with dependency error
|
|
class DependencyErrorNode:
|
|
"""Placeholder node that shows dependency installation instructions."""
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {"required": {}}
|
|
|
|
RETURN_TYPES = ("STRING",)
|
|
FUNCTION = "error_message"
|
|
CATEGORY = "depth"
|
|
|
|
def error_message(self):
|
|
missing = ", ".join(missing_dependencies)
|
|
message = f"Dependencies missing: {missing}. Please install with: pip install {' '.join(missing_dependencies)}"
|
|
print(f"DepthEstimation Node Error: {message}")
|
|
return (message,)
|
|
|
|
# Register the error node instead of the real node
|
|
NODE_CLASS_MAPPINGS = {
|
|
"DepthEstimationNode": DependencyErrorNode
|
|
}
|
|
|
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
|
"DepthEstimationNode": "Depth Estimation (Missing Dependencies)"
|
|
}
|
|
else:
|
|
# All dependencies are available, try to import the actual node
|
|
try:
|
|
from .depth_estimation_node import DepthEstimationNode
|
|
|
|
# Register the actual depth estimation node
|
|
NODE_CLASS_MAPPINGS = {
|
|
"DepthEstimationNode": DepthEstimationNode
|
|
}
|
|
|
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
|
"DepthEstimationNode": "Depth Estimation"
|
|
}
|
|
except Exception as e:
|
|
# Capture any import errors that might occur with transformers
|
|
logger.error(f"Error importing depth estimation node: {str(e)}")
|
|
|
|
# Create a more specific error node
|
|
class TransformersErrorNode:
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {"required": {}}
|
|
|
|
RETURN_TYPES = ("STRING",)
|
|
FUNCTION = "error_message"
|
|
CATEGORY = "depth"
|
|
|
|
def error_message(self):
|
|
if "Descriptors cannot be created directly" in str(e):
|
|
message = "Protobuf version conflict. Run: pip install protobuf==3.20.3"
|
|
else:
|
|
message = f"Error loading depth estimation: {str(e)}"
|
|
return (message,)
|
|
|
|
NODE_CLASS_MAPPINGS = {
|
|
"DepthEstimationNode": TransformersErrorNode
|
|
}
|
|
|
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
|
"DepthEstimationNode": "Depth Estimation (Error)"
|
|
}
|
|
|
|
# Module exports
|
|
__all__ = [
|
|
"NODE_CLASS_MAPPINGS",
|
|
"NODE_DISPLAY_NAME_MAPPINGS",
|
|
"__version__",
|
|
"WEB_DIRECTORY",
|
|
"DA3_AVAILABLE"
|
|
] |