Repository navigation
Releases
v1.10.0 — Improved print recognition, optional page orientation and custom domain packs
What changed
A new print recognizer, optional page orientation and contrast normalisation, and support for custom domain packs.
Printed pages
- New default recognizer,
rec-E-v7. Better recognition of large type, dark or coloured paper, faint typewriting, carbon copies, dot-matrix print and one-line formulas. Adds Slovak letters and common Greek letters and symbols. - Page orientation (
--orientation auto). Reads sideways and upside-down scans, keeping output coordinates in the original image. - Contrast normalisation (
--contrast auto). Helps with pale carbon copies and faint typewriting. Orientation and contrast are both off by default.
Custom domain packs
- Install a .tpack containing your own recognizer, vocabulary and settings, with an optional layout model. Select it by name through the CLI or API, or make it the default.
- New CLI flags:
--version,--list-domains,--check-domains. New API endpoint: GET /api/v1/version. - ALTO and PAGE XML record the pack name and version.
Compatibility
- Printed recognition changes by default. Set
TUZKAOCR_OCR_MODEL=rec-E-v5.onnxto keep the previous recognizer. - A pack marked as default takes over domain=default; use domain=print to select built-in print.
- Unknown API domains now return 422 instead of 400.
- No changes to the bundled handwritten, Kurrent or Kramarky models.
v1.9.0 — Column-first reading order, newspaper masthead pass, single-pass print layout + Docker image
What changed
Printed pages get a faster single-pass layout, column-first reading order, and a new
pass that reads newspaper mastheads. The CPU Docker image is now published with every
release. Handwritten, Kurrent and Kramarky output is unchanged.
Printed pages
- Single-pass layout. Each page is laid out once, at a scale matched to its text size,
replacing the old multi-pass escalation. Accuracy is the same or better, and newspapers process about 20% faster. - Column-first reading order (
TUZKAOCR_READING_ORDER=columns, default). Columns are
read top to bottom. - Mastheads (
TUZKAOCR_LARGE_TEXT=true, default). Newspaper titles and other very
large type are read as whole lines.
Docker image
docker run -d -p 8000:8000 ghcr.io/moravianlibrary/tuzkaocr:1.9.0Published to ghcr.io/moravianlibrary/tuzkaocr for amd64 and arm64 (1.9.0, 1.9,
latest). See Docker.
Compatibility
- Printed output changes in line segmentation and block order. For the previous block
order without the masthead pass, setTUZKAOCR_READING_ORDER=basicand
TUZKAOCR_LARGE_TEXT=false. - New settings:
TUZKAOCR_LAYOUT_LINE_HEIGHT,TUZKAOCR_READING_ORDER,
TUZKAOCR_LARGE_TEXT; CLI--reading-order,--no-large-text. - No API, model or dependency changes.
v1.8.0 — NDK ALTO profile, PAGE XML output, rec-I-v2 page-style handwritten recognizer, single-pass layout
What changed
ALTO output now targets the Czech NDK ingest profile by default. It adds Processing
provenance, page margins, styles with estimated font sizes, detected LANG, word and
character confidence, and hyphenation. The previous output shape is still available as
alto_profile=basic. A new PAGE XML output format (fmt=page) keeps line baselines and
polygons for correction tools such as Transkribus and eScriptorium. The handwritten and
Kurrent domains get a new writer-adaptive recognizer, rec-I-v2, which reads every line
with the style of its page. The handwritten, Kurrent and Kramarky domains now always use
single-pass layout.
ALTO: NDK profile (new default)
- New
ndkprofile, selected withTUZKAOCR_ALTO_PROFILE(server-wide), the
alto_profileform field (API) or--alto-profile(CLI).basicreproduces the
previous output shape. - Provenance is written as
Processingelements (IdPreOperationwhen the page was
downsampled for layout,IdLayout,IdRecognition). Each one carries the package
version and the model name inapplicationDescription.processingAgencyis written
whenTUZKAOCR_ALTO_AGENCY/--alto-agencyis set. sourceImageInformationrecords the source filename and, optionally, a
fileIdentifier(source_identifier/--source-identifier).PHYSICAL_IMG_NRcomes fromphysical_img_nr/--physical-img-nr, then from a
trailing page number in the filename (pr_0007.jp2→ 7), then defaults to1.PrintSpaceis the bounding box of the body text, and the four margins are always
present. With role classification on, header and footer/page-number blocks move into
TopMargin/BottomMargin.TextBlocks carry the detected region outline asShape/Polygon.- Confidence:
WCandCCon everyString, andACCURACYandPConPage. All four
are derived from the recognizer's per-character probabilities. - Hyphenation: a word broken across a line end is written as
HypPart1/HypPart2
fragments withSUBS_CONTENTand aHYPelement. Plain-text output is unchanged. - Styles:
ParagraphStyle/@ALIGNis measured per block.FONTSIZEis estimated from line
geometry and the scan resolution. The resolution comes fromTUZKAOCR_ALTO_DPI, then
the image metadata (below 100 dpi is ignored), thenTUZKAOCR_ALTO_PAGE_WIDTH_MM.
FONTFAMILYis written only whenTUZKAOCR_ALTO_FONTFAMILYis set. - Language: a new bundled detector,
lang-A-v1.npz(45 KB, NumPy only), setsLANGfor
Czech, German, English, Slovak, Polish and Latin. Other languages and pages
with very little text get noLANG.TUZKAOCR_ALTO_LANG/--alto-langforces a
value. - Identifiers are page-sequential (
P1_TB0001,P1_TL0001,P1_ST0001,P1_SP0001),
andSPelements carry coordinates. --ndk-warn(CLI) reports a missing agency and pages whosePHYSICAL_IMG_NRfell back
to1.- Known gaps are listed in the docs under NDK profile: remaining limitations. They are
ComposedBlock/Illustration,FONTSTYLE,ALTERNATIVE,ROTATION/POSITION/
QUALITY,StructureTag/@TYPE, and marginalia in side margins.
PAGE XML output (new)
- New output format
page:fmt=page(API) or--format page(CLI). It writes PAGE XML
in the 2019-07-15 schema, which Transkribus, eScriptorium, Kraken and OCR-D read, and
the output validates against the officialpagecontent.xsd. - Unlike ALTO, PAGE keeps the detected line geometry. Each
TextLinecarries its
Baselineand the oriented quadrilateral it was read from, so skewed and curved lines
survive a round trip into a correction tool. Regions carry the detected outline, words
carry rectangles, and every point is clipped to the page. TextEquiv/@confon words and lines uses the same recognizer confidence as ALTOWC.
An explicitReadingOrderlists the regions, andMetadatarecords the package
version and the layout and recognition models.primaryLanguagecomes from the same language detector as ALTOLANG, written as a
PAGE language name (Czech,German, …).TUZKAOCR_ALTO_LANGoverrides it as well.- With role classification on, a region's
typeis the dominant role of its lines
(heading,header,footer,page-number, otherwiseparagraph). Non-body lines
also get a Transkribus-stylecustom="… structure {type:heading;}". - PAGE has no per-character confidence and no hyphenation markup, so a word broken across
a line end stays as two words, as in plain text. The--alto-*options and
alto_profiledon't affect it, andmultistill produces ALTO + text only. - API results are stored as
<job_id>.page.xmland served by the usual result endpoint.
Recognition (handwritten + Kurrent)
rec-I-v2is the new default fordomain=handwrittenanddomain=kurrent, replacing
rec-H-v6. A small style model reads all lines of the page first, and the recognizer
then reads each line with that page-level style. A single-line page is read without
page context.- It ships as two files,
rec-I-v2.onnx(12.6 MB) andrec-I-v2.style.onnx(0.3 MB).
They must sit in the same directory. The style file is found by name, and startup fails
without it.--ocr-modelpointing at a page-style recognizer picks up its.style
sibling automatically. - Vocabulary widened 158 → 203 characters, mostly Western European accented letters
(è à ê ç ñ œ æ ø å …) plus& $ £ { } # ½ ¼ ¾ ¶ ẞ ” ’. New entries are appended, so
the print, Kramarky andrec-H-v6recognizers decode identically. rec-H-v6.onnxstays bundled for pinning.GET /api/v1/modelslistsrec-I-v2.onnx
but not the style file.
Layout: single pass for handwritten, Kurrent and Kramarky
- Adaptive downsampling now applies to the printed domain only.
handwritten,kurrent
andkramarkyalways use a single pass at the standard downsample, whatever
TUZKAOCR_ADAPTIVE_DOWNSAMPLEsays. - On handwriting, the finer passes split faint pencil and grid-paper lines into fragments.
A single pass is more accurate and about 3× faster in layout. - On Kramarky, a single pass is about 2× faster, at roughly +0.3 pp CER on the benchmark
set.
Model files
- Removed from the package:
dec-A-v4.onnx,dec-A-v3k5.onnx,rec-H-v4.int8.onnx,
rec-H-v3h-kurrent.int8.onnxandrole-G-v1.npz.
Other
- Empty numeric environment variables (for example
TUZKAOCR_ALTO_DPI=) now fall back to
their default instead of failing to parse.
Compatibility
- ALTO output changes by default.
OCRProcessingbecomesProcessing, identifiers
change fromblock_/line_/word_toP1_TB…/P1_TL…/P1_ST…,PrintSpaceno longer
covers the whole page, and several new elements and attributes are added. Consumers
written against the old shape should setTUZKAOCR_ALTO_PROFILE=basicor send
alto_profile=basic. - New output format
pagefor the APIfmtfield and the CLI--formatflag. Existing
formats are unchanged. - New optional API form fields:
alto_profile,source_identifierand
physical_img_nr. An unknownalto_profileor aphysical_img_nrbelow 1 returns
400. Existing requests are unaffected. - New CLI flags:
--alto-profile,--alto-agency,--alto-lang,--alto-dpi,
--alto-page-width-mm,--source-identifier,--physical-img-nrand--ndk-warn.
--source-identifierand--physical-img-nrare rejected together with--batch. - New env vars:
TUZKAOCR_ALTO_PROFILE,TUZKAOCR_ALTO_AGENCY,TUZKAOCR_ALTO_LANG,
TUZKAOCR_ALTO_FONTFAMILY,TUZKAOCR_ALTO_DPI,TUZKAOCR_ALTO_PAGE_WIDTH_MMand
TUZKAOCR_LANG_MODEL. For NDK ingest, set at leastTUZKAOCR_ALTO_AGENCY. - Handwritten and Kurrent output changes because of the new recognizer and single-pass
layout. To keep the previous recognizer, pin both
TUZKAOCR_HANDWRITTEN_OCR_MODEL=rec-H-v6.onnxand
TUZKAOCR_KURRENT_OCR_MODEL=rec-H-v6.onnx. - Kramarky output changes on pages that previously escalated to a finer layout pass.
Printed (default) recognition is unchanged. - Pins to removed models now fail at startup. This includes
rec-H-v3h-kurrent.int8.onnx, andrec-H-v4.int8.onnx. Move those pins torec-H-v6.onnxor
rec-I-v2.onnx. - A custom
TUZKAOCR_VOCABmust match the model in use.rec-I-v2needs all 203
characters and fails to load with a shorter vocabulary. - No new runtime dependencies.
v1.7.1 — Multi-user API key auth fix + documentation site
What changed
Multi-user API key authentication was broken: a missing
import made _KeyStore.load() raise NameError on the first authenticated request,
so a server configured with TUZKAOCR_API_KEYS_FILE rejected every call with a 500.
Requests failed closed, so no unauthorized access was possible.
This release fixes it and adds a full documentation site at https://moravianlibrary.github.io/TuzkaOCR/.
Multi-user API key authentication (bug fix)
api/routes.pycalledtime.monotonic()twice in_KeyStore.load()without
importingtime. On the first call the TTL check short-circuits (no path cached
yet), so theNameErrorsurfaced at the timestamp assignment — meaning the very
first authenticated request failed, not a later one.- The exception was raised inside the
_require_keydependency, so the request never
reached its handler and every authenticated endpoint returned 500. - The failure was masked at startup:
_validate_authreads, validates and counts the
key file correctly, logging[auth] multi-user, N key(s) loaded, so a server with a
valid key file looked healthy right up until the first request.
Documentation
- New documentation site published from
docs/, built with Material for MkDocs:
installation, CLI, HTTP API, Docker, configuration, models and domains, output
formats, and troubleshooting, plus a guided setup page for users new to the command
line. - Documents behaviour that was previously undocumented, including the full status-code
table, the legacy endpoints' differing field names and response shapes, job retention
and what survives a restart, and per-domain overrides ofTUZKAOCR_CROP_ENDPOINT_EXT
andTUZKAOCR_COLUMN_SPLIT. TUZKAOCR_SPOOL_DIRand the per-domain model override variables are now documented in
README.mdand listed intuzkaocr.env.
Compatibility
- No public API surface change — routes, CLI flags, API form fields, env vars and the
ALTO output schema are unchanged. - No model changes. Output is byte-identical to v1.7.0 across all four domains.
- No new runtime dependencies.
docs/requirements.txt(mkdocs, mkdocs-material) builds
the documentation only and is not installed bypip install ..
v1.7.0 — Unified handwritten/Kurrent recognizer + disk-spooled upload pipeline
What changed
The handwritten and Kurrent domains now share one recognizer (rec-H-v6, replacing
rec-H-v4 and the Kurrent-only rec-H-v3h-kurrent) on a widened 158-character
vocabulary. The API upload path was reworked: every upload is streamed to a
service-owned spool file and decoded once inside its worker, instead of being decoded in
the request handler and held in RAM for the whole time the job sits queued. ALTO output
gained ID sanitization and page-bounds clipping, the job store gained single-shot
terminal states and typed input errors, and the container images were fixed up.
Recognition (handwritten + Kurrent)
rec-H-v6.int8.onnxis the new default for bothdomain=handwrittenand
domain=kurrent; a single general handwritten recognizer supersedes the earlier
Kurrent-only specialist.- Shared vocabulary widened 134 → 158 characters: uppercase Czech diacritics
(Ů Ň Ť Ď Ó), Germanß Ä Ö Ü, Polishą ć ę ł ń ś ż ź Ł Ś Ż, a combining macron,
plus@and_. New entries are appended, so indices for existing characters are
unchanged and the print/Kramarky recognizers decode identically. - The superseded model files stay bundled, so existing
HANDWRITTEN_OCR_MODEL/
KURRENT_OCR_MODELpins still resolve.
Upload handling and memory
- Each accepted upload streams to a uniquely named spool file under
TUZKAOCR_SPOOL_DIR(system temp if unset); its worker decodes it once at processing
time. Previously the request handler decoded the image inline and the queued job
carried a decoded BGR array. - Queue depth no longer costs memory per queued job — the sizing guide loses its
MAX_QUEUE × ~8 MiBterm, leavingTUZKAOCR_PAGE_WORKERSas the memory lever. - Spool files are removed after processing, on a rejected submission, on executor-submit
failure, and for jobs that expire while still queued. An explicitly configured spool
directory is swept at startup to clear crash leftovers; the shared system temp
directory is never swept, and a configured directory must not be shared between
bare-metal instances. - Startup fails fast with a clear message if
TUZKAOCR_SPOOL_DIRis not writable. - Image decoding no longer runs on the event loop, so a huge or malformed upload can't
stall request handling.
Job store correctness
- Terminal transitions are single-shot: a job can no longer be overwritten after
done/failed, and results written for an already-finalized job are unlinked instead
of orphaned on disk. - Workers bail out if the job is no longer
queued, guarding against double dispatch. - Failed jobs carry an HTTP status. Undecodable or over-pixel-limit images surface as
422 from the result endpoint instead of a blanket 500. BaseExceptionin a worker (cancellation, interpreter shutdown) marks the job failed
and re-raises, instead of leaving it stuck inrunningforever.- The hourly cleanup timer reschedules itself even when a sweep raises, so one error no
longer stops cleanup for the process lifetime; it stops cleanly on shutdown. - Age expiry fails over-age
queuedjobs explicitly rather than dropping them silently;
runningjobs are left to finish.
ALTO output
- Page IDs are sanitized to valid XML names —
page_idcomes from the uploaded
filename, so spaces, diacritics or a leading digit no longer produce invalid ALTO. - All
TextBlock/TextLine/Stringcoordinates are clipped to the page box with
WIDTH/HEIGHTguaranteed ≥ 1, and block extents are derived from the clipped line
rects — no geometry can extend past the declared pageWIDTH/HEIGHT. build_altorejects non-positive page dimensions, and word content is coerced to a
string before serialization.
CLI and pipeline
- File decoding goes through
np.fromfile+cv2.imdecode, so non-ASCII paths work on
Windows and unreadable files give a clearCannot read imageerror. - The role classifier now honours the resolved device: with
TUZKAOCR_DEVICE=autoit
runs on CUDA instead of silently falling back to CPU, and it respects
TUZKAOCR_CPU_MEM_ARENA. TUZKAOCR_MAX_IMAGE_PIXELSis API-only; command-line processing has no pixel limit.
Containers and Compose
- Added
.dockerignore—.git, Python caches,results/,spool/and
api_keys.yamlstay out of the build context and the image. - Both images create
/app/spool, declare it as a volume, and set
TUZKAOCR_SPOOL_DIR=/app/spool. - Compose replaces the shared
./spoolbind mount with separate named volumes
(cpu_spool,gpu_spool), so the CPU and GPU services cannot sweep each other's
spool files. Dockerfile.gpuinstalls Python 3.11 from deadsnakes into a dedicated venv, replacing
the brokenpython3.11-pip/pipsymlink setup on the CUDA base image.
Compatibility
- No public API surface change — routes, CLI flags, API form fields, env vars and the
ALTO output schema are unchanged. - Error timing changed. A corrupt or over-
MAX_IMAGE_PIXELSimage previously
returned a synchronous 422 fromPOST /api/v1/process. It now returns 200 with a
job_id, and the failure appears as 422 onGET /api/v1/result/{job_id}. Clients
that only inspected the POST status must read job status/result. - Failed-job result responses return 422 for input errors instead of 500; other failures
stay 500. - ALTO is not byte-identical for pages whose line/word geometry ran past the page box, or
whose filename was not a valid XML name. Well-behaved pages are unchanged. - Handwritten and Kurrent output changes — new recognizer. Print (
default) and
Kramarky accuracy are unchanged. To keep the old Kurrent behaviour, pin
TUZKAOCR_KURRENT_OCR_MODEL=rec-H-v3h-kurrent.int8.onnx. tuzkaocr.pipeline.ensure_bgrwas removed; decoding always yields 3-channel BGR. Use
tuzkaocr.images.decode_image_path.- Compose users: the
./spoolbind mount is gone, so the host./spooldirectory can be
removed after upgrade. - No new dependencies.
v1.6.0 — New Kramarky layout model
What changed
The Kramarky domain gets a new layout model: dec-B-v1k.onnx replaces dec-A-v3k5.onnx
as the default for domain=kramarky. The old U-Net layout under-detected and fragmented
lines on small (~600 px) and faded/whitish broadsheet scans.
Compatibility
- No API or CLI changes;
domain=kramarkyselects the new model automatically. - Env override unchanged:
TUZKAOCR_KRAMARKY_LAYOUT_MODEL(new defaultdec-B-v1k.onnx). - Output changes for all Kramarky pages: line segmentation, counts, and geometry differ. ALTO provenance now reports
dec-B-v1kas the layoutsoftwareName. - Print, handwritten, and kurrent domains are untouched.
v1.5.0 — Handwritten & Kurrent domains, rec-H-v4 general handwriting model
What changed
The pipeline now covers four domains instead of two: print (default), kramarky,
handwritten, and kurrent. Each domain ships its own layout + recognizer pair and is
selectable per request (domain form field), per CLI run (--domain), or via env
overrides.
New domains & models
domain=handwritten— Czech handwriting, old and modern. Layout dec-B-v2h.onnx + recognizer rec-H-v4.int8.onnx, a general handwriting model.domain=kurrent— German Kurrent/Sütterlin script. Same dec-B-v2h layout + rec-H-v3h-kurrent.int8.onnx specialist.
Pipeline improvements (handwritten/kurrent only)
- Line crops are extended by 0.3× line-height at both baseline endpoints — handwriting layout endpoints are tight, and the first/last glyph of a line was frequently clipped before recognition.
- In-region N-column reading order: table/register pages with several columns in one region are now read column-by-column instead of interleaving rows across columns. Conservative activation (clustered narrow lines only); prose pages and marginalia keep the original order.
- max_width raised 1600 → 3400: very long lines (common in Kurrent registers) were silently truncated mid-line before recognition.
Compatibility
- No breaking changes. Clients that omit
domainor sendkramarkyget byte-identical behavior; print/kramarky layout, recognition, and postprocessing are untouched. - New accepted domain values:
handwrittenandkurrent("print" also resolves to the default models). - CLI: --domain gains
handwrittenandkurrentchoices; existing flags unchanged. - New env overrides:
TUZKAOCR_HANDWRITTEN_LAYOUT_MODEL / _OCR_MODEL,TUZKAOCR_KURRENT_LAYOUT_MODEL / _OCR_MODEL,TUZKAOCR_CROP_ENDPOINT_EXT,TUZKAOCR_COLUMN_SPLIT— all default to per-domain values above. - Config-default change:
max_width1600 → 3400 (affects only lines longer than 1600 px, which were previously truncated; output for those lines improves). - EXIF-tagged images now OCR in their upright orientation — output for such images changes; images without EXIF rotation are unaffected.
- Three model files added (~2×12 MB recognizers + 11 MB layout); no dependency changes.
v1.4.2 — Accept default domain (fixes 400 on advertised value)
What changed
The default domain value is now accepted by the API instead of being rejected with a 400. /api/v1/models advertised default in selectable_via_domain, but submitting domain=default failed validation — it now resolves to the general models, consistent with what the API advertises.
default domain accepted (bug fix)
/api/v1/modelslistsdefaultunderselectable_via_domain, but_validate_domainonly mappedNone/empty to the general models and rejecteddomain=defaultwith400 Unknown domain 'default'.default(alongsideNone/empty) now normalizes to the general models, so it behaves identically to omitting the field.
Compatibility
- No breaking changes. Clients that omit
domainor sendkramarkyare unaffected; requests withdomain=defaultthat previously returned 400 now succeed. - No public API surface change — CLI flags, API form fields, and env vars are unchanged.
- Layout, recognition, and model selection are unchanged —
defaultselects the same general models as omitting the field. Output is identical to v1.4.1. - No model, config-default, or dependency changes.
v1.4.1 — Faster dense pages, plus per-page confidence in the API.
What changed
Adaptive downsampling no longer recognizes resolution levels it discards, making dense pages faster with byte-identical output. Separately, the job-status response now exposes per-page OCR confidence (mean_conf) and line count (n_lines).
Faster adaptive downsampling (affects throughput, not output)
- The pipeline picks a working resolution by escalating ds3 → ds2 → ds1 on "starved" pages. Line-height / pitch / width starvation is a layout-only signal, so escalation is now decided from the layout pass alone — recognition runs only at the resolution that is kept.
- ~18% faster on dense periodical pages (where escalation fires); negligible change on simple pages that don't escalate.
- Output is byte-identical to v1.4.0 — verified across mixed and dense pages.
Per-page confidence in job status (additive)
- Job status now reports
mean_conf(mean OCR confidence over the page's recognized lines, 0–1) andn_lines(recognized line count). - Available on both
GET /api/v1/status/{job_id}and the legacyGET /status/{job_id}.
Compatibility
- No breaking changes. The new
mean_conf/n_linesfields are additive — existing clients that don't read them are unaffected. - No public API surface change — CLI flags, API form fields, and env vars are unchanged.
process()returns the same content as before (the new metadata is opt-in viawith_meta, used internally by the job runner). - Layout, recognition, and adaptive decisions are unchanged — the same resolution is chosen per page as in v1.4.0.
- No model, config-default, or dependency changes.
v1.4.0 — New line-role classifier (role-H5) + ALTO 4.4 validity fix
What changed
The experimental line-role classifier was replaced (role-G → role-H5) with a small visual+text+geometry model. Separately, the ALTO output was made schema-valid against ALTO 4.4 — fixing defects that were present in all output.
Role classification (optional, off by default)
- Per-line, 5 classes:
body/heading/header/footer/page_number(replaces role-G'sbody/prominent/pagenum; title and sub-headings are merged intoheading). - Per-class F1: page_number 0.97, header 0.89, body 0.96, heading ~0.80, footer ~0.75 — a clear upgrade over the geometry-only role-G.
ALTO 4.4 validity (affects all output)
- Layout and recognition provenance are now two separate
<OCRProcessing>elements (IdLayout/IdRecognition). Previously a single<OCRProcessing>held two<ocrProcessingStep>elements, which the schema forbids — so prior ALTO was invalid even with roles off. - Line roles are now emitted as
<Tags>/<StructureTag>referenced viaTextLine TAGREFS, replacing the invalidTYPEattribute. - Verified against
alto-4-4.xsd(xmllint) across varied pages, role tagging on and off.
Compatibility
- ALTO structure changes (now schema-valid): provenance is two
<OCRProcessing>elements; role tags moved fromTYPEto<Tags>/TAGREFS. ALTO consumers that read provenance or role tags must adjust; the change is what makes the output conformant. - No public API change — CLI flags, API form fields, and env vars are unchanged. Role tagging remains off by default (
--role-classifier/TUZKAOCR_ROLE_CLASSIFIER). - Default role-model filename changes (
role-G-v1.npz→role-H5.onnx); config default updated, so no action needed unless you pinROLE_MODEL. - Layout and recognition models/accuracy unchanged.
- No new dependencies.