Introduction
Optical Character Recognition sounds like advanced AI, but with Tesseract — Google's open-source OCR engine — it is a single function call away. This tutorial builds an app where you drop in a screenshot, scanned document, or photo of a page, and get clean, editable, copyable text out. It is the natural companion to the QR scanner: same upload-decode-display skeleton, but for human-readable text.
The one setup quirk: Tesseract is a native binary, not a pip package. Python talks to it through the pytesseract wrapper, so you install the engine separately (links below), then everything else is pure Python.
Features
Prerequisites
brew install tesseractsudo apt install tesseract-ocrpip install streamlit pytesseract pillowStep 1: Create the Script
Save as ocr_app.py:
import streamlit as st
import pytesseract
from PIL import Image
import io
st.set_page_config(page_title="OCR — Image to Text", page_icon="📝")
st.title("📝 Image to Text (OCR)")
uploaded = st.file_uploader("Upload an image with text", type=["png", "jpg", "jpeg", "webp"])
lang = st.selectbox("Language", ["eng", "hin", "spa", "fra", "deu"])
if uploaded:
img = Image.open(io.BytesIO(uploaded.getvalue()))
st.image(img, caption=f"{img.size[0]}×{img.size[1]} px", use_container_width=True)
if st.button("🔍 Extract Text", type="primary"):
with st.spinner("Reading the image..."):
text = pytesseract.image_to_string(img, lang=lang)
data = pytesseract.image_to_data(img, output_type=pytesseract.Output.DICT)
clean = [ln for ln in text.splitlines() if ln.strip()]
st.subheader(f"Extracted {len(clean)} lines")
st.code("\n".join(clean), language=None)
confidences = [int(c) for c in data["conf"] if int(c) > 0]
if confidences:
avg = sum(confidences) / len(confidences)
st.caption(f"Average confidence: {avg:.0f}%")Step 2: Run the App
streamlit run ocr_app.pyScreenshot a paragraph of text, upload it, and hit Extract — you should get the words back nearly perfectly. Handwriting and stylized fonts are where confidence drops.
How It Works
Tesseract works in stages: binarize the image (text black, background white), find connected regions, group them into lines and words, then classify each glyph against its trained models. pytesseract.image_to_string wraps the whole pipeline and returns plain text; image_to_data returns the same run as a dictionary — including per-word bounding boxes and confidence values — which is where the average-confidence caption comes from.
Image quality dominates results. Sharp, high-contrast, straight-on images score 95%+; skewed phone photos of glossy pages drop fast. The single most effective preprocessing step, when needed, is converting to grayscale and boosting contrast with Pillow — the same transforms covered in the Pillow image processing tutorial.
The language code matters more than people expect: lang="eng" forces English glyph models. Passing "eng+hin" runs both model sets and merges results, which is how multilingual receipts get read correctly.
Common Errors & Fixes
pytesseract.pytesseract.tesseract_cmd = r"C:\Program Files\Tesseract-OCR\tesseract.exe" at the top of the script.img.resize and convert to grayscale before OCR.brew install tesseract-lang on macOS, or tesseract-ocr-hin on Ubuntu).ImageOps.invert(img.convert("RGB")) before extraction.Key Concepts
+.What to Try Next
image_to_data coordinates — the annotation pattern from the QR scanner applies directly.pdf2image, then OCR each — combine with the PDF merger for a full document toolkit.FAQ
How accurate is Tesseract?
On clean printed text, 95–99% character accuracy. Handwriting, cursive, and low-resolution photos drop sharply — for those, a deep-learning OCR (EasyOCR, PaddleOCR) does better at the cost of heavier installs.
Can it read tables?
It reads the text but flattens the structure. Use image_to_data coordinates to reconstruct columns, or try Tesseract's TSV output for a starting grid.
Where does my image go?
Nowhere — decoding happens in your process via the local Tesseract binary. Like the PDF merger, the privacy story is the feature.