# Japanese vertical text OCR: reproducible teaching experiment

Run date: September 7, 2026. All three Japanese sentences and the annotation choices were authored for this example. No customer files or scanned book excerpts were used. Original teaching text, generated images and experiment scripts may be reused with attribution to BookTranslator. The third-party models and font retain their own licenses and are not included in this download directory.

## Corpus and target

`ground-truth.txt` contains the three body sentences (34 Unicode code points, excluding line breaks). `ruby-transcription.txt` contains four separately authored pronunciation associations. `horizontal.png`, `vertical.png`, and `vertical-furigana.png` display the same body. Three `body-column-N.png` images are manual body-only crops from the furigana page, numbered right to left.

The target is main text in reading order, not a lossless transcription of ruby. Missing ruby is not penalized by the body comparison; it would matter for an annotation-preserving deliverable. The renderer is intentionally simplified, including a small outlined circle for each vertical full stop. It does not implement complete JIS typesetting. No blur, skew, paper texture, historical fonts, handwriting, mixed-direction page, or manga layout is tested.

## Exact recorded environment

- Tesseract.js 7.0.0; Tesseract.js-core 7.0.0.
- Node.js v25.2.1, macOS ARM64.
- Python Pillow 12.3.0 for fixture generation.
- OEM 1 (LSTM only), `user_defined_dpi=300`.
- Whole horizontal/control images: `jpn`, PSM 6. Vertical/ruby/crops: `jpn_vert`, PSM 5.
- Body font 64 px, ruby font 24 px; PNG metadata 300 dpi. See `sample-manifest.json` for actual dimensions and hashes.
- Official `tessdata_fast` commit `87416418657359cb625c412a48b6e1d6d41c29bd`:
  - `jpn.traineddata` SHA-256 `1f5de9236d2e85f5fdf4b3c500f2d4926f8d9449f28f5394472d9e8d83b91b4d`.
  - `jpn_vert.traineddata` SHA-256 `bf1e2640954691797e2dc14f38533e601b59ee37958698ae0f0b81dc6f09c71b`.
- Core WASM hashes and complete call parameters are saved in `ocr-results.json`; font hash and rendering parameters in `sample-manifest.json`.

The initial `tessdata_best` attempt aborted before producing text with `missing function: _ZN9tesseract13DotProductSSEEPKfS1_i` in the available WebAssembly runtime. Recorded results use only `tessdata_fast`; this was a compatibility change, not a model-family accuracy comparison. All seven successful case outputs are retained. No paid OCR API or BookTranslator test was run.

## Reproduce without changing your project

Copy this experiment directory into a disposable working directory. Install the specific tools in that copy/its virtual environment, not into the BookTranslator application:

```bash
python -m venv .venv
.venv/bin/python -m pip install Pillow==12.3.0
npm install --no-save tesseract.js@7.0.0 tesseract.js-core@7.0.0
mkdir models fonts
curl -fL https://raw.githubusercontent.com/tesseract-ocr/tessdata_fast/87416418657359cb625c412a48b6e1d6d41c29bd/jpn.traineddata -o models/jpn.traineddata
curl -fL https://raw.githubusercontent.com/tesseract-ocr/tessdata_fast/87416418657359cb625c412a48b6e1d6d41c29bd/jpn_vert.traineddata -o models/jpn_vert.traineddata
curl -fL https://raw.githubusercontent.com/notofonts/noto-cjk/f8d157532fbfaeda587e826d4cd5b21a49186f7c/Sans/OTF/Japanese/NotoSansCJKjp-Regular.otf -o fonts/NotoSansCJKjp-Regular.otf
.venv/bin/python generate-samples.py fonts/NotoSansCJKjp-Regular.otf
node run-ocr.cjs models
.venv/bin/python evaluate.py
```

The scripts overwrite only their named experiment outputs beside themselves. `run-ocr.cjs` reads already-downloaded models and does not send images to an OCR service. On a different environment, compare your results rather than assuming they must be identical.

## Read the evidence

- `*.txt` for each OCR case: actual uncorrected returned text.
- Matching `*.tsv`: actual Tesseract output with layout boxes/confidence data.
- `ocr-results.json`: all raw text, model hashes and runtime versions.
- `body-comparison.csv`: NFC normalization, removal of all Unicode whitespace, then exact comparison and Levenshtein distance against body ground truth. Punctuation and character order are not normalized away.
- `error-annotations.csv`: agent-reviewed sample-level observations and limitations.
- `review-log.csv`: blank record for your own source files; initially Not tested.

The count is an edit distance for a specified transcription target, not a calibrated engine accuracy or a book-level error rate. One agent checked the images and compared the outputs with the authored ground truth. There was no independent human or blinded linguistic review. Cropping excludes ruby by known synthetic coordinates; it is not an automatic furigana-removal algorithm. Confidence numbers are engine outputs, not correctness proof.

## Primary references

- [Official Tesseract fast models](https://github.com/tesseract-ocr/tessdata_fast)
- [Tesseract image quality and page segmentation](https://tesseract-ocr.github.io/tessdoc/ImproveQuality.html)
- [Tesseract.js worker API](https://github.com/naptha/tesseract.js/blob/master/docs/api.md)
- [W3C Japanese text layout](https://www.w3.org/TR/jlreq/)
- [Noto CJK source and font license](https://github.com/notofonts/noto-cjk)
