multimodalart HF Staff commited on
Commit
bf7bee7
·
verified ·
1 Parent(s): 6c98289

Upload folder using huggingface_hub

Browse files
.gitattributes CHANGED
@@ -33,3 +33,7 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ cafe_interior.jpg filter=lfs diff=lfs merge=lfs -text
37
+ library_interior.jpg filter=lfs diff=lfs merge=lfs -text
38
+ living_room_blue_couch.jpg filter=lfs diff=lfs merge=lfs -text
39
+ spiral_staircase.jpg filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,13 +1,29 @@
1
  ---
2
- title: Spatiolm Spatial Understanding
3
- emoji: 🏃
4
- colorFrom: purple
5
- colorTo: gray
6
  sdk: gradio
7
- sdk_version: 6.22.0
8
- python_version: '3.12'
9
  app_file: app.py
10
- pinned: false
 
 
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: SpatioLM Spatial Understanding
3
+ emoji: 🧭
4
+ colorFrom: gray
5
+ colorTo: yellow
6
  sdk: gradio
7
+ sdk_version: 6.15.1
 
8
  app_file: app.py
9
+ short_description: Spatial reasoning VLM demo powered by SpatioLM
10
+ python_version: "3.12"
11
+ startup_duration_timeout: 30m
12
  ---
13
 
14
+ # SpatioLM: Spatial Understanding Demo
15
+
16
+ This Space demonstrates **SpatioLM-Understanding-InternVL3.5**, a vision-language
17
+ model with enhanced physical spatial intelligence. Upload an image and ask
18
+ spatial-reasoning questions such as "Which object is closer to the camera?" or
19
+ "Describe the spatial arrangement of objects in this scene."
20
+
21
+ SpatioLM introduces a plug-and-play spatio-vision module that learns physically
22
+ coherent representations from pseudo depth and camera supervision — without
23
+ requiring 3D input at inference time.
24
+
25
+ ## Links
26
+
27
+ - [Paper (arXiv)](https://arxiv.org/abs/2608.01899)
28
+ - [GitHub](https://github.com/xiaomi-research/spatio-lm)
29
+ - [Model on Hugging Face](https://huggingface.co/xiaomi-research/SpatioLM-Understanding-InternVL3.5)
app.py ADDED
@@ -0,0 +1,264 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ os.environ.setdefault("PYTORCH_CUDA_ALLOC_CONF", "expandable_segments:True")
4
+
5
+ import spaces # MUST come before any torch / CUDA-touching import
6
+ import torch
7
+ import torchvision.transforms as T
8
+ from PIL import Image
9
+ from torchvision.transforms.functional import InterpolationMode
10
+ from transformers import AutoTokenizer
11
+
12
+ # Register the custom SpatioLM model classes with AutoModel
13
+ import spatiolm.models # noqa: F401 – registers InternVL3RChatModel, DA3Model, etc.
14
+ from spatiolm.models import InternVL3RChatModel
15
+
16
+ MODEL_ID = "xiaomi-research/SpatioLM-Understanding-InternVL3.5"
17
+
18
+ # ---------------------------------------------------------------------------
19
+ # Image preprocessing – replicated from lmms_eval.models.simple.internvl2
20
+ # (we avoid pulling in the full lmms-eval dependency for a single function)
21
+ # ---------------------------------------------------------------------------
22
+ IMAGENET_MEAN = (0.485, 0.456, 0.406)
23
+ IMAGENET_STD = (0.229, 0.224, 0.225)
24
+ INPUT_SIZE = 448
25
+
26
+
27
+ def _build_transform(input_size=INPUT_SIZE):
28
+ transform = T.Compose([
29
+ T.Lambda(lambda img: img.convert("RGB") if img.mode != "RGB" else img),
30
+ T.Resize((input_size, input_size), interpolation=InterpolationMode.BICUBIC),
31
+ T.ToTensor(),
32
+ T.Normalize(mean=IMAGENET_MEAN, std=IMAGENET_STD),
33
+ ])
34
+ return transform
35
+
36
+
37
+ def _find_closest_aspect_ratio(aspect_ratio, target_ratios, width, height, image_size):
38
+ best_ratio_diff = float("inf")
39
+ best_ratio = (1, 1)
40
+ area = width * height
41
+ for ratio in target_ratios:
42
+ target_aspect_ratio = ratio[0] / ratio[1]
43
+ ratio_diff = abs(aspect_ratio - target_aspect_ratio)
44
+ if ratio_diff < best_ratio_diff:
45
+ best_ratio_diff = ratio_diff
46
+ best_ratio = ratio
47
+ elif ratio_diff == best_ratio_diff:
48
+ if area > 0.5 * image_size * image_size * ratio[0] * ratio[1]:
49
+ best_ratio = ratio
50
+ return best_ratio
51
+
52
+
53
+ def _dynamic_preprocess(image, min_num=1, max_num=6, image_size=INPUT_SIZE, use_thumbnail=False):
54
+ orig_width, orig_height = image.size
55
+ aspect_ratio = orig_width / orig_height
56
+
57
+ target_ratios = set(
58
+ (i, j)
59
+ for n in range(min_num, max_num + 1)
60
+ for i in range(1, n + 1)
61
+ for j in range(1, n + 1)
62
+ if i * j <= max_num and i * j >= min_num
63
+ )
64
+ target_ratios = sorted(target_ratios, key=lambda x: x[0] * x[1])
65
+
66
+ target_aspect_ratio = _find_closest_aspect_ratio(
67
+ aspect_ratio, target_ratios, orig_width, orig_height, image_size
68
+ )
69
+
70
+ target_width = image_size * target_aspect_ratio[0]
71
+ target_height = image_size * target_aspect_ratio[1]
72
+ blocks = target_aspect_ratio[0] * target_aspect_ratio[1]
73
+
74
+ resized_img = image.resize((target_width, target_height))
75
+ processed_images = []
76
+ for i in range(blocks):
77
+ box = (
78
+ (i % (target_width // image_size)) * image_size,
79
+ (i // (target_width // image_size)) * image_size,
80
+ ((i % (target_width // image_size)) + 1) * image_size,
81
+ ((i // (target_width // image_size)) + 1) * image_size,
82
+ )
83
+ split_img = resized_img.crop(box)
84
+ processed_images.append(split_img)
85
+ assert len(processed_images) == blocks
86
+ if use_thumbnail and len(processed_images) != 1:
87
+ thumbnail_img = image.resize((image_size, image_size))
88
+ processed_images.append(thumbnail_img)
89
+ return processed_images
90
+
91
+
92
+ def load_image(image, input_size=INPUT_SIZE, max_num=6):
93
+ """Load and preprocess an image into a tensor of tiles.
94
+
95
+ Args:
96
+ image: a PIL.Image or a path to an image file.
97
+ input_size: pixel size of each tile.
98
+ max_num: maximum number of tiles.
99
+
100
+ Returns:
101
+ torch.Tensor of shape (num_tiles, 3, input_size, input_size).
102
+ """
103
+ if isinstance(image, str):
104
+ image = Image.open(image).convert("RGB")
105
+ else:
106
+ image = image.convert("RGB")
107
+ transform = _build_transform(input_size=input_size)
108
+ images = _dynamic_preprocess(
109
+ image, image_size=input_size, use_thumbnail=True, max_num=max_num
110
+ )
111
+ pixel_values = [transform(img) for img in images]
112
+ pixel_values = torch.stack(pixel_values)
113
+ return pixel_values
114
+
115
+
116
+ # ---------------------------------------------------------------------------
117
+ # Model loading (module scope, eager .to("cuda") per ZeroGPU rules)
118
+ # ---------------------------------------------------------------------------
119
+ tokenizer = AutoTokenizer.from_pretrained(
120
+ MODEL_ID,
121
+ trust_remote_code=True,
122
+ use_fast=False,
123
+ )
124
+ model = InternVL3RChatModel.from_pretrained(
125
+ MODEL_ID,
126
+ dtype=torch.bfloat16,
127
+ low_cpu_mem_usage=True,
128
+ ).eval().to("cuda")
129
+
130
+
131
+ # ---------------------------------------------------------------------------
132
+ # Inference
133
+ # ---------------------------------------------------------------------------
134
+ @spaces.GPU(duration=60)
135
+ def answer(
136
+ image: Image.Image,
137
+ question: str,
138
+ max_new_tokens: int = 512,
139
+ temperature: float = 0.0,
140
+ top_p: float = 1.0,
141
+ ) -> str:
142
+ """Answer a spatial-reasoning question about an image.
143
+
144
+ Args:
145
+ image: the input image to analyse.
146
+ question: a natural-language question about the scene (e.g.
147
+ "Which object is closer to the camera?").
148
+ max_new_tokens: maximum number of tokens to generate.
149
+ temperature: sampling temperature; 0 for greedy.
150
+ top_p: nucleus-sampling probability mass.
151
+
152
+ Returns:
153
+ The model's textual answer.
154
+ """
155
+ if image is None:
156
+ return "Please upload an image."
157
+ if not question.strip():
158
+ return "Please enter a question."
159
+
160
+ pixel_values = load_image(image).to(dtype=torch.bfloat16, device="cuda")
161
+ num_patches_list = [pixel_values.shape[0]]
162
+
163
+ gen_config = {
164
+ "max_new_tokens": int(max_new_tokens),
165
+ "do_sample": temperature > 0,
166
+ "temperature": float(temperature) if temperature > 0 else 1.0,
167
+ "top_p": float(top_p),
168
+ }
169
+
170
+ response = model.chat(
171
+ tokenizer,
172
+ pixel_values,
173
+ question,
174
+ gen_config,
175
+ num_patches_list=num_patches_list,
176
+ )
177
+ return response
178
+
179
+
180
+ # ---------------------------------------------------------------------------
181
+ # Gradio UI
182
+ # ---------------------------------------------------------------------------
183
+ import gradio as gr
184
+
185
+ CSS = """
186
+ #col-container { max-width: 1100px; margin: 0 auto; }
187
+ .dark .gradio-container { color: var(--body-text-color); }
188
+ """
189
+
190
+ EXAMPLE_IMAGES = [
191
+ "cafe_interior.jpg",
192
+ "living_room_blue_couch.jpg",
193
+ "library_interior.jpg",
194
+ "city_skyline_toronto.jpg",
195
+ "spiral_staircase.jpg",
196
+ ]
197
+
198
+ EXAMPLES = [
199
+ ["cafe_interior.jpg", "Which table is closer to the camera, the one on the left or the one on the right?"],
200
+ ["living_room_blue_couch.jpg", "Describe the spatial arrangement of the furniture in this room."],
201
+ ["library_interior.jpg", "How many shelves can you see, and which one is closest to the viewer?"],
202
+ ["spiral_staircase.jpg", "Does this staircase spiral clockwise or counter-clockwise when viewed from above?"],
203
+ ]
204
+
205
+ with gr.Blocks(theme=gr.themes.Citrus(), css=CSS) as demo:
206
+ with gr.Column(elem_id="col-container"):
207
+ gr.Markdown(
208
+ "# SpatioLM: Spatial Understanding with Vision-Language Models\n"
209
+ "Ask spatial-reasoning questions about any image. SpatioLM enhances "
210
+ "physical spatial intelligence in VLMs without requiring 3D input at "
211
+ "inference time.\n\n"
212
+ "[Paper](https://arxiv.org/abs/2608.01899) | "
213
+ "[Code](https://github.com/xiaomi-research/spatio-lm) | "
214
+ "[Model](https://huggingface.co/xiaomi-research/SpatioLM-Understanding-InternVL3.5)"
215
+ )
216
+
217
+ with gr.Row():
218
+ with gr.Column(scale=1):
219
+ image_input = gr.Image(type="pil", label="Input Image")
220
+ question_input = gr.Textbox(
221
+ label="Question",
222
+ placeholder="e.g. Which object is closer to the camera?",
223
+ lines=2,
224
+ )
225
+ run_btn = gr.Button("Ask", variant="primary")
226
+ with gr.Column(scale=1):
227
+ output_text = gr.Textbox(
228
+ label="Answer",
229
+ lines=10,
230
+ interactive=False,
231
+ )
232
+
233
+ with gr.Accordion("Advanced settings", open=False):
234
+ max_tokens = gr.Slider(
235
+ minimum=32, maximum=2048, value=512, step=32,
236
+ label="Max new tokens",
237
+ )
238
+ temp_slider = gr.Slider(
239
+ minimum=0.0, maximum=2.0, value=0.0, step=0.1,
240
+ label="Temperature (0 = greedy)",
241
+ )
242
+ top_p_slider = gr.Slider(
243
+ minimum=0.1, maximum=1.0, value=1.0, step=0.05,
244
+ label="Top-p",
245
+ )
246
+
247
+ gr.Examples(
248
+ examples=EXAMPLES,
249
+ inputs=[image_input, question_input],
250
+ outputs=output_text,
251
+ fn=answer,
252
+ cache_examples=True,
253
+ cache_mode="lazy",
254
+ )
255
+
256
+ run_btn.click(
257
+ fn=answer,
258
+ inputs=[image_input, question_input, max_tokens, temp_slider, top_p_slider],
259
+ outputs=output_text,
260
+ api_name="answer",
261
+ )
262
+
263
+ if __name__ == "__main__":
264
+ demo.launch(mcp_server=True)
cafe_interior.jpg ADDED

Git LFS Details

  • SHA256: 3110d62ff20ae4ca98b3553e12e8e11ae53ffe1c4de6ac0987bb8e6ae68d337e
  • Pointer size: 131 Bytes
  • Size of remote file: 177 kB
city_skyline_toronto.jpg ADDED
library_interior.jpg ADDED

Git LFS Details

  • SHA256: ab228096c9acaae32597d58faa785e9fc44d5a27697a8af6bb0ebc994b700001
  • Pointer size: 131 Bytes
  • Size of remote file: 172 kB
living_room_blue_couch.jpg ADDED

Git LFS Details

  • SHA256: 31509acfb170d47fc181d7a1ead26c35081b53a7dc7ed14b4aedf02dea866dea
  • Pointer size: 131 Bytes
  • Size of remote file: 112 kB
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ torchvision
2
+ transformers>=4.57.1,<5
3
+ accelerate
4
+ sentencepiece
5
+ einops
6
+ timm
7
+ git+https://github.com/xiaomi-research/spatio-lm.git
8
+ https://huggingface.co/datasets/multimodalart/zerogpu-blackwell-wheels/resolve/main/wheels/pt211-cu130-cp312/flash_attn-2.7.4.post1-cp312-cp312-linux_x86_64.whl
spiral_staircase.jpg ADDED

Git LFS Details

  • SHA256: a4595f30a1438df19bb1f700147194e8eec79acb94186a3b2d63c84732455f83
  • Pointer size: 131 Bytes
  • Size of remote file: 280 kB