| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495 |
- import io
- import hashlib
- from typing import List
- import numpy as np
- from PIL import Image
- try:
- import torch
- except Exception: # pragma: no cover - ComfyUI provides torch
- torch = None
- def _ensure_torch_available():
- if torch is None:
- raise RuntimeError("PyTorch is required in ComfyUI runtime but was not found.")
- def tensor_to_pil_list(image_tensor) -> List[Image.Image]:
- """Convert ComfyUI IMAGE tensor [B,H,W,C] float32(0..1) to list of PIL.Image (RGB).
- Supports batch processing; returns one PIL image per batch.
- """
- _ensure_torch_available()
- if image_tensor is None:
- raise ValueError("image_tensor is None")
- if not isinstance(image_tensor, torch.Tensor):
- raise TypeError("Expected image_tensor to be a torch.Tensor")
- if image_tensor.ndim != 4 or image_tensor.shape[-1] != 3:
- raise ValueError(
- f"Expected image tensor shape [B,H,W,3], got {tuple(image_tensor.shape)}"
- )
- images: List[Image.Image] = []
- batch, height, width, channels = image_tensor.shape
- image_tensor = image_tensor.detach().cpu().clamp(0.0, 1.0)
- np_images = (image_tensor.numpy() * 255.0).astype(np.uint8)
- for i in range(batch):
- arr = np_images[i]
- img = Image.fromarray(arr, mode="RGB")
- images.append(img)
- return images
- def pil_list_to_tensor(images: List[Image.Image]):
- """Convert list of PIL.Image (RGB) to ComfyUI IMAGE tensor [B,H,W,C] float32(0..1)."""
- _ensure_torch_available()
- if not images:
- raise ValueError("images list is empty")
- tensors = []
- for img in images:
- if img.mode != "RGB":
- img = img.convert("RGB")
- arr = np.array(img).astype(np.float32) / 255.0
- t = torch.from_numpy(arr)
- tensors.append(t)
- # Stack along batch dimension; ComfyUI expects [B,H,W,C]
- batch_tensor = torch.stack(tensors, dim=0)
- return batch_tensor
- def pil_to_png_bytes(img: Image.Image) -> bytes:
- if img.mode != "RGB":
- img = img.convert("RGB")
- buf = io.BytesIO()
- img.save(buf, format="PNG")
- return buf.getvalue()
- def bytes_to_pil_image(data: bytes) -> Image.Image:
- return Image.open(io.BytesIO(data)).convert("RGB")
- def sha256_bytes(data: bytes) -> str:
- return hashlib.sha256(data).hexdigest()
- def hash_pil_image(img: Image.Image) -> str:
- return sha256_bytes(pil_to_png_bytes(img))
- def hash_pil_images(images: List[Image.Image]) -> str:
- hasher = hashlib.sha256()
- for img in images:
- hasher.update(pil_to_png_bytes(img))
- return hasher.hexdigest()
|