File size: 4,012 Bytes
8292a86
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ec8284c
8292a86
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ec8284c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import spaces  # MUST come before any CUDA-touching import
import torch
import gradio as gr
from transformers import AutoProcessor, PaliGemmaForConditionalGeneration

MODEL_ID = "JamesZar/OliveGemma-3B"

processor = AutoProcessor.from_pretrained(MODEL_ID)
model = PaliGemmaForConditionalGeneration.from_pretrained(
    MODEL_ID, torch_dtype=torch.bfloat16
).to("cuda").eval()

QUESTIONS = [
    "What is the name of this dish?",
    "What are the likely ingredients of this dish?",
    "What visible ingredients can you see?",
    "What visual evidence supports this dish?",
    "How is this dish different from a visually similar one?",
]


@spaces.GPU(duration=60)
def recognize(image, question: str, max_new_tokens: int = 64) -> str:
    """Recognise a Mediterranean or European dish from an image and answer a question about it.

    Args:
        image: A food photograph.
        question: What to ask the model about the food (dish name, ingredients, etc.).
        max_new_tokens: Maximum number of new tokens to generate.
    """
    from PIL import Image

    if image is None:
        return "Please upload an image."
    if not isinstance(image, Image.Image):
        image = Image.open(image)
    image = image.convert("RGB")

    # PaliGemma prompt format used during training
    prompt = f"<image>answer en {question}\n"
    inputs = processor(text=prompt, images=image, return_tensors="pt").to(model.device)
    in_len = inputs["input_ids"].shape[-1]

    with torch.inference_mode():
        out = model.generate(
            **inputs,
            max_new_tokens=int(max_new_tokens),
            do_sample=False,
        )

    answer = processor.decode(out[0][in_len:], skip_special_tokens=True).strip()
    return answer


CSS = """
#col-container { max-width: 1100px; margin: 0 auto; }
.dark .gradio-container { color: var(--body-text-color); }
"""

with gr.Blocks() as demo:
    gr.Markdown(
        "# OliveGemma 🫒\n"
        "A 3B visual-language model for fine-grained Mediterranean & European food recognition. "
        "Upload a food photo and ask about the dish name, ingredients, or visual evidence.\n\n"
        "Model: [`JamesZar/OliveGemma-3B`](https://huggingface.co/JamesZar/OliveGemma-3B) · "
        "Paper: [2608.03428](https://huggingface.co/papers/2608.03428) · "
        "Code: [GitHub](https://github.com/tsiokris/OliveGemma)"
    )

    with gr.Column(elem_id="col-container"):
        with gr.Row():
            image_input = gr.Image(type="pil", label="Food image", scale=1)
            with gr.Column(scale=1):
                question_input = gr.Dropdown(
                    choices=QUESTIONS,
                    value=QUESTIONS[0],
                    label="Question",
                    interactive=True,
                )
                recognize_btn = gr.Button("Recognise", variant="primary")

        output_text = gr.Textbox(label="Answer", lines=4, interactive=False)

        with gr.Accordion("Advanced settings", open=False):
            max_tokens = gr.Slider(
                minimum=16, maximum=256, value=64, step=16,
                label="Max new tokens",
            )

        gr.Examples(
            examples=[
                ["examples/pizza_board.jpg", QUESTIONS[0]],
                ["examples/sushi_nigiri.jpg", QUESTIONS[0]],
                ["examples/pancakes_berries.jpg", QUESTIONS[0]],
                ["examples/gourmet_burger.jpg", QUESTIONS[1]],
                ["examples/macarons.jpg", QUESTIONS[0]],
                ["examples/cake.jpg", QUESTIONS[2]],
            ],
            inputs=[image_input, question_input],
            outputs=output_text,
            fn=recognize,
            cache_examples=True,
            cache_mode="lazy",
        )

    recognize_btn.click(
        fn=recognize,
        inputs=[image_input, question_input, max_tokens],
        outputs=output_text,
        api_name="recognize",
    )

if __name__ == "__main__":
    demo.launch(mcp_server=True, theme=gr.themes.Citrus(), css=CSS)