Spaces:
Running on Zero
Running on Zero
| import json | |
| import gradio as gr | |
| import spaces, torch | |
| from transformers import AutoModelForCausalLM, AutoTokenizer | |
| from PIL import Image | |
| from typing import Literal | |
| # @spaces.GPU(duration=30) | |
| def load_model(): | |
| # moondream2 | |
| # return AutoModelForCausalLM.from_pretrained( | |
| # "vikhyatk/moondream2", | |
| # revision="2025-04-14", | |
| # trust_remote_code=True, | |
| # device_map={"": "cuda"}, | |
| # ) | |
| # moondream3-preview | |
| moondream = AutoModelForCausalLM.from_pretrained( | |
| "moondream/moondream3-preview", | |
| trust_remote_code=True, | |
| dtype=torch.bfloat16, | |
| device_map={"": "cuda"}, | |
| ) | |
| moondream.compile() | |
| return moondream | |
| _MODEL_ZOO = { | |
| "moondream3-preview": load_model(), # calling spaces.GPU decorated functions outside ZeroGPU scope will cause a PickingError | |
| "moondream2": AutoModelForCausalLM.from_pretrained( | |
| "vikhyatk/moondream2", | |
| revision="2025-06-21", | |
| trust_remote_code=True, | |
| device_map={"": "cuda"}, | |
| ), | |
| } | |
| def detect( | |
| im: Image.Image, | |
| object_name: str, | |
| mode: Literal["point", "object_detection", "query"], | |
| model_name: Literal["moondream2", "moondream3-preview"] = "moondream3-preview", | |
| reasoning: bool = False, | |
| settings: dict = {"temperature": 0.0, "top_p": 0.95, "max_tokens": 512}, | |
| ) -> list[dict] | dict: | |
| """Open-vocabulary object localization and visual question answering with the Moondream vision-language model. The shape of the returned JSON depends on the "mode" argument. In "point" mode it returns a JSON list with one entry per located instance of "object_name"; each entry is an object {"x": float, "y": float} giving that instance's center as coordinates NORMALIZED to 0.0-1.0 of the input image (x is fraction of width from the left edge, y is fraction of height from the top edge). In "object_detection" mode it returns a JSON list with one entry per located instance; each entry is a bounding box {"x_min": float, "y_min": float, "x_max": float, "y_max": float} where all four values are NORMALIZED to 0.0-1.0 of the ORIGINAL input image ("x_min"/"y_min" is the top-left corner, "x_max"/"y_max" is the bottom-right corner) -- this is the albumentations "albumentations" bounding-box format (normalized [x_min, y_min, x_max, y_max]), documented at https://albumentations.ai/docs/3-basic-usage/bounding-boxes-augmentations/#bounding-box-formats -- values are NOT absolute pixels and the box is NOT [x, y, width, height]. In "query" mode it returns a JSON object {"answer": string} answering "object_name" as a natural-language question about the image. | |
| Args: | |
| im: The RGB image to run detection or answer a question about. | |
| object_name: In "point"/"object_detection" mode, the open-vocabulary name of the object to locate (e.g. "person", "red car"); in "query" mode, the natural-language question to ask about the image. | |
| mode: "point" returns normalized center points, "object_detection" returns normalized bounding boxes, "query" returns a text answer. | |
| model_name: Which Moondream checkpoint to run: "moondream3-preview" (default, more capable) or "moondream2". | |
| reasoning: Enable chain-of-thought reasoning; only applies to "query" mode on the moondream3-preview model and is ignored otherwise. | |
| settings: Inference parameters as a JSON object; for "query" mode use "temperature"/"top_p"/"max_tokens", for "point"/"object_detection" use "max_objects"; ignored for the moondream2 model. | |
| """ | |
| model = _MODEL_ZOO[model_name] | |
| is_v2 = model_name == "moondream2" | |
| if isinstance(settings, str): | |
| settings = json.loads(settings) | |
| inf_params = {} if is_v2 else {"settings": settings} | |
| if not is_v2 and mode == "query": | |
| inf_params["reasoning"] = reasoning | |
| if mode == "point": | |
| return model.point(im, object_name, **inf_params)["points"] | |
| elif mode == "object_detection": | |
| return model.detect(im, object_name, **inf_params)["objects"] | |
| elif mode == "query": | |
| return model.query(im, object_name, **inf_params) | |
| demo = gr.Interface( | |
| fn=detect, | |
| title="moondream-pointer", | |
| description="using [moondream3-preview](https://huggingface.co/moondream/moondream3-preview) and [moondream2](https://huggingface.co/vikhyatk/moondream2) for object grounding", | |
| inputs=[ | |
| gr.Image(label="Input Image", type="pil"), | |
| gr.Textbox( | |
| label="Object / Question", | |
| info="object to detector (for points / object_detection) or question for a query", | |
| ), | |
| gr.Dropdown(label="Mode", choices=["point", "object_detection", "query"]), | |
| gr.Dropdown(label="Model Variant", choices=list(_MODEL_ZOO.keys())), | |
| gr.Checkbox( | |
| label="Reasoning", | |
| value=False, | |
| info="enable [chain-of-thought](https://huggingface.co/moondream/moondream3-preview#query) (query mode only)", | |
| ), | |
| gr.Textbox( | |
| label="Settings (JSON)", | |
| value='{"temperature": 0.0, "top_p": 0.95, "max_tokens": 512}', | |
| info="query: temperature / top_p / max_tokens Β· point & object_detection: max_objects", | |
| ), | |
| ], | |
| outputs=gr.JSON(label="Output JSON"), | |
| ) | |
| demo.launch( | |
| mcp_server=True, app_kwargs={"docs_url": "/docs"} # add FastAPI Swagger API Docs | |
| ) | |