Skip to content

fix(ocr): make the OCR render scale configurable instead of hardcoded#3877

Open
Anai-Guo wants to merge 1 commit into
docling-project:mainfrom
Anai-Guo:fix/ocr-configurable-scale-3866
Open

fix(ocr): make the OCR render scale configurable instead of hardcoded#3877
Anai-Guo wants to merge 1 commit into
docling-project:mainfrom
Anai-Guo:fix/ocr-configurable-scale-3866

Conversation

@Anai-Guo

Copy link
Copy Markdown

Fixes #3866

Problem

Every local OCR engine hardcoded its render scale:

self.scale = 3  # multiplier for 72 dpi == 216 dpi.

so pages are always upscaled to 216 DPI before being handed to the OCR engine. As reported in #3866, that is the wrong default for sources that are already high resolution: the reporter's clean flattened-acroform image OCR'd correctly with tesseract directly, but produced errors through Docling — and the errors disappeared when the temp image was not enlarged. There was no way to change this short of editing the model class.

Change

Add a scale field to the shared OcrOptions base and have the engines read self.options.scale instead of the literal:

  • EasyOcrModel
  • RapidOcrModel
  • OcrMacModel
  • NemotronOcrModel
  • TesseractOcrModel (tesserocr)
  • TesseractOcrCliModel

The default is 3.0, so behaviour is unchanged for anyone who does not set it. gt=0.0 rejects a zero/negative scale at validation time.

This follows the pattern already used elsewhere in the codebase — KserveV2OcrOptions.scale and PictureDescriptionBaseOptions.scale are both configurable float multipliers. KserveV2OcrOptions keeps its own 2.0 default via the existing override.

converter = DocumentConverter(
    format_options={
        InputFormat.IMAGE: ImageFormatOption(
            pipeline_options=PdfPipelineOptions(
                do_ocr=True,
                ocr_options=TesseractCliOcrOptions(scale=1.0),  # no upscaling
            )
        )
    }
)

The one non-mechanical edit is NemotronOcrModel._to_bounding_rect, whose scale: int parameter annotation becomes scale: float so ty stays happy.

Verification

  • ruff@0.15.12 check / format --check clean on the changed files (pinned pre-commit version).
  • Added test_ocr_scale_is_configurable in tests/test_options.py, which asserts the default stays 3.0, that a custom scale reaches the model instance, and that scale=0 is rejected. Run against a local build:
tesseract model.scale = 1.0
easyocr model.scale = 1.5
default model.scale = 3.0
OK: scale=0 rejected

A dynamic "figure out the effective resolution automatically" heuristic — the reporter's stretch suggestion — is deliberately left out of scope here; this PR only removes the hardcode.

🤖 Generated with Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

DCO Check Passed

Thanks @Anai-Guo, all your commits are properly signed off. 🎉

@mergify

mergify Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Merge Protections

🟢 Merge protection satisfied — ready to merge.

Show 1 satisfied protection

🟢 Enforce conventional commit

Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/

  • title ~= ^(fix|feat|docs|style|refactor|perf|test|build|ci|chore|revert)(?:\(.+\))?(!)?:

Every local OCR engine hardcoded `self.scale = 3`, so pages were always
rendered at 216 DPI before OCR. For sources that are already high
resolution this upscaling degrades recognition, and there was no way to
turn it down short of editing the model class.

Add a `scale` field to the shared `OcrOptions` base (default 3.0, so
behaviour is unchanged) and have the EasyOCR, RapidOCR, ocrmac, Nemotron
and both Tesseract engines read it. This mirrors the existing
`KserveV2OcrOptions.scale` and `PictureDescriptionBaseOptions.scale`.

Fixes docling-project#3866

Signed-off-by: Anai-Guo <antai12232931@outlook.com>
Assisted-by: Claude
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hardcoded scale in TesseractOcrCliModel may not be appropropriate for every image

1 participant