Files

132 lines
4.4 KiB
Python

import torch
import numpy as np
from PIL import Image
import folder_paths
import os
import random
import json
class AnyType(str):
def __ne__(self, __value: object) -> bool:
return False
class SimpleDisplayNode:
"""
A ComfyUI node that inspects any input and displays its details (metadata, shape, etc.)
and a preview if it is an image tensor.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"any_input": (AnyType("*"),),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("text_info",)
FUNCTION = "analyze_and_display"
CATEGORY = "DLWW/Utils"
OUTPUT_NODE = True
def analyze_and_display(self, any_input):
# 1. Analyze the text/metadata
text_info = self._get_details(any_input)
# 2. Prepare the UI payload
ui_payload = {"text": [text_info]}
# 3. Check if it is an image/video and save a preview
if isinstance(any_input, torch.Tensor):
# Check if it looks like an image [B, H, W, C]
# Heuristic: 4 dims, last dim is 1, 3, or 4 (channels)
if any_input.ndim == 4 and any_input.shape[-1] in [1, 3, 4]:
preview_filename = self._save_preview(any_input)
if preview_filename:
# Append image data to the payload
ui_payload["images"] = [{
"filename": preview_filename,
"type": "temp",
"subfolder": ""
}]
return {"ui": ui_payload, "result": (text_info,)}
@staticmethod
def _save_preview(tensor):
try:
# Convert Tensor (0.0-1.0) to Numpy (0-255 uint8)
# Clip values to ensure valid color range
img_np = 255. * tensor.cpu().numpy()
img_np = np.clip(img_np, 0, 255).astype(np.uint8)
# Convert to PIL Images
images = []
for i in range(img_np.shape[0]):
# Handle 1-channel (grayscale) images
if img_np.shape[-1] == 1:
img = Image.fromarray(img_np[i].squeeze(-1), mode="L")
else:
img = Image.fromarray(img_np[i])
images.append(img)
if not images:
return None
# Generate a random filename in the temp folder
output_dir = folder_paths.get_temp_directory()
filename = f"debug_preview_{random.randint(100000, 999999)}.webp"
file_path = os.path.join(output_dir, filename)
# Save: If batch size > 1, save as animated WEBP (Video)
if len(images) > 1:
images[0].save(
file_path,
save_all=True,
append_images=images[1:],
duration=100, # 100ms per frame = 10fps
loop=0,
format="WEBP"
)
else:
images[0].save(file_path, format="WEBP")
return filename
except Exception as e:
print(f"SimpleDisplayNode: Failed to save preview: {e}")
return None
@staticmethod
def _get_details(data):
try:
if isinstance(data, torch.Tensor):
info = (
f"Type: torch.Tensor\n"
f"Shape: {list(data.shape)}\n"
f"Dtype: {data.dtype}\n"
f"Device: {data.device}\n"
f"Range: {data.min():.2f} to {data.max():.2f}\n"
f"Mean: {data.mean():.2f}, Std: {data.std():.2f}"
)
return info
elif isinstance(data, (int, float, bool, str)):
return f"Type: {type(data).__name__}\nValue: {data}"
elif isinstance(data, list):
return f"Type: List\nLength: {len(data)}\nSample: {str(data)[:200]}..."
elif isinstance(data, dict):
return f"Type: Dict\nKeys: {list(data.keys())}\nSample: {str(data)[:200]}..."
elif data is None:
return "Type: NoneType\nValue: None"
else:
# Fallback for other objects
return f"Type: {type(data).__name__}\nString Rep: {str(data)[:500]}"
except Exception as e:
return f"Error analyzing data: {e}"