Hybrid Mode & OCR
OpenDataLoader's hybrid mode combines local processing with AI-powered extraction for the best of both worlds. In this lesson, you'll learn when and how to use it.
Understanding Processing Modes
| Mode | Speed | Accuracy | Use Case |
|---|---|---|---|
| Local | Fast (~0.1s/page) | Good | Clean, digital PDFs |
| Hybrid | Medium (~0.5s/page) | Best (0.907) | Complex documents |
| Force OCR | Slower | Variable | Scanned documents |
Setting Up Hybrid Mode
Hybrid mode requires the OpenDataLoader server running locally:
Step 1: Start the Server
# Install and start the hybrid server
pip install opendataloader-pdf[hybrid]
opendataloader-server start
Or using Docker:
docker run -p 8000:8000 opendataloader/hybrid-server
Step 2: Use Hybrid Mode
import opendataloader_pdf
opendataloader_pdf.convert(
input_path="complex-document.pdf",
output_dir="output/",
format="json",
hybrid=True # Enable AI-enhanced extraction
)
OCR for Scanned Documents
Automatic OCR Detection
OpenDataLoader automatically detects scanned pages and applies OCR:
# OCR is automatic when needed
opendataloader_pdf.convert(
input_path="scanned-contract.pdf",
output_dir="output/",
format="markdown"
)
Forcing OCR
Sometimes PDFs have embedded text that's incorrect (copy-paste garbled text). Force OCR to re-extract:
opendataloader_pdf.convert(
input_path="garbled-text.pdf",
output_dir="output/",
format="markdown",
force_ocr=True # Ignore embedded text, use OCR
)
Multi-Language OCR
Specify language(s) for better accuracy:
# Single language
opendataloader_pdf.convert(
input_path="german-document.pdf",
output_dir="output/",
format="markdown",
ocr_lang="deu" # German
)
# Multiple languages
opendataloader_pdf.convert(
input_path="multilingual.pdf",
output_dir="output/",
format="markdown",
ocr_lang="eng+fra+deu" # English, French, German
)
Common Language Codes
| Code | Language |
|---|---|
eng | English |
fra | French |
deu | German |
spa | Spanish |
chi_sim | Chinese (Simplified) |
chi_tra | Chinese (Traditional) |
jpn | Japanese |
kor | Korean |
ara | Arabic |
hin | Hindi |
Formula Extraction
OpenDataLoader extracts mathematical formulas as LaTeX:
opendataloader_pdf.convert(
input_path="math-paper.pdf",
output_dir="output/",
format="json",
hybrid=True # Needed for formula extraction
)
Formula Output
{
"type": "formula",
"page number": 3,
"bounding box": [100.0, 400.0, 450.0, 450.0],
"content": "E = mc^2",
"latex": "E = mc^{2}"
}
Complex Formulas
{
"type": "formula",
"content": "integral",
"latex": "\\int_{0}^{\\infty} e^{-x^2} dx = \\frac{\\sqrt{\\pi}}{2}"
}
Using Extracted Formulas
import json
with open('output/math-paper.json', 'r') as f:
data = json.load(f)
# Extract all formulas
formulas = [el for el in data['kids'] if el['type'] == 'formula']
for formula in formulas:
print(f"Formula on page {formula['page number']}:")
print(f" LaTeX: {formula.get('latex', formula['content'])}")
Image Descriptions
Hybrid mode can generate AI descriptions for images:
opendataloader_pdf.convert(
input_path="report-with-charts.pdf",
output_dir="output/",
format="json",
hybrid=True,
describe_images=True # Generate image descriptions
)
Image Output
{
"type": "image",
"page number": 2,
"bounding box": [72.0, 300.0, 400.0, 500.0],
"content": "Bar chart showing quarterly revenue growth from Q1 to Q4 2024, with values increasing from $1.2M to $2.8M",
"image_path": "output/images/image_002_001.png"
}
When to Use Hybrid Mode
Use Hybrid Mode For:
- Scanned documents - OCR extraction
- Complex tables - Better structure detection
- Mathematical papers - LaTeX formula extraction
- Documents with charts - Image descriptions
- Multi-column academic papers - Improved reading order
Stay with Local Mode For:
- Clean digital PDFs - No AI needed
- High-volume processing - Speed priority
- Sensitive documents - No external API calls
- Simple text extraction - Overkill for basic needs
Checking Document Quality
Before choosing a mode, analyze the PDF:
import opendataloader_pdf
import json
# Quick analysis with local mode
opendataloader_pdf.convert(
input_path="document.pdf",
output_dir="output/",
format="json"
)
with open('output/document.json', 'r') as f:
data = json.load(f)
# Check for potential issues
elements = data['kids']
tables = [e for e in elements if e['type'] == 'table']
formulas = [e for e in elements if e['type'] == 'formula']
images = [e for e in elements if e['type'] == 'image']
print(f"Tables: {len(tables)}")
print(f"Formulas: {len(formulas)}")
print(f"Images: {len(images)}")
# Recommend hybrid if complex elements found
if formulas or len(tables) > 5:
print("\nā Consider using hybrid mode for better accuracy")
Challenge: Scanned Document Processor
Create a script that:
- Detects if a PDF is scanned (image-based)
- Applies appropriate OCR settings
- Extracts and validates the text quality
import opendataloader_pdf
import json
def process_scanned_document(pdf_path, language='eng'):
"""
Process a potentially scanned PDF with smart OCR handling.
"""
# Your code here:
# 1. First, try local extraction
# 2. Check if text was extracted (or if it's mostly images)
# 3. If scanned, re-process with OCR
# 4. Validate extraction quality
# 5. Return structured data
pass
def detect_scanned_pdf(json_data):
"""
Heuristic to detect if PDF is scanned.
Returns True if likely scanned.
"""
elements = json_data.get('kids', [])
# Count element types
images = sum(1 for e in elements if e['type'] == 'image')
text_elements = sum(1 for e in elements if e['type'] in ['paragraph', 'heading'])
# High image-to-text ratio suggests scanned
if images > 0 and text_elements == 0:
return True
# Check for very short text content (garbled OCR)
total_text = sum(len(e.get('content', '')) for e in elements)
if total_text < 100 and images > 0:
return True
return False
# Process a document
result = process_scanned_document("mystery-document.pdf", language="eng")
print(result)
Expected output:
Analyzing: mystery-document.pdf
- Initial extraction: 2 images, 0 text elements
- Detected as: SCANNED DOCUMENT
- Applying OCR with language: eng
- Re-extraction: 0 images, 45 text elements
- Text quality score: 0.92
Extraction complete:
- Pages: 3
- Paragraphs: 42
- Headings: 3
- Total words: 1,847