166 lines
6.2 KiB
Python
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"]
|