Spaces:
Running on Zero
Running on Zero
| """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 | |