Files
nofunstudio-Node_Fun_ComfyUI/iframe_view.py
T
2025-02-01 15:28:56 -07:00

166 lines
6.2 KiB
Python

import os
import PIL
from PIL import Image, ImageOps
import hashlib
import base64
from io import BytesIO
import torch
import numpy as np
import time
import json
import traceback
from aiohttp import web
import folder_paths
import asyncio
DEBUG = True
THREEVIEW_DICT = {} # Global dict (can be retained for compatibility if needed)
# Global dictionary to store texture data (populated from the JS side via postMessage)
TEXTURE_STORE = {}
def get_global_texture_value(unique_id, key):
if unique_id in TEXTURE_STORE:
return TEXTURE_STORE[unique_id].get(key, None)
return None
def process_texture_data(data):
if not data:
print("[process_texture_data] No texture data received.")
return torch.zeros((1, 64, 64, 3), dtype=torch.float32)
try:
print("Raw data received:")
if isinstance(data, dict) and "data" in data:
if isinstance(data["data"], str):
print("[process_texture_data] Received base64 encoded texture data.")
b64data = data["data"]
if b64data.startswith("data:"):
print("[process_texture_data] Stripping data URL header from base64 string.")
b64data = b64data.split(",")[-1]
image_bytes = base64.b64decode(b64data)
pil_image = Image.open(BytesIO(image_bytes)).convert("RGB")
image = np.array(pil_image)
print(f"[process_texture_data] PIL image shape: {image.shape}")
else:
print("[process_texture_data] Received raw numeric texture data.")
width = data.get("width", 64)
height = data.get("height", 64)
pixel_array = np.array(data["data"], dtype=np.uint8)
try:
image = pixel_array.reshape(height, width, 3)
print(f"[process_texture_data] Reshaped image to: {image.shape}")
except Exception as reshape_err:
print(f"[process_texture_data] Error reshaping array: {reshape_err}")
raise
else:
print("[process_texture_data] Data format not recognized, returning default.")
return torch.zeros((1, 64, 64, 3), dtype=torch.float32)
print(f"[process_texture_data] Post reshape image stats: shape={image.shape}, min={image.min()}, max={image.max()}")
image = image.astype(np.float32) / 255.0
tensor = torch.from_numpy(image)[None,]
print(f"[process_texture_data] Tensor shape: {tensor.shape}, range=[{tensor.min().item():.3f}, {tensor.max().item():.3f}]")
return tensor
except Exception as e:
print(f"[process_texture_data] Error processing texture data: {e}")
traceback.print_exc()
return torch.zeros((1, 64, 64, 3), dtype=torch.float32)
class IframeView:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"url": ("STRING", {"default": "https://example.com"}),
"width": ("INT", {"default": 512, "min": 64, "max": 2048}),
"height": ("INT", {"default": 512, "min": 64, "max": 2048}),
"scene_state": (
"STRING",
{"multiline": True, "default": "{\"camera\": {\"position\": [0,0,5]}, \"animation\": false}"}
),
},
"hidden": {
"unique_id": "UNIQUE_ID",
"color": "TEXTURE",
"canny": "TEXTURE",
"depth": "TEXTURE",
"normal": "TEXTURE"
}
}
RETURN_TYPES = ("IMAGE", "IMAGE", "IMAGE", "IMAGE")
RETURN_NAMES = ("color", "canny", "depth", "normal")
FUNCTION = "process_iframe"
CATEGORY = "lth"
async def _process_iframe_async(self, url, width, height, scene_state, unique_id, color=None, canny=None, depth=None, normal=None):
try:
print("\n[process_iframe] Called with inputs:")
print(" url:", url)
print(" width:", width, "height:", height)
print(" scene_state:", scene_state)
print(" unique_id:", unique_id)
# Since the iframe is always sending fully processed texture data,
# there is no need to trigger an intermediate save check.
# Directly retrieve the texture data.
if color is None:
color = get_global_texture_value(unique_id, "color")
if canny is None:
canny = get_global_texture_value(unique_id, "canny")
if depth is None:
depth = get_global_texture_value(unique_id, "depth")
if normal is None:
normal = get_global_texture_value(unique_id, "normal")
try:
state_data = json.loads(scene_state)
except json.JSONDecodeError as e:
print(f"[process_iframe] Invalid scene state JSON: {e}")
state_data = {}
color_tensor = process_texture_data(color)
canny_tensor = process_texture_data(canny)
depth_tensor = process_texture_data(depth)
normal_tensor = process_texture_data(normal)
return (color_tensor, canny_tensor, depth_tensor, normal_tensor)
except Exception as e:
print(f"[process_iframe] Error: {e}")
empty = torch.zeros((1, 64, 64, 3), dtype=torch.float32)
return (empty, empty, empty, empty)
def process_iframe(self, url, width, height, scene_state, unique_id, color=None, canny=None, depth=None, normal=None):
"""
Synchronous wrapper for _process_iframe_async.
"""
return asyncio.run(
self._process_iframe_async(
url, width, height, scene_state, unique_id, color, canny, depth, normal
)
)
NODE_CLASS_MAPPINGS = {
"IframeView": IframeView
}
NODE_DISPLAY_NAME_MAPPINGS = {
"IframeView": "Iframe View"
}
WEB_DIRECTORY = "./js"
GREEN = "\033[92m"
LIGHT_YELLOW = "\033[93m"
MAGENTA = "\033[95m"
BLUE = "\033[94m"
CLEAR = "\033[0m"
nodesNames = ", ".join(NODE_DISPLAY_NAME_MAPPINGS.values())
print(f"\n{MAGENTA}* {GREEN}lo-th -> {LIGHT_YELLOW}{nodesNames} {BLUE}<Loaded>{CLEAR}")
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"]