Views
No views yet
| Vision Model | Max Resolution | Pre-Trained Weights |
|---|---|---|
| PS3-1.5K-SigLIP | 1512 * 1512 | nvidia/PS3-1.5K-SigLIP |
| PS3-4K-SigLIP | 3780 * 3780 | nvidia/PS3-4K-SigLIP |
| PS3-1.5K-C-RADIOv2 | 1536 * 1536 | nvidia/PS3-1.5K-C-RADIOv2 |
| PS3-4K-C-RADIOv2 | 3840 * 3840 | nvidia/PS3-4K-C-RADIOv2 |
| PS3-1.5K-SigLIP2 | 1512 * 1512 | nvidia/PS3-1.5K-SigLIP2 |
| PS3-4K-SigLIP2 | 3780 * 3780 | nvidia/PS3-4K-SigLIP2 |
| PS3_Lang-1.5K-SigLIP2 | 1512 * 1512 | nvidia/PS3_Lang-1.5K-SigLIP2 |
| PS3_Lang-4K-SigLIP2 | 3780 * 3780 | nvidia/PS3_Lang-4K-SigLIP2 |

pip install ps3-torch1cd PS3
2pip install -e .1from PIL import Image
2from ps3 import PS3VisionModel, PS3ImageProcessor
3
4# Load the PS3 model and processor.
5vision_model = PS3VisionModel.from_pretrained("nvidia/PS3-4K-SigLIP2")
6processor = PS3ImageProcessor.from_pretrained("nvidia/PS3-4K-SigLIP2")
7vision_model.cuda().eval()
8
9# You can replace it with your own image.
10image = Image.open("assets/test_images/dock.jpg")
11
12# Preprocess the image.
13x = processor(image)["pixel_values"][0].unsqueeze(0).cuda()1outs = vision_model(x, num_look_close="all")
2features = outs.last_hidden_state
3print(features.shape) # (1, 88209, 1152)num_look_close, i.e., how many times to run the high-res selection and encoding.1outs = vision_model(x, num_look_close=2)
2features = outs.last_hidden_state
3print(features.shape) # (1, 5849, 1152)num_token_look_close.1outs = vision_model(x, num_token_look_close=3000)
2features = outs.last_hidden_state
3print(features.shape) # (1, 3729, 1152)1############## Helper functions for visiualization ##############
2
3# install cv2, matplotlib, scipy for visualization purpose
4os.system("pip install opencv-python matplotlib scipy")
5from torchvision import transforms
6import numpy as np
7import os
8import cv2
9import matplotlib.pyplot as plt
10from scipy.ndimage import gaussian_filter
11
12def create_heatmap_overlay(image, heatmap, alpha=0.4, colormap=plt.cm.jet, sigma=10.0):
13 if len(image.shape) == 2:
14 image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)
15
16 smoothed_heatmap = gaussian_filter(heatmap.astype(np.float32), sigma=sigma)
17 smoothed_heatmap = (smoothed_heatmap - smoothed_heatmap.min()) / \
18 (smoothed_heatmap.max() - smoothed_heatmap.min())
19 colored_heatmap = (colormap(smoothed_heatmap) * 255).astype(np.uint8)
20
21 if colored_heatmap.shape[-1] == 4:
22 colored_heatmap = colored_heatmap[:, :, :3]
23
24 overlay = cv2.addWeighted(image, 1 - alpha, colored_heatmap, alpha, 0)
25 return Image.fromarray(overlay)
26
27def save_visualization(selection_probs, image, output_dir):
28 os.makedirs(output_dir, exist_ok=True)
29 resize_transform = transforms.Resize(image.size[::-1])
30 for i, prob in enumerate(selection_probs):
31 prob = (prob - prob.min()) / (prob.max() - prob.min() + 1e-6)
32 prob = resize_transform(prob)
33 prob = prob.squeeze(0).detach().cpu().numpy()
34 # overlay the selection probability map on the original image
35 overlay = create_heatmap_overlay(np.array(image), prob)
36 overlay.save(os.path.join(output_dir, f"selection_prob_scale_{i}.png"))
37 image.save(os.path.join(output_dir, f"image.png"))
38
39#################### End of helper functions ####################
40
41selection_probs = outs.selection_probs
42print([p.shape for p in selection_probs]) # [(1, 54, 54), (1, 108, 108), (1, 270, 270)]
43save_visualization(selection_probs, image, "save_path/bottom_up_selection_probs")selection_probs contains the selection probability map for each scale. In this case, the feature map of each scale has shapes of 54x54, 108x108, and 270x270. The selection probability reflects how salient/important each patch is and patches with higher probability are selected first. You can visit the demo for more visualization.
1from ps3 import PS3Tokenizer, PS3TextModel
2
3tokenizer = PS3Tokenizer.from_pretrained("nvidia/PS3-4K-SigLIP2")
4text_model = PS3TextModel.from_pretrained("nvidia/PS3-4K-SigLIP2")
5text_model.cuda().eval()
6
7text = ["A tall spire with a cross at the top of the building."]
8text = tokenizer(text).cuda()
9prompt = text_model(text).prompt1outs = vision_model(x, num_look_close=2, prompt=prompt)
2features = outs.last_hidden_state
3print(features.shape) # (1, 5849, 1152)1selection_probs = outs.selection_probs
2save_visualization(selection_probs, image, "save_path/top_down_selection_probs_1")
1text = ["A green rope on the green and red boat."]
2text = tokenizer(text).cuda()
3prompt = text_model(text).prompt
4outs = vision_model(x, num_look_close=2, prompt=prompt)
5selection_probs = outs.selection_probs
6save_visualization(selection_probs, image, "save_path/top_down_selection_probs_2")
1feature_maps = vision_model.vision_model.format_features_into_feature_maps(outs.last_hidden_state, outs.selection_maps)
2print([x.shape for x in feature_maps]) # [(1, 1152, 27, 27), (1, 1152, 54, 54), (1, 1152, 108, 108), (1, 1152, 270, 270)]feature_maps which is a list of feature maps (B * C * H * W) for each scale and each feature map contains the actual feature for the selected patches at that scaleand zero vector for the unselected patches.1class PS3VisionModel(PS3PreTrainedModel):
2 ...
3 def forward(
4 self,
5 pixel_values,
6 num_look_close,
7 num_token_look_close=None,
8 prompt=None,
9 gt_selection_maps=None,
10 smooth_selection_prob=False,
11 only_select_first_n_scale=None,
12 is_global_text=None,
13 pool_gt_token_only=False,
14 ):
15 ...pixel_values: the input images with shape (B, C, H, W).num_look_close: how many times to run high-res selection and encoding. PS3 selects and processes 2560 patches each time. If set to all then it selects all the high-res patches. If set to 0 then PS3 only returns the low-res features. If set to a larger number than what it needs to encode all the high-res patches, then PS3 will clamp it to the max number needed.num_token_look_close: (optinoal) how many high-res patches to select and process. Similar to num_look_close but num_token_look_close directly specifies the number of high-res tokens instead of number of running high-res encoding.prompt: (optional) the prompt embedding used to select high-res patches. The prompt embedding can be embedding of some text, or some embedding output by an LLM (see the paper). The shape of prompt embedding is (B, C) where B is the batch size (same in pixel_values) and C is the embedding dimension (same as PS3 token embedding dimension). If prompt=None, then PS3 will select high-res patches based on visual saliency (bottom-up selection).gt_selection_maps: (optional) the ground truth selection maps for the image. It should be a tensor of 0/1 values with shape (B, h, w). Regions with value 1 means they should be selected. When selecting high-res patches, PS3 will interpolate the gt_selection_maps to the same size as the feature map at each scale, prioritize selecting the tokens where the value is 1, and if there's still budget for selecting more tokens, it will select the rest based on the original selection probability.smooth_selection_prob: (optional) smooth the selection probability map such that the selected patches won't be distributed too scarcely each time it runs high-res selection. It slightly improves the performance occasinoally when selecting all the patches but usually hurts when selecting parts of the patches.only_select_first_n_scale: (optional) only select the first n high-res scales. For example, for PS3-4K model, if only_select_first_n_scale=2, then it only selects and processes scales of 756 and 1512, and ignores the scale of 3780.is_global_text: (optional) only return the pooled low-res feautres. It will only be used during pre-training.pool_gt_token_only: (optional) only pool the tokens inside the gt selection regions. It will only be used during pre-training.1@article{shi2025scaling,
2 title={Scaling Vision Pre-Training to 4K Resolution},
3 author={Shi, Baifeng and Li, Boyi and Cai, Han and Lu, Yao and Liu, Sifei and Pavone, Marco and Kautz, Jan and Han, Song and Darrell, Trevor and Molchanov, Pavlo and others},
4 journal={arXiv preprint arXiv:2503.19903},
5 year={2025}
6}