New to SCRY?
Watch the complete 2-minute walkthrough on YouTube: https://youtu.be/a3_iOsLBzW4
See beyond the pixels.
scry is a small Linux command-line utility that lets you drag-select a region of an image, runs OCR on it, and puts the extracted text on your clipboard β all from a single command.
python -m scry screenshot.pngDrag a box around some text, release the mouse, and the text is on your clipboard before you've had time to reach for it.
- Click-and-drag text selection from any image file, via a small Tkinter window
- OCR via Tesseract, with automatic upscaling of small crops for better accuracy
- Clipboard integration β extracted text is copied automatically
- stdout output β extracted text is also printed, so scry composes with pipes and scripts
- Desktop notifications on completion (optional, skippable)
- Multi-language OCR support via Tesseract language codes
(
eng,eng+fra, etc.) - Clean, predictable exit codes for scripting
- No configuration files, no background processes, no persistent state β scry does one thing per invocation and exits
To set expectations correctly: scry does not capture your screen (you give it an existing image file), does not do batch or multi-region selection, does not zoom or pan the selection window, and does not run as a background service. See Known Limitations for the full list.
scry is not yet published on PyPI. Install it from source:
git clone https://github.com/Nekoyazuma/scry.git
cd scry
pip install -e .This installs scry's Python dependencies (Pillow, pytesseract,
pyperclip) via pip. It does not install the system-level tools
scry depends on β see below.
scry relies on a few system packages that aren't distributed through
pip:
| Tool | Purpose | Required? |
|---|---|---|
tesseract-ocr (the tesseract binary) |
OCR engine | Yes |
Tkinter (python3-tk or equivalent) |
Selection window | Yes |
A clipboard tool (xclip for X11, wl-clipboard for Wayland) |
Copying extracted text | Yes |
notify-send (from libnotify) |
Desktop toast on completion | No β scry works fine without it |
Debian / Ubuntu / Kali:
sudo apt install tesseract-ocr python3-tk xclip libnotify-bin(If you're on Wayland, swap xclip for wl-clipboard.)
Arch Linux:
sudo pacman -S tesseract tesseract-data-eng tk xclip libnotifyFedora:
sudo dnf install tesseract python3-tkinter xclip libnotifyIf a required tool is missing, scry tells you exactly which command to run for your distro instead of failing with a raw error β see Troubleshooting.
scry requires Python 3.9 or later. There is no separate "Python
installation" step beyond having Python and pip available β the
pip install -e . command above pulls in scry's Python dependencies
automatically.
The general form:
python -m scry <image> [options]Run it against an image, then drag a box around the text you want:
python -m scry screenshot.pngA window opens showing the image. Click and drag to select a region; release the mouse to extract the text. Press Escape, or close the window, to cancel without extracting anything.
usage: scry [-h] [-l LANG] [-v] [--no-notify] [--version] image
See beyond the pixels. Drag-select text from an image, straight to your
clipboard.
positional arguments:
image Path to the image to select text from.
options:
-h, --help show this help message and exit
-l LANG, --lang LANG Tesseract language code(s), e.g. 'eng' or 'eng+fra'
(default: eng).
-v, --verbose Increase log verbosity to stderr (-v for INFO, -vv for
DEBUG).
--no-notify Skip the desktop notification after copying.
--version show program's version number and exit
Basic use:
python -m scry diagram.pngExtract French text:
python -m scry receipt.png --lang fraExtract mixed English and French text:
python -m scry menu.png --lang eng+fraRun without a desktop notification (useful in scripts or when calling scry repeatedly):
python -m scry screenshot.png --no-notifySee what's happening under the hood:
python -m scry screenshot.png -v # INFO-level logging
python -m scry screenshot.png -vv # DEBUG-level logging, incl. tracebacks on internal errorsSince the extracted text is also printed to stdout, scry composes with normal shell tools:
python -m scry error_dialog.png | grep -i "exception"
python -m scry note.png > extracted.txt(stderr is reserved for logging and never mixes with the extracted text on stdout, so piping stdout is always safe.)
Each run follows the same fixed pipeline:
load image
|
v
select a region (drag in the Tk window)
|
v
crop to that region
|
v
run OCR (Tesseract, via pytesseract)
|
v
copy the extracted text to the clipboard
|
v
print the extracted text to stdout
|
v
show a desktop notification (unless --no-notify)
A few specifics worth knowing:
- Empty selections aren't errors. If you release the mouse without
really dragging (a drag smaller than 4Γ4 pixels), or press Escape, or
close the window, scry exits quietly with exit code
1β nothing is printed, copied, or notified. - "No text found" isn't an error either. If OCR runs successfully
but finds nothing in your selection, scry still exits
0, prints an empty line, and shows a "No text found" notification instead of "Copied to clipboard". - The extracted text always reaches stdout, even if the clipboard copy itself fails β so you don't lose the result to a missing clipboard tool.
- A failed clipboard copy never triggers a notification. You'll see the error on stderr instead, with instructions for fixing it.
- A failed notification never fails the run. Desktop notifications
are best-effort; if
notify-sendisn't installed, scry silently continues without one.
| Code | Meaning |
|---|---|
0 |
Success (including "no text found") |
1 |
Selection was cancelled by the user |
2 |
The image path was missing, unreadable, or not a valid image |
3 |
The OCR engine failed |
4 |
No clipboard tool was available |
5 |
An unexpected internal error occurred |
scry is built to stay small. A few rules the codebase follows throughout:
- One job per run. No config files, no daemon, no persistent state between invocations.
- Plain functions over classes. Most of scry's internals
(
crop(),ocr.extract_text(),clipboard.copy(),notify.send()) are single functions, not classes wrapped around a single implementation. - Abstractions are earned, not anticipated. The one exception is
RegionSelector, an abstract base class β it already backs two real implementations (the real Tkinter window, and a fake used in tests), which is the actual bar scry uses before introducing an abstraction anywhere. - Every failure is either anticipated or a bug. scry defines a
specific exception and exit code for every failure mode it expects
(bad image, cancelled selection, OCR failure, clipboard failure).
Anything else is treated as a genuine bug: it gets a full traceback
(visible only with
-vv), a clean one-line message on stderr, and exit code5β never a raw, unhandled traceback.
scry has unit test coverage across its modules β exception hierarchy, CLI parsing, image loading, region/crop math, the OCR and clipboard wrappers (with distro-specific error messages), and desktop notifications. GUI-driven code (the actual Tkinter window) is covered where its logic can be tested without a display; opening a real window is verified manually rather than in the automated suite.
Run the test suite:
pip install pytest
pytest -vSome tests are conditional on your environment:
- The OCR round-trip test only runs if
tesseractis on yourPATH(it's skipped otherwise). - Tests that touch the clipboard, OCR engine, or notifications mock
those calls, so the full suite does not require
tesseract,xclip,wl-clipboard, ornotify-sendto be installed to pass.
- OCR accuracy depends entirely on Tesseract and your image/crop quality. Blurry screenshots, small text, low contrast, or unusual fonts will produce worse results β this is a property of the underlying OCR engine, not something scry can compensate for beyond the automatic upscaling it already applies to small crops.
- No multi-monitor spanning. The selection window opens on whichever monitor your window manager treats as primary/default; scry does not detect or target a specific monitor.
- No zoom or pan in the selection window β very large images are scaled down to fit your screen, and you select against that scaled view.
- No post-drag adjustment. If your drag misses, you redo it; there are no resize handles.
- Mouse-only selection. There is currently no keyboard-driven way to make a selection.
- Single selection per run. scry extracts one region per invocation; there's no batch or multi-region mode.
- Linux only. scry has been developed and tested on Linux (X11 and
XWayland); it is not tested on Wayland-native clipboard tooling
beyond what
pyperclip/wl-clipboardsupport, and it does not target Windows or macOS.
"Tesseract is not installed. Install it with: ..."
The tesseract binary isn't on your PATH. scry detects your distro
and prints the exact command to install it β run the suggested
command and try again.
"No clipboard tool available. Install it with: ..."
Same idea, for the clipboard: scry detects whether you're on X11 or
Wayland (via $WAYLAND_DISPLAY) and your distro, and prints the exact
install command. Note that the extracted text still reaches stdout even
when this happens β you don't lose it.
No desktop notification appears.
If notify-send (from libnotify) isn't installed, this is expected
and harmless β notifications are optional and fail silently by design.
Run with -vv if you want to confirm this is what's happening (you'll
see a DEBUG-level line noting the notification attempt was skipped).
"A required system dependency is missing: No module named 'tkinter'"
python3-tk (or your distro's Tkinter package) isn't installed. Install
it using the command for your distro under
System dependencies above.
The window doesn't seem to open, or nothing happens.
Make sure you have a working X11 or Wayland session β scry needs an
actual display. Try running with -vv to see detailed logging on
stderr as scry loads the image and opens the selection window.
I got "Image file not found" / "Not a valid image file". Double-check the path you passed, and that the file is actually a valid image scry's Pillow-based loader can decode. These messages mean scry successfully looked at the path β the problem is with what it found there.
I want more detail on what went wrong.
Re-run with -vv. Default logging is quiet by design (stderr is
otherwise clean unless something notable happens); -v adds INFO
level detail, and -vv adds full DEBUG detail, including complete
tracebacks for unexpected internal errors.
scry is a small, deliberately minimal codebase. If you're interested in contributing, it's worth understanding the project's design philosophy first β see Project philosophy above. In short: contributions that keep scry small and add capability only where it's clearly needed are the best fit; contributions that add configuration surfaces, new abstractions, or speculative features ahead of an actual need are likely to be a harder sell.
Bug reports, fixes, and small, focused improvements are welcome via issues and pull requests.
scry is released under the MIT License.
