Pointf5ive commited on
Commit
560aea7
·
1 Parent(s): db8f498

Prevent indefinite OCR hangs with hard tesseract batch timeout

Browse files
Files changed (1) hide show
  1. smoke_signal_tab.py +61 -8
smoke_signal_tab.py CHANGED
@@ -771,6 +771,10 @@ try:
771
  SS_TESSERACT_MIN_CONF_KEEP = max(0.0, min(1.0, float(os.environ.get("SS_TESSERACT_MIN_CONF_KEEP", "0.58"))))
772
  except Exception:
773
  SS_TESSERACT_MIN_CONF_KEEP = 0.58
 
 
 
 
774
 
775
 
776
  def _load_surya_runtime():
@@ -1004,14 +1008,47 @@ def _run_tesseract_batch(images):
1004
  return [_ocr_single(img) for img in images]
1005
 
1006
  outputs = [None] * len(images)
1007
- with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
1008
- futures = {executor.submit(_ocr_single, img): idx for idx, img in enumerate(images)}
1009
- for future in concurrent.futures.as_completed(futures):
1010
- idx = futures[future]
1011
- try:
1012
- outputs[idx] = future.result()
1013
- except Exception as e:
1014
- outputs[idx] = {"regions": [], "confidence": 0.0, "method": f"error-tesseract-future ({e})"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1015
 
1016
  return outputs
1017
 
@@ -1230,6 +1267,7 @@ def run_ocr(progress=gr.Progress(track_tqdm=False)) -> tuple:
1230
  )
1231
  else:
1232
  fallback_preds = _run_tesseract_batch(batch_images)
 
1233
  for item, pred in zip(batch_items_with_images, fallback_preds):
1234
  ocr_lookup[item["page_num"]] = pred
1235
  ocr_processed_pages += 1
@@ -1237,11 +1275,19 @@ def run_ocr(progress=gr.Progress(track_tqdm=False)) -> tuple:
1237
  (total_pages_planned + ocr_processed_pages, total_work_units),
1238
  desc=f"Tesseract {book_id} p{item['page_num']}",
1239
  )
 
 
 
 
 
 
 
1240
  except Exception as e:
1241
  # If Surya batch fails, try Tesseract for this batch before giving up.
1242
  if surya is not None:
1243
  log.append(log_line(f" ⚠ {book_id} batch {batch_pages[0]}-{batch_pages[-1]} Surya error: {e}; retrying with Tesseract"))
1244
  fallback_preds = _run_tesseract_batch(batch_images)
 
1245
  for item, pred in zip(batch_items_with_images, fallback_preds):
1246
  ocr_lookup[item["page_num"]] = pred
1247
  ocr_processed_pages += 1
@@ -1249,6 +1295,13 @@ def run_ocr(progress=gr.Progress(track_tqdm=False)) -> tuple:
1249
  (total_pages_planned + ocr_processed_pages, total_work_units),
1250
  desc=f"Tesseract retry {book_id} p{item['page_num']}",
1251
  )
 
 
 
 
 
 
 
1252
  else:
1253
  for item in batch_items_with_images:
1254
  ocr_lookup[item["page_num"]] = {
 
771
  SS_TESSERACT_MIN_CONF_KEEP = max(0.0, min(1.0, float(os.environ.get("SS_TESSERACT_MIN_CONF_KEEP", "0.58"))))
772
  except Exception:
773
  SS_TESSERACT_MIN_CONF_KEEP = 0.58
774
+ try:
775
+ SS_TESSERACT_BATCH_HARD_TIMEOUT_SEC = max(15, int(os.environ.get("SS_TESSERACT_BATCH_HARD_TIMEOUT_SEC", "90")))
776
+ except Exception:
777
+ SS_TESSERACT_BATCH_HARD_TIMEOUT_SEC = 90
778
 
779
 
780
  def _load_surya_runtime():
 
1008
  return [_ocr_single(img) for img in images]
1009
 
1010
  outputs = [None] * len(images)
1011
+ executor = concurrent.futures.ThreadPoolExecutor(max_workers=max_workers)
1012
+ futures = {executor.submit(_ocr_single, img): idx for idx, img in enumerate(images)}
1013
+ pending = set(futures.keys())
1014
+ batch_timeout = max(
1015
+ SS_TESSERACT_BATCH_HARD_TIMEOUT_SEC,
1016
+ int((len(images) / max(max_workers, 1)) * SS_TESSERACT_TIMEOUT_SEC * 2 + 15),
1017
+ )
1018
+ deadline = time.monotonic() + batch_timeout
1019
+
1020
+ try:
1021
+ while pending and time.monotonic() < deadline:
1022
+ just_done, pending = concurrent.futures.wait(
1023
+ pending,
1024
+ timeout=0.75,
1025
+ return_when=concurrent.futures.FIRST_COMPLETED,
1026
+ )
1027
+ if not just_done:
1028
+ continue
1029
+ for future in just_done:
1030
+ idx = futures[future]
1031
+ try:
1032
+ outputs[idx] = future.result()
1033
+ except Exception as e:
1034
+ outputs[idx] = {"regions": [], "confidence": 0.0, "method": f"error-tesseract-future ({e})"}
1035
+
1036
+ if pending:
1037
+ for future in pending:
1038
+ idx = futures[future]
1039
+ outputs[idx] = {
1040
+ "regions": [],
1041
+ "confidence": 0.0,
1042
+ "method": f"error-tesseract-batch-timeout ({batch_timeout}s)",
1043
+ }
1044
+ future.cancel()
1045
+ finally:
1046
+ # Avoid blocking the OCR run forever if one worker hangs in external OCR process.
1047
+ executor.shutdown(wait=False, cancel_futures=True)
1048
+
1049
+ for i, out in enumerate(outputs):
1050
+ if out is None:
1051
+ outputs[i] = {"regions": [], "confidence": 0.0, "method": "error-tesseract-missing-output"}
1052
 
1053
  return outputs
1054
 
 
1267
  )
1268
  else:
1269
  fallback_preds = _run_tesseract_batch(batch_images)
1270
+ timeout_count = sum(1 for pred in fallback_preds if "batch-timeout" in str(pred.get("method", "")))
1271
  for item, pred in zip(batch_items_with_images, fallback_preds):
1272
  ocr_lookup[item["page_num"]] = pred
1273
  ocr_processed_pages += 1
 
1275
  (total_pages_planned + ocr_processed_pages, total_work_units),
1276
  desc=f"Tesseract {book_id} p{item['page_num']}",
1277
  )
1278
+ if timeout_count:
1279
+ log.append(
1280
+ log_line(
1281
+ f" ⚠ {book_id} batch {batch_pages[0]}-{batch_pages[-1]}: "
1282
+ f"{timeout_count}/{len(fallback_preds)} tesseract timeouts"
1283
+ )
1284
+ )
1285
  except Exception as e:
1286
  # If Surya batch fails, try Tesseract for this batch before giving up.
1287
  if surya is not None:
1288
  log.append(log_line(f" ⚠ {book_id} batch {batch_pages[0]}-{batch_pages[-1]} Surya error: {e}; retrying with Tesseract"))
1289
  fallback_preds = _run_tesseract_batch(batch_images)
1290
+ timeout_count = sum(1 for pred in fallback_preds if "batch-timeout" in str(pred.get("method", "")))
1291
  for item, pred in zip(batch_items_with_images, fallback_preds):
1292
  ocr_lookup[item["page_num"]] = pred
1293
  ocr_processed_pages += 1
 
1295
  (total_pages_planned + ocr_processed_pages, total_work_units),
1296
  desc=f"Tesseract retry {book_id} p{item['page_num']}",
1297
  )
1298
+ if timeout_count:
1299
+ log.append(
1300
+ log_line(
1301
+ f" ⚠ {book_id} batch {batch_pages[0]}-{batch_pages[-1]} retry: "
1302
+ f"{timeout_count}/{len(fallback_preds)} tesseract timeouts"
1303
+ )
1304
+ )
1305
  else:
1306
  for item in batch_items_with_images:
1307
  ocr_lookup[item["page_num"]] = {