Repository navigation
arabic_PP-OCRv5_mobile_rec: native aspect-ratio width under-allocates CTC steps for full-width lines (36.7% → 12.9% CER) #18349
Description
🐛 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.
Activity
[-]`arabic_PP-OCRv5_mobile_rec`: native aspect-ratio width under-allocates CTC steps for full-width lines (36.7% → 12.9% CER)[/-][+]arabic_PP-OCRv5_mobile_rec: native aspect-ratio width under-allocates CTC steps for full-width lines (36.7% → 12.9% CER)[/+]wmjlincy commentedon Sep 16, 2026
Thanks for the extremely thorough investigation — the paired setup (shared detection boxes, identical weights) and the steps-per-character breakdown make the CTC time-axis hypothesis well supported. A few points in response:
We agree this is a real failure mode. Dynamic aspect-ratio resize assumes a roughly uniform character-to-pixel ratio across scripts; Arabic's ligature/kerning density means the stock resize (mean input width ~695px → ~86 CTC steps) starves the decoder exactly where it matters. Your 2–3 steps/character sweet spot is consistent with CTC capacity limits.
PR welcome. We won't change the default resize from a two-book study either, but a configurable per-crop width policy is low-risk and useful for Arabic/other connected scripts. What we'd prefer:
(1) A new optional argument on the recognition preprocessing path, e.g. text_rec_width_scale (multiplier, default 1.0 = current behavior) and text_rec_width_limit (cap), or equivalently a rec_resize_limit_type that accepts "w/h scaled"-style policy. Backwards compatibility is non-negotiable — default must reproduce current behavior exactly.
(2) This should live in the recognition image-resize preprocessing, i.e. the RecResizeIMG-style operator (paddleocr.data.imaug.rec_img_aug for the 2.x-style codepath, or paddlex/inference/models/text_recognition/processors.py for the pipeline path that consumes text_rec_input_shape), not in the model itself. The pipeline already threads text_rec_input_shape through, so an additive option there is the natural extension.
Before merging we'll want cross-model checks, not to block you but to size the blast radius: at minimum one server-side model (arabic_PP-OCRv5_server_rec) and one non-Arabic model (e.g. en_PP-OCRv5_mobile_rec) run on a standard benchmark (e.g. a subset of [relevant dataset]) to confirm width_scale=1.0 is bit-identical and that >1 doesn't regress Latin. Your clamp formula (clamp(round(scale·48·w/h), 320, 1280)) looks like the right shape.
Naming/docs: if the PR includes the synthetic degradation harness from your MRE as a test, that would be a great regression guard for Arabic rec preprocessing.
Ahmed-Sinkeat commentedon Sep 16, 2026
Thanks, happy to open the PR. Two questions before I start:
OCRReisizeNormImglives in PaddleX, so I'm planning a PaddleX PR (resize operator + predictor/pipeline option) followed by a PaddleOCR PR that exposestext_rec_width_scale/text_rec_width_limit. Is that the split you'd like?I'll start with the default-is-bit-identical check on
developin the meantime.Ahmed-Sinkeat commentedon Sep 16, 2026
One more thing on the cross-model check:
arabic_PP-OCRv5_server_recdoesn't seem to be published. It isn't in PaddleX's model list, on Hugging Face, or on BOS. Wouldarabic_PP-OCRv3_mobile_recwork as the second Arabic model, withPP-OCRv5_server_recas the server-side no-regression check?Progress so far, on a local patch to PaddleX
develop(OCRReisizeNormImggainswidth_scale, default 1.0, andwidth_limit, defaultNone= the existing 3200):width_scale=2.0, width_limit=1280through the library's own resize path: 41.4% → 9.2% CER on book 1 and 36.8% → 12.8% on book 2, in line with the numbers above.Ahmed-Sinkeat commentedon Oct 6, 2026
PR is up: PaddlePaddle/PaddleX#5215
Since
arabic_PP-OCRv5_server_recisn't published and the benchmark question was open, I usedarabic_PP-OCRv3_mobile_recas the second Arabic model anden_PP-OCRv5_mobile_rec/PP-OCRv5_server_recon 200 IIIT5K test words. Defaults are identical to stock on all four models. The option helps both Arabic models and does not help English (94.5% → 93.0% exact on the mobile model, unchanged on server), so it stays opt-in. Happy to rerun on whichever benchmark you prefer. The PaddleOCR-side arguments will follow once the PaddleX side is agreed.