Command line¶
natocr ships an optional CLI so you can OCR straight from the shell - no Python
required. Its dependencies (typer and
rich) live in the cli extra to keep
library-only installs lean:
That gives you a natocr command (and a matching python -m natocr):
The synopsis:
natocr [OPTIONS] [IMAGE]...
-l, --lang TEXT recognition language, or auto to detect
(default: en)
-f, --format [text|json|hocr|pdf|table]
output format (default: text)
-o, --output PATH output file, or a directory when there
are multiple inputs
--min-confidence N drop detections below this confidence
--list-languages print the supported languages and exit
--version print the version and exit
Note
The natocr command itself is always installed with the package (Python
entry points can't be conditional). Without the cli extra it just prints
a one-line pointer to pip install 'natocr[cli]' instead of a traceback.
Inputs¶
Pass one or more image files in any
supported format. Glob patterns are expanded
too (quote them so your shell doesn't get there first), and - reads raw image
bytes from stdin. This works the same on macOS and Windows - handy on Windows,
where the shell doesn't expand globs for you:
natocr page1.png page2.png # several files
natocr 'scans/**/*.tiff' # glob, including ** recursion
cat scan.png | natocr - # from a pipe
Multi-page inputs (TIFF, DjVu, GIF, animated PNG, multi-image HEIC/HEIF) are
read page by page, exactly like recognize().
If one file in a batch fails to decode, natocr reports it on stderr, keeps going, and exits non-zero at the end - one bad scan doesn't sink the run. Batches writing to a directory also get a progress bar on stderr, so stdout stays clean for pipes.
Output formats¶
text (default)¶
Plain recognized text on stdout. Pages of a multi-page input are separated by a
form feed (\f), the same convention tesseract uses:
table¶
A colorized table for humans - one row per detected line with its confidence and bounding box, one table per input:
receipt.png - 1 page
┏━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━━━━━━━━━━━┓
┃ page ┃ text ┃ conf ┃ bounds (x, y, w, h) ┃
┡━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━━━━━━━━━━━┩
│ 1 │ Cosmos Coffee │ 0.97 │ (24, 18, 180, 32) │
│ 1 │ Sagano $4.50 │ 0.92 │ (24, 70, 276, 28) │
└──────┴───────────────┴──────┴─────────────────────┘
json¶
One compact JSON object per input, newline-delimited (NDJSON), so the shape is
identical whether you pass one file or a thousand - pipe it straight into jq:
Each object carries the file name, language, and per-page text, line grouping, confidences, and pixel bounding boxes:
{
"file": "scan.png",
"language": "en",
"pages": [
{
"text": "Cosmos Coffee Sagano $4.50",
"confidence": 0.93,
"lines": ["Cosmos Coffee", "Sagano $4.50"],
"elements": [
{
"text": "Cosmos Coffee",
"confidence": 0.97,
"bounds": {"x": 24.0, "y": 18.0, "width": 180.0, "height": 32.0}
}
]
}
]
}
hocr¶
hOCR is the standard XHTML interchange format for OCR layout - each word keeps its bounding box and confidence, so downstream tools (PDF assemblers, layout analyzers, editors like hocr-tools) can consume it:
pdf¶
A searchable PDF: each page embeds the original image with an invisible text layer laid over it, so the output looks like the scan but supports text selection, copy, and search - the same trick tesseract's PDF output uses:
PDF is binary, so writing it to an interactive terminal is refused - pipe it or
use -o.
Choosing where output goes¶
Without -o/--output everything streams to stdout. With it:
natocr scan.png -o scan.txt # single input: a file path
natocr '*.png' -f json -o results/ # multiple inputs: a directory
For a directory, each output is named after its input with the format's
extension (.txt, .json, .hocr, .pdf) - scan.png becomes scan.txt,
and stdin becomes stdin.txt. The directory is created if it doesn't exist.
Note
hocr and pdf produce one document per input, so multiple inputs
require -o DIR. text, table, and json stream fine either way.
Language¶
-l/--lang picks the recognition language (default en), and
--list-languages prints what the current machine supports - the same live
query as supported_languages. On a terminal
it renders as a table; piped, it's plain lines for grep:
--lang auto turns on
automatic language detection - on
macOS 13+ Vision detects the language(s) per line during recognition, and on
Windows the text's language is detected after a first pass and recognition
re-runs with the matching installed pack when needed:
Filtering by confidence¶
--min-confidence drops detections below a threshold before rendering, the
CLI face of OCRResult.filter():
Note
Windows Runtime OCR doesn't report confidence, so on Windows detections have no score and are kept regardless (they can't be judged).
Exit codes¶
| Code | Meaning |
|---|---|
0 |
every input processed successfully |
1 |
at least one input failed (unreadable file, backend error, missing cli extra) |
2 |
usage error (bad flag, unmatched glob, missing -o for binary output) |