Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions docling/datamodel/pipeline_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,20 @@ class OcrOptions(BaseOptions):
),
]

scale: Annotated[
float,
Field(
description=(
"Image scale multiplier applied before running OCR. The page is "
"rendered at 72 DPI times this factor, so the default 3 yields "
"216 DPI. Lower it when the source image is already high "
"resolution and upscaling degrades recognition."
),
examples=[1.0, 3.0],
gt=0.0,
),
] = 3.0

# Deprecated: superseded by `OcrMode.FULL_PAGE`. Kept for backwards compatibility
# When set to True it forces `mode` to FULL_PAGE
force_full_page_ocr: Annotated[
Expand Down
3 changes: 2 additions & 1 deletion docling/models/stages/ocr/easyocr_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ def __init__(
)
self.options: EasyOcrOptions

self.scale = 3 # multiplier for 72 dpi == 216 dpi.
# multiplier for 72 dpi; the default 3.0 == 216 dpi.
self.scale = self.options.scale

if self.enabled:
try:
Expand Down
5 changes: 3 additions & 2 deletions docling/models/stages/ocr/nemotron_ocr_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ def __init__(
accelerator_options=accelerator_options,
)
self.options: NemotronOcrOptions
self.scale = 3 # multiplier for 72 dpi == 216 dpi.
# multiplier for 72 dpi; the default 3.0 == 216 dpi.
self.scale = self.options.scale

if self.enabled:
self.validate_runtime(accelerator_options=accelerator_options)
Expand Down Expand Up @@ -246,7 +247,7 @@ def _prediction_to_cell(
ocr_rect: BoundingBox,
image_width: int,
image_height: int,
scale: int,
scale: float,
) -> TextCell:
# `nemotron_ocr` returns normalized `left/right` and an inverted
# pair `lower/upper`, where `lower` is the top Y and `upper` is the
Expand Down
3 changes: 2 additions & 1 deletion docling/models/stages/ocr/ocr_mac_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ def __init__(
)
self.options: OcrMacOptions

self.scale = 3 # multiplier for 72 dpi == 216 dpi.
# multiplier for 72 dpi; the default 3.0 == 216 dpi.
self.scale = self.options.scale

if self.enabled:
if "darwin" != sys.platform:
Expand Down
3 changes: 2 additions & 1 deletion docling/models/stages/ocr/rapid_ocr_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,8 @@ def __init__(
)
self.options: RapidOcrOptions

self.scale = 3 # multiplier for 72 dpi == 216 dpi.
# multiplier for 72 dpi; the default 3.0 == 216 dpi.
self.scale = self.options.scale

if self.enabled:
try:
Expand Down
3 changes: 2 additions & 1 deletion docling/models/stages/ocr/tesseract_ocr_cli_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ def __init__(
)
self.options: TesseractCliOcrOptions

self.scale = 3 # multiplier for 72 dpi == 216 dpi.
# multiplier for 72 dpi; the default 3.0 == 216 dpi.
self.scale = self.options.scale

self._name: Optional[str] = None
self._version: Optional[str] = None
Expand Down
3 changes: 2 additions & 1 deletion docling/models/stages/ocr/tesseract_ocr_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ def __init__(
)
self.options: TesseractOcrOptions
self._is_auto: bool = "auto" in self.options.lang
self.scale = 3 # multiplier for 72 dpi == 216 dpi.
# multiplier for 72 dpi; the default 3.0 == 216 dpi.
self.scale = self.options.scale
self.reader = None
self.script_readers: dict[str, tesserocr.PyTessBaseAPI] = {}

Expand Down
32 changes: 32 additions & 0 deletions tests/test_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from unittest.mock import Mock, patch

import pytest
from pydantic import ValidationError

from docling.backend.docling_parse_backend import (
DoclingParseDocumentBackend,
Expand All @@ -21,17 +22,21 @@
ApiKserveV2ImageClassificationEngineOptions,
)
from docling.datamodel.pipeline_options import (
EasyOcrOptions,
NemotronOcrOptions,
PdfPipelineOptions,
TableFormerMode,
TesseractCliOcrOptions,
)
from docling.document_converter import (
ConversionError,
DocumentConverter,
PdfFormatOption,
)
from docling.models.factories import get_ocr_factory
from docling.models.stages.ocr.easyocr_model import EasyOcrModel
from docling.models.stages.ocr.nemotron_ocr_model import NemotronOcrModel
from docling.models.stages.ocr.tesseract_ocr_cli_model import TesseractOcrCliModel
from docling.pipeline.legacy_standard_pdf_pipeline import LegacyStandardPdfPipeline


Expand Down Expand Up @@ -455,6 +460,33 @@ def test_page_error_carries_page_no(test_doc_path):
assert not err.error_message.startswith("Page ")


def test_ocr_scale_is_configurable():
"""The OCR render scale comes from the options instead of a hardcoded 3."""
assert TesseractCliOcrOptions().scale == 3.0
assert EasyOcrOptions().scale == 3.0

accelerator_options = AcceleratorOptions()

tesseract_model = TesseractOcrCliModel(
enabled=False,
artifacts_path=None,
options=TesseractCliOcrOptions(scale=1.0),
accelerator_options=accelerator_options,
)
assert tesseract_model.scale == 1.0

easyocr_model = EasyOcrModel(
enabled=False,
artifacts_path=None,
options=EasyOcrOptions(scale=1.5),
accelerator_options=accelerator_options,
)
assert easyocr_model.scale == 1.5

with pytest.raises(ValidationError):
TesseractCliOcrOptions(scale=0)


def test_nemotron_ocr_backend_registration():
factory = get_ocr_factory(allow_external_plugins=False)

Expand Down