DevelopmentMarch 24, 20254 min read

Image to Text (OCR) App using Python and Streamlit

Extract text from images with Python, Streamlit, and Tesseract OCR — upload a screenshot or photo and get editable text instantly.

Galvan

Galvan

Founder & Creator

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
  • Python packages:
  • code
    pip install streamlit pytesseract pillow

    Step 1: Create the Script

    Save as ocr_app.py:

    code
    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

    code
    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, set pytesseract.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× with img.resize and convert to grayscale before OCR.
  • Selected language returns nothing — the language pack is not installed (brew install tesseract-lang on macOS, or tesseract-ocr-hin on 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_string` vs `image_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_data coordinates — 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.