File size: 3,492 Bytes
384c8e8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
115
116
import spaces
import torch
import gradio as gr
from PIL import Image
from transformers import AutoProcessor, AutoModelForImageTextToText
from peft import PeftModel

BASE_MODEL_ID = "google/gemma-3-12b-it"
ADAPTER_ID = "historyHulk/ModiTrans-12B-Gemma-Teacher"
PROMPT = "Translitrate the following Modi script to Devnagri script."
MAX_NEW_TOKENS = 350

# Model + processor are loaded once at startup and kept on CPU.
# They are moved to GPU inside the @spaces.GPU-decorated function,
# which is how ZeroGPU spaces work (GPU only attached per-call).
print("Loading processor...")
processor = AutoProcessor.from_pretrained(BASE_MODEL_ID)

print("Loading base model...")
base_model = AutoModelForImageTextToText.from_pretrained(
    BASE_MODEL_ID,
    torch_dtype=torch.bfloat16,
)

print("Loading LoRA adapter...")
model = PeftModel.from_pretrained(
    base_model,
    ADAPTER_ID,
    torch_dtype=torch.bfloat16,
)
model.eval()


@spaces.GPU
def transliterate(image: Image.Image) -> str:
    if image is None:
        return "Please upload an image of Modi script."

    device = "cuda"
    model.to(device)

    image = image.convert("RGB").resize((1024, 512))

    messages = [
        {
            "role": "user",
            "content": [
                {"type": "image", "image": image},
                {"type": "text", "text": PROMPT},
            ],
        },
    ]

    inputs = processor.apply_chat_template(
        messages,
        add_generation_prompt=True,
        tokenize=True,
        return_dict=True,
        return_tensors="pt",
    ).to(device, dtype=torch.bfloat16)

    input_len = inputs["input_ids"].shape[-1]

    with torch.no_grad():
        output_ids = model.generate(
            **inputs,
            max_new_tokens=MAX_NEW_TOKENS,
            do_sample=False,
        )

    generated = output_ids[0][input_len:]
    text = processor.decode(generated, skip_special_tokens=True)
    return text.strip()


with gr.Blocks(title="ModiTrans: Modi Script to Devanagari") as demo:
    gr.Markdown(
        """
        # ModiTrans — Modi Script to Devanagari Transliteration

        Upload a scanned image of historic **Modi script** text and this model
        will transliterate it into modern **Devanagari** script.

        Uses [`historyHulk/ModiTrans-12B-Gemma-Teacher`](https://huggingface.co/historyHulk/ModiTrans-12B-Gemma-Teacher),
        a LoRA adapter on `google/gemma-3-12b-it`, from the paper
        *"Historic Scripts to Modern Vision: A Novel Dataset and A VLM Framework
        for Transliteration of Modi Script to Devanagari"* (ICDAR 2025).

        > This is a gated model — the Space owner's `HF_TOKEN` must have accepted
        > access on the model page for inference to work.
        """
    )

    with gr.Row():
        with gr.Column():
            image_input = gr.Image(type="pil", label="Modi Script Image")
            run_btn = gr.Button("Transliterate", variant="primary")
        with gr.Column():
            output_text = gr.Textbox(
                label="Devanagari Transliteration", lines=8
            )

    run_btn.click(fn=transliterate, inputs=image_input, outputs=output_text)
    image_input.change(fn=transliterate, inputs=image_input, outputs=output_text)

    gr.Markdown(
        """
        ---
        **Citation:** Kausadikar, H., Kale, T., Susladkar, O., Mittal, S.
        *Historic Scripts to Modern Vision.* ICDAR 2025 (Springer LNCS).
        """
    )

if __name__ == "__main__":
    demo.launch()