Document OCR Processing
The OCR layer is three coordinating classes, all under src/ocr/. Pick the right one for the document type — they are not interchangeable.
Components
1. OCRProcessor
The default processor for individual pages. Handles:
- Images (PNG, JPG, TIFF) via Tesseract (
pytesseract) and optionally Google Vision. - PDF by rasterizing pages with
pdf2imagethen routing to image OCR. - DOCX by direct text extraction via
python-docx(no OCR — text is already there).
Authentication for Google Vision uses either:
GOOGLE_APPLICATION_CREDENTIALS(path to service-account JSON), orGOOGLE_CLOUD_API_KEY(REST key — preferred in production).
The app prefers the API key when both are set (see app.py, app.py).
2. EnhancedOCRProcessor
For large multi-page documents. Wraps OCRProcessor and adds:
- Streaming page-by-page processing with progress callbacks (used by the SSE channel).
- Integration with the
IntelligentTextChunkerfor downstream chunking. Seeenhanced_ocr_processor.py. - Memory-bounded processing (one page in memory at a time).
Use this when total page count > ~20 or the file is > ~50 MB.
3. AdvancedDocumentParser
Structure-aware parser for dictionary-style documents. Detects:
- Two-column layouts.
- Headword/definition pairs.
- Pronunciation parentheticals.
- Section headings.
Output is a ParsedDocument consumed by AITrainingDataGenerator — that's the path from raw scan → training pairs.
Pipeline integration
from src.ocr.ocr_processor import OCRProcessor
from src.ocr.enhanced_ocr_processor import EnhancedOCRProcessor
from src.ocr.advanced_document_parser import AdvancedDocumentParser
# Single image
text, conf = OCRProcessor().process_image(path, language="chk+eng")
# Large PDF with progress streaming
def on_progress(stage, pct, **kw): push_sse(stage, pct, **kw)
processor = EnhancedOCRProcessor(progress_callback=on_progress)
result = processor.process_document(path)
# Dictionary-style structure extraction
parsed = AdvancedDocumentParser().parse(path)
The Flask layer at app.py decides which processor to invoke based on file size and the ?process_now flag.
Tesseract languages
Required Tesseract data packs:
eng(always)chkif available locally — Chuukese is not a packaged Tesseract language. The Dockerfile installs onlytesseract-ocr-eng(see Dockerfile). Chuukese-specific accents are recovered in post-processing, not at the Tesseract layer.
When you call into Tesseract for Chuukese pages, pass lang="eng" — better than lang="chk" which will warn-and-fallback.
Post-processing for accented characters
Common OCR confusions on Chuukese text:
| Tesseract output | Likely correction |
|---|---|
a after consonant |
á |
e in stressed position |
é |
o followed by n/s |
ó |
u with macron-like glyph |
ū |
1 between letters |
l |
0 between letters |
o |
OCRProcessor applies a conservative pass; aggressive correction lives in scripts/identify_base_words.py and scripts/check_hyphens.py for batch cleanup.
Confidence scoring
Each extracted word/entry carries a confidence_score (0.0–1.0). Sources:
- Tesseract per-word confidence (averaged).
- Google Vision page-level confidence (when used).
- Heuristic boosts when the word matches an existing dictionary entry exactly.
The PublicationDetail page surfaces these scores so editors can review low-confidence rows. Updates land via POST /api/dictionary/entries.
Common workflows
Reprocess a single page
proc = OCRProcessor()
text, conf = proc.process_image(f"uploads/{pub_id}/{page_filename}", language="eng")
Process a new publication end-to-end
Use the upload endpoint with process_now=true:
POST /api/publications/<id>/upload
form-data: file=<page>, process_now=true
The SSE stream surfaces per-page progress.
Pitfalls
- The repo previously had classes named
ChuukeseOCRProcessor/BatchOCRProcessor. They are gone — use the three classes above. pdf2imagerequirespoppler-utils(installed in Dockerfile). Local devs without poppler will get a confusingpdftoppmerror.- DOCX extraction does not go through OCR — if a DOCX contains scanned page images you'll get empty text. Convert to PDF first.
- Google Vision quotas are billed per-page — guard expensive calls behind
if not text or conf < 0.5. - The chunker integrated by
EnhancedOCRProcessordoes not protect scripture references; for Bible/brochure inputs wrap the call withprotect_scripture_references(see the scripture-reference-parsing skill).