Files
agentic-mobile-control/tests/test_ocr.py
T
q792602257 17a709c92f
Tests / Test tests.test_device_config.test_device_config_store_settings_get_set_and_defaults failed
fix(perception): disable PaddleOCR doc-unwarping for screenshots
PaddleOCR's OCR.yaml pipeline defaults to use_doc_orientation_classify
and use_doc_unwarping enabled, which are meant for photographed paper
documents. Applied to a flat, upright device screenshot, UVDoc
geometrically warps the image before detection, and returns box
coordinates in that warped space with no inverse mapping back to the
original image.

Verified on a real screenshot: with unwarping on, the same detected
element ("新项目") shifts from y=158 to y=71 versus the original image,
and 2 boxes near the top edge (status bar time/battery) are dropped
entirely. Disabling both flags by default (still overridable via
explicit kwargs) makes detected boxes match the original screenshot.
2026-07-15 16:51:36 +08:00

80 lines
2.4 KiB
Python

from __future__ import annotations
import numpy as np
import pytest
from core.models import Bounds
from perception.ocr import OCRBox, PaddleOCREngine, parse_paddle_result, run_ocr
def test_paddle_ocr_engine_disables_doc_preprocessing_by_default() -> None:
# Screenshots are flat, upright digital captures, not photographed paper
# documents: PaddleOCR's document-unwarping preprocessing has nothing real
# to correct and instead warps the image, so detected box coordinates no
# longer line up with the original screenshot.
engine = PaddleOCREngine(lang="ch")
assert engine.kwargs["use_doc_orientation_classify"] is False
assert engine.kwargs["use_doc_unwarping"] is False
def test_paddle_ocr_engine_lets_callers_override_doc_preprocessing() -> None:
engine = PaddleOCREngine(use_doc_unwarping=True)
assert engine.kwargs["use_doc_unwarping"] is True
def test_parse_paddle_result_accepts_ndarray_fields() -> None:
boxes = parse_paddle_result(
{
"rec_texts": np.array(["Search"]),
"rec_scores": np.array([0.95]),
"rec_boxes": np.array([[1, 2, 10, 12]]),
}
)
assert boxes == [
OCRBox(
text="Search",
bounds=Bounds(x=1.0, y=2.0, width=9.0, height=10.0),
confidence=0.95,
)
]
class CallableJsonResult:
def json(self) -> dict[str, object]:
return {
"res": {
"rec_texts": ["Search"],
"rec_scores": [0.95],
"rec_boxes": [[1, 2, 10, 12]],
}
}
def test_parse_paddle_result_accepts_callable_json_result() -> None:
assert parse_paddle_result(CallableJsonResult()) == [
OCRBox(
text="Search",
bounds=Bounds(x=1.0, y=2.0, width=9.0, height=10.0),
confidence=0.95,
)
]
class FailingOCREngine:
def extract(self, image: bytes | str) -> list[OCRBox]:
raise ValueError(
"The truth value of an array with more than one element is ambiguous"
)
def test_run_ocr_degrades_when_ocr_engine_raises() -> None:
assert run_ocr(b"image", engine=FailingOCREngine()) == [] # type: ignore[arg-type]
def test_run_ocr_strict_mode_preserves_engine_error() -> None:
with pytest.raises(ValueError, match="truth value"):
run_ocr(b"image", engine=FailingOCREngine(), strict=True) # type: ignore[arg-type]