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
- Upload any image — PNG, JPG, or WebP screenshots and photos.
- Instant extraction — full text with layout preserved line-by-line.
- Language selection — English plus any Tesseract language packs you have.
- Confidence scores — see how sure the engine is per word.
- Copy-ready output — text in a code block with a built-in copy button.
Prerequisites
- Python 3.8+ — from python.org.
- Tesseract engine:
- macOS:
brew install tesseract - Ubuntu:
sudo apt install tesseract-ocr - Windows: installer from the UB-Mannheim build
- macOS:
- Python packages:
pip install streamlit pytesseract pillow
Step 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.py
Screenshot 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
TesseractNotFoundError— the engine binary is not on your PATH. On Windows, setpytesseract.pytesseract.tesseract_cmd = r"C:\Program Files\Tesseract-OCR\tesseract.exe"at the top of the script.- Garbage output (
~and|everywhere) — the image is too low-contrast or too small. Upscale 2× withimg.resizeand convert to grayscale before OCR. - Selected language returns nothing — the language pack is not installed (
brew install tesseract-langon macOS, ortesseract-ocr-hinon Ubuntu). - Empty text from an obviously-texted image — inverted colors (dark mode screenshots). Flip with
ImageOps.invert(img.convert("RGB"))before extraction.
Key Concepts
- Engine vs wrapper — Tesseract is the binary; pytesseract is its Python remote control.
image_to_stringvsimage_to_data— plain text vs words-with-positions-and-confidence.- Preprocessing wins — grayscale, contrast, and upscaling beat any parameter tuning.
- Language packs — glyph models per language, combinable with
+.
What to Try Next
- Draw bounding boxes on the image using
image_to_datacoordinates — the annotation pattern from the QR scanner applies directly. - Add a PDF input tab: rasterize pages with
pdf2image, then OCR each — combine with the PDF merger for a full document toolkit. - Send extracted text to the sentiment analyzer for an end-to-end pipeline.
- Add a search-in-image box that highlights matching words from the OCR data.
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.