Engineers and system administrators frequently encounter text trapped inside visual formats: error dialogs in remote desktop sessions, system architecture
Engineers and system administrators frequently encounter text trapped inside visual formats: error dialogs in remote desktop sessions, system architecture diagrams, screenshot-based documentation, or locked PDF presentations. Manually transcribing logs or configuration strings from screenshots is slow, tedious, and prone to human error—especially when dealing with long cryptographic keys, stack traces, or complex URLs.
This script provides a lightweight command-line OCR (Optical Character Recognition) utility that converts image screenshots into clean, structured plain text. By integrating Pillow for image manipulation and pytesseract as an interface to the open-source Tesseract OCR engine, it offers a quick CLI tool for single images, automated batch pipelines, or shell script integration.
The utility follows a linear pipeline: CLI argument parsing -> file validation -> image preprocessing -> OCR extraction -> text formatting/output.
When an image file path is provided, the script validates its existence and opens it using Pillow (PIL). Optional preprocessing steps—such as converting to grayscale, boosting contrast, or applying thresholding—can be applied before feeding the image to Tesseract. These adjustments significantly improve recognition accuracy for low-contrast screenshots or terminal capture outputs. Finally, pytesseract passes the image array to the underlying system Tesseract engine, which performs layout analysis and character recognition, returning the extracted text to standard output or a designated file.
#!/usr/bin/env python3
"""
Screenshot to Text OCR Extractor
Extracts raw text from screenshot images using PIL and PyTesseract.
Supports optional image preprocessing to enhance OCR accuracy.
"""
import argparse
import os
import sys
from pathlib import Path
from typing import Optional
from PIL import Image, ImageEnhance, ImageFilter
import pytesseract
# Configure Tesseract binary path if set in environment
TESSERACT_CMD: Optional[str] = os.environ.get("TESSERACT_CMD")
if TESSERACT_CMD:
pytesseract.pytesseract.tesseract_cmd = TESSERACT_CMD
# Default configuration options from environment variables
DEFAULT_LANG: str = os.environ.get("OCR_DEFAULT_LANG", "eng")
DEFAULT_PSM: int = int(os.environ.get("OCR_DEFAULT_PSM", "3"))
def preprocess_image(image: Image.Image, mode: str) -> Image.Image:
"""
Applies image processing transforms to enhance text legibility for OCR.
Args:
image: Source PIL Image object.
mode: Preprocessing technique ('grayscale', 'contrast', 'threshold', or 'none').
Returns:
Processed PIL Image object.
"""
if mode == "none":
return image
# Convert to grayscale first for all processing modes
gray_image = image.convert("L")
if mode == "grayscale":
return gray_image
if mode == "contrast":
enhancer = ImageEnhance.Contrast(gray_image)
return enhancer.enhance(2.0)
if mode == "threshold":
# Simple binarization: pixels > 150 become white, others black
threshold_value = 150
return gray_image.point(lambda pixel: 255 if pixel > threshold_value else 0)
return image
def extract_text(
image_path: Path,
lang: str = "eng",
psm: int = 3,
preprocess_mode: str = "none"
) -> str:
"""
Loads an image file, preprocesses it, and performs OCR text extraction.
Args:
image_path: Path to the image file.
lang: Tesseract language code (e.g., 'eng', 'spa', 'deu').
psm: Page Segmentation Mode (0-13).
preprocess_mode: Image transformation mode before extraction.
Returns:
Extracted text string.
Raises:
FileNotFoundError: If the image path does not exist.
RuntimeError: If Tesseract fails or is not properly installed.
"""
if not image_path.exists():
raise FileNotFoundError(f"Target image file not found: {image_path}")
try:
with Image.open(image_path) as img:
processed_img = preprocess_image(img, preprocess_mode)
# Configure custom page segmentation mode
custom_config = f"--psm {psm}"
extracted_text = pytesseract.image_to_string(
processed_img,
lang=lang,
config=custom_config
)
return extracted_text.strip()
except pytesseract.TesseractNotFoundError:
raise RuntimeError(
"Tesseract OCR executable was not found on your system PATH.\n"
"Please install Tesseract-OCR or set the TESSERACT_CMD environment variable."
)
except pytesseract.TesseractError as err:
raise RuntimeError(f"Tesseract engine processing failed: {err}")
except Exception as err:
raise RuntimeError(f"An error occurred while reading image: {err}")
def build_cli_parser() -> argparse.ArgumentParser:
"""Builds and returns the command line argument parser."""
parser = argparse.ArgumentParser(
description="Extract text from screenshot images using Tesseract OCR."
)
parser.add_argument(
"-i", "--image",
type=Path,
required=True,
help="Path to the screenshot image file."
)
parser.add_argument(
"-o", "--output",
type=Path,
default=None,
help="Optional path to save extracted text output."
)
parser.add_argument(
"-l", "--lang",
type=str,
default=DEFAULT_LANG,
help=f"Language for OCR processing (default: {DEFAULT_LANG})."
)
parser.add_argument(
"--psm",
type=int,
default=DEFAULT_PSM,
help=f"Page Segmentation Mode (0-13, default: {DEFAULT_PSM})."
)
parser.add_argument(
"-p", "--preprocess",
choices=["none", "grayscale", "contrast", "threshold"],
default="none",
help="Image preprocessing mode to improve extraction quality."
)
return parser
def main() -> None:
"""CLI entrypoint for the OCR script."""
parser = build_cli_parser()
args = parser.parse_args()
try:
text = extract_text(
image_path=args.image,
lang=args.lang,
psm=args.psm,
preprocess_mode=args.preprocess
)
if not text:
print("Warning: No text could be detected in the provided image.", file=sys.stderr)
if args.output:
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(text, encoding="utf-8")
print(f"Successfully saved extracted text to {args.output}")
else:
print(text)
except (FileNotFoundError, RuntimeError) as err:
print(f"Error: {err}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
This script supports configuration through both system environment variables and command-line flags.
| Environment Variable | Default Value | Description |
|---|---|---|
TESSERACT_CMD |
None | Path to the binary executable if Tesseract is not installed in standard system PATH. |
OCR_DEFAULT_LANG |
eng |
Default 3-letter ISO language code used when --lang is omitted. |
OCR_DEFAULT_PSM |
3 |
Default Page Segmentation Mode used when --psm is omitted. |
Before running the script, ensure the Tesseract system library is installed:
brew install tesseractsudo apt-get install tesseract-ocrTESSERACT_CMD="C:\Program Files\Tesseract-OCR\tesseract.exe".Extract text from a standard terminal screenshot and print directly to stdout.
python3 ocr_extract.py --image terminal_error.png
Output:
[ERROR] 2026-03-30 10:14:02 - Failed to bind port 8080: Address already in use
Traceback (most recent call last):
File "server.py", line 42, in start
server.bind(("0.0.0.0", 8080))
OSError: [Errno 98] Address already in use
Process a low-contrast image using grayscale conversion and contrast enhancement, saving the result directly to a file.
python3 ocr_extract.py --image low_contrast_log.png --preprocess contrast --output extracted_log.txt
Output:
Successfully saved extracted text to extracted_log.txt
Extract text from a single line of text in Spanish using Page Segmentation Mode 7 (Treat image as a single text line).
python3 ocr_extract.py --image Spanish_Banner.png --lang spa --psm 7
Output:
Acceso denegado. Consulte con el administrador.
The script provides explicit error reporting for common execution failures:
FileNotFoundError): Verifies that the source image path exists on the filesystem prior to attempting to read it.TesseractNotFoundError): Catches missing system binaries and provides clear instructions on setting TESSERACT_CMD or installing the package.TesseractError): If a language code specified via --lang is missing its corresponding traineddata file (e.g., spa.traineddata), Tesseract raises an engine error, which is caught and printed without dumping Python backtraces.stderr.Here are several ways you can expand this script for operational workflows:
pyperclip to automatically capture screenshots straight from the OS system clipboard without needing an intermediate saved file.watchdog to automatically scan and convert any new image files added to a designated ~/Screenshots folder.pytesseract call to generate bounding box coordinates (image_to_boxes) or search-searchable PDFs (image_to_pdf_or_hocr) rather than raw text.re module to filter output specifically for URLs, IP addresses, AWS Access Keys, or log timestamps.