rxbrain-embodied-cognition / inference_utils.py
multimodalart's picture
multimodalart HF Staff
Upload folder using huggingface_hub
4fe31ed verified
Raw
History Blame Contribute Delete
2.67 kB
"""Inference-time utilities for HY-Unified.
Self-contained slice of the pieces the inference scripts need — the image
transform used to derive train/inference resolution parity, and the
task-instruction prompts that cue each generation mode.
The SAME task strings are used at train and inference (parity is load-bearing:
``interleave_inference`` / ``multiframe_inference`` build the prompt the same
way the model was trained). Single source of truth, imported by the scripts.
"""
import torch
from PIL import Image
import torchvision.transforms as T
import torchvision.transforms.functional as TF
from torchvision.transforms import InterpolationMode
# Task-instruction prompts appended to the user turn so each generation MODE is
# explicitly cued.
TASK_INSTRUCTION_SINGLE_FRAME = "Generate future image of the goal"
TASK_INSTRUCTION_MULTI_FRAME = "Generate future video of the goal"
TASK_INSTRUCTION_INTERLEAVE = "Generate interleave goal planning"
class VAEImageTransform:
"""Aspect-preserving resize + normalize to [-1, 1] for VAE encoding.
Sides are clamped to be ``stride``-divisible and within ``[min_size,
max_size]``; extreme aspect ratios are re-scaled to stay inside ``max_size``
(otherwise latent position IDs could go out of bounds).
"""
def __init__(self, max_size=512, min_size=256, stride=16):
self.max_size = max_size
self.min_size = min_size
self.stride = stride
def _make_divisible(self, val):
return max(self.stride, round(val / self.stride) * self.stride)
def __call__(self, img: Image.Image) -> torch.Tensor:
w, h = img.size
scale = min(self.max_size / max(w, h), 1.0)
scale = max(scale, self.min_size / min(w, h))
new_w = self._make_divisible(round(w * scale))
new_h = self._make_divisible(round(h * scale))
# Clamp to max_size to handle extreme aspect ratios where the min_size
# constraint overrides the max_size constraint (e.g. w=1000,h=50 would
# produce new_w=5120 which causes position ID out-of-bounds).
max_div = self._make_divisible(self.max_size)
if new_w > max_div or new_h > max_div:
# Re-scale to fit within max_size while preserving aspect ratio.
shrink = min(max_div / new_w, max_div / new_h)
new_w = self._make_divisible(round(new_w * shrink))
new_h = self._make_divisible(round(new_h * shrink))
img = TF.resize(img, (new_h, new_w), InterpolationMode.BICUBIC, antialias=True)
tensor = TF.to_tensor(img) # [0, 1]
tensor = T.Normalize([0.5] * 3, [0.5] * 3)(tensor) # [-1, 1]
return tensor