π Project Page Β | Β π» GitHub Β | Β π€ Models Β | Β π HF Paper Β | Β π€ HF Blog
Datasets and benchmarks that span vision, language, and camera modalities remain scarce in the domain of spatial multimodal intelligence. Puffin-16M is a large-scale, camera-centric dataset that substantially scales up Puffin-4M, comprising ~16.5 million samples across two complementary parts:
Two small held-out benchmarks accompany the training corpus for evaluation, rendered from panoramas disjoint from the training scenes and packaged in the same format as their training counterparts:
<hash>.jpg β <hash>.json, one shard each). Captions keep the scene-and-camera description; instead of camera_height, each JSON records the image width / height.cameras.json per scene, one shard) with the same per-frame annotation schema as Puffin-Traj-1M.
| Developed by | Kang Liao, Yihang Luo, Xiao-Ming Wu, Linyi Jin, Size Wu, Chunyu Lin, Yao Zhao, Fei Wang, Wei Li, Chen Change Loy |
| Affiliations | S-Lab, Nanyang Technological University; University of Michigan; Beijing Jiaotong University; ACE Robotics |
| First released | arXiv preprint, 2026 |
| Dataset type | Camera-centric multimodal dataset for physical-world perception and 3D world modeling |
| Modality | Image β Text+Camera; Text+Camera β Image; Image+Camera β Image; Image+Camera β Text |
Each caption is a JSON file, named by a random hash that matches its paired image (<hash>.jpg β <hash>.json). It keeps the original scene-and-camera caption and adds a camera_height field:
{
"caption": "The image shows a rocky foreground with a clear blue sky and scattered clouds above ... The camera parameters (roll, pitch, field-of-view, and radial distortion) are: 0.0977, 0.2316, 1.0756, 0.0000.",
"camera_height": "Eye-level shot"
}
camera_height is one of: Eye-level shot, Low-position shot, High-position shot, Aerial shot, Underwater shot.
Each trajectory scene is a folder (named by a random hash) containing its frames and a single cameras.json with per-frame camera annotations:
{
"motion_type": "pitch", "image_height": 640, "image_width": 640,
"vfov_deg": 84.47, "focal_px": 352.49, "images_per_segment": 90,
"frames": [
{
"file_path": "000001.jpg",
"roll_deg": 43.27, "pitch_deg": 44.5, "yaw_deg": 3.38,
"camera_intrinsics": [[fx, 0, cx], [0, fy, cy], [0, 0, 1]],
"camera_pose": [[ ... 4x4 camera-to-world ... ]]
}
]
}
Each perspective view is rendered from a panorama with camera parameters sampled as:
| Parameter | Range / Distribution | Notes |
|---|---|---|
| Roll | [β45Β°, +45Β°] | uniform |
| Pitch | [β45Β°, +45Β°] | uniform |
| Vertical FoV | [20Β°, 105Β°] | uniform |
| Radial distortion kβ | 0 | pinhole camera model (no distortion) |
In each caption, the four numbers following "The camera parameters (roll, pitch, field-of-view, and radial distortion) are:" are given in radians; the radial-distortion term is always 0 under the pinhole model.
The paired camera_height field further categorizes the shooting height into five levels ordered by increasing altitude, spanning underwater scenes up to aerial and space-like views:
camera_height | Viewpoint |
|---|---|
Underwater shot | below the water surface (ocean depths) |
Low-position shot | low, near ground / sea level (looking up) |
Eye-level shot | human eye level (the most common case) |
High-position shot | elevated viewpoint (looking down from height) |
Aerial shot | aerial / bird's-eye / space-like overhead view |
The altitude figures in the illustration (e.g. ~10 km, ~50 km, 100 km+) are indicative only and do not define hard numeric cut-offs for the labels.
Each trajectory is a continuous single-axis camera motion rendered from a panorama:
| Parameter | Range / Options | Notes |
|---|---|---|
| Motion type | pitch / roll / yaw | one axis varies per segment |
| Sub-pattern | mono_pos, mono_neg, bi_pos_neg, bi_neg_pos | monotonic (up/down, cw/ccw) or bidirectional |
| Roll | [β45Β°, +45Β°] | initial; held constant unless it is the moving axis |
| Pitch | [β45Β°, +45Β°] | initial; held constant unless it is the moving axis |
| Yaw | base β [β180Β°, +180Β°] | swept when yaw is the moving axis |
| Vertical FoV | [60Β°, 100Β°] | uniform, constant within a trajectory |
| Frames / step | 90 frames, 1Β° per frame | each segment covers ~90Β° of motion |
Per frame, cameras.json stores roll_deg / pitch_deg / yaw_deg (degrees), the 3Γ3 camera_intrinsics, and the 4Γ4 camera_pose (camera-to-world).
DATA_PATH/
ββ Puffin-Cam-15M/ # 15,338,221 single-view image-caption-camera samples
β ββ image_folder/ # perspective images (rendered from panoramas)
β β ββ 000000.zip # 10,000 images per shard, named by random hash
β β ββ ... # 000000.zip ... 001533.zip
β ββ cap_folder/ # captions: scene description + camera parameters + camera height
β β ββ 000000.zip # <hash>.json, paired with the same hash in image_folder
β β ββ ...
β ββ summary.json # index mapping each image to its caption
ββ Puffin-Cam-15M-Bench/ # held-out benchmark: 600 image-caption-camera pairs
β ββ image_folder/000000.zip # 600 images, named by hash
β ββ cap_folder/000000.zip # 600 captions, <hash>.json paired with image_folder
ββ Puffin-Traj-1M/ # 1,136,696 camera trajectories (90-frame sequences)
β ββ data/
β β ββ 000000.zip # 500 scenes per shard; each scene: <hash>/000001.jpg ... 000090.jpg + cameras.json
β β ββ ...
β ββ summary.json # index of trajectory scenes
ββ Puffin-Traj-1M-Bench/ # held-out benchmark: 100 trajectories
β ββ data/000001.zip # 100 scenes; each scene: <hash>/000001.jpg ... 000090.jpg + cameras.json
ββ README.md
Both summary.json files index the (hashed) samples so that images and their annotations can be located within the shard archives.
You can download the entire Puffin-16M dataset using the following command:
hf download KangLiao/Puffin-16M --repo-type dataset
Puffin-Cam-15M is approximately 2.4 TB, and Puffin-Traj-1M is approximately 9.2 TB (~11.6 TB in total); the two held-out benchmarks are small (~100 MB and ~1.1 GB). You may also download a single part, e.g.:
hf download KangLiao/Puffin-16M --repo-type dataset --include "Puffin-Cam-15M/*"
# benchmarks only
hf download KangLiao/Puffin-16M --repo-type dataset --include "Puffin-Cam-15M-Bench/*" "Puffin-Traj-1M-Bench/*"
Pixel-wise camera maps are omitted due to their large total size, but can be generated from the provided camera parameters using scripts/camera/cam_dataset.py in our GitHub repository.
If Puffin-16M or Puffin-World supports your research, please cite our paper:
@article{liao2026puffinworld,
title = {Puffin-World: Scaling a Unified Multimodal Model with Native 3D World States},
author = {Liao, Kang and Luo, Yihang and Wu, Xiao-Ming and Jin, Linyi and Wu, Size and Lin, Chunyu and Zhao, Yao and Wang, Fei and Li, Wei and Loy, Chen Change},
journal = {arXiv preprint arXiv:2609.04196},
year = {2026}
}
This project is licensed under NTU S-Lab License 1.0.
500 commits
π Project Page Β | Β π» GitHub Β | Β π€ Models Β | Β π HF Paper Β | Β π€ HF Blog
Datasets and benchmarks that span vision, language, and camera modalities remain scarce in the domain of spatial multimodal intelligence. Puffin-16M is a large-scale, camera-centric dataset that substantially scales up Puffin-4M, comprising ~16.5 million samples across two complementary parts:
Two small held-out benchmarks accompany the training corpus for evaluation, rendered from panoramas disjoint from the training scenes and packaged in the same format as their training counterparts:
<hash>.jpg β <hash>.json, one shard each). Captions keep the scene-and-camera description; instead of camera_height, each JSON records the image width / height.cameras.json per scene, one shard) with the same per-frame annotation schema as Puffin-Traj-1M.
| Developed by | Kang Liao, Yihang Luo, Xiao-Ming Wu, Linyi Jin, Size Wu, Chunyu Lin, Yao Zhao, Fei Wang, Wei Li, Chen Change Loy |
| Affiliations | S-Lab, Nanyang Technological University; University of Michigan; Beijing Jiaotong University; ACE Robotics |
| First released | arXiv preprint, 2026 |
| Dataset type | Camera-centric multimodal dataset for physical-world perception and 3D world modeling |
| Modality | Image β Text+Camera; Text+Camera β Image; Image+Camera β Image; Image+Camera β Text |
Each caption is a JSON file, named by a random hash that matches its paired image (<hash>.jpg β <hash>.json). It keeps the original scene-and-camera caption and adds a camera_height field:
{
"caption": "The image shows a rocky foreground with a clear blue sky and scattered clouds above ... The camera parameters (roll, pitch, field-of-view, and radial distortion) are: 0.0977, 0.2316, 1.0756, 0.0000.",
"camera_height": "Eye-level shot"
}
camera_height is one of: Eye-level shot, Low-position shot, High-position shot, Aerial shot, Underwater shot.
Each trajectory scene is a folder (named by a random hash) containing its frames and a single cameras.json with per-frame camera annotations:
{
"motion_type": "pitch", "image_height": 640, "image_width": 640,
"vfov_deg": 84.47, "focal_px": 352.49, "images_per_segment": 90,
"frames": [
{
"file_path": "000001.jpg",
"roll_deg": 43.27, "pitch_deg": 44.5, "yaw_deg": 3.38,
"camera_intrinsics": [[fx, 0, cx], [0, fy, cy], [0, 0, 1]],
"camera_pose": [[ ... 4x4 camera-to-world ... ]]
}
]
}
Each perspective view is rendered from a panorama with camera parameters sampled as:
| Parameter | Range / Distribution | Notes |
|---|---|---|
| Roll | [β45Β°, +45Β°] | uniform |
| Pitch | [β45Β°, +45Β°] | uniform |
| Vertical FoV | [20Β°, 105Β°] | uniform |
| Radial distortion kβ | 0 | pinhole camera model (no distortion) |
In each caption, the four numbers following "The camera parameters (roll, pitch, field-of-view, and radial distortion) are:" are given in radians; the radial-distortion term is always 0 under the pinhole model.
The paired camera_height field further categorizes the shooting height into five levels ordered by increasing altitude, spanning underwater scenes up to aerial and space-like views:
camera_height | Viewpoint |
|---|---|
Underwater shot | below the water surface (ocean depths) |
Low-position shot | low, near ground / sea level (looking up) |
Eye-level shot | human eye level (the most common case) |
High-position shot | elevated viewpoint (looking down from height) |
Aerial shot | aerial / bird's-eye / space-like overhead view |
The altitude figures in the illustration (e.g. ~10 km, ~50 km, 100 km+) are indicative only and do not define hard numeric cut-offs for the labels.
Each trajectory is a continuous single-axis camera motion rendered from a panorama:
| Parameter | Range / Options | Notes |
|---|---|---|
| Motion type | pitch / roll / yaw | one axis varies per segment |
| Sub-pattern | mono_pos, mono_neg, bi_pos_neg, bi_neg_pos | monotonic (up/down, cw/ccw) or bidirectional |
| Roll | [β45Β°, +45Β°] | initial; held constant unless it is the moving axis |
| Pitch | [β45Β°, +45Β°] | initial; held constant unless it is the moving axis |
| Yaw | base β [β180Β°, +180Β°] | swept when yaw is the moving axis |
| Vertical FoV | [60Β°, 100Β°] | uniform, constant within a trajectory |
| Frames / step | 90 frames, 1Β° per frame | each segment covers ~90Β° of motion |
Per frame, cameras.json stores roll_deg / pitch_deg / yaw_deg (degrees), the 3Γ3 camera_intrinsics, and the 4Γ4 camera_pose (camera-to-world).
DATA_PATH/
ββ Puffin-Cam-15M/ # 15,338,221 single-view image-caption-camera samples
β ββ image_folder/ # perspective images (rendered from panoramas)
β β ββ 000000.zip # 10,000 images per shard, named by random hash
β β ββ ... # 000000.zip ... 001533.zip
β ββ cap_folder/ # captions: scene description + camera parameters + camera height
β β ββ 000000.zip # <hash>.json, paired with the same hash in image_folder
β β ββ ...
β ββ summary.json # index mapping each image to its caption
ββ Puffin-Cam-15M-Bench/ # held-out benchmark: 600 image-caption-camera pairs
β ββ image_folder/000000.zip # 600 images, named by hash
β ββ cap_folder/000000.zip # 600 captions, <hash>.json paired with image_folder
ββ Puffin-Traj-1M/ # 1,136,696 camera trajectories (90-frame sequences)
β ββ data/
β β ββ 000000.zip # 500 scenes per shard; each scene: <hash>/000001.jpg ... 000090.jpg + cameras.json
β β ββ ...
β ββ summary.json # index of trajectory scenes
ββ Puffin-Traj-1M-Bench/ # held-out benchmark: 100 trajectories
β ββ data/000001.zip # 100 scenes; each scene: <hash>/000001.jpg ... 000090.jpg + cameras.json
ββ README.md
Both summary.json files index the (hashed) samples so that images and their annotations can be located within the shard archives.
You can download the entire Puffin-16M dataset using the following command:
hf download KangLiao/Puffin-16M --repo-type dataset
Puffin-Cam-15M is approximately 2.4 TB, and Puffin-Traj-1M is approximately 9.2 TB (~11.6 TB in total); the two held-out benchmarks are small (~100 MB and ~1.1 GB). You may also download a single part, e.g.:
hf download KangLiao/Puffin-16M --repo-type dataset --include "Puffin-Cam-15M/*"
# benchmarks only
hf download KangLiao/Puffin-16M --repo-type dataset --include "Puffin-Cam-15M-Bench/*" "Puffin-Traj-1M-Bench/*"
Pixel-wise camera maps are omitted due to their large total size, but can be generated from the provided camera parameters using scripts/camera/cam_dataset.py in our GitHub repository.
If Puffin-16M or Puffin-World supports your research, please cite our paper:
@article{liao2026puffinworld,
title = {Puffin-World: Scaling a Unified Multimodal Model with Native 3D World States},
author = {Liao, Kang and Luo, Yihang and Wu, Xiao-Ming and Jin, Linyi and Wu, Size and Lin, Chunyu and Zhao, Yao and Wang, Fei and Li, Wei and Loy, Chen Change},
journal = {arXiv preprint arXiv:2609.04196},
year = {2026}
}
This project is licensed under NTU S-Lab License 1.0.
500 commits