hoytshao commited on
Commit
966ed33
·
verified ·
1 Parent(s): 0208bfc

Update app.py

Browse files

1. Asyncio cleanup error
2. Logging for header/footer
3. Optional DEBUG level

Files changed (1) hide show
  1. app.py +65 -7
app.py CHANGED
@@ -2,14 +2,38 @@
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
  import os
6
  import re
 
7
  import tempfile
8
  import yaml
9
  import gradio as gr
10
 
11
  import glmocr
12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  GLMOCR_BASE = os.path.dirname(glmocr.__file__)
14
  CONFIG_PATH = os.path.join(GLMOCR_BASE, "config.yaml")
15
  FORMATTER_PATH = os.path.join(GLMOCR_BASE, "postprocess", "result_formatter.py")
@@ -91,6 +115,7 @@ except Exception:
91
 
92
  def get_header_footer_zones(regions, norm_height=1000):
93
  if not regions:
 
94
  return None, None
95
  y_tops, y_bottoms = [], []
96
  for r in regions:
@@ -99,8 +124,12 @@ def get_header_footer_zones(regions, norm_height=1000):
99
  y_tops.append(bbox[1])
100
  y_bottoms.append(bbox[3])
101
  if not y_tops:
 
102
  return None, None
103
- return min(y_tops) / norm_height, max(y_bottoms) / norm_height
 
 
 
104
 
105
 
106
  def extract_zone_text_pdf(pdf_path, page_num, y_start_frac, y_end_frac):
@@ -112,13 +141,16 @@ def extract_zone_text_pdf(pdf_path, page_num, y_start_frac, y_end_frac):
112
  rect = fitz.Rect(0, h * y_start_frac, w, h * y_end_frac)
113
  text = page.get_text(clip=rect).strip()
114
  doc.close()
 
115
  return text
116
- except Exception:
 
117
  return ""
118
 
119
 
120
  def ocr_zone(image_path, y_start_frac, y_end_frac):
121
  """Run OCR on a horizontal band of the image. Skips MaaS if crop is too small (API returns 400)."""
 
122
  try:
123
  from PIL import Image
124
  img = Image.open(image_path).convert("RGB")
@@ -126,11 +158,17 @@ def ocr_zone(image_path, y_start_frac, y_end_frac):
126
  y0 = max(0, int(h * y_start_frac))
127
  y1 = min(h, int(h * y_end_frac))
128
  if y1 <= y0:
 
129
  return ""
130
  crop = img.crop((0, y0, w, y1))
131
  cw, ch = crop.size
132
- if ch < MIN_CROP_HEIGHT or (cw * ch) < MIN_CROP_PIXELS:
133
- return "" # MaaS rejects very small images with 400
 
 
 
 
 
134
  fd, path = tempfile.mkstemp(suffix=".jpg")
135
  os.close(fd)
136
  try:
@@ -140,14 +178,17 @@ def ocr_zone(image_path, y_start_frac, y_end_frac):
140
  if not isinstance(out, list):
141
  out = [out]
142
  if out and getattr(out[0], "markdown_result", None):
143
- return (out[0].markdown_result or "").strip()
 
 
 
144
  finally:
145
  try:
146
  os.unlink(path)
147
  except Exception:
148
  pass
149
- except Exception:
150
- pass
151
  return ""
152
 
153
 
@@ -177,6 +218,7 @@ def run_ocr(uploaded_file):
177
 
178
  path = uploaded_file.name if hasattr(uploaded_file, "name") else str(uploaded_file)
179
  is_pdf = path.lower().endswith(".pdf")
 
180
  parser = get_parser()
181
 
182
  if is_pdf:
@@ -188,6 +230,7 @@ def run_ocr(uploaded_file):
188
  pix.save(img_path)
189
  page_images.append(img_path)
190
  doc.close()
 
191
  results = parser.parse(page_images)
192
  else:
193
  page_images = [path]
@@ -209,6 +252,11 @@ def run_ocr(uploaded_file):
209
  if fs >= 1.0:
210
  fs = 1.0 - DEFAULT_ZONE_FRAC # ensure we always OCR a bottom band
211
 
 
 
 
 
 
212
  parts = []
213
 
214
  # Always run header band (top 8–12% of page)
@@ -217,10 +265,14 @@ def run_ocr(uploaded_file):
217
  hdr = ""
218
  if is_pdf:
219
  hdr = extract_zone_text_pdf(path, page_num, 0, he)
 
 
220
  if not (hdr and hdr.strip()):
221
  hdr = ocr_zone(img_path, 0, he)
222
  if hdr and hdr.strip():
223
  parts.append(hdr.strip())
 
 
224
 
225
  if page_md:
226
  parts.append(page_md)
@@ -231,17 +283,23 @@ def run_ocr(uploaded_file):
231
  ftr = ""
232
  if is_pdf:
233
  ftr = extract_zone_text_pdf(path, page_num, fs, 1.0)
 
 
234
  if not (ftr and ftr.strip()):
235
  ftr = ocr_zone(img_path, fs, 1.0)
236
  if ftr and ftr.strip():
237
  parts.append(ftr.strip())
 
 
238
 
239
  if parts:
240
  all_pages.append("\n\n".join(parts))
241
 
 
242
  return "\n\n---\n\n".join(all_pages) if all_pages else "(No content)"
243
  except Exception as e:
244
  import traceback
 
245
  return f"Error: {e}\n\n{traceback.format_exc()}"
246
 
247
 
 
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
+ import logging
6
  import os
7
  import re
8
+ import sys
9
  import tempfile
10
  import yaml
11
  import gradio as gr
12
 
13
  import glmocr
14
 
15
+ # Log to stderr so Hugging Face / Docker show it in logs. Set GLMOCR_LOG=DEBUG for more detail.
16
+ _log_level = os.environ.get("GLMOCR_LOG", "INFO").upper()
17
+ logging.basicConfig(
18
+ level=getattr(logging, _log_level, logging.INFO),
19
+ format="[%(levelname)s] %(name)s: %(message)s",
20
+ stream=sys.stderr,
21
+ )
22
+ log = logging.getLogger("glmocr_app")
23
+
24
+ # Suppress Python 3.13 asyncio cleanup noise (Invalid file descriptor in BaseEventLoop.__del__)
25
+ try:
26
+ import asyncio
27
+ _orig_close = asyncio.BaseEventLoop.close
28
+ def _safe_close(self):
29
+ try:
30
+ _orig_close(self)
31
+ except (ValueError, OSError):
32
+ pass
33
+ asyncio.BaseEventLoop.close = _safe_close
34
+ except Exception:
35
+ pass
36
+
37
  GLMOCR_BASE = os.path.dirname(glmocr.__file__)
38
  CONFIG_PATH = os.path.join(GLMOCR_BASE, "config.yaml")
39
  FORMATTER_PATH = os.path.join(GLMOCR_BASE, "postprocess", "result_formatter.py")
 
115
 
116
  def get_header_footer_zones(regions, norm_height=1000):
117
  if not regions:
118
+ log.debug("get_header_footer_zones: no regions")
119
  return None, None
120
  y_tops, y_bottoms = [], []
121
  for r in regions:
 
124
  y_tops.append(bbox[1])
125
  y_bottoms.append(bbox[3])
126
  if not y_tops:
127
+ log.debug("get_header_footer_zones: no bboxes in regions")
128
  return None, None
129
+ he_frac = min(y_tops) / norm_height
130
+ fs_frac = max(y_bottoms) / norm_height
131
+ log.debug("get_header_footer_zones: regions=%s -> header_end_frac=%.3f footer_start_frac=%.3f", len(regions), he_frac, fs_frac)
132
+ return he_frac, fs_frac
133
 
134
 
135
  def extract_zone_text_pdf(pdf_path, page_num, y_start_frac, y_end_frac):
 
141
  rect = fitz.Rect(0, h * y_start_frac, w, h * y_end_frac)
142
  text = page.get_text(clip=rect).strip()
143
  doc.close()
144
+ log.debug("extract_zone_text_pdf page=%s y=%.2f-%.2f -> %d chars", page_num, y_start_frac, y_end_frac, len(text))
145
  return text
146
+ except Exception as e:
147
+ log.debug("extract_zone_text_pdf failed: %s", e)
148
  return ""
149
 
150
 
151
  def ocr_zone(image_path, y_start_frac, y_end_frac):
152
  """Run OCR on a horizontal band of the image. Skips MaaS if crop is too small (API returns 400)."""
153
+ zone_name = "header" if y_end_frac < 0.5 else "footer"
154
  try:
155
  from PIL import Image
156
  img = Image.open(image_path).convert("RGB")
 
158
  y0 = max(0, int(h * y_start_frac))
159
  y1 = min(h, int(h * y_end_frac))
160
  if y1 <= y0:
161
+ log.info("[%s] ocr_zone: skip (zero height band y0=%s y1=%s)", zone_name, y0, y1)
162
  return ""
163
  crop = img.crop((0, y0, w, y1))
164
  cw, ch = crop.size
165
+ pixels = cw * ch
166
+ if ch < MIN_CROP_HEIGHT or pixels < MIN_CROP_PIXELS:
167
+ log.info(
168
+ "[%s] ocr_zone: skip (crop too small: %dx%d=%d pixels, need height>=%d and pixels>=%d)",
169
+ zone_name, cw, ch, pixels, MIN_CROP_HEIGHT, MIN_CROP_PIXELS,
170
+ )
171
+ return ""
172
  fd, path = tempfile.mkstemp(suffix=".jpg")
173
  os.close(fd)
174
  try:
 
178
  if not isinstance(out, list):
179
  out = [out]
180
  if out and getattr(out[0], "markdown_result", None):
181
+ text = (out[0].markdown_result or "").strip()
182
+ log.info("[%s] ocr_zone: crop %dx%d -> %d chars", zone_name, cw, ch, len(text))
183
+ return text
184
+ log.info("[%s] ocr_zone: crop %dx%d -> empty result from API", zone_name, cw, ch)
185
  finally:
186
  try:
187
  os.unlink(path)
188
  except Exception:
189
  pass
190
+ except Exception as e:
191
+ log.warning("[%s] ocr_zone failed: %s", zone_name, e, exc_info=True)
192
  return ""
193
 
194
 
 
218
 
219
  path = uploaded_file.name if hasattr(uploaded_file, "name") else str(uploaded_file)
220
  is_pdf = path.lower().endswith(".pdf")
221
+ log.info("run_ocr: path=%s is_pdf=%s", path, is_pdf)
222
  parser = get_parser()
223
 
224
  if is_pdf:
 
230
  pix.save(img_path)
231
  page_images.append(img_path)
232
  doc.close()
233
+ log.info("run_ocr: PDF has %d pages, running MaaS on page images", len(page_images))
234
  results = parser.parse(page_images)
235
  else:
236
  page_images = [path]
 
252
  if fs >= 1.0:
253
  fs = 1.0 - DEFAULT_ZONE_FRAC # ensure we always OCR a bottom band
254
 
255
+ log.info(
256
+ "run_ocr: page %d regions=%s he=%.3f fs=%.3f body_md_len=%d",
257
+ page_num, len(regions), he, fs, len(page_md or ""),
258
+ )
259
+
260
  parts = []
261
 
262
  # Always run header band (top 8–12% of page)
 
265
  hdr = ""
266
  if is_pdf:
267
  hdr = extract_zone_text_pdf(path, page_num, 0, he)
268
+ if hdr and hdr.strip():
269
+ log.info("run_ocr: page %d header from PDF extract: %d chars", page_num, len(hdr.strip()))
270
  if not (hdr and hdr.strip()):
271
  hdr = ocr_zone(img_path, 0, he)
272
  if hdr and hdr.strip():
273
  parts.append(hdr.strip())
274
+ elif not (hdr and hdr.strip()) and he > 0:
275
+ log.info("run_ocr: page %d header empty (PDF extract + ocr_zone)", page_num)
276
 
277
  if page_md:
278
  parts.append(page_md)
 
283
  ftr = ""
284
  if is_pdf:
285
  ftr = extract_zone_text_pdf(path, page_num, fs, 1.0)
286
+ if ftr and ftr.strip():
287
+ log.info("run_ocr: page %d footer from PDF extract: %d chars", page_num, len(ftr.strip()))
288
  if not (ftr and ftr.strip()):
289
  ftr = ocr_zone(img_path, fs, 1.0)
290
  if ftr and ftr.strip():
291
  parts.append(ftr.strip())
292
+ elif not (ftr and ftr.strip()) and fs < 1.0:
293
+ log.info("run_ocr: page %d footer empty (PDF extract + ocr_zone)", page_num)
294
 
295
  if parts:
296
  all_pages.append("\n\n".join(parts))
297
 
298
+ log.info("run_ocr: done, %d pages in output", len(all_pages))
299
  return "\n\n---\n\n".join(all_pages) if all_pages else "(No content)"
300
  except Exception as e:
301
  import traceback
302
+ log.exception("run_ocr failed: %s", e)
303
  return f"Error: {e}\n\n{traceback.format_exc()}"
304
 
305