rehan953 commited on
Commit
0532153
Β·
verified Β·
1 Parent(s): e4d155c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +66 -239
app.py CHANGED
@@ -2,7 +2,6 @@ import gradio as gr
2
  import yaml
3
  import re
4
  import os
5
- import json
6
 
7
  import glmocr
8
  GLMOCR_BASE = os.path.dirname(glmocr.__file__)
@@ -13,8 +12,8 @@ formatter_path = os.path.join(GLMOCR_BASE, "postprocess", "result_formatter.py")
13
  with open(config_path, "r") as f:
14
  config = yaml.safe_load(f)
15
 
16
- config["pipeline"]["maas"]["enabled"] = True
17
- config["pipeline"]["maas"]["api_key"] = "4570c28bdea5493c9efae9dae68edc66.sGbA9DLlcX1GlvqV"
18
 
19
  config["pipeline"]["result_formatter"]["abandon"] = [
20
  "number", "footnote", "aside_text",
@@ -47,33 +46,8 @@ with open(formatter_path, "w") as f:
47
 
48
  print("βœ… result_formatter.py fixed")
49
 
50
- ABANDON = set(config["pipeline"]["result_formatter"]["abandon"])
51
-
52
- # ── STEP 3: Extract text from a specific zone of a PDF page ───
53
- # We use this ONLY to recover text from zones the API missed.
54
- # We figure out what zones were missed by looking at bbox_2d gaps.
55
- def extract_zone_text(pdf_path, page_num, y_start_frac, y_end_frac):
56
- """Extract text from a vertical fraction of a PDF page."""
57
- import fitz
58
- doc = fitz.open(pdf_path)
59
- page = doc[page_num]
60
- h = page.rect.height
61
- w = page.rect.width
62
- rect = fitz.Rect(0, h * y_start_frac, w, h * y_end_frac)
63
- text = page.get_text(clip=rect).strip()
64
- doc.close()
65
- return text
66
-
67
- def get_missed_zones(regions, page_height, page_width, min_gap_frac=0.05):
68
- """
69
- Look at bbox_2d of API regions and find vertical gaps at the
70
- top and bottom of the page that were not covered by any region.
71
- Returns: (top_gap_frac, bottom_gap_frac) or None if no gap.
72
- """
73
- if not regions:
74
- return None, None
75
-
76
- # Collect all y-coordinates from bbox_2d
77
  y_tops = []
78
  y_bottoms = []
79
  for r in regions:
@@ -81,274 +55,127 @@ def get_missed_zones(regions, page_height, page_width, min_gap_frac=0.05):
81
  if bbox and len(bbox) == 4:
82
  y_tops.append(bbox[1])
83
  y_bottoms.append(bbox[3])
84
-
85
  if not y_tops:
86
  return None, None
 
 
 
 
 
87
 
88
- first_region_y = min(y_tops)
89
- last_region_y = max(y_bottoms)
90
-
91
- # Top gap: if first region starts below 10% of page height
92
- top_gap = None
93
- if first_region_y > page_height * 0.10:
94
- top_gap = first_region_y / page_height # as fraction
95
 
96
- # Bottom gap: if last region ends above 90% of page height
97
- bottom_gap = None
98
- if last_region_y < page_height * 0.90:
99
- bottom_gap = last_region_y / page_height # as fraction
 
 
 
 
 
 
100
 
101
- return top_gap, bottom_gap
102
 
103
- # ── STEP 4: Parse one page result ────────────────────────────
104
- def get_page_regions(page_result):
105
  if hasattr(page_result, "json_result"):
106
  jr = page_result.json_result
107
  if isinstance(jr, list) and len(jr) > 0:
108
- regions = jr[0] if isinstance(jr[0], list) else jr
109
- if isinstance(regions, list):
110
- return regions, "json"
111
- if hasattr(page_result, "markdown_result"):
112
- md = page_result.markdown_result
113
- if md and isinstance(md, str) and md.strip():
114
- return md.strip(), "raw"
115
- return [], "empty"
116
-
117
- # ── STEP 5: Build readable HTML from JSON ────────────────────
118
- def json_to_readable_html(pages_json):
119
- html = """
120
- <style>
121
- .page-block { border: 2px solid #333; border-radius: 8px; margin-bottom: 24px; overflow: hidden; }
122
- .page-title { background: #333; color: white; padding: 8px 16px; font-size: 16px; font-weight: bold; }
123
- .region { padding: 10px 16px; border-bottom: 1px solid #eee; }
124
- .region:last-child { border-bottom: none; }
125
- .region-label { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 12px; font-weight: bold; margin-bottom: 6px; }
126
- .label-header { background: #3b82f6; color: white; }
127
- .label-footer { background: #22c55e; color: white; }
128
- .label-table { background: #f59e0b; color: white; }
129
- .label-text { background: #6b7280; color: white; }
130
- .label-other { background: #8b5cf6; color: white; }
131
- .region-content { font-size: 13px; color: #222; white-space: pre-wrap; word-break: break-word; }
132
- .region-content table { border-collapse: collapse; width: 100%; margin-top: 4px; }
133
- .region-content td, .region-content th { border: 1px solid #ccc; padding: 4px 8px; font-size: 12px; }
134
- .region-content th { background: #f3f4f6; }
135
- .source-badge { font-size: 10px; color: #999; margin-left: 8px; }
136
- .empty-page { padding: 16px; color: #999; font-style: italic; }
137
- </style>
138
- """
139
- for page in pages_json:
140
- page_num = page.get("page", "?")
141
- regions = page.get("regions", [])
142
- html += f'<div class="page-block">'
143
- html += f'<div class="page-title">πŸ“„ Page {page_num}</div>'
144
- if not regions:
145
- html += '<div class="empty-page">Empty page</div>'
146
- else:
147
- for region in regions:
148
- label = region.get("label", "text")
149
- content = region.get("content", "")
150
- source = region.get("source", "")
151
- label_class = {"header": "label-header", "footer": "label-footer",
152
- "table": "label-table", "text": "label-text"}.get(label, "label-other")
153
- source_text = "(PDF text layer)" if source == "pdf_text_layer" else "(API)"
154
- html += f'<div class="region">'
155
- html += f'<span class="region-label {label_class}">{label.upper()}</span>'
156
- html += f'<span class="source-badge">{source_text}</span>'
157
- html += f'<div class="region-content">{content}</div>'
158
- html += f'</div>'
159
- html += '</div>'
160
- return html
161
-
162
- # ── STEP 6: Main OCR function ─────────────────────────────────
163
  def run_ocr(uploaded_file):
164
  if uploaded_file is None:
165
- return "Please upload a file.", "No regions detected.", "<p>No output yet.</p>"
166
 
167
  try:
168
  from glmocr import parse
169
 
170
- path = uploaded_file.name if hasattr(uploaded_file, "name") else str(uploaded_file)
171
  is_pdf = path.lower().endswith(".pdf")
172
 
173
  if is_pdf:
174
  import fitz
175
- doc = fitz.open(path)
176
  page_images = []
177
  page_heights = []
178
- page_widths = []
179
  for i in range(len(doc)):
180
  pix = doc[i].get_pixmap(matrix=fitz.Matrix(1.5, 1.5), alpha=False)
181
  img_path = f"/tmp/maas_page_{i}.png"
182
  pix.save(img_path)
183
  page_images.append(img_path)
184
- # Store original page dimensions for bbox math
185
- page_heights.append(doc[i].rect.height)
186
- page_widths.append(doc[i].rect.width)
187
  doc.close()
188
  results = parse(page_images)
189
  if not isinstance(results, list):
190
  results = [results]
191
  else:
192
  page_heights = []
193
- page_widths = []
194
  results = parse(path)
195
  if not isinstance(results, list):
196
  results = [results]
197
 
198
- total_headers = 0
199
- total_footers = 0
200
- all_pages_json = []
201
- all_summary = []
202
 
203
  for page_num, page_result in enumerate(results):
204
- page_summary = [f"── PAGE {page_num + 1} of {len(results)} ──"]
205
- page_regions = []
206
- index = 0
207
 
208
- # Get API regions first so we can analyze bbox gaps
209
- data, dtype = get_page_regions(page_result)
210
- api_regions = data if dtype == "json" else []
211
-
212
- # For PDFs: check if API missed a top or bottom zone
213
  if is_pdf and page_num < len(page_heights):
214
- ph = page_heights[page_num]
215
- pw = page_widths[page_num]
216
-
217
- # Scale bbox_2d: images were rendered at 1.5x, so bbox coords
218
- # are in image pixels. Convert back to PDF page fractions.
219
- # API bbox_2d uses the image dimensions (1.5x scaled).
220
- # We need fractions of original page height.
221
- top_gap, bottom_gap = get_missed_zones(
222
- api_regions,
223
- ph * 1.5, # image height = page height * 1.5
224
- pw * 1.5,
225
- )
226
-
227
- # Top gap = missed header zone
228
  if top_gap is not None:
229
  hdr = extract_zone_text(path, page_num, 0, top_gap)
230
  if hdr:
231
- total_headers += 1
232
- page_regions.append({
233
- "index": index,
234
- "label": "header",
235
- "content": hdr,
236
- "source": "pdf_text_layer"
237
- })
238
- page_summary.append("πŸ”΅ HEADER: " + hdr[:80])
239
- index += 1
240
-
241
- # Bottom gap = missed footer zone
242
  if bottom_gap is not None:
243
  ftr = extract_zone_text(path, page_num, bottom_gap, 1.0)
244
  if ftr:
245
- total_footers += 1
246
- page_regions.append({
247
- "index": index,
248
- "label": "footer",
249
- "content": ftr,
250
- "source": "pdf_text_layer"
251
- })
252
- page_summary.append("🟒 FOOTER: " + ftr[:80])
253
- index += 1
254
-
255
- # Now add API regions
256
- if dtype == "json":
257
- for region in api_regions:
258
- if not isinstance(region, dict):
259
- continue
260
- label = region.get("label", "text")
261
- content = str(region.get("content", ""))
262
- bbox = region.get("bbox_2d", [])
263
-
264
- if label in ABANDON:
265
- continue
266
-
267
- entry = {
268
- "index": index,
269
- "label": label,
270
- "content": content,
271
- "source": "api"
272
- }
273
- if bbox:
274
- entry["bbox_2d"] = bbox
275
-
276
- if label == "header":
277
- total_headers += 1
278
- page_summary.append("πŸ”΅ HEADER (API): " + content[:80])
279
- elif label == "footer":
280
- total_footers += 1
281
- page_summary.append("🟒 FOOTER (API): " + content[:80])
282
- else:
283
- page_summary.append("[" + label + "]: " + content[:80])
284
-
285
- page_regions.append(entry)
286
- index += 1
287
-
288
- elif dtype == "raw":
289
- page_regions.append({
290
- "index": index,
291
- "label": "text",
292
- "content": data,
293
- "source": "api_raw"
294
- })
295
- page_summary.append("[raw markdown β€” " + str(len(data)) + " chars]")
296
-
297
  else:
298
- page_summary.append("[empty page]")
 
299
 
300
- all_pages_json.append({
301
- "page": page_num + 1,
302
- "regions": page_regions
303
- })
304
- all_summary.extend(page_summary)
305
- all_summary.append("")
306
 
307
- final_json = json.dumps(all_pages_json, indent=2, ensure_ascii=False)
308
-
309
- summary = (
310
- "Total pages : " + str(len(results)) + "\n"
311
- + "Headers found : " + str(total_headers) + "\n"
312
- + "Footers found : " + str(total_footers) + "\n"
313
- + "─" * 40 + "\n"
314
- + "\n".join(all_summary)
315
- )
316
-
317
- readable = json_to_readable_html(all_pages_json)
318
- return final_json, summary, readable
319
 
320
  except Exception as e:
321
  import traceback
322
- err = "Error: " + str(e) + "\n\n" + traceback.format_exc()
323
- return err, "Failed.", "<pre>" + err + "</pre>"
324
 
325
- # ── STEP 7: Gradio UI ─────────────────────────────────────────
326
- with gr.Blocks(title="GLM-OCR β€” MaaS") as demo:
327
  gr.Markdown("""
328
- # πŸ” GLM-OCR β€” Zhipu MaaS
329
- Upload PDF or image. Headers πŸ”΅ and Footers 🟒 detected automatically.
330
- Works for any PDF β€” no hard-coded assumptions.
331
- Multi-page PDFs fully supported.
332
  """)
333
 
334
  file_input = gr.File(
335
  label="Upload PDF or Image",
336
  file_types=[".pdf", ".png", ".jpg", ".jpeg", ".tiff", ".bmp"]
337
  )
338
- run_btn = gr.Button("β–Ά Run OCR", variant="primary", size="lg")
339
-
340
- with gr.Tabs():
341
- with gr.Tab("πŸ“‹ Readable View"):
342
- readable_out = gr.HTML(label="")
343
- with gr.Tab("{ } JSON Output"):
344
- json_out = gr.Textbox(lines=40, label="Raw JSON")
345
- with gr.Tab("πŸ—‚οΈ Detected Regions"):
346
- regions_out = gr.Textbox(lines=30, label="")
347
-
348
- run_btn.click(
349
- fn=run_ocr,
350
- inputs=file_input,
351
- outputs=[json_out, regions_out, readable_out]
352
- )
353
 
354
  demo.launch()
 
2
  import yaml
3
  import re
4
  import os
 
5
 
6
  import glmocr
7
  GLMOCR_BASE = os.path.dirname(glmocr.__file__)
 
12
  with open(config_path, "r") as f:
13
  config = yaml.safe_load(f)
14
 
15
+ config["pipeline"]["maas"]["enabled"] = True
16
+ config["pipeline"]["maas"]["api_key"] = "4570c28bdea5493c9efae9dae68edc66.sGbA9DLlcX1GlvqV"
17
 
18
  config["pipeline"]["result_formatter"]["abandon"] = [
19
  "number", "footnote", "aside_text",
 
46
 
47
  print("βœ… result_formatter.py fixed")
48
 
49
+ # ── STEP 3: Helper functions ──────────────────────────────────
50
+ def get_missed_zones(regions, img_height):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  y_tops = []
52
  y_bottoms = []
53
  for r in regions:
 
55
  if bbox and len(bbox) == 4:
56
  y_tops.append(bbox[1])
57
  y_bottoms.append(bbox[3])
 
58
  if not y_tops:
59
  return None, None
60
+ first_y = min(y_tops)
61
+ last_y = max(y_bottoms)
62
+ top_gap = (first_y / img_height) if first_y > img_height * 0.08 else None
63
+ bottom_gap = (last_y / img_height) if last_y < img_height * 0.92 else None
64
+ return top_gap, bottom_gap
65
 
 
 
 
 
 
 
 
66
 
67
+ def extract_zone_text(pdf_path, page_num, y_start_frac, y_end_frac):
68
+ import fitz
69
+ doc = fitz.open(pdf_path)
70
+ page = doc[page_num]
71
+ h = page.rect.height
72
+ w = page.rect.width
73
+ rect = fitz.Rect(0, h * y_start_frac, w, h * y_end_frac)
74
+ text = page.get_text(clip=rect).strip()
75
+ doc.close()
76
+ return text
77
 
 
78
 
79
+ def get_page_data(page_result):
80
+ regions = []
81
  if hasattr(page_result, "json_result"):
82
  jr = page_result.json_result
83
  if isinstance(jr, list) and len(jr) > 0:
84
+ r = jr[0] if isinstance(jr[0], list) else jr
85
+ if isinstance(r, list):
86
+ regions = r
87
+ md = ""
88
+ if hasattr(page_result, "markdown_result") and page_result.markdown_result:
89
+ md = page_result.markdown_result.strip()
90
+ return md, regions
91
+
92
+
93
+ # ── STEP 4: Main OCR function ─────────────────────────────────
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  def run_ocr(uploaded_file):
95
  if uploaded_file is None:
96
+ return "Please upload a file."
97
 
98
  try:
99
  from glmocr import parse
100
 
101
+ path = uploaded_file.name if hasattr(uploaded_file, "name") else str(uploaded_file)
102
  is_pdf = path.lower().endswith(".pdf")
103
 
104
  if is_pdf:
105
  import fitz
106
+ doc = fitz.open(path)
107
  page_images = []
108
  page_heights = []
 
109
  for i in range(len(doc)):
110
  pix = doc[i].get_pixmap(matrix=fitz.Matrix(1.5, 1.5), alpha=False)
111
  img_path = f"/tmp/maas_page_{i}.png"
112
  pix.save(img_path)
113
  page_images.append(img_path)
114
+ page_heights.append(doc[i].rect.height * 1.5)
 
 
115
  doc.close()
116
  results = parse(page_images)
117
  if not isinstance(results, list):
118
  results = [results]
119
  else:
120
  page_heights = []
 
121
  results = parse(path)
122
  if not isinstance(results, list):
123
  results = [results]
124
 
125
+ all_pages_md = []
 
 
 
126
 
127
  for page_num, page_result in enumerate(results):
128
+ page_md, regions = get_page_data(page_result)
129
+ parts = []
 
130
 
 
 
 
 
 
131
  if is_pdf and page_num < len(page_heights):
132
+ top_gap, bottom_gap = get_missed_zones(regions, page_heights[page_num])
133
+
134
+ # HEADER at TOP
 
 
 
 
 
 
 
 
 
 
 
135
  if top_gap is not None:
136
  hdr = extract_zone_text(path, page_num, 0, top_gap)
137
  if hdr:
138
+ parts.append(hdr)
139
+
140
+ # BODY in MIDDLE
141
+ if page_md:
142
+ parts.append(page_md)
143
+
144
+ # FOOTER at BOTTOM
 
 
 
 
145
  if bottom_gap is not None:
146
  ftr = extract_zone_text(path, page_num, bottom_gap, 1.0)
147
  if ftr:
148
+ parts.append(ftr)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  else:
150
+ if page_md:
151
+ parts.append(page_md)
152
 
153
+ if parts:
154
+ all_pages_md.append("\n\n".join(parts))
 
 
 
 
155
 
156
+ final = "\n\n---\n\n".join(all_pages_md) if all_pages_md else "(No content)"
157
+ return final
 
 
 
 
 
 
 
 
 
 
158
 
159
  except Exception as e:
160
  import traceback
161
+ return "Error: " + str(e) + "\n\n" + traceback.format_exc()
162
+
163
 
164
+ # ── STEP 5: Gradio UI ─────────────────────────────────────────
165
+ with gr.Blocks(title="GLM-OCR") as demo:
166
  gr.Markdown("""
167
+ # πŸ” GLM-OCR
168
+ Upload a PDF or image to extract text.
169
+ Headers and footers included in correct positions.
 
170
  """)
171
 
172
  file_input = gr.File(
173
  label="Upload PDF or Image",
174
  file_types=[".pdf", ".png", ".jpg", ".jpeg", ".tiff", ".bmp"]
175
  )
176
+ run_btn = gr.Button("β–Ά Run OCR", variant="primary", size="lg")
177
+ output_box = gr.Textbox(lines=40, label="Output")
178
+
179
+ run_btn.click(fn=run_ocr, inputs=file_input, outputs=output_box)
 
 
 
 
 
 
 
 
 
 
 
180
 
181
  demo.launch()