rehan953 commited on
Commit
b0e24e3
Β·
verified Β·
1 Parent(s): 7d3501d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +47 -23
app.py CHANGED
@@ -1,6 +1,8 @@
1
  """
2
- GLM-OCR Hugging Face Space app with client-side header fallback for MaaS.
 
3
  """
 
4
  import asyncio
5
  try:
6
  _orig_close = asyncio.BaseEventLoop.close
@@ -30,8 +32,9 @@ FORMATTER_PATH = os.path.join(GLMOCR_BASE, "postprocess", "result_formatter.py")
30
 
31
  DEFAULT_ZONE_FRAC = float(os.environ.get("GLMOCR_DEFAULT_ZONE_FRAC", "0.12"))
32
  MIN_CROP_HEIGHT = int(os.environ.get("GLMOCR_MIN_CROP_HEIGHT", "112"))
33
- MIN_CROP_PIXELS = int(os.environ.get("GLMOCR_MIN_CROP_PIXELS", "12544"))
34
- PDF_HEADER_BAND_FRAC = float(os.environ.get("GLMOCR_PDF_HEADER_BAND", "0.15"))
 
35
 
36
  _parser = None
37
 
@@ -46,7 +49,7 @@ def get_parser():
46
  return _parser
47
 
48
  # ---------------------------------------------------------------------------
49
- # 1. Config
50
  # ---------------------------------------------------------------------------
51
  try:
52
  with open(CONFIG_PATH, "r") as f:
@@ -82,10 +85,10 @@ try:
82
  with open(CONFIG_PATH, "w") as f:
83
  yaml.dump(config, f, default_flow_style=False, sort_keys=False)
84
  except Exception:
85
- pass
86
 
87
  # ---------------------------------------------------------------------------
88
- # 2. result_formatter
89
  # ---------------------------------------------------------------------------
90
  try:
91
  with open(FORMATTER_PATH, "r") as f:
@@ -100,21 +103,22 @@ except Exception:
100
  pass
101
 
102
 
103
- def get_header_zone(regions, norm_height=1000):
104
- """Return only the top gap fraction. Never touch the bottom."""
105
  if not regions:
106
- return None
107
- y_tops = []
108
  for r in regions:
109
  bbox = r.get("bbox_2d") if isinstance(r, dict) else getattr(r, "bbox_2d", None)
110
  if bbox and len(bbox) >= 4:
111
  y_tops.append(bbox[1])
 
112
  if not y_tops:
113
- return None
114
- return min(y_tops) / norm_height
115
 
116
 
117
  def extract_zone_text_pdf(pdf_path, page_num, y_start_frac, y_end_frac):
 
118
  try:
119
  import pymupdf as fitz
120
  doc = fitz.open(pdf_path)
@@ -129,6 +133,7 @@ def extract_zone_text_pdf(pdf_path, page_num, y_start_frac, y_end_frac):
129
 
130
 
131
  def extract_pdf_text_in_band(pdf_path, page_num, y_start_frac, y_end_frac):
 
132
  try:
133
  import pymupdf as fitz
134
  doc = fitz.open(pdf_path)
@@ -136,7 +141,7 @@ def extract_pdf_text_in_band(pdf_path, page_num, y_start_frac, y_end_frac):
136
  h = page.rect.height
137
  y_lo = h * y_start_frac
138
  y_hi = h * y_end_frac
139
- words = page.get_text("words")
140
  doc.close()
141
  parts = []
142
  for w in words:
@@ -150,6 +155,8 @@ def extract_pdf_text_in_band(pdf_path, page_num, y_start_frac, y_end_frac):
150
 
151
 
152
  def ocr_zone(image_path, y_start_frac, y_end_frac):
 
 
153
  try:
154
  from PIL import Image
155
  img = Image.open(image_path).convert("RGB")
@@ -166,8 +173,12 @@ def ocr_zone(image_path, y_start_frac, y_end_frac):
166
  if (need_w * need_h) < MIN_CROP_PIXELS:
167
  need_w = max(need_w, (MIN_CROP_PIXELS + need_h - 1) // need_h)
168
  canvas = Image.new("RGB", (need_w, need_h), (255, 255, 255))
169
- canvas.paste(crop, (0, 0))
 
 
 
170
  crop = canvas
 
171
  fd, path = tempfile.mkstemp(suffix=".jpg")
172
  os.close(fd)
173
  try:
@@ -177,14 +188,15 @@ def ocr_zone(image_path, y_start_frac, y_end_frac):
177
  if not isinstance(out, list):
178
  out = [out]
179
  if out and getattr(out[0], "markdown_result", None):
180
- return (out[0].markdown_result or "").strip()
 
181
  finally:
182
  try:
183
  os.unlink(path)
184
  except Exception:
185
  pass
186
  except Exception as e:
187
- log.warning("[header] ocr_zone failed: %s", e, exc_info=True)
188
  return ""
189
 
190
 
@@ -236,14 +248,18 @@ def run_ocr(uploaded_file):
236
  all_pages = []
237
  for page_num, page_result in enumerate(results):
238
  page_md, regions = get_page_md_and_regions(page_result)
 
239
 
240
- he = get_header_zone(regions, 1000)
241
- if he is None or he <= 0:
 
242
  he = DEFAULT_ZONE_FRAC
 
 
243
 
244
  parts = []
245
 
246
- # ── HEADER at TOP only ────────────────────────────
247
  if page_num < len(page_images):
248
  img_path = page_images[page_num]
249
  hdr = ""
@@ -256,13 +272,21 @@ def run_ocr(uploaded_file):
256
  if hdr and hdr.strip():
257
  parts.append(hdr.strip())
258
 
259
- # ── BODY in MIDDLE ────────────────────────────────
260
  if page_md:
261
  parts.append(page_md)
262
 
263
- # ── NO footer extraction ──────────────────────────
264
- # The API already captures real footer text correctly.
265
- # Extracting bottom zone duplicates body content.
 
 
 
 
 
 
 
 
 
266
 
267
  if parts:
268
  all_pages.append("\n\n".join(parts))
 
1
  """
2
+ GLM-OCR Hugging Face Space app with client-side header/footer fallback for MaaS.
3
+ Works for every PDF and every image: uses API bboxes when available, else minimal band.
4
  """
5
+ # Patch asyncio first (before Gradio imports it) to suppress Python 3.13 cleanup noise
6
  import asyncio
7
  try:
8
  _orig_close = asyncio.BaseEventLoop.close
 
32
 
33
  DEFAULT_ZONE_FRAC = float(os.environ.get("GLMOCR_DEFAULT_ZONE_FRAC", "0.12"))
34
  MIN_CROP_HEIGHT = int(os.environ.get("GLMOCR_MIN_CROP_HEIGHT", "112"))
35
+ MIN_CROP_PIXELS = int(os.environ.get("GLMOCR_MIN_CROP_PIXELS", "12544")) # 112*112
36
+ PDF_HEADER_BAND_FRAC = float(os.environ.get("GLMOCR_PDF_HEADER_BAND", "0.15")) # top 15%
37
+ PDF_FOOTER_BAND_FRAC = float(os.environ.get("GLMOCR_PDF_FOOTER_BAND", "0.85")) # bottom 15%
38
 
39
  _parser = None
40
 
 
49
  return _parser
50
 
51
  # ---------------------------------------------------------------------------
52
+ # 1. Config: set MaaS and optionally include headers/footers for non-MaaS
53
  # ---------------------------------------------------------------------------
54
  try:
55
  with open(CONFIG_PATH, "r") as f:
 
85
  with open(CONFIG_PATH, "w") as f:
86
  yaml.dump(config, f, default_flow_style=False, sort_keys=False)
87
  except Exception:
88
+ pass # e.g. read-only package on Hugging Face; MaaS ignores layout anyway
89
 
90
  # ---------------------------------------------------------------------------
91
+ # 2. result_formatter: stop stripping header/footer (best-effort; may be read-only)
92
  # ---------------------------------------------------------------------------
93
  try:
94
  with open(FORMATTER_PATH, "r") as f:
 
103
  pass
104
 
105
 
106
+ def get_header_footer_zones(regions, norm_height=1000):
 
107
  if not regions:
108
+ return None, None
109
+ y_tops, y_bottoms = [], []
110
  for r in regions:
111
  bbox = r.get("bbox_2d") if isinstance(r, dict) else getattr(r, "bbox_2d", None)
112
  if bbox and len(bbox) >= 4:
113
  y_tops.append(bbox[1])
114
+ y_bottoms.append(bbox[3])
115
  if not y_tops:
116
+ return None, None
117
+ return min(y_tops) / norm_height, max(y_bottoms) / norm_height
118
 
119
 
120
  def extract_zone_text_pdf(pdf_path, page_num, y_start_frac, y_end_frac):
121
+ """Extract text from a horizontal band using a clip rect."""
122
  try:
123
  import pymupdf as fitz
124
  doc = fitz.open(pdf_path)
 
133
 
134
 
135
  def extract_pdf_text_in_band(pdf_path, page_num, y_start_frac, y_end_frac):
136
+ """Extract all text whose word bbox falls in the given vertical band (by position)."""
137
  try:
138
  import pymupdf as fitz
139
  doc = fitz.open(pdf_path)
 
141
  h = page.rect.height
142
  y_lo = h * y_start_frac
143
  y_hi = h * y_end_frac
144
+ words = page.get_text("words") # list of (x0, y0, x1, y1, word, ...)
145
  doc.close()
146
  parts = []
147
  for w in words:
 
155
 
156
 
157
  def ocr_zone(image_path, y_start_frac, y_end_frac):
158
+ """Run OCR on a horizontal band. Pads small crops to meet API minimum size."""
159
+ zone_name = "header" if y_end_frac < 0.5 else "footer"
160
  try:
161
  from PIL import Image
162
  img = Image.open(image_path).convert("RGB")
 
173
  if (need_w * need_h) < MIN_CROP_PIXELS:
174
  need_w = max(need_w, (MIN_CROP_PIXELS + need_h - 1) // need_h)
175
  canvas = Image.new("RGB", (need_w, need_h), (255, 255, 255))
176
+ if zone_name == "header":
177
+ canvas.paste(crop, (0, 0))
178
+ else:
179
+ canvas.paste(crop, (0, need_h - ch))
180
  crop = canvas
181
+ cw, ch = crop.size
182
  fd, path = tempfile.mkstemp(suffix=".jpg")
183
  os.close(fd)
184
  try:
 
188
  if not isinstance(out, list):
189
  out = [out]
190
  if out and getattr(out[0], "markdown_result", None):
191
+ text = (out[0].markdown_result or "").strip()
192
+ return text
193
  finally:
194
  try:
195
  os.unlink(path)
196
  except Exception:
197
  pass
198
  except Exception as e:
199
+ log.warning("[%s] ocr_zone failed: %s", zone_name, e, exc_info=True)
200
  return ""
201
 
202
 
 
248
  all_pages = []
249
  for page_num, page_result in enumerate(results):
250
  page_md, regions = get_page_md_and_regions(page_result)
251
+ header_end_frac, footer_start_frac = get_header_footer_zones(regions, 1000)
252
 
253
+ he = header_end_frac if header_end_frac is not None else DEFAULT_ZONE_FRAC
254
+ fs = footer_start_frac if footer_start_frac is not None else (1.0 - DEFAULT_ZONE_FRAC)
255
+ if he <= 0:
256
  he = DEFAULT_ZONE_FRAC
257
+ if fs >= 1.0:
258
+ fs = 1.0 - DEFAULT_ZONE_FRAC
259
 
260
  parts = []
261
 
262
+ # Always run header band (top 8-12% of page)
263
  if page_num < len(page_images):
264
  img_path = page_images[page_num]
265
  hdr = ""
 
272
  if hdr and hdr.strip():
273
  parts.append(hdr.strip())
274
 
 
275
  if page_md:
276
  parts.append(page_md)
277
 
278
+ # Always run footer band (bottom 8-12% of page)
279
+ if page_num < len(page_images):
280
+ img_path = page_images[page_num]
281
+ ftr = ""
282
+ if is_pdf:
283
+ ftr = extract_zone_text_pdf(path, page_num, fs, 1.0)
284
+ if not (ftr and ftr.strip()):
285
+ ftr = extract_pdf_text_in_band(path, page_num, PDF_FOOTER_BAND_FRAC, 1.0)
286
+ if not (ftr and ftr.strip()):
287
+ ftr = ocr_zone(img_path, fs, 1.0)
288
+ if ftr and ftr.strip():
289
+ parts.append(ftr.strip())
290
 
291
  if parts:
292
  all_pages.append("\n\n".join(parts))