> Compact edition: JPEG previews are resized to a 2200-pixel longest edge at quality 92 for display only. All OCR transcripts, settings, logs and original source files are unchanged. The full package contains the exact PNG inputs used in testing. Accuracy has not been assessed.

# Russian OCR test outputs

**Accuracy has not been assessed.** This package is for independent review; execution success is not an accuracy verdict.

- Source documents: 9
- Main attempts: 468
- Setup / compatibility attempts: 21
- All recorded statuses: {'success': 485, 'error': 4}

## Read the results

Open `index.html` locally. Select a document, preprocessing variant and repetition. The source image appears beside the transcripts. The checkbox includes preflight and failed compatibility attempts.
`ALL_TRANSCRIPTS.md` contains every output; `results.csv` indexes settings/timings; `results.json` retains structured data. Each `tests/` folder contains the literal raw text, original result, command and logs.

## Main configurations

| Configuration | Attempts | Successes | Empty successes | Errors / unsupported / timeouts | Median wall seconds |
|---|---:|---:|---:|---|---:|
| Tesseract · automatic layout (PSM 3) | 36 | 36 | 0 | {} | 1.57 |
| Tesseract · single block (PSM 6) | 36 | 36 | 0 | {} | 2.18 |
| EasyOCR · Russian / CPU | 36 | 36 | 0 | {} | 25.30 |
| Apple Vision · correction off | 36 | 36 | 0 | {} | 0.36 |
| Apple Vision · correction on | 36 | 36 | 0 | {} | 0.40 |
| Qwen3.5-4B · local MLX / 4-bit | 36 | 36 | 0 | {} | 20.71 |
| TrOCR · Russian handwriting | 36 | 36 | 0 | {} | 14.11 |
| TrOCR · archival Cyrillic handwriting | 36 | 36 | 0 | {} | 14.63 |
| GLM-OCR · bf16 | 36 | 36 | 0 | {} | 6.57 |
| PaddleOCR-VL 1.6 · 4-bit | 36 | 36 | 0 | {} | 47.62 |
| dots.ocr · 4-bit | 36 | 36 | 0 | {} | 37.23 |
| HunyuanOCR · bf16 MLX conversion | 36 | 36 | 0 | {} | 19.39 |
| olmOCR 2 · 7B · local MLX 8-bit | 36 | 36 | 0 | {} | 33.66 |

## Handwriting-focused expansion

New checkpoints include two distinct Russian/Cyrillic handwriting fine-tunes of TrOCR and independent document-OCR VLM architectures. Publisher training claims are not measured accuracy on these photos.
TrOCR is not a whole-page recognizer: CRAFT supplies unreviewed word/line regions, which are recognized separately and assembled by vertical center then horizontal position. This heuristic is not column/layout aware. No text regions were manually labeled as printed or handwritten; printed text is outside the archival checkpoint stated training scope.
Detected PNG crops and their literal predictions are included under each TrOCR result in the offline viewer. Detection/cropping was cached and timed separately; recognizer wall times do not include that cached cost.
Round 1 Qwen used a 4096-token page cap. New VLMs use up to 8192 tokens; TrOCR limits apply to individual regions (128 for the general Russian checkpoint, 64 for archival). Flags are possible incompleteness, not accuracy judgments.

| Checkpoint | Family | Input | Precision | Frozen source |
|---|---|---|---|---|
| dots.ocr · 4-bit | dots.ocr | whole_page | 4-bit | `mlx-community/dots.ocr-4bit@4ab989e403d4f8cafa5fdeede5b2290a706c2405` |
| GLM-OCR · bf16 | GLM-OCR | whole_page | bfloat16 | `mlx-community/GLM-OCR-bf16@24f15402e83baa0a80eeeaecf5480e172abc6f2e` |
| HunyuanOCR · bf16 MLX conversion | HunyuanOCR | whole_page | bfloat16 | `hadeseus/HunyuanOCR-mlx@dd3aded48198a89ffcd1caf4a1d48afb13431d19` |
| olmOCR 2 · 7B · local MLX 8-bit | olmOCR 2 | whole_page | 8-bit | `mlx-community/olmOCR-2-7B-1025-8bit@2c8072b3179f86df2090de9d511ec5b7ad72ed0a` |
| PaddleOCR-VL 1.6 · 4-bit | PaddleOCR-VL | whole_page | 4-bit | `mlx-community/PaddleOCR-VL-1.6-4bit@c8987b277bdd8aaec8bd265d154f391bd03ad165` |
| TrOCR · archival Cyrillic handwriting | TrOCR | detected_line_crops | float32 | `Hukyl/trocr-kgb-archive@deb820274be524b3b3726c45a110f23e271ea016` |
| TrOCR · Russian handwriting | TrOCR | detected_line_crops | float32 | `kazars24/trocr-base-handwritten-ru@5342fbb29ec56eb677f553738c2fcc2befd6b0ab` |

## Methods and limits

- Every supplied photo remains a page-level case; TrOCR uses automatic word/line crops within it. Printed/handwritten categories and legibility were not manually judged. No reference transcription, CER/WER, spelling repairs or translation were applied.
- `original`: lossless PNG after EXIF orientation. `gray_up`: grayscale, autocontrast cutoff 1%, Lanczos scaling up to 2× with a 5000-pixel edge cap; actual scale and source/variant hashes are in each result.
- Two main repetitions use identical settings. Wall time is cold-recognizer-process time, not cold OS/model-cache time or pure warmed inference. For TrOCR, precomputed detection/cropping cost is recorded separately. Shared machine load can affect timings.
- Tesseract: explicit Russian tessdata_best, LSTM OEM 1, PSM 3 or 6. EasyOCR: Russian Cyrillic recognizer, CRAFT, CPU and greedy decoding. Apple Vision: accurate recognition, Russian, correction off/on.
- MLX: local Qwen3.5-4B 4-bit snapshot, temperature 0, seed 0, thinking disabled, 4096 output-token cap and 2,097,152 image-pixel budget. See effective processor config and exact model revision in setup metadata.
- Inference subprocesses deny network; public tool/weight downloads occurred during setup. No cloud image processing was used.
- Native confidence is not calibrated accuracy. Process RSS is not total unified/GPU memory. An output ending at the token cap may be incomplete.
- Failed setup/compatibility attempts are retained separately, including their literal errors. Sources are byte-identical copies and need not be retransmitted from this Mac to open the bundle.

## Setup / compatibility attempts

- `pilot-olmocr2/pow_01__original__olmocr2__1`: success; output retained; [raw text](tests/pilot-olmocr2/pow_01__original__olmocr2__1/raw.txt), [JSON](tests/pilot-olmocr2/pow_01__original__olmocr2__1/result.json).
- `pilot-mlx-fixed/pow_01__original__mlx__1`: error; RuntimeError: [METAL] Command buffer execution failed: Insufficient Memory (00000008:kIOGPUCommandBufferCallbackErrorOutOfMemory).; [raw text](tests/pilot-mlx-fixed/pow_01__original__mlx__1/raw.txt), [JSON](tests/pilot-mlx-fixed/pow_01__original__mlx__1/result.json).
- `pilot-mlx-memory-released/pow_01__original__mlx__1`: success; output retained; [raw text](tests/pilot-mlx-memory-released/pow_01__original__mlx__1/raw.txt), [JSON](tests/pilot-mlx-memory-released/pow_01__original__mlx__1/result.json).
- `pilot-native/pow_01__original__tess3__1`: success; output retained; [raw text](tests/pilot-native/pow_01__original__tess3__1/raw.txt), [JSON](tests/pilot-native/pow_01__original__tess3__1/result.json).
- `pilot-native/pow_01__original__tess6__1`: success; output retained; [raw text](tests/pilot-native/pow_01__original__tess6__1/raw.txt), [JSON](tests/pilot-native/pow_01__original__tess6__1/result.json).
- `pilot-native/pow_01__original__vision_raw__1`: success; output retained; [raw text](tests/pilot-native/pow_01__original__vision_raw__1/raw.txt), [JSON](tests/pilot-native/pow_01__original__vision_raw__1/result.json).
- `pilot-native/pow_01__original__vision_corrected__1`: success; output retained; [raw text](tests/pilot-native/pow_01__original__vision_corrected__1/raw.txt), [JSON](tests/pilot-native/pow_01__original__vision_corrected__1/result.json).
- `pilot-neural/pow_01__original__easy__1`: success; output retained; [raw text](tests/pilot-neural/pow_01__original__easy__1/raw.txt), [JSON](tests/pilot-neural/pow_01__original__easy__1/result.json).
- `pilot-neural/pow_01__original__mlx__1`: error; AttributeError: 'Qwen3VLImageProcessor' object has no attribute 'size'; [raw text](tests/pilot-neural/pow_01__original__mlx__1/raw.txt), [JSON](tests/pilot-neural/pow_01__original__mlx__1/result.json).
- `pilot-round2-compat/pow_01__original__paddle_vl__1`: error; ValueError: not enough values to unpack (expected 5, got 4); [raw text](tests/pilot-round2-compat/pow_01__original__paddle_vl__1/raw.txt), [JSON](tests/pilot-round2-compat/pow_01__original__paddle_vl__1/result.json).
- `pilot-round2-compat/pow_01__original__dots_ocr__1`: success; output retained; [raw text](tests/pilot-round2-compat/pow_01__original__dots_ocr__1/raw.txt), [JSON](tests/pilot-round2-compat/pow_01__original__dots_ocr__1/result.json).
- `pilot-round2-initial/pow_01__original__trocr_ru__1`: success; output retained; [raw text](tests/pilot-round2-initial/pow_01__original__trocr_ru__1/raw.txt), [JSON](tests/pilot-round2-initial/pow_01__original__trocr_ru__1/result.json).
- `pilot-round2-initial/pow_01__original__trocr_archive__1`: success; output retained; [raw text](tests/pilot-round2-initial/pow_01__original__trocr_archive__1/raw.txt), [JSON](tests/pilot-round2-initial/pow_01__original__trocr_archive__1/result.json).
- `pilot-round2-initial/pow_01__original__glm_ocr__1`: success; output retained; [raw text](tests/pilot-round2-initial/pow_01__original__glm_ocr__1/raw.txt), [JSON](tests/pilot-round2-initial/pow_01__original__glm_ocr__1/result.json).
- `pilot-round2-initial/pow_01__original__paddle_vl__1`: error; ValueError: Unrecognized image processor in /Users/user/experiments/russian-ocr/models/round2/paddle_vl. Should have a `image_processor_type` key in its preprocessor_config.json of config.json, or one of the following `model_type` keys in its config.json: aria, beit, bit, blip, bridgetower, chameleon, chinese_clip, chmv2, clip, cohere2_vision, cohere_compass, conditional_detr, convnext, cosmos3_edge, deepseek_ocr2, deepseek_vl, deepseek_vl_hybrid, deformable_detr, deit, depth_pro, detr, dinov3_vit, dpt, efficientloftr, efficientnet, eomt, ernie4_5_vl_moe, flava, fuyu, gemma3, gemma4, gemma4_unified, glm46v, glm4v, glm5_next, glm_image, glmga, glpn, got_ocr2, grounding-dino, hunyuan_vl, idefics, idefics2, idefics3, imagegpt, janus, kimi_k25, layoutlmv2, layoutlmv3, levit, lfm2_vl, lightglue, llama4, llava, llava_next, llava_onevision, mask2former, maskformer, minicpmv4_6, minimax_m3_vl, mllama, mobilenet_v1, mobilenet_v2, mobilevit, muse_glimmer, nemotron_h_omni, neomme, nougat, oneformer, ovis2, owlv2, owlvit, paddleocr_vl, perceiver, perception_lm, phi4_multimodal, pi0, pix2struct, pixtral, poolformer, pp_chart2table, pp_doclayout_v2, pp_doclayout_v3, pp_formulanet, pp_lcnet, pp_ocrv5_server_det, pp_ocrv5_server_rec, pp_ocrv6_small_rec, prompt_depth_anything, pvt, qwen2_vl, rf_detr, rt_detr, sam, sam2, sam3, sapiens2, segformer, seggpt, siglip, siglip2, slanext, smolvlm, step3p7, superglue, superpoint, swin2sr, textnet, tipsv2, tipsv2_dpt, tvp, uvdoc, video_llama_3, videomae, vilt, vit, vitmatte, vitpose, yolos, zoedepth, aimv2, aimv2_vision_model, align, altclip, aya_vision, blip-2, clipseg, colpali, colqwen2, convnextv2, cosmos3_omni, cvt, data2vec-vision, deimv2, depth_anything, dinat, dinov2, donut-swin, edgetam, embedding_gemma2, emu3, eomt_dinov3, exaone4_5, florence2, focalnet, gemma3n, git, granite4_vision, groupvit, hiera, hyperclovax_vision_v2, ijepa, inkling_mm_model, instructblip, internvl, kosmos-2, kosmos-2.5, layoutxlm, lighton_ocr, llava_next_video, lw_detr, metaclip_2, mgp-str, minicpmv4_7, mistral3, mlcd, mm-grounding-dino, mobilevitv2, omdet-turbo, paligemma, pixio, pp_ocrv5_mobile_det, pp_ocrv5_mobile_rec, pp_ocrv6_medium_det, pp_ocrv6_small_det, pp_ocrv6_tiny_rec, pvt_v2, qianfan_ocr, qwen2_5_omni, qwen2_5_vl, qwen3_5, qwen3_5_moe, qwen3_omni_moe, qwen3_vl, qwen4_exp, regnet, resnet, sam2_video, sam3_lite_text, sam3_tracker, sam3_tracker_video, sam3_video, sam_hq, shieldgemma2, slanet, swiftformer, swin, swinv2, t5gemma2, t5gemma2_encoder, table-transformer, timesformer, timm_wrapper, trocr, udop, upernet, video_llava, vipllava, vit_mae, vit_msn, vivit, xclip; [raw text](tests/pilot-round2-initial/pow_01__original__paddle_vl__1/raw.txt), [JSON](tests/pilot-round2-initial/pow_01__original__paddle_vl__1/result.json).
- `pilot-round2-initial/pow_01__original__dots_ocr__1`: success; output retained; [raw text](tests/pilot-round2-initial/pow_01__original__dots_ocr__1/raw.txt), [JSON](tests/pilot-round2-initial/pow_01__original__dots_ocr__1/result.json).
- `pilot-round2-initial/pow_01__original__hunyuan_ocr__1`: success; output retained; [raw text](tests/pilot-round2-initial/pow_01__original__hunyuan_ocr__1/raw.txt), [JSON](tests/pilot-round2-initial/pow_01__original__hunyuan_ocr__1/result.json).
- `pilot-round2-native/pow_01__original__glm_ocr__1`: success; output retained; [raw text](tests/pilot-round2-native/pow_01__original__glm_ocr__1/raw.txt), [JSON](tests/pilot-round2-native/pow_01__original__glm_ocr__1/result.json).
- `pilot-round2-native/pow_01__original__paddle_vl__1`: success; output retained; [raw text](tests/pilot-round2-native/pow_01__original__paddle_vl__1/raw.txt), [JSON](tests/pilot-round2-native/pow_01__original__paddle_vl__1/result.json).
- `pilot-round2-native/pow_01__original__dots_ocr__1`: success; output retained; [raw text](tests/pilot-round2-native/pow_01__original__dots_ocr__1/raw.txt), [JSON](tests/pilot-round2-native/pow_01__original__dots_ocr__1/result.json).
- `pilot-round2-native/pow_01__original__hunyuan_ocr__1`: success; output retained; [raw text](tests/pilot-round2-native/pow_01__original__hunyuan_ocr__1/raw.txt), [JSON](tests/pilot-round2-native/pow_01__original__hunyuan_ocr__1/result.json).
