What is OCR and When to Use It?
OCR (Optical Character Recognition) is the technology that converts images of text into editable, searchable digital text. Primary use cases:
- Document digitization: invoices, contracts, scanned forms
- Accessibility: making PDFs searchable for visually impaired users
- RPA automation: extracting data from images for ERP/CRM systems
- Receipt scanning: accounting apps that photograph paper receipts
- License plate recognition: vehicle identification systems
- Historical texts: digitizing paper archives
Installation
# Tesseract OCR engine (Google)
# Ubuntu: sudo apt install tesseract-ocr tesseract-ocr-eng
# macOS: brew install tesseract tesseract-lang
# Windows: download installer from github.com/UB-Mannheim/tesseract/wiki
pip install pytesseract Pillow opencv-python-headless easyocr
Method 1: pytesseract (Tesseract from Python)
import pytesseract
from PIL import Image
import cv2
import numpy as np
from pathlib import Path
# If tesseract is not in PATH (Windows):
# pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
def basic_ocr(image_path, language='eng'):
"""
Extract text from an image with Tesseract.
language: 'eng' (English), 'spa' (Spanish), 'eng+spa' (both)
"""
img = Image.open(image_path)
text = pytesseract.image_to_string(img, lang=language)
print(f"Extracted text ({len(text)} characters):")
print(text[:500])
return text
def ocr_with_data(image_path, language='eng'):
"""Extract text with position and confidence for each word."""
img = Image.open(image_path)
data = pytesseract.image_to_data(
img, lang=language,
output_type=pytesseract.Output.DICT
)
words = []
for i, word in enumerate(data['text']):
if word.strip() and data['conf'][i] > 50:
words.append({
'text': word,
'confidence': data['conf'][i],
'x': data['left'][i],
'y': data['top'][i],
'width': data['width'][i],
'height': data['height'][i],
})
print(f"Words detected: {len(words)}")
return words
def ocr_digits_only(image_path):
"""Special mode for extracting only digits (useful for prices, dates)."""
img = Image.open(image_path)
config = '--psm 6 --oem 3 -c tessedit_char_whitelist=0123456789.,$/%-'
text = pytesseract.image_to_string(img, config=config)
return text.strip()
text = basic_ocr('invoice.png', language='eng')
OpenCV Preprocessing (Key to Good OCR Quality)
def preprocess_for_ocr(image_path, save_debug=False):
"""
Image preprocessing pipeline to improve OCR quality.
Preprocessing can improve accuracy by 20-60%.
"""
img = cv2.imread(str(image_path))
if img is None:
raise ValueError(f"Could not read: {image_path}")
# 1. Convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 2. Upscale if resolution is too low (< 300 DPI equivalent)
h, w = gray.shape
if w < 1000:
scale = 1000 / w
gray = cv2.resize(gray, None, fx=scale, fy=scale,
interpolation=cv2.INTER_CUBIC)
# 3. Denoise with bilateral filter (preserves text edges)
denoised = cv2.bilateralFilter(gray, 9, 75, 75)
# 4. Adaptive thresholding (better than global for uneven lighting)
binary = cv2.adaptiveThreshold(
denoised, 255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 11, 2
)
# 5. Slight dilation to join fragmented characters
kernel = np.ones((1, 1), np.uint8)
dilated = cv2.dilate(binary, kernel, iterations=1)
if save_debug:
cv2.imwrite('debug_preprocessed.png', dilated)
return dilated
def correct_skew(image_cv2):
"""
Detect and correct text skew in the image.
Critical for accurate OCR on tilted scanned documents.
"""
edges = cv2.Canny(image_cv2, 50, 150, apertureSize=3)
lines = cv2.HoughLines(edges, 1, np.pi/180, 200)
if lines is None:
return image_cv2, 0
angles = []
for line in lines[:10]:
rho, theta = line[0]
angle = (theta * 180 / np.pi) - 90
if abs(angle) < 45:
angles.append(angle)
if not angles:
return image_cv2, 0
median_angle = np.median(angles)
if abs(median_angle) < 0.5:
return image_cv2, median_angle
h, w = image_cv2.shape[:2]
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, median_angle, 1.0)
corrected = cv2.warpAffine(image_cv2, M, (w, h),
flags=cv2.INTER_CUBIC,
borderMode=cv2.BORDER_REPLICATE)
print(f"Skew corrected: {median_angle:.2f}°")
return corrected, median_angle
def full_ocr_pipeline(image_path, language='eng'):
"""Complete pipeline: preprocess + skew correction + OCR."""
img_proc = preprocess_for_ocr(image_path)
img_corr, angle = correct_skew(img_proc)
img_pil = Image.fromarray(img_corr)
config = '--oem 3 --psm 6'
text = pytesseract.image_to_string(img_pil, lang=language, config=config)
return text.strip()
text = full_ocr_pipeline('scanned_document.jpg', language='eng')
print(text)
Method 2: EasyOCR (No External Installation)
import easyocr
def ocr_easyocr(image_path, languages=['en'], gpu=False):
"""
EasyOCR: no Tesseract installation required.
Supports 80+ languages with neural networks.
gpu=True to accelerate with CUDA if available.
"""
reader = easyocr.Reader(languages, gpu=gpu)
results = reader.readtext(str(image_path))
texts = []
for (bbox, text, confidence) in results:
if confidence > 0.3:
texts.append({
'text': text,
'confidence': confidence,
'bbox': bbox,
})
full_text = ' '.join(t['text'] for t in texts)
print(f"EasyOCR: {len(texts)} text fragments detected")
return full_text, texts
def draw_ocr_results(image_path, results, output='ocr_result.jpg'):
"""Draw bounding boxes on the image with detected text."""
img = cv2.imread(str(image_path))
for item in results:
pts = np.array(item['bbox'], dtype=np.int32)
cv2.polylines(img, [pts], True, (0, 255, 0), 2)
x, y = int(item['bbox'][0][0]), int(item['bbox'][0][1]) - 5
cv2.putText(img, item['text'][:20], (x, y),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1)
cv2.imwrite(output, img)
print(f"Visual result: {output}")
text, details = ocr_easyocr('image_with_text.png', languages=['en'])
draw_ocr_results('image_with_text.png', details)
PDF OCR: Extract Text from Scanned PDFs
def pdf_to_text_ocr(pdf_path, language='eng', dpi=300):
"""
Convert a scanned PDF (image) to text via OCR.
pip install pdf2image
Ubuntu: sudo apt install poppler-utils
"""
try:
from pdf2image import convert_from_path
except ImportError:
raise ImportError("pip install pdf2image # and: apt install poppler-utils")
print(f"Processing PDF with OCR: {pdf_path}")
pages = convert_from_path(str(pdf_path), dpi=dpi)
full_text = []
for i, page in enumerate(pages, 1):
print(f" Page {i}/{len(pages)}...")
img_np = np.array(page)
img_gray = cv2.cvtColor(img_np, cv2.COLOR_RGB2GRAY)
_, img_bin = cv2.threshold(img_gray, 0, 255,
cv2.THRESH_BINARY + cv2.THRESH_OTSU)
img_pil = Image.fromarray(img_bin)
page_text = pytesseract.image_to_string(
img_pil, lang=language, config='--oem 3 --psm 6'
)
full_text.append(f"--- Page {i} ---\n{page_text}")
result = '\n\n'.join(full_text)
print(f"Total text extracted: {len(result):,} characters")
return result
text = pdf_to_text_ocr('scanned_invoice.pdf', language='eng')
print(text[:1000])
OCR Engine Comparison
| Engine | Accuracy | Speed | Languages | Installation |
|---|---|---|---|---|
| Tesseract 5 (pytesseract) | ✅ High | ✅ Fast | 100+ | System required |
| EasyOCR | ✅✅ Very high | ⚠️ Moderate | 80+ | pip install |
| PaddleOCR | ✅✅ Very high | ✅ Fast | 80+ | pip install |
| Google Cloud Vision | ✅✅✅ Best | ✅✅ Fast API | 60+ | Paid API |
| AWS Textract | ✅✅✅ | ✅✅ | English-primary | Paid API |
Conclusion
For OCR in Python, the choice depends on the use case: pytesseract for production environments where Tesseract is already installed and you need maximum speed; EasyOCR for projects where you cannot install system software and need 80+ language support; PaddleOCR for recognizing text in tables or complex documents. OpenCV preprocessing (skew correction, adaptive binarization, noise removal) can improve accuracy by 20–60% before applying any OCR engine.
Related conversions
Document conversions that follow this topic naturally: