Surya OCR Tutorial: Document AI for Text Detection, OCR, and Layout Analysis
Surya is a powerful open-source Document AI library for OCR (Optical Character Recognition), text detection, layout analysis, reading order detection, and table recognition. Developed by Vik Paruchuri (creator of Marker), Surya supports over 90 languages and delivers accuracy that is highly competitive with commercial solutions like Google Cloud Vision.
In this tutorial, we will learn how to use Surya for various document AI tasks, from installation to advanced usage for processing complex documents.
Why Surya?
Before we begin, here are some reasons why Surya is worth considering:
- Multi-language: Supports 90+ languages including CJK, Arabic, Indonesian, and many more
- High accuracy: Benchmarks show accuracy comparable to or better than Google Cloud Vision across many languages
- Open-source: GPL-3.0 license, free for non-commercial use
- Feature-complete: OCR, text detection, layout analysis, table recognition, and reading order detection in one package
- GPU acceleration: CUDA support for faster processing
- Easy to use: Simple and intuitive API
Installation
System Requirements
- Python 3.9 or newer
- PyTorch (installed automatically)
- NVIDIA GPU with CUDA (optional but highly recommended for performance)
Installation via pip
pip install surya-ocr
Installation from Source (for development)
git clone https://github.com/VikParuchuri/surya.git
cd surya
pip install -e .
Verify Installation
import surya
print(surya.version)
Additional Dependencies
To process PDF files, also install:
pip install pypdfium2
Basic Usage
1. Text Detection
Text detection is the first step in the OCR pipeline. Surya detects areas where text is present in an image.
from surya.detection import DetectionPredictor
from PIL import Image
Load image
image = Image.open("document.png")
Initialize detector
detector = DetectionPredictor()
Detect text
predictions = detector([image])
View detection results
for prediction in predictions:
for bbox in prediction.bboxes:
print(f"Bounding box: {bbox.bbox}")
print(f"Confidence: {bbox.confidence:.4f}")
print(f"Polygon: {bbox.polygon}")
print()
2. OCR (Optical Character Recognition)
After detecting text areas, we can perform OCR to extract text from images.
from surya.recognition import RecognitionPredictor
from surya.detection import DetectionPredictor
from surya.ocr import OCRPredictor
from PIL import Image
Load image
image = Image.open("document.png")
Initialize predictors
detpredictor = DetectionPredictor()
recpredictor = RecognitionPredictor()
ocrpredictor = OCRPredictor(detpredictor, recpredictor)
Run OCR
predictions = ocrpredictor([image])
Display results
for prediction in predictions:
for textline in prediction.textlines:
print(f"Text: {textline.text}")
print(f"Confidence: {textline.confidence:.4f}")
print(f"BBox: {textline.bbox}")
print()
3. Specifying Languages
Surya can detect languages automatically, but you can also specify languages explicitly for more accurate results:
from surya.recognition import RecognitionPredictor
from surya.detection import DetectionPredictor
from surya.ocr import OCRPredictor
from PIL import Image
image = Image.open("documentmixed.png")
detpredictor = DetectionPredictor()
recpredictor = RecognitionPredictor()
ocrpredictor = OCRPredictor(detpredictor, recpredictor)
Specify language: English
predictions = ocrpredictor([image], langs=[["en"]])
for prediction in predictions:
for textline in prediction.textlines:
print(f"{textline.text}")
Language codes follow the ISO 639 standard. Some examples: en (English), id (Indonesian), zh (Chinese), ja (Japanese), ar (Arabic).
Layout Analysis
Layout analysis identifies document structure such as paragraphs, headings, tables, figures, and other elements.
from surya.detection import DetectionPredictor
from surya.layout import LayoutPredictor
from PIL import Image
image = Image.open("document.png")
detpredictor = DetectionPredictor()
layoutpredictor = LayoutPredictor()
Analyze layout
predictions = layoutpredictor([image])
for prediction in predictions:
for layoutbox in prediction.bboxes:
print(f"Label: {layoutbox.label}")
print(f"BBox: {layoutbox.bbox}")
print(f"Confidence: {layoutbox.confidence:.4f}")
print()
Available labels in layout analysis include:
Text- Regular text paragraphsTitle- Titles or headingsTable- TablesFigure- Images or diagramsList- ListsCaption- Image captionsHeader- Page headersFooter- Page footersPageNumber- Page numbers
Table Recognition
Surya can detect and extract table structures from documents.
from surya.detection import DetectionPredictor
from surya.layout import LayoutPredictor
from surya.tablerec import TableRecPredictor
from PIL import Image
image = Image.open("documenttable.png")
detpredictor = DetectionPredictor()
layoutpredictor = LayoutPredictor()
tablepredictor = TableRecPredictor()
Detect layout first to find tables
layoutpredictions = layoutpredictor([image])
Get table areas
tableboxes = []
for pred in layoutpredictions:
for box in pred.bboxes:
if box.label == "Table":
tableboxes.append(box.bbox)
Recognize tables
if tableboxes:
tablepredictions = tablepredictor(
[image],
[tableboxes[0]] # Process first table
)
for prediction in tablepredictions:
for cell in prediction.cells:
print(f"Row: {cell.row}, Col: {cell.col}")
print(f"Text: {cell.text}")
print(f"BBox: {cell.bbox}")
print()
Reading Order Detection
Reading order detection determines the correct reading sequence for elements in a document, which is especially useful for multi-column documents.
from surya.detection import DetectionPredictor
from surya.layout import LayoutPredictor
from surya.ordering import OrderingPredictor
from PIL import Image
image = Image.open("documentmulticolumn.png")
detpredictor = DetectionPredictor()
layoutpredictor = LayoutPredictor()
orderingpredictor = OrderingPredictor()
Get layout
layoutpredictions = layoutpredictor([image])
Determine reading order
orderpredictions = orderingpredictor(
[image],
[layoutpredictions[0].bboxes]
)
for prediction in orderpredictions:
for item in prediction.bboxes:
print(f"Order: {item.position}, Label: {item.label}")
print(f"BBox: {item.bbox}")
print()
Advanced Usage
Batch Processing Multiple Documents
When you need to process many documents at once, use batch processing for efficiency:
from surya.recognition import RecognitionPredictor
from surya.detection import DetectionPredictor
from surya.ocr import OCRPredictor
from PIL import Image
import os
detpredictor = DetectionPredictor()
recpredictor = RecognitionPredictor()
ocrpredictor = OCRPredictor(detpredictor, recpredictor)
Load all images
imagedir = "documents/"
images = []
filenames = []
for filename in os.listdir(imagedir):
if filename.endswith((".png", ".jpg", ".jpeg")):
img = Image.open(os.path.join(imagedir, filename))
images.append(img)
filenames.append(filename)
Batch process (Surya optimizes GPU usage automatically)
predictions = ocrpredictor(images, langs=[["en"]] len(images))
Save results
for filename, prediction in zip(filenames, predictions):
print(f"\n=== {filename} ===")
fulltext = "\n".join([line.text for line in prediction.textlines])
print(fulltext)
# Save to file
outputpath = f"output/{filename}.txt"
with open(outputpath, "w", encoding="utf-8") as f:
f.write(fulltext)
Processing PDF Files
from surya.recognition import RecognitionPredictor
from surya.detection import DetectionPredictor
from surya.ocr import OCRPredictor
import pypdfium2 as pdfium
from PIL import Image
def pdftoimages(pdfpath, dpi=150):
"""Convert PDF pages to a list of PIL Images."""
pdf = pdfium.PdfDocument(pdfpath)
images = []
for i in range(len(pdf)):
page = pdf[i]
bitmap = page.render(scale=dpi / 72)
pilimage = bitmap.topil()
images.append(pilimage)
return images
Convert PDF to images
images = pdftoimages("report.pdf")
print(f"Total pages: {len(images)}")
Initialize OCR
detpredictor = DetectionPredictor()
recpredictor = RecognitionPredictor()
ocrpredictor = OCRPredictor(detpredictor, recpredictor)
OCR all pages
predictions = ocrpredictor(images, langs=[["en"]] len(images))
Combine results
fulldocument = []
for i, prediction in enumerate(predictions):
pagetext = "\n".join([line.text for line in prediction.textlines])
fulldocument.append(f"--- Page {i+1} ---\n{pagetext}")
result = "\n\n".join(fulldocument)
print(result)
Complete Pipeline: OCR + Layout + Table Recognition
Here is a complete pipeline that combines all of Surya's features:
from surya.recognition import RecognitionPredictor
from surya.detection import DetectionPredictor
from surya.layout import LayoutPredictor
from surya.tablerec import TableRecPredictor
from surya.ordering import OrderingPredictor
from surya.ocr import OCRPredictor
from PIL import Image
import json
def processdocument(imagepath, language="en"):
"""Complete pipeline for document processing."""
image = Image.open(imagepath)
# Initialize all predictors
detpredictor = DetectionPredictor()
recpredictor = RecognitionPredictor()
layoutpredictor = LayoutPredictor()
tablepredictor = TableRecPredictor()
orderingpredictor = OrderingPredictor()
ocrpredictor = OCRPredictor(detpredictor, recpredictor)
# Step 1: Layout analysis
layoutpreds = layoutpredictor([image])
layout = layoutpreds[0]
# Step 2: Reading order
orderpreds = orderingpredictor([image], [layout.bboxes])
order = orderpreds[0]
# Step 3: OCR
ocrpreds = ocrpredictor([image], langs=[[language]])
ocr = ocrpreds[0]
# Step 4: Table recognition (if tables exist)
tables = []
for box in layout.bboxes:
if box.label == "Table":
tablepred = tablepredictor([image], [box.bbox])
tables.append({
"bbox": box.bbox,
"cells": [
{
"row": cell.row,
"col": cell.col,
"text": cell.text,
"bbox": cell.bbox
}
for cell in tablepred[0].cells
]
})
# Compile results
result = {
"textlines": [
{
"text": line.text,
"confidence": line.confidence,
"bbox": line.bbox
}
for line in ocr.textlines
],
"layout": [
{
"label": box.label,
"bbox": box.bbox,
"confidence": box.confidence
}
for box in layout.bboxes
],
"readingorder": [
{
"position": item.position,
"label": item.label,
"bbox": item.bbox
}
for item in order.bboxes
],
"tables": tables
}
return result
Use the pipeline
result = processdocument("complexdocument.png", language="en")
Save results to JSON
with open("analysisresult.json", "w", encoding="utf-8") as f:
json.dump(result, f, ensureascii=False, indent=2)
Display text in correct reading order
print("=== Document Text (Reading Order) ===")
for line in result["textlines"]:
print(f"[{line['confidence']:.2f}] {line['text']}")
Visualizing Detection Results
You can visualize detection results using PIL or matplotlib:
from surya.detection import DetectionPredictor
from PIL import Image, ImageDraw, ImageFont
image = Image.open("document.png")
detector = DetectionPredictor()
predictions = detector([image])
Create a copy for visualization
visimage = image.copy()
draw = ImageDraw.Draw(visimage)
for prediction in predictions:
for bbox in prediction.bboxes:
coords = bbox.bbox # [x1, y1, x2, y2]
draw.rectangle(coords, outline="red", width=2)
visimage.save("detectionresult.png")
print("Visualization saved to detectionresult.png")
Using the CLI
Surya also provides a command-line interface for quick usage:
# OCR
suryaocr document.png --langs en
Text detection
suryadetect document.png
Layout analysis
suryalayout document.png
Table recognition
suryatable document.png
To process a folder of images:
suryaocr ./documentsfolder/ --langs en --resultsdir ./output/
Configuration and Optimization
GPU Settings
import os
Use a specific GPU
os.environ["CUDAVISIBLEDEVICES"] = "0"
Set batch sizes (adjust based on GPU VRAM)
os.environ["RECOGNITIONBATCHSIZE"] = "64"
os.environ["DETECTORBATCHSIZE"] = "16"
os.environ["LAYOUTBATCHSIZE"] = "16"
os.environ["ORDERBATCHSIZE"] = "16"
os.environ["TABLERECBATCHSIZE"] = "16"
Running on CPU
If you don't have a GPU, Surya can still run on CPU, though it will be slower:
import torch
Force CPU mode
device = "cpu"
Reduce batch sizes for CPU
os.environ["RECOGNITIONBATCHSIZE"] = "8"
os.environ["DETECTORBATCHSIZE"] = "4"
Performance Optimization Tips
def resizeimage(image, maxsize=3000):
"""Resize image while maintaining aspect ratio."""
w, h = image.size
if max(w, h) > maxsize:
ratio = maxsize / max(w, h)
neww = int(w ratio)
newh = int(h ratio)
return image.resize((neww, newh), Image.LANCZOS)
return image
~/.cache/huggingface/import gc
import torch
def processlargepdf(pdfpath):
images = pdftoimages(pdfpath)
results = []
for i, img in enumerate(images):
pred = ocrpredictor([img], langs=[["en"]])
results.append(pred[0])
# Clean memory every 10 pages
if (i + 1) % 10 == 0:
gc.collect()
if torch.cuda.isavailable():
torch.cuda.emptycache()
return results
Best Practices
1. Image Pre-processing
Input quality significantly affects OCR results. Here are some pre-processing tips:
from PIL import Image, ImageEnhance, ImageFilter
def preprocessforocr(imagepath):
"""Pre-process image for better OCR results."""
img = Image.open(imagepath)
# Convert to RGB if needed
if img.mode != "RGB":
img = img.convert("RGB")
# Increase contrast
enhancer = ImageEnhance.Contrast(img)
img = enhancer.enhance(1.5)
# Increase sharpness
enhancer = ImageEnhance.Sharpness(img)
img = enhancer.enhance(2.0)
return img
2. Graceful Error Handling
def safeocr(imagepath, language="en"):
"""OCR with proper error handling."""
try:
image = Image.open(image
path)
if image.mode not in ("RGB", "L"):
image = image.convert("RGB")
detpredictor = DetectionPredictor()
recpredictor = RecognitionPredictor()
ocrpredictor = OCRPredictor(detpredictor, recpredictor)
predictions = ocrpredictor([image], langs=[[language]])
if not predictions or not predictions[0].textlines:
return {"success": False, "error": "No text detected"}
textlines = [
{
"text": line.text,
"confidence": line.confidence
}
for line in predictions[0].textlines
]
return {
"success": True,
"text": "\n".join([l["text"] for l in textlines]),
"lines": textlines,
"avgconfidence": sum(l["confidence"] for l in textlines) / len(textlines)
}
except FileNotFoundError:
return {"success": False, "error": f"File not found: {imagepath}"}
except Exception as e:
return {"success": False, "error": str(e)}
3. Post-processing OCR Results
import re
def cleanocrtext(text):
"""Clean up OCR results."""
# Remove excess spaces
text = re.sub(r' +', ' ', text)
# Fix line breaks
text = re.sub(r'\n{3,}', '\n\n', text)
# Remove non-printable characters
text = re.sub(r'[^\S\n]+', ' ', text)
# Strip each line
lines = [line.strip() for line in text.split('\n')]
text = '\n'.join(lines)
return text.strip()
4. Exporting Results to Various Formats
import json
import csv
def exportresults(predictions, outputformat="json", outputpath="output"):
"""Export OCR results to various formats."""
lines = []
for pred in predictions:
for line in pred.textlines:
lines.append({
"text": line.text,
"confidence": line.confidence,
"bbox": line.bbox
})
if outputformat == "json":
with open(f"{outputpath}.json", "w", encoding="utf-8") as f:
json.dump(lines, f, ensureascii=False, indent=2)
elif outputformat == "csv":
with open(f"{outputpath}.csv", "w", encoding="utf-8", newline="") as f:
writer = csv.DictWriter(f, fieldnames=["text", "confidence", "bbox"])
writer.writeheader()
for line in lines:
line["bbox"] = str(line["bbox"])
writer.writerow(line)
elif outputformat == "txt":
with open(f"{outputpath}.txt", "w", encoding="utf-8") as f:
for line in lines:
f.write(f"{line['text']}\n")
print(f"Results saved to {outputpath}.{outputformat}")
Comparison with Alternatives
| Feature | Surya | Tesseract | EasyOCR | PaddleOCR |
|---------|-------|-----------|---------|-----------|
| Languages Supported | 90+ | 100+ | 80+ | 80+ |
| GPU Support | Yes | Limited | Yes | Yes |
| Layout Analysis | Yes | Limited | No | Yes |
| Table Recognition | Yes | No | No | Yes |
| Reading Order | Yes | No | No | No |
| Accuracy (general) | High | Medium | High | High |
| Speed | Fast (GPU) | Fast (CPU) | Medium | Fast |
| License | GPL-3.0 | Apache-2.0 | Apache-2.0 | Apache-2.0 |
Conclusion
Surya is a comprehensive and powerful Document AI solution. With multi-language OCR, text detection, layout analysis, table recognition, and reading order detection, Surya is a top choice for various document processing needs.
Key takeaways:
pip install surya-ocrSurya is actively developed with continuous model improvements and new features. For commercial use, make sure to review the GPL-3.0 license details and available commercial licensing options.