#18349·PaddleOCR

arabic_PP-OCRv5_mobile_rec: native aspect-ratio width under-allocates CTC steps for full-width lines (36.7% → 12.9% CER)

Author: Ahmed-SinkeatCreated Sep 7, 2026Updated Sep 16, 2026

Bug (问题描述)

Summary

On scanned Arabic book pages, arabic_PP-OCRv5_mobile_rec through the stock pipeline scored 36.7% and 41.8% CER on two different books. Pre-resizing each detected line crop to

clamp(round(2.0 * 48 * w / h), 320, 1280)

before recognition—with the same model weights and detection boxes—reduced CER to 12.9% and 9.8%, respectively.

The evidence points to the horizontal/CTC time-step budget as the main reason for this difference. This model's CTC output has floor(input_width / 8) time steps, measured directly from the logits tensor.

Observed decoder geometry

For the second book, the stock resize produced a mean input width of 695 px and a mean of 86.55 CTC steps over 204 cached line crops. That is 17,656 steps in total, or 1.63 steps per page-reference character over the 10,801-character corpus.

In a development sweep, I evaluated the same 160 lines under several width policies. Pooling the resulting 1,430 line-policy observations and grouping them by steps per reference character gave:

steps / character line-policy observations median CER
0–2 234 16.4%
2–3 483 6.9%
3–4 304 10.0%
4–5 183 15.5%
5–6 79 21.7%
6–8 31 33.3%
8–12 19 94.4%
12+ 97 100.0%

The lowest observed median CER was in the 2–3-step bin. The stock aspect-ratio resize placed dense, full-width Arabic lines to the left of that region. Very wide fixed inputs also performed poorly on short lines, so a per-crop policy worked better than one fixed width.

These measurements strongly associate accuracy with the available time axis, but they do not completely isolate CTC sequence capacity from horizontal resampling. I am therefore reporting the measured behavior rather than claiming a universal optimum for this model or other scripts.

Measurements on two books

I evaluated ten pages per book. Ground truth was read from the page images, detection ran once and was shared by every PaddleOCR configuration, and CER was calculated over the complete reconstructed page text.

system book 1: classical, vocalised book 2: modern commentary
stock pipeline (native aspect ratio) 41.8% 36.7%
fixed 1280 px 13.1% 14.9%
clamp(round(2·48·w/h), 320, 1280) 9.8% 12.9%
Tesseract 5.5.3 tessdata_best/ara, for reference 13.5% 13.6%

The books differ in author, publisher, typeface, period, vocalisation pattern, and source scan resolution. The width rule was frozen before the second-book evaluation.

On book 2, a 2,000-sample paired page bootstrap never favoured the stock pipeline. The 95% interval for the improvement was 13.9–34.2 CER points. The individual CER intervals were 25.5–47.7% for stock and 10.2–15.3% for the per-crop width rule.

Synthetic degradation check

In a separate recognition-only ablation, I reused the clean-page detection boxes and applied them to copies downsampled to approximately 100 dpi effective resolution, restored to the original dimensions, and then damaged with JPEG quality 30 plus Gaussian noise:

condition stock per-crop width rule
clean 36.7% 12.9%
synthetic degradation 45.8% 14.4%

Thus the wider configuration lost 1.5 CER points while stock lost 9.1 points in this ablation. Because page geometry and detection boxes were held fixed, the degradation did not change either policy's allocated input width. This result only shows that the per-crop width configuration was more robust to this synthetic damage; it does not show that degradation itself reduced the stock CTC budget.

Existing configuration surface and proposed scope

PaddleOCR already exposes text_rec_input_shape on the OCR pipeline, and TextRecognition exposes input_shape. Those options allow a fixed recognition shape; the fixed-1280 row above demonstrates that this can recover much of the loss. A fixed width nevertheless over-allocates the time axis to short lines and performed worse than the bounded per-crop rule.

I am not proposing a change to the default resize on the basis of one model, one language, and twenty pages. A global change would need cross-model and cross-language evaluation.

Would a configurable per-crop recognition-width multiplier/cap, or a resize-policy callback, be welcome as a PR? If so, where would maintainers prefer that policy to live?

Caveats

The references were transcribed by one person from the page images and cross-checked against published digital editions where corresponding text existed. Two pages in book 2—a publisher's preface and an index—had no corresponding second source. Because every width configuration was scored against the same references and detection boxes, reference errors affect the absolute CER values more than the paired comparison. This remains a careful two-book evaluation, not a gold-standard benchmark.

‍♂️ Environment (运行环境)

OS Linux x86_64
CPU Intel Core i5-1135G7
Python 3.12.14
paddleocr 3.7.0
paddlex 3.7.2
paddlepaddle 3.3.1, CPU, enable_mkldnn=False
recognition model arabic_PP-OCRv5_mobile_rec
detection model used in the page evaluation PP-OCRv6_medium_det
external reference Tesseract 5.5.3, tessdata_best/ara

Minimal Reproducible Example (最小可复现问题的Demo)

This example generates its own synthetic Arabic line, so it does not require or redistribute a scanned-book crop. It uses Noto Naskh Arabic; set FONT to the location of NotoNaskhArabic-Regular.ttf on the machine. Pillow must have Raqm support for Arabic shaping.

from pathlib import Path

import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFont
from paddleocr import TextRecognition

REC_H = 48
FONT = Path("/usr/share/fonts/noto/NotoNaskhArabic-Regular.ttf")
TEXT = (
    "هذا نص عربي طويل لاختبار التعرف الضوئي على الحروف "
    "في سطر كامل من كتاب مطبوع بجودة واضحة"
)


def make_crop():
    crop = Image.new("RGB", (1800, 110), "white")
    font = ImageFont.truetype(str(FONT), 48)
    ImageDraw.Draw(crop).text(
        (1780, 5),
        TEXT,
        font=font,
        fill="black",
        anchor="ra",
        direction="rtl",
        language="ar",
    )
    return crop


def resize_to(crop, width):
    bgr = np.asarray(crop)[:, :, ::-1]
    return cv2.resize(bgr, (int(width), REC_H))


model = TextRecognition(
    model_name="arabic_PP-OCRv5_mobile_rec",
    enable_mkldnn=False,
)

crop = make_crop()
w, h = crop.size

# Equivalent tensor-width calculation for the stock dynamic path at this ratio.
native = min(max(int(REC_H * w / h), 320), 3200)

# Per-crop rule evaluated above.
wider = min(max(round(2.0 * REC_H * w / h), 320), 1280)

for name, target_width in (("native", native), ("wider", wider)):
    result = model.predict([resize_to(crop, target_width)])[0]
    print(
        name,
        f"{target_width}px -> {target_width // 8} CTC steps",
        repr(result["rec_text"]),
        float(result["rec_score"]),
    )

Output in the environment above:

native 785px -> 98 CTC steps 'يعاة' 0.5521028638
wider 1280px -> 160 CTC steps 'هذا نص عربي طويل لاختبار التعرف الضوئي على الحروف في سطر كامل من كتاب مطبوع بجودة واضحة' 0.9631704688

The exact confidence may vary by platform, but the native-width output collapses while the wider input recovers the generated reference line. No model parameter or checkpoint is changed.