#241·GLM-OCR

Decimal separators (comma) lost in numeric cells when OCRing an isolated crop of a table region vs. full-page OCR

Author: textlastigCreated Aug 28, 2026Updated Sep 22, 2026
### System Info / 系統信息 - Backend: MLX (mlx_vlm), model mlx-community/GLM-OCR-bf16 - Served via the SDK's own glmocr server (/glmocr/parse), not vLLM - Apple Silicon Mac, macOS - Deterministic decoding configured: temperature: 0.0, top_p: 0.00001, top_k: 1 (per config.yaml) ### Who can help? / 谁可以帮助到您? _No response_ ### Information / 问题信息 - [ ] The official example scripts / 官方的示例脚本 - [x] My own modified scripts / 我自己修改的脚本和任务 ### Reproduction / 复现过程 As a workaround for #240 (missing/garbled table footer rows during full-page OCR), we tried re-recognizing just the affected table region: cropping the page to the table's own detected bbox_2d (with modest padding), wrapping it as a single-page PDF, and submitting that alone to /glmocr/parse, e.g.: from mlx_vlm import load, generate from mlx_vlm.prompt_utils import apply_chat_template model, processor = load("mlx-community/GLM-OCR-bf16") prompt = "Table Recognition:" formatted_prompt = apply_chat_template( processor, model.config, prompt, num_images=1) result = generate( model, processor, formatted_prompt, image=["./invoice_table_crop_only.png"], # same table, but as an isolated crop instead of the full page max_tokens=2048, verbose=True, ) Steps to reproduce the behavior: Run OCR on the full page containing the table from #TODO-issue1-number — numeric cells come back with a decimal comma (10,72, 2,300, etc.), as shown there. Crop the page down to just that table's own detected bbox_2d and run OCR again on the crop alone. Compare the two outputs — every numeric cell in the crop has lost its decimal separator. Full-page output for this table (from #TODO-issue1-number, line 1): 10,72, 13,41, 2,300, etc. — with commas. Isolated-crop output for the identical table:
DatumGOÄLeistungAnz.AuslagenFaktorHonorar
13.02.241Beratung(persönlich oder telefon.)123001072
6UntersuchungOrgansystemAuge/HNO123001341
1201subj.Refraktionsphär.-zylindr.Gl.123001193
1202objektiveRefraktionsbestimmung12300992
1207Prüfung./Bestimmung vonMehrstärken-o.Prismenbrille12300938
1242Binoc.Unters.d.Augenhintergrundes123002038
1256Applanationstonometrie118001049
SummeHonorarEUR:8623
RechnungsbetragEUR:8623
10,72 → 1072, 2,300 → 2300, and so on for every numeric cell, both in json_result and markdown_result. Note this run did recover the two footer rows from #240 (label and amount both present this time) — but 86,23 came back as 8623, i.e. the same comma loss affecting the rest of the table. We compared our crop pipeline against the SDK's own internal per-region cropping (glmocr/utils/image_utils.py::crop_image_region, glmocr/pipeline/_workers.py::_flush_layout_batch) and found identical padding (none), identical DPI (200), identical prompt (Table Recognition:), and identical sampling params — so no obvious tunable difference. The one architectural difference we found: /glmocr/parse always runs the full pipeline (layout detection → per-region crop → recognition), so submitting an already-cropped table image causes layout detection to run a second time on an already-tiny, atypical (very dense, no page context) image — something the internal per-region path never does, since there the bbox from a single page-level layout pass is used directly. This might be related to #228 (crop-based inference reported as generally worse) but that report doesn't mention number formatting specifically, so filing separately in case the mechanism differs. ### Expected behavior / 期待表现 Numeric cells should keep their decimal separator regardless of whether the table is recognized as part of a full page or as an isolated crop — the same 10,72 should not become 1072 just because of how the input image was framed. Is there a way to submit a region for recognition using a known layout label (e.g. "this is a table") without re-running layout detection on the crop? That would let us bypass this second-pass degradation entirely.