view2space-4b / app.py
multimodalart's picture
multimodalart HF Staff
Upload app.py with huggingface_hub
25b179b verified
Raw
History Blame Contribute Delete
4.92 kB
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
@spaces.GPU(duration=90)
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)