File size: 2,279 Bytes
ff7d684
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f5dcfe4
ff7d684
f5dcfe4
ff7d684
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import spaces
import torch
import gradio as gr
from transformers import Qwen2VLForConditionalGeneration, AutoProcessor

MODEL_ID = "NAMAA-Space/Qari-OCR-v0.3-VL-2B-Instruct"

# High max_pixels keeps small Arabic glyphs + tashkeel legible (OCR needs detail).
# 28 is Qwen2-VL's patch factor; 4096*28*28 β‰ˆ 3.2M px β‰ˆ a 300-DPI textbook page.
processor = AutoProcessor.from_pretrained(
    MODEL_ID, min_pixels=256 * 28 * 28, max_pixels=4096 * 28 * 28
)
# Loaded on CPU at startup β€” ZeroGPU only attaches a GPU inside @spaces.GPU functions.
model = Qwen2VLForConditionalGeneration.from_pretrained(MODEL_ID, torch_dtype=torch.bfloat16)

PROMPT = (
    "Below is an image of one page of an Arabic school textbook. "
    "Transcribe ALL the Arabic text exactly as printed, preserving line breaks, "
    "headings, and right-to-left reading order. Keep diacritics (tashkeel) if present. "
    "Ignore any faint diagonal draft watermark. Output only the transcribed text."
)


@spaces.GPU(duration=120)
def ocr(image):
    if image is None:
        return ""
    model.to("cuda")
    messages = [{"role": "user", "content": [
        {"type": "image", "image": image},
        {"type": "text", "text": PROMPT},
    ]}]
    text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
    # Feed the PIL image straight to the processor β€” avoids qwen-vl-utils/torchvision.
    inputs = processor(
        text=[text], images=[image], padding=True, return_tensors="pt",
    ).to("cuda")
    with torch.no_grad():
        generated = model.generate(**inputs, max_new_tokens=4096, do_sample=False)
    trimmed = [g[len(i):] for i, g in zip(inputs.input_ids, generated)]
    return processor.batch_decode(
        trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False
    )[0]


with gr.Blocks(title="Qari-OCR β€” Arabic page OCR") as demo:
    gr.Markdown("## Qari-OCR β€” Arabic textbook page OCR\nUpload a page image, or call the `/ocr` API.")
    with gr.Row():
        inp = gr.Image(type="pil", label="Page image")
        out = gr.Textbox(label="Transcribed Arabic", lines=25, rtl=True)
    gr.Button("Run OCR", variant="primary").click(ocr, inp, out, api_name="ocr")

if __name__ == "__main__":
    demo.queue(max_size=16).launch()