Pointf5ive commited on
Commit
7ad227d
·
1 Parent(s): f7a5a9f

Accelerate Tesseract fallback with parallel OCR and timeouts

Browse files
Files changed (1) hide show
  1. smoke_signal_tab.py +46 -5
smoke_signal_tab.py CHANGED
@@ -22,6 +22,7 @@ Self-improvement loop:
22
  """
23
 
24
  import csv
 
25
  import hashlib
26
  import json
27
  import os
@@ -729,6 +730,18 @@ try:
729
  SS_RENDER_DPI_FALLBACK = max(72, int(os.environ.get("SS_RENDER_DPI_FALLBACK", "200")))
730
  except Exception:
731
  SS_RENDER_DPI_FALLBACK = 200
 
 
 
 
 
 
 
 
 
 
 
 
732
 
733
 
734
  def _load_surya_runtime():
@@ -886,14 +899,17 @@ def _run_tesseract_batch(images):
886
  except Exception as e:
887
  return [{"regions": [], "confidence": 0.0, "method": f"error-no-tesseract ({e})"} for _ in images]
888
 
889
- outputs = []
890
- for img in images:
 
 
891
  try:
892
  data = pytesseract.image_to_data(
893
  img,
894
  lang="eng",
895
- config="--oem 1 --psm 6",
896
  output_type=pytesseract.Output.DICT,
 
897
  )
898
  regions = []
899
  conf_weighted = 0.0
@@ -930,9 +946,28 @@ def _run_tesseract_batch(images):
930
  word_count += wc
931
 
932
  avg_conf = round(conf_weighted / max(word_count, 1), 4) if regions else 0.0
933
- outputs.append({"regions": regions, "confidence": avg_conf, "method": "tesseract"})
 
 
934
  except Exception as e:
935
- outputs.append({"regions": [], "confidence": 0.0, "method": f"error-tesseract ({e})"})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
936
 
937
  return outputs
938
 
@@ -976,6 +1011,12 @@ def run_ocr(progress=gr.Progress(track_tqdm=False)) -> tuple:
976
  log.append(log_line(f"✓ Surya models loaded ({surya['api']})"))
977
  else:
978
  log.append(log_line(f"⚠ Surya unavailable ({surya_error}) — using Tesseract fallback for this run"))
 
 
 
 
 
 
979
 
980
  total_pages_planned = 0
981
  total_ocr_targets_planned = 0
 
22
  """
23
 
24
  import csv
25
+ import concurrent.futures
26
  import hashlib
27
  import json
28
  import os
 
730
  SS_RENDER_DPI_FALLBACK = max(72, int(os.environ.get("SS_RENDER_DPI_FALLBACK", "200")))
731
  except Exception:
732
  SS_RENDER_DPI_FALLBACK = 200
733
+ try:
734
+ SS_TESSERACT_WORKERS = max(1, int(os.environ.get("SS_TESSERACT_WORKERS", "2")))
735
+ except Exception:
736
+ SS_TESSERACT_WORKERS = 2
737
+ try:
738
+ SS_TESSERACT_TIMEOUT_SEC = max(1, int(os.environ.get("SS_TESSERACT_TIMEOUT_SEC", "18")))
739
+ except Exception:
740
+ SS_TESSERACT_TIMEOUT_SEC = 18
741
+ try:
742
+ SS_TESSERACT_PSM = max(1, int(os.environ.get("SS_TESSERACT_PSM", "6")))
743
+ except Exception:
744
+ SS_TESSERACT_PSM = 6
745
 
746
 
747
  def _load_surya_runtime():
 
899
  except Exception as e:
900
  return [{"regions": [], "confidence": 0.0, "method": f"error-no-tesseract ({e})"} for _ in images]
901
 
902
+ # Prefer parallel image-level OCR with single-threaded internal OpenMP for better CPU utilization.
903
+ os.environ.setdefault("OMP_THREAD_LIMIT", "1")
904
+
905
+ def _ocr_single(img):
906
  try:
907
  data = pytesseract.image_to_data(
908
  img,
909
  lang="eng",
910
+ config=f"--oem 1 --psm {SS_TESSERACT_PSM}",
911
  output_type=pytesseract.Output.DICT,
912
+ timeout=SS_TESSERACT_TIMEOUT_SEC,
913
  )
914
  regions = []
915
  conf_weighted = 0.0
 
946
  word_count += wc
947
 
948
  avg_conf = round(conf_weighted / max(word_count, 1), 4) if regions else 0.0
949
+ return {"regions": regions, "confidence": avg_conf, "method": "tesseract"}
950
+ except RuntimeError as e:
951
+ return {"regions": [], "confidence": 0.0, "method": f"error-tesseract-timeout ({e})"}
952
  except Exception as e:
953
+ return {"regions": [], "confidence": 0.0, "method": f"error-tesseract ({e})"}
954
+
955
+ if not images:
956
+ return []
957
+
958
+ max_workers = min(SS_TESSERACT_WORKERS, len(images), max(1, os.cpu_count() or 1))
959
+ if max_workers <= 1:
960
+ return [_ocr_single(img) for img in images]
961
+
962
+ outputs = [None] * len(images)
963
+ with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
964
+ futures = {executor.submit(_ocr_single, img): idx for idx, img in enumerate(images)}
965
+ for future in concurrent.futures.as_completed(futures):
966
+ idx = futures[future]
967
+ try:
968
+ outputs[idx] = future.result()
969
+ except Exception as e:
970
+ outputs[idx] = {"regions": [], "confidence": 0.0, "method": f"error-tesseract-future ({e})"}
971
 
972
  return outputs
973
 
 
1011
  log.append(log_line(f"✓ Surya models loaded ({surya['api']})"))
1012
  else:
1013
  log.append(log_line(f"⚠ Surya unavailable ({surya_error}) — using Tesseract fallback for this run"))
1014
+ log.append(
1015
+ log_line(
1016
+ f"ℹ Tesseract fallback config: workers={SS_TESSERACT_WORKERS} timeout={SS_TESSERACT_TIMEOUT_SEC}s "
1017
+ f"psm={SS_TESSERACT_PSM} dpi={SS_RENDER_DPI_FALLBACK}"
1018
+ )
1019
+ )
1020
 
1021
  total_pages_planned = 0
1022
  total_ocr_targets_planned = 0