Spaces:
Running on Zero
Running on Zero
| import os | |
| os.environ.setdefault("PYTORCH_CUDA_ALLOC_CONF", "expandable_segments:True") | |
| import spaces # MUST come before torch / any CUDA-touching import | |
| import torch | |
| import gradio as gr | |
| from PIL import Image | |
| from transformers import AutoProcessor, Qwen3VLForConditionalGeneration | |
| MODEL_ID = "Pokerme/view2space_4b" | |
| processor = AutoProcessor.from_pretrained(MODEL_ID) | |
| model = Qwen3VLForConditionalGeneration.from_pretrained( | |
| MODEL_ID, | |
| torch_dtype=torch.bfloat16, | |
| attn_implementation="sdpa", | |
| ).to("cuda").eval() | |
| def _load_images(file_objs): | |
| """Load uploaded file paths into PIL Images.""" | |
| if not file_objs: | |
| return [] | |
| images = [] | |
| for f in file_objs: | |
| if isinstance(f, str): | |
| path = f | |
| else: | |
| path = f.name if hasattr(f, "name") else str(f) | |
| images.append(Image.open(path).convert("RGB")) | |
| return images | |
| def answer( | |
| images: list | None, | |
| prompt: str, | |
| max_new_tokens: int, | |
| temperature: float, | |
| top_p: float, | |
| ) -> str: | |
| """Answer a visual-reasoning question about one or more images using VIEW2SPACE. | |
| Args: | |
| images: one or more input image files (multi-view observations supported). | |
| prompt: the question or instruction about the image(s). | |
| max_new_tokens: maximum number of tokens to generate. | |
| temperature: sampling temperature (0 = greedy, higher = more creative). | |
| top_p: nucleus-sampling probability mass. | |
| """ | |
| pil_images = _load_images(images) | |
| if not pil_images: | |
| return "Please provide at least one image." | |
| content = [] | |
| for _ in pil_images: | |
| content.append({"type": "image"}) | |
| content.append({"type": "text", "text": prompt}) | |
| messages = [{"role": "user", "content": content}] | |
| text = processor.apply_chat_template( | |
| messages, tokenize=False, add_generation_prompt=True | |
| ) | |
| inputs = processor( | |
| text=text, | |
| images=pil_images, | |
| return_tensors="pt", | |
| ).to("cuda") | |
| gen_kwargs = dict( | |
| **inputs, | |
| max_new_tokens=int(max_new_tokens), | |
| ) | |
| if float(temperature) > 0: | |
| gen_kwargs["do_sample"] = True | |
| gen_kwargs["temperature"] = float(temperature) | |
| gen_kwargs["top_p"] = float(top_p) | |
| with torch.inference_mode(): | |
| gen_ids = model.generate(**gen_kwargs) | |
| trimmed = gen_ids[0][inputs["input_ids"].shape[-1]:] | |
| result = processor.decode(trimmed, skip_special_tokens=True) | |
| return result | |
| CSS = """ | |
| #col-container { max-width: 1100px; margin: 0 auto; } | |
| .dark .gradio-container { color: var(--body-text-color); } | |
| """ | |
| with gr.Blocks(theme=gr.themes.Citrus(), css=CSS) as demo: | |
| gr.Markdown( | |
| """ | |
| # VIEW2SPACE-4B: Multi-View Visual Reasoning | |
| A 4.4B-parameter vision-language model based on Qwen3-VL that reasons about | |
| scenes from sparse multi-view observations. Upload one or more images and ask | |
| a question — the model integrates information across views to answer. | |
| [Model card](https://huggingface.co/Pokerme/view2space_4b) | | |
| [Paper](http://arxiv.org/abs/2603.16506) | | |
| [Code](https://github.com/pokerme7777/VIEW2SPACE) | |
| """ | |
| ) | |
| with gr.Column(elem_id="col-container"): | |
| images_in = gr.File( | |
| label="Input image(s)", | |
| file_count="multiple", | |
| file_types=["image"], | |
| type="filepath", | |
| ) | |
| prompt = gr.Textbox( | |
| label="Question / instruction", | |
| placeholder="e.g. What animal is on the candy?", | |
| lines=2, | |
| ) | |
| run = gr.Button("Run", variant="primary") | |
| output = gr.Textbox(label="Model response", lines=6, interactive=False) | |
| with gr.Accordion("Advanced settings", open=False): | |
| max_new_tokens = gr.Slider( | |
| 16, 1024, value=256, step=16, label="Max new tokens" | |
| ) | |
| temperature = gr.Slider( | |
| 0.0, 2.0, value=0.0, step=0.1, label="Temperature (0 = greedy)" | |
| ) | |
| top_p = gr.Slider(0.1, 1.0, value=0.9, step=0.05, label="Top-p") | |
| gr.Examples( | |
| examples=[ | |
| [[os.path.join("examples", "candy.jpg")], "What animal is on the candy?"], | |
| [[os.path.join("examples", "dog.jpg")], "Describe this image in one sentence."], | |
| [[os.path.join("examples", "rubber_ducks.jpg")], "How many rubber ducks are in the image?"], | |
| [[os.path.join("examples", "cake.jpg")], "What is the main subject of this image?"], | |
| ], | |
| inputs=[images_in, prompt], | |
| outputs=output, | |
| fn=answer, | |
| cache_examples=True, | |
| cache_mode="lazy", | |
| ) | |
| run.click( | |
| fn=answer, | |
| inputs=[images_in, prompt, max_new_tokens, temperature, top_p], | |
| outputs=output, | |
| api_name="answer", | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch(mcp_server=True) |