first commit
Some checks are pending
Build/Publish Develop Docs / deploy (push) Waiting to run

This commit is contained in:
2025-07-02 08:57:16 +03:00
commit 56532cc9a9
1901 changed files with 457695 additions and 0 deletions

0
tests/__init__.py Normal file
View File

0
tests/models/__init__.py Normal file
View File

View File

@@ -0,0 +1,9 @@
def check_result_item_keys(result_item):
assert result_item.keys() == {
"input_path",
"page_index",
"input_img",
"class_ids",
"scores",
"label_names",
}

View File

@@ -0,0 +1,7 @@
def check_result_item_keys(result_item):
assert result_item.keys() == {
"input_path",
"page_index",
"input_img",
"boxes",
}

View File

@@ -0,0 +1,23 @@
import pytest
from paddleocr import DocImgOrientationClassification
from ..testing_utils import TEST_DATA_DIR, check_simple_inference_result
from .image_classification_common import check_result_item_keys
@pytest.fixture(scope="module")
def doc_img_orientation_classification_predictor():
return DocImgOrientationClassification()
@pytest.mark.parametrize(
"image_path",
[
TEST_DATA_DIR / "book_rot180.jpg",
],
)
def test_predict(doc_img_orientation_classification_predictor, image_path):
result = doc_img_orientation_classification_predictor.predict(str(image_path))
check_simple_inference_result(result)
check_result_item_keys(result[0])

View File

@@ -0,0 +1,53 @@
import pytest
from paddleocr import DocVLM
from ..testing_utils import (
TEST_DATA_DIR,
check_simple_inference_result,
check_wrapper_simple_inference_param_forwarding,
)
@pytest.fixture(scope="module")
def doc_vlm_predictor():
return DocVLM()
@pytest.mark.resource_intensive
@pytest.mark.parametrize(
"image_path",
[
TEST_DATA_DIR / "medal_table.png",
],
)
def test_predict(doc_vlm_predictor, image_path):
result = doc_vlm_predictor.predict(str(image_path))
check_simple_inference_result(result)
assert result[0].keys() == {
"input_path",
"page_index",
"input_img",
"result",
}
@pytest.mark.resource_intensive
@pytest.mark.parametrize(
"params",
[
{},
],
)
def test_predict_params(
monkeypatch,
doc_vlm_predictor,
params,
):
check_wrapper_simple_inference_param_forwarding(
monkeypatch,
doc_vlm_predictor,
"paddlex_predictor",
"dummy_path",
params,
)

View File

@@ -0,0 +1,51 @@
import pytest
from paddleocr import FormulaRecognition
from ..testing_utils import (
TEST_DATA_DIR,
check_simple_inference_result,
check_wrapper_simple_inference_param_forwarding,
)
@pytest.fixture(scope="module")
def formula_recognition_predictor():
return FormulaRecognition()
@pytest.mark.parametrize(
"image_path",
[
TEST_DATA_DIR / "formula.png",
],
)
def test_predict(formula_recognition_predictor, image_path):
result = formula_recognition_predictor.predict(str(image_path))
check_simple_inference_result(result)
assert result[0].keys() == {
"input_path",
"page_index",
"input_img",
"rec_formula",
}
@pytest.mark.parametrize(
"params",
[
{},
],
)
def test_predict_params(
monkeypatch,
formula_recognition_predictor,
params,
):
check_wrapper_simple_inference_param_forwarding(
monkeypatch,
formula_recognition_predictor,
"paddlex_predictor",
"dummy_path",
params,
)

View File

@@ -0,0 +1,51 @@
import pytest
from paddleocr import LayoutDetection
from ..testing_utils import (
TEST_DATA_DIR,
check_simple_inference_result,
check_wrapper_simple_inference_param_forwarding,
)
from .object_detection_common import check_result_item_keys
@pytest.fixture(scope="module")
def layout_detection_predictor():
return LayoutDetection()
@pytest.mark.parametrize(
"image_path",
[
TEST_DATA_DIR / "doc_with_formula.png",
],
)
def test_predict(layout_detection_predictor, image_path):
result = layout_detection_predictor.predict(str(image_path))
check_simple_inference_result(result)
check_result_item_keys(result[0])
@pytest.mark.parametrize(
"params",
[
{"img_size": 640},
{"threshold": 0.5},
{"layout_nms": True},
{"layout_unclip_ratio": True},
{"layout_merge_bboxes_mode": True},
],
)
def test_predict_params(
monkeypatch,
layout_detection_predictor,
params,
):
check_wrapper_simple_inference_param_forwarding(
monkeypatch,
layout_detection_predictor,
"paddlex_predictor",
"dummy_path",
params,
)

View File

@@ -0,0 +1,55 @@
import pytest
from paddleocr import SealTextDetection
from ..testing_utils import (
TEST_DATA_DIR,
check_simple_inference_result,
check_wrapper_simple_inference_param_forwarding,
)
@pytest.fixture(scope="module")
def seal_text_detection_predictor():
return SealTextDetection()
@pytest.mark.parametrize(
"image_path",
[
TEST_DATA_DIR / "seal.png",
],
)
def test_predict(seal_text_detection_predictor, image_path):
result = seal_text_detection_predictor.predict(str(image_path))
check_simple_inference_result(result)
assert result[0].keys() == {
"input_path",
"page_index",
"input_img",
"dt_polys",
"dt_scores",
}
@pytest.mark.parametrize(
"params",
[
{"limit_side_len": 640, "limit_type": "min"},
{"thresh": 0.5},
{"box_thresh": 0.3},
{"unclip_ratio": 3.0},
],
)
def test_predict_params(
monkeypatch,
seal_text_detection_predictor,
params,
):
check_wrapper_simple_inference_param_forwarding(
monkeypatch,
seal_text_detection_predictor,
"paddlex_predictor",
"dummy_path",
params,
)

View File

@@ -0,0 +1,48 @@
import pytest
from paddleocr import TableCellsDetection
from ..testing_utils import (
TEST_DATA_DIR,
check_simple_inference_result,
check_wrapper_simple_inference_param_forwarding,
)
from .object_detection_common import check_result_item_keys
@pytest.fixture(scope="module")
def table_cells_detection_predictor():
return TableCellsDetection()
@pytest.mark.parametrize(
"image_path",
[
TEST_DATA_DIR / "table.jpg",
],
)
def test_predict(table_cells_detection_predictor, image_path):
result = table_cells_detection_predictor.predict(str(image_path))
check_simple_inference_result(result)
check_result_item_keys(result[0])
@pytest.mark.parametrize(
"params",
[
{"img_size": 640},
{"threshold": 0.5},
],
)
def test_predict_params(
monkeypatch,
table_cells_detection_predictor,
params,
):
check_wrapper_simple_inference_param_forwarding(
monkeypatch,
table_cells_detection_predictor,
"paddlex_predictor",
"dummy_path",
params,
)

View File

@@ -0,0 +1,23 @@
import pytest
from paddleocr import TableClassification
from ..testing_utils import TEST_DATA_DIR, check_simple_inference_result
from .image_classification_common import check_result_item_keys
@pytest.fixture(scope="module")
def table_classification_predictor():
return TableClassification()
@pytest.mark.parametrize(
"image_path",
[
TEST_DATA_DIR / "table.jpg",
],
)
def test_predict(table_classification_predictor, image_path):
result = table_classification_predictor.predict(str(image_path))
check_simple_inference_result(result)
check_result_item_keys(result[0])

View File

@@ -0,0 +1,53 @@
import pytest
from paddleocr import TableStructureRecognition
from ..testing_utils import (
TEST_DATA_DIR,
check_simple_inference_result,
check_wrapper_simple_inference_param_forwarding,
)
@pytest.fixture(scope="module")
def table_structure_recognition_predictor():
return TableStructureRecognition()
@pytest.mark.parametrize(
"image_path",
[
TEST_DATA_DIR / "table.jpg",
],
)
def test_predict(table_structure_recognition_predictor, image_path):
result = table_structure_recognition_predictor.predict(str(image_path))
check_simple_inference_result(result)
assert result[0].keys() == {
"input_path",
"page_index",
"input_img",
"bbox",
"structure",
"structure_score",
}
@pytest.mark.parametrize(
"params",
[
{},
],
)
def test_predict_params(
monkeypatch,
table_structure_recognition_predictor,
params,
):
check_wrapper_simple_inference_param_forwarding(
monkeypatch,
table_structure_recognition_predictor,
"paddlex_predictor",
"dummy_path",
params,
)

View File

@@ -0,0 +1,55 @@
import pytest
from paddleocr import TextDetection
from ..testing_utils import (
TEST_DATA_DIR,
check_simple_inference_result,
check_wrapper_simple_inference_param_forwarding,
)
@pytest.fixture(scope="module")
def text_detection_predictor():
return TextDetection()
@pytest.mark.parametrize(
"image_path",
[
TEST_DATA_DIR / "table.jpg",
],
)
def test_predict(text_detection_predictor, image_path):
result = text_detection_predictor.predict(str(image_path))
check_simple_inference_result(result)
assert result[0].keys() == {
"input_path",
"page_index",
"input_img",
"dt_polys",
"dt_scores",
}
@pytest.mark.parametrize(
"params",
[
{"limit_side_len": 640, "limit_type": "min"},
{"thresh": 0.5},
{"box_thresh": 0.3},
{"unclip_ratio": 3.0},
],
)
def test_predict_params(
monkeypatch,
text_detection_predictor,
params,
):
check_wrapper_simple_inference_param_forwarding(
monkeypatch,
text_detection_predictor,
"paddlex_predictor",
"dummy_path",
params,
)

View File

@@ -0,0 +1,51 @@
import pytest
from paddleocr import TextImageUnwarping
from ..testing_utils import (
TEST_DATA_DIR,
check_simple_inference_result,
check_wrapper_simple_inference_param_forwarding,
)
@pytest.fixture(scope="module")
def text_image_unwarping_predictor():
return TextImageUnwarping()
@pytest.mark.parametrize(
"image_path",
[
TEST_DATA_DIR / "book.jpg",
],
)
def test_predict(text_image_unwarping_predictor, image_path):
result = text_image_unwarping_predictor.predict(str(image_path))
check_simple_inference_result(result)
assert result[0].keys() == {
"input_path",
"page_index",
"input_img",
"doctr_img",
}
@pytest.mark.parametrize(
"params",
[
{},
],
)
def test_predict_params(
monkeypatch,
text_image_unwarping_predictor,
params,
):
check_wrapper_simple_inference_param_forwarding(
monkeypatch,
text_image_unwarping_predictor,
"paddlex_predictor",
"dummy_path",
params,
)

View File

@@ -0,0 +1,29 @@
import pytest
from paddleocr import TextRecognition
from ..testing_utils import TEST_DATA_DIR, check_simple_inference_result
@pytest.fixture(scope="module")
def text_recognition_predictor():
return TextRecognition()
@pytest.mark.parametrize(
"image_path",
[
TEST_DATA_DIR / "textline.png",
],
)
def test_predict(text_recognition_predictor, image_path):
result = text_recognition_predictor.predict(str(image_path))
check_simple_inference_result(result)
assert result[0].keys() == {
"input_path",
"page_index",
"input_img",
"rec_text",
"rec_score",
"vis_font",
}

View File

@@ -0,0 +1,23 @@
import pytest
from paddleocr import TextLineOrientationClassification
from ..testing_utils import TEST_DATA_DIR, check_simple_inference_result
from .image_classification_common import check_result_item_keys
@pytest.fixture(scope="module")
def textline_orientation_classification_predictor():
return TextLineOrientationClassification()
@pytest.mark.parametrize(
"image_path",
[
TEST_DATA_DIR / "textline_rot180.jpg",
],
)
def test_predict(textline_orientation_classification_predictor, image_path):
result = textline_orientation_classification_predictor.predict(str(image_path))
check_simple_inference_result(result)
check_result_item_keys(result[0])

View File

View File

@@ -0,0 +1,57 @@
import pytest
from paddleocr import DocPreprocessor
from ..testing_utils import (
TEST_DATA_DIR,
check_simple_inference_result,
check_wrapper_simple_inference_param_forwarding,
)
@pytest.fixture(scope="module")
def ocr_engine() -> DocPreprocessor:
return DocPreprocessor()
@pytest.mark.parametrize(
"image_path",
[
TEST_DATA_DIR / "book_rot180.jpg",
],
)
def test_predict(ocr_engine: DocPreprocessor, image_path: str) -> None:
"""
Test PaddleOCR's doc preprocessor functionality.
Args:
ocr_engine: An instance of `DocPreprocessor`.
image_path: Path to the image to be processed.
"""
result = ocr_engine.predict(str(image_path))
check_simple_inference_result(result)
res = result[0]
assert res["angle"] in {0, 90, 180, 270, -1}
assert res["rot_img"] is not None
assert res["output_img"] is not None
@pytest.mark.parametrize(
"params",
[
{"use_doc_orientation_classify": False},
{"use_doc_unwarping": False},
],
)
def test_predict_params(
monkeypatch,
ocr_engine: DocPreprocessor,
params: dict,
) -> None:
check_wrapper_simple_inference_param_forwarding(
monkeypatch,
ocr_engine,
"paddlex_pipeline",
"dummy_path",
params,
)

View File

@@ -0,0 +1,43 @@
import pytest
from paddleocr import DocUnderstanding
from ..testing_utils import (
TEST_DATA_DIR,
check_simple_inference_result,
check_wrapper_simple_inference_param_forwarding,
)
@pytest.fixture(scope="module")
def ocr_engine() -> DocUnderstanding:
return DocUnderstanding()
@pytest.mark.resource_intensive
@pytest.mark.parametrize(
"input",
[
{
"image": str(TEST_DATA_DIR / "medal_table.png"),
"query": "识别这份表格的内容",
},
{
"image": str(TEST_DATA_DIR / "table.jpg"),
"query": "识别这份表格的内容",
},
],
)
def test_predict(ocr_engine: DocUnderstanding, input: dict) -> None:
"""
Test PaddleOCR's doc understanding functionality.
Args:
ocr_engine: An instance of `DocUnderstanding`.
input: Input dict to be processed.
"""
result = ocr_engine.predict(input)
check_simple_inference_result(result)
res = result[0]
assert res["result"] is not None
assert isinstance(res["result"], str)

View File

@@ -0,0 +1,68 @@
import pytest
from paddleocr import FormulaRecognitionPipeline
from ..testing_utils import (
TEST_DATA_DIR,
check_simple_inference_result,
check_wrapper_simple_inference_param_forwarding,
)
@pytest.fixture(scope="module")
def formula_recognition_engine() -> FormulaRecognitionPipeline:
return FormulaRecognitionPipeline()
# TODO: Should we separate unit tests and integration tests?
@pytest.mark.parametrize(
"image_path",
[
TEST_DATA_DIR / "doc_with_formula.png",
],
)
def test_predict(
formula_recognition_engine: FormulaRecognitionPipeline, image_path: str
) -> None:
"""
Test FormulaRecognitionPipeline's formula_recognition functionality.
Args:
formula_recognition_engine: An instance of `FormulaRecognitionPipeline`.
image_path: Path to the image to be processed.
"""
result = formula_recognition_engine.predict(str(image_path))
check_simple_inference_result(result)
res = result[0]
assert isinstance(res["formula_res_list"], list)
assert len(res["formula_res_list"]) > 0
# TODO: Also check passing `None`
@pytest.mark.parametrize(
"params",
[
{"use_doc_orientation_classify": False},
{"use_doc_unwarping": False},
{"use_layout_detection": False},
{"layout_threshold": 0.5},
{"layout_nms": True},
{"layout_unclip_ratio": 1.5},
{"layout_merge_bboxes_mode": "large"},
],
)
def test_predict_params(
monkeypatch,
formula_recognition_engine: FormulaRecognitionPipeline,
params: dict,
) -> None:
check_wrapper_simple_inference_param_forwarding(
monkeypatch,
formula_recognition_engine,
"paddlex_pipeline",
"dummy_path",
params,
)
# TODO: Test init params

125
tests/pipelines/test_ocr.py Normal file
View File

@@ -0,0 +1,125 @@
import pytest
from paddleocr import PaddleOCR
from ..testing_utils import (
TEST_DATA_DIR,
check_simple_inference_result,
check_wrapper_simple_inference_param_forwarding,
)
@pytest.fixture(scope="module")
def ocr_engine() -> PaddleOCR:
return PaddleOCR()
# TODO: Should we separate unit tests and integration tests?
@pytest.mark.parametrize(
"image_path",
[
TEST_DATA_DIR / "table.jpg",
],
)
def test_predict(ocr_engine: PaddleOCR, image_path: str) -> None:
"""
Test PaddleOCR's OCR functionality.
Args:
ocr_engine: An instance of `PaddleOCR`.
image_path: Path to the image to be processed.
"""
result = ocr_engine.predict(str(image_path))
check_simple_inference_result(result)
res = result[0]
assert len(res["dt_polys"]) > 0
assert isinstance(res["rec_texts"], list)
assert len(res["rec_texts"]) > 0
for text in res["rec_texts"]:
assert isinstance(text, str)
# TODO: Also check passing `None`
@pytest.mark.parametrize(
"params",
[
{"use_doc_orientation_classify": False},
{"use_doc_unwarping": False},
{"use_textline_orientation": False},
{"text_det_limit_side_len": 640, "text_det_limit_type": "min"},
{"text_det_thresh": 0.5},
{"text_det_box_thresh": 0.3},
{"text_det_unclip_ratio": 3.0},
{"text_rec_score_thresh": 0.5},
],
)
def test_predict_params(
monkeypatch,
ocr_engine: PaddleOCR,
params: dict,
) -> None:
check_wrapper_simple_inference_param_forwarding(
monkeypatch,
ocr_engine,
"paddlex_pipeline",
"dummy_path",
params,
)
# TODO: Test init params
def test_lang_and_ocr_version():
ocr_engine = PaddleOCR(lang="ch", ocr_version="PP-OCRv5")
assert ocr_engine._params["text_detection_model_name"] == "PP-OCRv5_server_det"
assert ocr_engine._params["text_recognition_model_name"] == "PP-OCRv5_server_rec"
ocr_engine = PaddleOCR(lang="chinese_cht", ocr_version="PP-OCRv5")
assert ocr_engine._params["text_detection_model_name"] == "PP-OCRv5_server_det"
assert ocr_engine._params["text_recognition_model_name"] == "PP-OCRv5_server_rec"
ocr_engine = PaddleOCR(lang="en", ocr_version="PP-OCRv5")
assert ocr_engine._params["text_detection_model_name"] == "PP-OCRv5_server_det"
assert ocr_engine._params["text_recognition_model_name"] == "PP-OCRv5_server_rec"
ocr_engine = PaddleOCR(lang="japan", ocr_version="PP-OCRv5")
assert ocr_engine._params["text_detection_model_name"] == "PP-OCRv5_server_det"
assert ocr_engine._params["text_recognition_model_name"] == "PP-OCRv5_server_rec"
ocr_engine = PaddleOCR(lang="ch", ocr_version="PP-OCRv4")
assert ocr_engine._params["text_detection_model_name"] == "PP-OCRv4_mobile_det"
assert ocr_engine._params["text_recognition_model_name"] == "PP-OCRv4_mobile_rec"
ocr_engine = PaddleOCR(lang="en", ocr_version="PP-OCRv4")
assert ocr_engine._params["text_detection_model_name"] == "PP-OCRv4_mobile_det"
assert ocr_engine._params["text_recognition_model_name"] == "en_PP-OCRv4_mobile_rec"
ocr_engine = PaddleOCR(lang="ch", ocr_version="PP-OCRv3")
assert ocr_engine._params["text_detection_model_name"] == "PP-OCRv3_mobile_det"
assert ocr_engine._params["text_recognition_model_name"] == "PP-OCRv3_mobile_rec"
ocr_engine = PaddleOCR(lang="en", ocr_version="PP-OCRv3")
assert ocr_engine._params["text_detection_model_name"] == "PP-OCRv3_mobile_det"
assert ocr_engine._params["text_recognition_model_name"] == "en_PP-OCRv3_mobile_rec"
ocr_engine = PaddleOCR(lang="fr", ocr_version="PP-OCRv3")
assert ocr_engine._params["text_detection_model_name"] == "PP-OCRv3_mobile_det"
assert (
ocr_engine._params["text_recognition_model_name"] == "latin_PP-OCRv3_mobile_rec"
)
ocr_engine = PaddleOCR(lang="ar", ocr_version="PP-OCRv3")
assert ocr_engine._params["text_detection_model_name"] == "PP-OCRv3_mobile_det"
assert (
ocr_engine._params["text_recognition_model_name"]
== "arabic_PP-OCRv3_mobile_rec"
)
ocr_engine = PaddleOCR(lang="ru", ocr_version="PP-OCRv3")
assert ocr_engine._params["text_detection_model_name"] == "PP-OCRv3_mobile_det"
assert (
ocr_engine._params["text_recognition_model_name"]
== "cyrillic_PP-OCRv3_mobile_rec"
)
ocr_engine = PaddleOCR(lang="hi", ocr_version="PP-OCRv3")
assert ocr_engine._params["text_detection_model_name"] == "PP-OCRv3_mobile_det"
assert (
ocr_engine._params["text_recognition_model_name"]
== "devanagari_PP-OCRv3_mobile_rec"
)
ocr_engine = PaddleOCR(lang="japan", ocr_version="PP-OCRv3")
assert ocr_engine._params["text_detection_model_name"] == "PP-OCRv3_mobile_det"
assert (
ocr_engine._params["text_recognition_model_name"] == "japan_PP-OCRv3_mobile_rec"
)

View File

@@ -0,0 +1,80 @@
import pytest
from paddleocr import PPChatOCRv4Doc
from ..testing_utils import TEST_DATA_DIR
@pytest.fixture(scope="module")
def pp_chatocrv4_doc_pipeline():
return PPChatOCRv4Doc()
@pytest.mark.parametrize(
"image_path",
[
TEST_DATA_DIR / "doc_with_formula.png",
],
)
def test_visual_predict(pp_chatocrv4_doc_pipeline, image_path):
result = pp_chatocrv4_doc_pipeline.visual_predict(str(image_path))
assert result is not None
assert isinstance(result, list)
assert len(result) == 1
res = result[0]
assert isinstance(res, dict)
assert res.keys() == {"visual_info", "layout_parsing_result"}
assert isinstance(res["visual_info"], dict)
assert isinstance(res["layout_parsing_result"], dict)
@pytest.mark.parametrize(
"params",
[
{"use_doc_orientation_classify": False},
{"use_doc_unwarping": False},
{"use_table_recognition": False},
{"layout_threshold": 0.88},
{"layout_threshold": [0.45, 0.4]},
{"layout_threshold": {0: 0.45, 2: 0.48, 7: 0.4}},
{"layout_nms": False},
{"layout_unclip_ratio": 1.1},
{"layout_unclip_ratio": [1.2, 1.5]},
{"layout_unclip_ratio": {0: 1.2, 2: 1.5, 7: 1.8}},
{"layout_merge_bboxes_mode": "large"},
{"layout_merge_bboxes_mode": {0: "large", 2: "small", 7: "union"}},
{"text_det_limit_side_len": 640, "text_det_limit_type": "min"},
{"text_det_thresh": 0.5},
{"text_det_box_thresh": 0.3},
{"text_det_unclip_ratio": 3.0},
{"text_rec_score_thresh": 0.5},
],
)
def test_predict_params(
monkeypatch,
pp_chatocrv4_doc_pipeline,
params,
):
def _dummy_visual_predict(input, **params):
yield {"visual_info": {}, "layout_parsing_result": params}
monkeypatch.setattr(
pp_chatocrv4_doc_pipeline.paddlex_pipeline,
"visual_predict",
_dummy_visual_predict,
)
result = pp_chatocrv4_doc_pipeline.visual_predict(
input,
**params,
)
assert isinstance(result, list)
assert len(result) == 1
res = result[0]
res = res["layout_parsing_result"]
for k, v in params.items():
assert res[k] == v
# TODO: Test constructor and other methods

View File

@@ -0,0 +1,80 @@
import pytest
from paddleocr import PPDocTranslation
from ..testing_utils import TEST_DATA_DIR
@pytest.fixture(scope="module")
def pp_doctranslation_pipeline():
return PPDocTranslation()
@pytest.mark.parametrize(
"image_path",
[
TEST_DATA_DIR / "book.jpg",
],
)
def test_visual_predict(pp_doctranslation_pipeline, image_path):
result = pp_doctranslation_pipeline.visual_predict(str(image_path))
assert result is not None
assert isinstance(result, list)
assert len(result) == 1
res = result[0]
assert isinstance(res, dict)
assert res.keys() == {"layout_parsing_result"}
assert isinstance(res["layout_parsing_result"], dict)
@pytest.mark.parametrize(
"params",
[
{"use_doc_orientation_classify": False},
{"use_doc_unwarping": False},
{"use_table_recognition": False},
{"use_formula_recognition": False},
{"layout_threshold": 0.88},
{"layout_threshold": [0.45, 0.4]},
{"layout_threshold": {0: 0.45, 2: 0.48, 7: 0.4}},
{"layout_nms": False},
{"layout_unclip_ratio": 1.1},
{"layout_unclip_ratio": [1.2, 1.5]},
{"layout_unclip_ratio": {0: 1.2, 2: 1.5, 7: 1.8}},
{"layout_merge_bboxes_mode": "large"},
{"layout_merge_bboxes_mode": {0: "large", 2: "small", 7: "union"}},
{"text_det_limit_side_len": 640, "text_det_limit_type": "min"},
{"text_det_thresh": 0.5},
{"text_det_box_thresh": 0.3},
{"text_det_unclip_ratio": 3.0},
{"text_rec_score_thresh": 0.5},
],
)
def test_predict_params(
monkeypatch,
pp_doctranslation_pipeline,
params,
):
def _dummy_visual_predict(input, **params):
yield {"layout_parsing_result": params}
monkeypatch.setattr(
pp_doctranslation_pipeline.paddlex_pipeline,
"visual_predict",
_dummy_visual_predict,
)
result = pp_doctranslation_pipeline.visual_predict(
input,
**params,
)
assert isinstance(result, list)
assert len(result) == 1
res = result[0]
res = res["layout_parsing_result"]
for k, v in params.items():
assert res[k] == v
# TODO: Test constructor and other methods

View File

@@ -0,0 +1,71 @@
import pytest
from paddleocr import PPStructureV3
from ..testing_utils import (
TEST_DATA_DIR,
check_simple_inference_result,
check_wrapper_simple_inference_param_forwarding,
)
@pytest.fixture(scope="module")
def pp_structurev3_pipeline():
return PPStructureV3()
@pytest.mark.parametrize(
"image_path",
[
TEST_DATA_DIR / "doc_with_formula.png",
],
)
def test_visual_predict(pp_structurev3_pipeline, image_path):
result = pp_structurev3_pipeline.predict(str(image_path))
check_simple_inference_result(result)
res = result[0]
overall_ocr_res = res["overall_ocr_res"]
assert len(overall_ocr_res["dt_polys"]) > 0
assert len(overall_ocr_res["rec_texts"]) > 0
assert len(overall_ocr_res["rec_polys"]) > 0
assert len(overall_ocr_res["rec_boxes"]) > 0
@pytest.mark.parametrize(
"params",
[
{"use_doc_orientation_classify": False},
{"use_doc_unwarping": False},
{"use_table_recognition": False},
{"use_formula_recognition": False},
{"layout_threshold": 0.88},
{"layout_threshold": [0.45, 0.4]},
{"layout_threshold": {0: 0.45, 2: 0.48, 7: 0.4}},
{"layout_nms": False},
{"layout_unclip_ratio": 1.1},
{"layout_unclip_ratio": [1.2, 1.5]},
{"layout_unclip_ratio": {0: 1.2, 2: 1.5, 7: 1.8}},
{"layout_merge_bboxes_mode": "large"},
{"layout_merge_bboxes_mode": {0: "large", 2: "small", 7: "union"}},
{"text_det_limit_side_len": 640, "text_det_limit_type": "min"},
{"text_det_thresh": 0.5},
{"text_det_box_thresh": 0.3},
{"text_det_unclip_ratio": 3.0},
{"text_rec_score_thresh": 0.5},
],
)
def test_predict_params(
monkeypatch,
pp_structurev3_pipeline,
params,
):
check_wrapper_simple_inference_param_forwarding(
monkeypatch,
pp_structurev3_pipeline,
"paddlex_pipeline",
"dummy_path",
params,
)
# TODO: Test constructor and other methods

View File

@@ -0,0 +1,70 @@
import pytest
from paddleocr import SealRecognition
from ..testing_utils import (
TEST_DATA_DIR,
check_simple_inference_result,
check_wrapper_simple_inference_param_forwarding,
)
@pytest.fixture(scope="module")
def ocr_engine() -> SealRecognition:
return SealRecognition()
@pytest.mark.parametrize(
"image_path",
[
TEST_DATA_DIR / "seal.png",
],
)
def test_predict(ocr_engine: SealRecognition, image_path: str) -> None:
"""
Test PaddleOCR's seal recognition functionality.
Args:
ocr_engine: An instance of `SealRecognition`.
image_path: Path to the image to be processed.
"""
result = ocr_engine.predict(str(image_path))
check_simple_inference_result(result)
res = result[0]["seal_res_list"][0]
assert len(res["dt_polys"]) > 0
assert isinstance(res["rec_texts"], list)
assert len(res["rec_texts"]) > 0
for text in res["rec_texts"]:
assert isinstance(text, str)
@pytest.mark.parametrize(
"params",
[
{"use_doc_orientation_classify": False, "use_doc_unwarping": False},
{"use_layout_detection": False},
{"layout_det_res": None},
{"layout_threshold": 0.5},
{"layout_nms": False},
{"layout_unclip_ratio": 1.0},
{"layout_merge_bboxes_mode": "large"},
{"seal_det_limit_side_len": 736},
{"seal_det_limit_type": "min"},
{"seal_det_thresh": 0.5},
{"seal_det_box_thresh": 0.6},
{"seal_det_unclip_ratio": 0.5},
{"seal_rec_score_thresh": 0.05},
],
)
def test_predict_params(
monkeypatch,
ocr_engine: SealRecognition,
params: dict,
) -> None:
check_wrapper_simple_inference_param_forwarding(
monkeypatch,
ocr_engine,
"paddlex_pipeline",
"dummy_path",
params,
)

View File

@@ -0,0 +1,64 @@
import pytest
from paddleocr import TableRecognitionPipelineV2
from ..testing_utils import (
TEST_DATA_DIR,
check_simple_inference_result,
check_wrapper_simple_inference_param_forwarding,
)
@pytest.fixture(scope="module")
def table_recognition_v2_pipeline():
return TableRecognitionPipelineV2()
@pytest.mark.parametrize(
"image_path",
[
TEST_DATA_DIR / "table.jpg",
],
)
def test_visual_predict(table_recognition_v2_pipeline, image_path):
result = table_recognition_v2_pipeline.predict(
str(image_path), use_doc_orientation_classify=False, use_doc_unwarping=False
)
check_simple_inference_result(result)
res = result[0]
assert len(res["table_res_list"]) > 0
assert isinstance(res["table_res_list"][0], dict)
assert len(res["table_res_list"][0]["cell_box_list"]) > 0
assert isinstance(res["table_res_list"][0]["pred_html"], str)
assert isinstance(res["table_res_list"][0]["table_ocr_pred"], dict)
@pytest.mark.parametrize(
"params",
[
{"use_doc_orientation_classify": False},
{"use_doc_unwarping": False},
{"use_layout_detection": False},
{"use_ocr_model": False},
{"text_det_limit_side_len": 640, "text_det_limit_type": "min"},
{"text_det_thresh": 0.5},
{"text_det_box_thresh": 0.3},
{"text_det_unclip_ratio": 3.0},
{"text_rec_score_thresh": 0.5},
],
)
def test_predict_params(
monkeypatch,
table_recognition_v2_pipeline,
params,
):
check_wrapper_simple_inference_param_forwarding(
monkeypatch,
table_recognition_v2_pipeline,
"paddlex_pipeline",
"dummy_path",
params,
)
# TODO: Test constructor and other methods

View File

@@ -0,0 +1,64 @@
import os
import sys
import numpy as np
import paddle
import pytest
current_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.abspath(os.path.join(current_dir, "..")))
from ppocr.postprocess.cls_postprocess import ClsPostProcess
# Fixtures for common test inputs
@pytest.fixture
def preds_tensor():
return paddle.to_tensor(np.array([[0.1, 0.7, 0.2], [0.3, 0.3, 0.4]]))
@pytest.fixture
def label_list():
return {0: "class0", 1: "class1", 2: "class2"}
# Parameterize tests to cover multiple scenarios
@pytest.mark.parametrize(
"label_list, expected",
[
({0: "class0", 1: "class1", 2: "class2"}, [("class1", 0.7), ("class2", 0.4)]),
(None, [(1, 0.7), (2, 0.4)]),
],
)
def test_cls_post_process_with_and_without_label_list(
preds_tensor, label_list, expected
):
post_process = ClsPostProcess(label_list=label_list)
result = post_process(preds_tensor)
assert isinstance(result, list), "Result should be a list"
assert result == expected, f"Expected {expected}, got {result}"
# Test with a key in the prediction dictionary
def test_cls_post_process_with_key(preds_tensor, label_list):
preds_dict = {"key": preds_tensor}
post_process = ClsPostProcess(label_list=label_list, key="key")
result = post_process(preds_dict)
expected = [("class1", 0.7), ("class2", 0.4)]
assert isinstance(result, list), "Result should be a list"
assert result == expected, f"Expected {expected}, got {result}"
# Test with label input
def test_cls_post_process_with_label(preds_tensor, label_list):
labels = [2, 0]
post_process = ClsPostProcess(label_list=label_list)
result, label_result = post_process(preds_tensor, labels)
expected_result = [("class1", 0.7), ("class2", 0.4)]
expected_label_result = [("class2", 1.0), ("class0", 1.0)]
assert isinstance(result, list), "Result should be a list"
assert result == expected_result, f"Expected {expected_result}, got {result}"
assert isinstance(label_result, list), "Label result should be a list"
assert (
label_result == expected_label_result
), f"Expected {expected_label_result}, got {label_result}"

BIN
tests/test_files/book.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 482 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

BIN
tests/test_files/seal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

BIN
tests/test_files/table.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

195
tests/test_formula_model.py Normal file
View File

@@ -0,0 +1,195 @@
import sys
import os
from pathlib import Path
from typing import Any
import paddle
import pytest
current_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.abspath(os.path.join(current_dir, "..")))
from ppocr.modeling.backbones.rec_donut_swin import DonutSwinModel, DonutSwinModelOutput
from ppocr.modeling.backbones.rec_pphgnetv2 import PPHGNetV2_B4_Formula
from ppocr.modeling.backbones.rec_vary_vit import Vary_VIT_B_Formula
from ppocr.modeling.heads.rec_unimernet_head import UniMERNetHead
from ppocr.modeling.heads.rec_ppformulanet_head import PPFormulaNet_Head
@pytest.fixture
def sample_image():
return paddle.randn([1, 1, 192, 672])
@pytest.fixture
def sample_image_ppformulanet_s():
return paddle.randn([1, 1, 384, 384])
@pytest.fixture
def sample_image_ppformulanet_l():
return paddle.randn([1, 1, 768, 768])
@pytest.fixture
def encoder_feat():
encoded_feat = paddle.randn([1, 126, 1024])
return DonutSwinModelOutput(
last_hidden_state=encoded_feat,
)
@pytest.fixture
def encoder_feat_ppformulanet_s():
encoded_feat = paddle.randn([1, 144, 2048])
return DonutSwinModelOutput(
last_hidden_state=encoded_feat,
)
@pytest.fixture
def encoder_feat_ppformulanet_l():
encoded_feat = paddle.randn([1, 144, 1024])
return DonutSwinModelOutput(
last_hidden_state=encoded_feat,
)
def test_unimernet_backbone(sample_image):
"""
Test UniMERNet backbone.
Args:
sample_image: sample image to be processed.
"""
backbone = DonutSwinModel(
hidden_size=1024,
num_layers=4,
num_heads=[4, 8, 16, 32],
add_pooling_layer=True,
use_mask_token=False,
)
backbone.eval()
with paddle.no_grad():
result = backbone(sample_image)
encoder_feat = result[0]
assert encoder_feat.shape == [1, 126, 1024]
def test_unimernet_head(encoder_feat):
"""
Test UniMERNet head.
Args:
encoder_feat: encoder feature from unimernet backbone.
"""
head = UniMERNetHead(
max_new_tokens=5,
decoder_start_token_id=0,
temperature=0.2,
do_sample=False,
top_p=0.95,
encoder_hidden_size=1024,
is_export=False,
length_aware=True,
)
head.eval()
with paddle.no_grad():
result = head(encoder_feat)
assert result.shape == [1, 6]
def test_ppformulanet_s_backbone(sample_image_ppformulanet_s):
"""
Test PP-FormulaNet-S backbone.
Args:
sample_image_ppformulanet_s: sample image to be processed.
"""
backbone = PPHGNetV2_B4_Formula(
class_num=1024,
)
backbone.eval()
with paddle.no_grad():
result = backbone(sample_image_ppformulanet_s)
encoder_feat = result[0]
assert encoder_feat.shape == [1, 144, 2048]
def test_ppformulanet_s_head(encoder_feat_ppformulanet_s):
"""
Test PP-FormulaNet-S head.
Args:
encoder_feat_ppformulanet_s: encoder feature from PP-FormulaNet-S backbone.
"""
head = PPFormulaNet_Head(
max_new_tokens=6,
decoder_start_token_id=0,
decoder_ffn_dim=1536,
decoder_hidden_size=384,
decoder_layers=2,
temperature=0.2,
do_sample=False,
top_p=0.95,
encoder_hidden_size=2048,
is_export=False,
length_aware=True,
use_parallel=True,
parallel_step=3,
)
head.eval()
with paddle.no_grad():
result = head(encoder_feat_ppformulanet_s)
assert result.shape == [1, 9]
def test_ppformulanet_l_backbone(sample_image_ppformulanet_l):
"""
Test PP-FormulaNet-L backbone.
Args:
sample_image_ppformulanet_l: sample image to be processed.
"""
backbone = Vary_VIT_B_Formula(
image_size=768,
encoder_embed_dim=768,
encoder_depth=12,
encoder_num_heads=12,
encoder_global_attn_indexes=[2, 5, 8, 11],
)
backbone.eval()
with paddle.no_grad():
result = backbone(sample_image_ppformulanet_l)
encoder_feat = result[0]
assert encoder_feat.shape == [1, 144, 1024]
def test_ppformulanet_l_head(encoder_feat_ppformulanet_l):
"""
Test PP-FormulaNet-L head.
Args:
encoder_feat_ppformulanet_l: encoder feature from PP-FormulaNet-L Head.
"""
head = PPFormulaNet_Head(
max_new_tokens=6,
decoder_start_token_id=0,
decoder_ffn_dim=2048,
decoder_hidden_size=512,
decoder_layers=8,
temperature=0.2,
do_sample=False,
top_p=0.95,
encoder_hidden_size=1024,
is_export=False,
length_aware=False,
use_parallel=False,
parallel_step=0,
)
head.eval()
with paddle.no_grad():
result = head(encoder_feat_ppformulanet_l)
assert result.shape == [1, 7]

181
tests/test_iaa_augment.py Normal file
View File

@@ -0,0 +1,181 @@
import os
import sys
import pytest
import numpy as np
import random
current_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.abspath(os.path.join(current_dir, "..")))
from ppocr.data.imaug.iaa_augment import IaaAugment
# Set a fixed random seed to ensure test reproducibility
np.random.seed(42)
random.seed(42)
# Fixture to provide a sample image for tests
@pytest.fixture
def sample_image():
# Create a 100x100 pixel dummy image with 3 color channels (RGB)
return np.random.randint(0, 256, (100, 100, 3), dtype=np.uint8)
# Fixture to provide sample polygons for tests
@pytest.fixture
def sample_polys():
# Create dummy polygons as sample data
polys = [
np.array([[10, 10], [20, 10], [20, 20], [10, 20]], dtype=np.float32),
np.array([[30, 30], [40, 30], [40, 40], [30, 40]], dtype=np.float32),
]
return polys
# Helper function to create a data dictionary for testing
def create_data(sample_image, sample_polys):
return {
"image": sample_image.copy(),
"polys": [poly.copy() for poly in sample_polys],
}
# Test the default behavior of the augmenter (without specified arguments)
def test_iaa_augment_default(sample_image, sample_polys):
data = create_data(sample_image, sample_polys)
augmenter = IaaAugment()
transformed_data = augmenter(data)
# Check the data types and structure of the transformed image and polygons
assert isinstance(
transformed_data["image"], np.ndarray
), "Image should be a numpy array"
assert isinstance(
transformed_data["polys"], np.ndarray
), "Polys should be a numpy array"
assert transformed_data["image"].ndim == 3, "Image should be 3-dimensional"
# Verify that the polygons have been transformed
polys_changed = any(
not np.allclose(orig_poly, trans_poly)
for orig_poly, trans_poly in zip(sample_polys, transformed_data["polys"])
)
assert polys_changed, "Polygons should have been transformed"
# Test the augmenter with empty arguments, meaning no transformations should occur
def test_iaa_augment_none(sample_image, sample_polys):
data = create_data(sample_image, sample_polys)
augmenter = IaaAugment(augmenter_args=[])
transformed_data = augmenter(data)
# Check that the image and polygons remain unchanged
assert np.array_equal(
data["image"], transformed_data["image"]
), "Image should be unchanged"
for orig_poly, transformed_poly in zip(data["polys"], transformed_data["polys"]):
assert np.array_equal(
orig_poly, transformed_poly
), "Polygons should be unchanged"
# Parameterized test to check various augmenter arguments and expected image shapes
@pytest.mark.parametrize(
"augmenter_args, expected_shape",
[
([], (100, 100, 3)),
([{"type": "Resize", "args": {"size": [0.5, 0.5]}}], (50, 50, 3)),
([{"type": "Resize", "args": {"size": [2.0, 2.0]}}], (200, 200, 3)),
],
)
def test_iaa_augment_resize(sample_image, sample_polys, augmenter_args, expected_shape):
data = create_data(sample_image, sample_polys)
augmenter = IaaAugment(augmenter_args=augmenter_args)
transformed_data = augmenter(data)
# Verify that the transformed image has the expected shape
assert (
transformed_data["image"].shape == expected_shape
), f"Expected image shape {expected_shape}, got {transformed_data['image'].shape}"
# Test custom augmenter arguments with specific transformations
def test_iaa_augment_custom(sample_image, sample_polys):
data = create_data(sample_image, sample_polys)
augmenter_args = [
{"type": "Affine", "args": {"rotate": [45, 45]}}, # Apply 45-degree rotation
{"type": "Resize", "args": {"size": [0.5, 0.5]}},
]
augmenter = IaaAugment(augmenter_args=augmenter_args)
transformed_data = augmenter(data)
# Check the expected image dimensions after resizing
expected_height = int(sample_image.shape[0] * 0.5)
expected_width = int(sample_image.shape[1] * 0.5)
assert (
transformed_data["image"].shape[0] == expected_height
), "Image height should be scaled by 0.5"
assert (
transformed_data["image"].shape[1] == expected_width
), "Image width should be scaled by 0.5"
# Verify that the polygons have been transformed
polys_changed = any(
not np.allclose(orig_poly, trans_poly)
for orig_poly, trans_poly in zip(sample_polys, transformed_data["polys"])
)
assert polys_changed, "Polygons should have been transformed"
# Test that an unknown transformation type raises an AttributeError
def test_iaa_augment_unknown_transform():
augmenter_args = [{"type": "UnknownTransform", "args": {}}]
with pytest.raises(AttributeError):
IaaAugment(augmenter_args=augmenter_args)
# Test that an invalid resize size parameter raises a ValueError
def test_iaa_augment_invalid_resize_size(sample_image, sample_polys):
augmenter_args = [{"type": "Resize", "args": {"size": "invalid_size"}}]
with pytest.raises(ValueError) as exc_info:
IaaAugment(augmenter_args=augmenter_args)
assert "'size' must be a list or tuple of two numbers" in str(exc_info.value)
# Test that polygons are transformed as expected
def test_iaa_augment_polys_transformation(sample_image, sample_polys):
data = create_data(sample_image, sample_polys)
augmenter_args = [
{"type": "Affine", "args": {"rotate": [90, 90]}}, # Apply 90-degree rotation
]
augmenter = IaaAugment(augmenter_args=augmenter_args)
transformed_data = augmenter(data)
# Verify that the polygons have been transformed
polys_changed = any(
not np.allclose(orig_poly, trans_poly)
for orig_poly, trans_poly in zip(sample_polys, transformed_data["polys"])
)
assert polys_changed, "Polygons should have been transformed"
# Test multiple transformations applied to the augmenter
def test_iaa_augment_multiple_transforms(sample_image, sample_polys):
augmenter_args = [
{"type": "Fliplr", "args": {"p": 1.0}}, # Always apply horizontal flip
{"type": "Affine", "args": {"shear": 10}},
]
data = create_data(sample_image, sample_polys)
augmenter = IaaAugment(augmenter_args=augmenter_args)
transformed_data = augmenter(data)
# Ensure the image has been transformed
images_different = not np.array_equal(transformed_data["image"], sample_image)
assert images_different, "Image should be transformed"
# Ensure the polygons have been transformed
polys_changed = any(
not np.allclose(orig_poly, trans_poly)
for orig_poly, trans_poly in zip(sample_polys, transformed_data["polys"])
)
assert polys_changed, "Polygons should have been transformed"

View File

36
tests/testing_utils.py Normal file
View File

@@ -0,0 +1,36 @@
from pathlib import Path
TEST_DATA_DIR = Path(__file__).parent / "test_files"
def check_simple_inference_result(result, *, expected_length=1):
assert result is not None
assert isinstance(result, list)
assert len(result) == expected_length
for res in result:
assert isinstance(res, dict)
def check_wrapper_simple_inference_param_forwarding(
monkeypatch,
wrapper,
wrapped_obj_attr_name,
input,
params,
):
def _dummy_predict(input, **params):
yield params
monkeypatch.setattr(
getattr(wrapper, wrapped_obj_attr_name), "predict", _dummy_predict
)
result = getattr(wrapper, "predict")(
input,
**params,
)
assert isinstance(result, list)
assert len(result) == 1
for k, v in params.items():
assert result[0][k] == v