Vision-language model nodes for ComfyUI using Tencent's Penguin-VL. Includes image query, video query, batch image query, and model loader nodes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
48 lines
1.4 KiB
Python
48 lines
1.4 KiB
Python
"""
|
|
Tensor conversion utilities for ComfyUI <-> PIL.
|
|
"""
|
|
|
|
import torch
|
|
import numpy as np
|
|
from PIL import Image
|
|
from typing import List
|
|
|
|
|
|
def comfyui_tensor_to_pil(tensor: torch.Tensor) -> Image.Image:
|
|
"""
|
|
Convert a single ComfyUI IMAGE frame [H, W, C] (float 0-1) to PIL.
|
|
"""
|
|
np_img = (tensor.cpu().numpy() * 255).clip(0, 255).astype(np.uint8)
|
|
return Image.fromarray(np_img)
|
|
|
|
|
|
def comfyui_batch_to_pil_list(tensor: torch.Tensor) -> List[Image.Image]:
|
|
"""
|
|
Convert ComfyUI IMAGE batch [B, H, W, C] to list of PIL images.
|
|
"""
|
|
return [comfyui_tensor_to_pil(tensor[i]) for i in range(tensor.shape[0])]
|
|
|
|
|
|
def resize_long_side(img: Image.Image, max_size: int) -> Image.Image:
|
|
"""
|
|
Resize a PIL image so the long side equals max_size, preserving aspect ratio.
|
|
Returns the image unchanged if already within bounds.
|
|
"""
|
|
w, h = img.size
|
|
if max(w, h) <= max_size:
|
|
return img
|
|
scale = max_size / max(w, h)
|
|
return img.resize((int(w * scale), int(h * scale)), Image.LANCZOS)
|
|
|
|
|
|
def sample_frames_uniformly(tensor: torch.Tensor, max_frames: int) -> torch.Tensor:
|
|
"""
|
|
Uniformly sample max_frames from a batch tensor [B, H, W, C].
|
|
If B <= max_frames, returns tensor unchanged.
|
|
"""
|
|
B = tensor.shape[0]
|
|
if B <= max_frames:
|
|
return tensor
|
|
indices = torch.linspace(0, B - 1, max_frames).long()
|
|
return tensor[indices]
|