opentau.utils.vision_utils
Vision-tower helpers reused across policies (pi06, pi07, …).
Residents:
bilinear_resample_pos_embed()adapts SigLIP / ViT-style learned position-embedding tables to a different patch count when the policy runs the vision tower at a non-published resolution (offline weight surgery).patch_grid_hw()/pad_to_patch_multiple()support running the SigLIP tower at native (non-square, non-patch-multiple) input resolutions at forward time: the stride-patch_sizeconv patch embedding silently floor-crops any sub-patch remainder (e.g. 180×320 with patch 14 drops 12 pixel rows and 12 columns), so callers pad up to the next patch multiple and enableinterpolate_pos_encodinginstead.
Why this is its own module rather than inline in a policy: π0.6 uses Gemma 3-4B at 448×448 (1024 patches) but google/gemma-3-4b-pt ships at 896×896 (4096 patches), so any script that bootstraps an “untrained” π0.6 checkpoint from the public VLM weights must resample. π0.7 uses the same backbone family and will inherit the same need — sharing the helper here keeps the recipe identical across them and avoids drift between re-implementations.
Functions
|
Bilinearly resample a learned |
|
Pad the trailing two (H, W) dims up to the next multiple of |
|
Patch-grid shape |
- opentau.utils.vision_utils.bilinear_resample_pos_embed(old_pos: Tensor, target_num_patches: int) Tensor[source]
Bilinearly resample a learned
(N_patches, dim)position-embedding table to a different patch count, preserving dtype.This is the standard recipe for adapting SigLIP / ViT position embeddings when running a published VLM at a different image resolution. Both the source and target patch counts must be perfect squares (square grids). Computation is performed in fp32 (
F.interpolaterejects bf16 on CPU) and cast back to the input dtype before return.The transform is fully deterministic: two calls with bit-identical inputs return bit-identical outputs, with no RNG consumption. This matters for bootstrapping checkpoints whose downstream tests compare weights byte- for-byte against an independently-computed reference.
- Parameters:
old_pos – Source position-embedding table, shape
(N_old, dim)withN_olda perfect square.target_num_patches – Desired output
N_new, must be a perfect square.
- Returns:
Resampled tensor of shape
(target_num_patches, dim)and same dtype asold_pos. Returnsold_posunchanged whenN_old == target_num_patches(no copy).- Raises:
AssertionError – if either patch count is not a perfect square.
- opentau.utils.vision_utils.pad_to_patch_multiple(img: Tensor, patch_size: int, pad_value: float = -1.0) Tensor[source]
Pad the trailing two (H, W) dims up to the next multiple of
patch_size.Padding goes on the bottom and right so the image origin stays pixel-aligned: every patch that contains real content keeps exactly the pixels it would have at a divisible resolution, and only the last patch row/column mixes in padding. The default
pad_value=-1.0is black in the[-1, 1]range SigLIP consumes; callers padding[0, 1]-range pixels should pass0.0.- Parameters:
img – Image tensor of shape
(..., H, W).patch_size – ViT patch size to pad up to a multiple of.
pad_value – Fill value for the padded band.
- Returns:
The input unchanged (no copy) when
HandWare already multiples ofpatch_size, else a padded copy of shape(..., ceil(H/p)*p, ceil(W/p)*p).
- opentau.utils.vision_utils.patch_grid_hw(height: int, width: int, patch_size: int) tuple[int, int][source]
Patch-grid shape
(grid_h, grid_w)that fully covers an image.Uses ceiling division, so a resolution that does not divide
patch_sizestill gets a grid covering every pixel — the caller is expected to pad the image up togrid * patch_sizewithpad_to_patch_multiple()before the conv patch embedding (which would otherwise floor-crop the remainder).- Parameters:
height – Image height in pixels.
width – Image width in pixels.
patch_size – ViT patch size (14 for SigLIP so400m).
- Returns:
(grid_h, grid_w)patch counts per axis.