mrboost commited on
Commit
5570f76
·
verified ·
1 Parent(s): faff2e9

Deploy vision-ocr-studio on ZeroGPU

Browse files
Files changed (3) hide show
  1. README.md +21 -8
  2. app.py +132 -0
  3. requirements.txt +6 -0
README.md CHANGED
@@ -1,13 +1,26 @@
1
  ---
2
- title: Vision Ocr Studio
3
- emoji: 🌍
4
- colorFrom: pink
5
- colorTo: pink
6
  sdk: gradio
7
- sdk_version: 6.31.0
8
- python_version: '3.12'
9
  app_file: app.py
10
- pinned: false
 
 
 
 
 
 
 
 
 
 
 
 
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
1
  ---
2
+ title: Smart Document, Blueprint & Invoice OCR Studio
3
+ emoji: 📄
4
+ colorFrom: blue
5
+ colorTo: green
6
  sdk: gradio
7
+ sdk_version: 6.13.0
 
8
  app_file: app.py
9
+ python_version: "3.12"
10
+ startup_duration_timeout: 1h
11
+ short_description: Qwen2.5-VL Document, Blueprint, Table & Invoice OCR
12
+ models:
13
+ - Qwen/Qwen2.5-VL-3B-Instruct
14
+ tags:
15
+ - ocr
16
+ - vision-language
17
+ - qwen2.5-vl
18
+ - document-ai
19
+ - invoice-parser
20
+ - zerogpu
21
+ - mcp-server
22
  ---
23
 
24
+ # 📄 Smart Document, Blueprint & Invoice OCR Studio
25
+
26
+ Upload **complex invoices, electrical panel schedules, blueprints, receipts, tables, or UI screenshots** and extract clean **Markdown or structured JSON** powered by **[Qwen/Qwen2.5-VL-3B-Instruct](https://huggingface.co/Qwen/Qwen2.5-VL-3B-Instruct)** on **ZeroGPU (`zero-a10g`)** at **$0/hour**.
app.py ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Qwen2.5-VL Smart Document, Blueprint & Invoice OCR Studio on ZeroGPU."""
2
+
3
+ import os
4
+
5
+ os.environ.setdefault("PYTORCH_ALLOC_CONF", "backend:cudaMallocAsync")
6
+ os.environ.setdefault("PYTORCH_CUDA_ALLOC_CONF", "backend:cudaMallocAsync")
7
+
8
+ import spaces # Must be imported before torch
9
+ import gradio as gr
10
+ import PIL.Image
11
+ import torch
12
+ from transformers import AutoProcessor, Qwen2_5_VLForConditionalGeneration
13
+
14
+ MODEL_ID = "Qwen/Qwen2.5-VL-3B-Instruct"
15
+
16
+ EXTRACTION_PRESETS = {
17
+ "Full Clean Markdown + Tables (Default)": (
18
+ "Extract all text, headings, and tables from this image into clean, well-formatted GitHub Flavored Markdown. "
19
+ "Preserve exact numbers, part numbers, prices, and table columns."
20
+ ),
21
+ "Structured Invoice / Receipt JSON": (
22
+ "Extract all fields from this invoice or receipt as valid JSON with keys: vendor, date, invoice_number, "
23
+ "line_items (description, quantity, unit_price, total), subtotal, tax, and total_amount."
24
+ ),
25
+ "Electrical Panel / Blueprint / Spec Analysis": (
26
+ "Analyze this electrical diagram, panel schedule, or technical blueprint. List all visible circuits, "
27
+ "breaker ratings, wire gauges, load calculations, labels, and technical notes in structured Markdown."
28
+ ),
29
+ "Custom Question / Prompt": "",
30
+ }
31
+
32
+ print(f"[vision-ocr-studio] Preloading {MODEL_ID} at module scope for ZeroGPU...", flush=True)
33
+ model = Qwen2_5_VLForConditionalGeneration.from_pretrained(
34
+ MODEL_ID,
35
+ torch_dtype=torch.bfloat16,
36
+ )
37
+ model.to("cuda")
38
+ model.eval()
39
+ processor = AutoProcessor.from_pretrained(MODEL_ID)
40
+ print("[vision-ocr-studio] Qwen2.5-VL OCR engine ready on ZeroGPU!", flush=True)
41
+
42
+
43
+ @spaces.GPU(duration=60)
44
+ def analyze_document(
45
+ image_path: str | None,
46
+ preset: str = "Full Clean Markdown + Tables (Default)",
47
+ custom_instruction: str = "",
48
+ max_tokens: int = 1024,
49
+ ) -> str:
50
+ """Run vision-language OCR or structured extraction on an uploaded document/image."""
51
+ if not image_path:
52
+ raise gr.Error("Please upload a document, blueprint, invoice, or screenshot image first.")
53
+
54
+ instruction = (custom_instruction or "").strip() or EXTRACTION_PRESETS.get(
55
+ preset, EXTRACTION_PRESETS["Full Clean Markdown + Tables (Default)"]
56
+ )
57
+ img = PIL.Image.open(image_path).convert("RGB")
58
+ if max(img.size) > 1600:
59
+ scale = 1600.0 / max(img.size)
60
+ img = img.resize((int(img.width * scale), int(img.height * scale)), PIL.Image.Resampling.LANCZOS)
61
+
62
+ messages = [
63
+ {
64
+ "role": "user",
65
+ "content": [
66
+ {"type": "image", "image": img},
67
+ {"type": "text", "text": instruction},
68
+ ],
69
+ }
70
+ ]
71
+ text_prompt = processor.apply_chat_template(
72
+ messages, tokenize=False, add_generation_prompt=True
73
+ )
74
+ inputs = processor(
75
+ text=[text_prompt],
76
+ images=[img],
77
+ padding=True,
78
+ return_tensors="pt",
79
+ ).to("cuda")
80
+
81
+ with torch.no_grad():
82
+ generated_ids = model.generate(**inputs, max_new_tokens=int(max_tokens))
83
+
84
+ trimmed_ids = [
85
+ out_ids[len(in_ids):]
86
+ for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
87
+ ]
88
+ output_text = processor.batch_decode(
89
+ trimmed_ids, skip_special_tokens=True, clean_up_tokenization_spaces=False
90
+ )[0]
91
+ return output_text.strip()
92
+
93
+
94
+ with gr.Blocks(title="Smart Document, Blueprint & Invoice OCR Studio", fill_width=True) as demo:
95
+ gr.Markdown(
96
+ "# 📄 Smart Document, Blueprint & Invoice OCR Studio\n"
97
+ "Powered by **[Qwen/Qwen2.5-VL-3B-Instruct](https://huggingface.co/Qwen/Qwen2.5-VL-3B-Instruct)** on **ZeroGPU (`zero-a10g`)** at **$0/hour**—extract clean **Markdown, JSON, tables, invoices, or electrical specs** from any image."
98
+ )
99
+ with gr.Row():
100
+ with gr.Column(scale=1):
101
+ img_in = gr.Image(
102
+ label="Upload Document, Invoice, Electrical Schedule, Blueprint, or Screenshot",
103
+ type="filepath",
104
+ )
105
+ preset_dd = gr.Dropdown(
106
+ choices=list(EXTRACTION_PRESETS),
107
+ value="Full Clean Markdown + Tables (Default)",
108
+ label="Extraction Mode",
109
+ )
110
+ custom_box = gr.Textbox(
111
+ label="Custom Instruction / Question (Optional — overrides preset when filled)",
112
+ lines=3,
113
+ placeholder="e.g., Extract all circuit breaker amps and wire sizes into a JSON array...",
114
+ )
115
+ max_tok = gr.Slider(256, 2048, value=1024, step=128, label="Max Output Tokens")
116
+ extract_btn = gr.Button("📄 Extract & Analyze Document (ZeroGPU)", variant="primary")
117
+ with gr.Column(scale=1):
118
+ md_out = gr.Textbox(
119
+ label="Extracted Output (Markdown / JSON)",
120
+ lines=18,
121
+ buttons=["copy"],
122
+ )
123
+
124
+ extract_btn.click(
125
+ fn=analyze_document,
126
+ inputs=[img_in, preset_dd, custom_box, max_tok],
127
+ outputs=md_out,
128
+ api_name="extract_document",
129
+ )
130
+
131
+ if __name__ == "__main__":
132
+ demo.launch(theme=gr.themes.Ocean(), mcp_server=True)
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ transformers>=4.49.0
2
+ accelerate>=1.6.0
3
+ safetensors
4
+ qwen-vl-utils
5
+ Pillow
6
+ torchvision