diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/codestyle.yml similarity index 69% rename from .github/workflows/pre-commit.yml rename to .github/workflows/codestyle.yml index 55dd8360e1..f49912f42a 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/codestyle.yml @@ -1,4 +1,4 @@ -name: pre-commit +name: PaddleOCR Code Style Check on: pull_request: @@ -6,11 +6,13 @@ on: branches: ['main', 'release/*'] jobs: - pre-commit: + check-code-style: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v3 + - uses: actions/checkout@v4 + with: + ref: ${{ github.ref }} + - uses: actions/setup-python@v5 with: python-version: '3.10' # Install Dependencies for Python diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml new file mode 100644 index 0000000000..04333130f7 --- /dev/null +++ b/.github/workflows/tests.yaml @@ -0,0 +1,30 @@ +name: PaddleOCR PR Tests + +on: + push: + pull_request: + branches: ["main", "release/*"] + +permissions: + contents: read + +jobs: + test-pr: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pytest + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + pip install "paddlepaddle==2.5" requests + pip install -e . + - name: Test with pytest + run: | + pytest tests/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5cc16ba237..a94ef86abd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -35,3 +35,16 @@ repos: hooks: - id: black files: (.*\.(py|pyi|bzl)|BUILD|.*\.BUILD|WORKSPACE)$ + +# Flake8 +- repo: https://github.com/pycqa/flake8 + rev: 7.0.0 + hooks: + - id: flake8 + args: + - --count + - --select=E9,F63,F7,F82 + - --show-source + - --statistics + exclude: ^benchmark/|^test_tipc/ + diff --git a/benchmark/PaddleOCR_DBNet/data_loader/modules/augment.py b/benchmark/PaddleOCR_DBNet/data_loader/modules/augment.py index d2edd93d7a..bd0e483c8b 100644 --- a/benchmark/PaddleOCR_DBNet/data_loader/modules/augment.py +++ b/benchmark/PaddleOCR_DBNet/data_loader/modules/augment.py @@ -25,7 +25,7 @@ def __call__(self, data: dict): return data data["img"] = ( random_noise(data["img"], mode="gaussian", clip=True) * 255 - ).astype(im.dtype) + ).astype(data["img"].dtype) return data diff --git a/deploy/hubserving/kie_ser/module.py b/deploy/hubserving/kie_ser/module.py index 2c046be07b..fa95a91f76 100644 --- a/deploy/hubserving/kie_ser/module.py +++ b/deploy/hubserving/kie_ser/module.py @@ -142,7 +142,7 @@ def serving_method(self, images, **kwargs): if __name__ == "__main__": - ocr = OCRSystem() + ocr = KIESer() ocr._initialize() image_path = [ "./doc/imgs/11.jpg", diff --git a/deploy/hubserving/kie_ser_re/module.py b/deploy/hubserving/kie_ser_re/module.py index 4f2bc4479c..5e30a51a7e 100644 --- a/deploy/hubserving/kie_ser_re/module.py +++ b/deploy/hubserving/kie_ser_re/module.py @@ -144,7 +144,7 @@ def serving_method(self, images, **kwargs): if __name__ == "__main__": - ocr = OCRSystem() + ocr = KIESerRE() ocr._initialize() image_path = [ "./doc/imgs/11.jpg", diff --git a/ppocr/data/imaug/label_ops.py b/ppocr/data/imaug/label_ops.py index 39e413b2d2..58c6610f8c 100644 --- a/ppocr/data/imaug/label_ops.py +++ b/ppocr/data/imaug/label_ops.py @@ -841,11 +841,11 @@ def __call__(self, data): return data def xyxyxyxy2xywh(self, boxes): - new_bboxes = np.zeros([len(bboxes), 4]) - new_bboxes[:, 0] = bboxes[:, 0::2].min() # x1 - new_bboxes[:, 1] = bboxes[:, 1::2].min() # y1 - new_bboxes[:, 2] = bboxes[:, 0::2].max() - new_bboxes[:, 0] # w - new_bboxes[:, 3] = bboxes[:, 1::2].max() - new_bboxes[:, 1] # h + new_bboxes = np.zeros([len(boxes), 4]) + new_bboxes[:, 0] = boxes[:, 0::2].min() # x1 + new_bboxes[:, 1] = boxes[:, 1::2].min() # y1 + new_bboxes[:, 2] = boxes[:, 0::2].max() - new_bboxes[:, 0] # w + new_bboxes[:, 3] = boxes[:, 1::2].max() - new_bboxes[:, 1] # h return new_bboxes def xyxy2xywh(self, bboxes): diff --git a/ppocr/losses/distillation_loss.py b/ppocr/losses/distillation_loss.py index 4d0f751c5f..6313f8d8ea 100644 --- a/ppocr/losses/distillation_loss.py +++ b/ppocr/losses/distillation_loss.py @@ -1184,7 +1184,9 @@ def forward(self, predicts, batch): loss = super().forward(out1, out2, ctc_label) if isinstance(loss, dict): for key in loss: - loss_dict["{}_{}_{}".format(self.name, model_name, idx)] = loss[key] + loss_dict[ + "{}_{}_{}".format(self.name, self.model_name_pairs, idx) + ] = loss[key] else: loss_dict["{}_{}".format(self.name, idx)] = loss return loss_dict diff --git a/ppocr/metrics/vqa_token_re_metric.py b/ppocr/metrics/vqa_token_re_metric.py index d39917000f..8c85be5763 100644 --- a/ppocr/metrics/vqa_token_re_metric.py +++ b/ppocr/metrics/vqa_token_re_metric.py @@ -19,7 +19,7 @@ import numpy as np import paddle -__all__ = ["KIEMetric"] +__all__ = ["VQAReTokenMetric"] class VQAReTokenMetric(object): diff --git a/ppocr/metrics/vqa_token_ser_metric.py b/ppocr/metrics/vqa_token_ser_metric.py index b6033c3ae5..3afcb0518b 100644 --- a/ppocr/metrics/vqa_token_ser_metric.py +++ b/ppocr/metrics/vqa_token_ser_metric.py @@ -19,7 +19,7 @@ import numpy as np import paddle -__all__ = ["KIEMetric"] +__all__ = ["VQASerTokenMetric"] class VQASerTokenMetric(object): diff --git a/ppocr/modeling/backbones/rec_efficientb3_pren.py b/ppocr/modeling/backbones/rec_efficientb3_pren.py index d153ad6d87..916a090e23 100644 --- a/ppocr/modeling/backbones/rec_efficientb3_pren.py +++ b/ppocr/modeling/backbones/rec_efficientb3_pren.py @@ -27,7 +27,7 @@ import paddle.nn as nn import paddle.nn.functional as F -__all__ = ["EfficientNetb3"] +__all__ = ["EfficientNetb3_PREN"] GlobalParams = collections.namedtuple( "GlobalParams", diff --git a/ppocr/modeling/heads/rec_aster_head.py b/ppocr/modeling/heads/rec_aster_head.py index ba0acaeebe..dbef77f68b 100644 --- a/ppocr/modeling/heads/rec_aster_head.py +++ b/ppocr/modeling/heads/rec_aster_head.py @@ -132,7 +132,7 @@ def sample(self, x): # Decoder state = paddle.zeros([1, batch_size, self.sDim]) - predicted_ids, predicted_scores = [], [] + predicted_ids, predicted_scores, predicted = [], [], None for i in range(self.max_len_labels): if i == 0: y_prev = paddle.full(shape=[batch_size], fill_value=self.num_classes) diff --git a/ppocr/utils/loggers/wandb_logger.py b/ppocr/utils/loggers/wandb_logger.py index 83596d86d1..3b528b3fa9 100644 --- a/ppocr/utils/loggers/wandb_logger.py +++ b/ppocr/utils/loggers/wandb_logger.py @@ -1,5 +1,8 @@ import os from .base_logger import BaseLogger +from ppocr.utils.logging import get_logger + +logger = get_logger() class WandbLogger(BaseLogger): @@ -11,7 +14,7 @@ def __init__( entity=None, save_dir=None, config=None, - **kwargs + **kwargs, ): try: import wandb diff --git a/tests/test_paddleocr_api.py b/tests/test_paddleocr_api.py new file mode 100644 index 0000000000..0af794d2b8 --- /dev/null +++ b/tests/test_paddleocr_api.py @@ -0,0 +1,116 @@ +from typing import Any + +import pytest +from paddleocr import PaddleOCR, PPStructure + + +# Test image paths +IMAGE_PATHS_OCR = ["./doc/imgs_en/254.jpg", "./doc/imgs_en/img_10.jpg"] +IMAGE_PATHS_STRUCTURE = [ + "./ppstructure/docs/table/layout.jpg", + "./ppstructure/docs/table/1.png", +] + + +@pytest.fixture(params=["en", "ch"]) +def ocr_engine(request: Any) -> PaddleOCR: + """ + Initialize PaddleOCR engine with different languages. + + Args: + request: pytest fixture request object. + + Returns: + An instance of PaddleOCR. + """ + return PaddleOCR(lang=request.param) + + +def test_ocr_initialization(ocr_engine: PaddleOCR) -> None: + """ + Test PaddleOCR initialization. + + Args: + ocr_engine: An instance of PaddleOCR. + """ + assert ocr_engine is not None + + +@pytest.mark.parametrize("image_path", IMAGE_PATHS_OCR) +def test_ocr_function(ocr_engine: PaddleOCR, image_path: str) -> None: + """ + Test PaddleOCR OCR functionality with different images. + + Args: + ocr_engine: An instance of PaddleOCR. + image_path: Path to the image to be processed. + """ + result = ocr_engine.ocr(image_path) + assert result is not None + assert isinstance(result, list) + + +@pytest.mark.parametrize("image_path", IMAGE_PATHS_OCR) +def test_ocr_det_only(ocr_engine: PaddleOCR, image_path: str) -> None: + """ + Test PaddleOCR OCR functionality with detection only. + + Args: + ocr_engine: An instance of PaddleOCR. + image_path: Path to the image to be processed. + """ + result = ocr_engine.ocr(image_path, det=True, rec=False) + assert result is not None + assert isinstance(result, list) + + +@pytest.mark.parametrize("image_path", IMAGE_PATHS_OCR) +def test_ocr_rec_only(ocr_engine: PaddleOCR, image_path: str) -> None: + """ + Test PaddleOCR OCR functionality with recognition only. + + Args: + ocr_engine: An instance of PaddleOCR. + image_path: Path to the image to be processed. + """ + result = ocr_engine.ocr(image_path, det=False, rec=True) + assert result is not None + assert isinstance(result, list) + + +@pytest.fixture(params=["en", "ch"]) +def structure_engine(request: Any) -> PPStructure: + """ + Initialize PPStructure engine with different languages. + + Args: + request: pytest fixture request object. + + Returns: + An instance of PPStructure. + """ + return PPStructure(lang=request.param) + + +def test_structure_initialization(structure_engine: PPStructure) -> None: + """ + Test PPStructure initialization. + + Args: + structure_engine: An instance of PPStructure. + """ + assert structure_engine is not None + + +@pytest.mark.parametrize("image_path", IMAGE_PATHS_STRUCTURE) +def test_structure_function(structure_engine: PPStructure, image_path: str) -> None: + """ + Test PPStructure structure analysis functionality with different images. + + Args: + structure_engine: An instance of PPStructure. + image_path: Path to the image to be processed. + """ + result = structure_engine(image_path) + assert result is not None + assert isinstance(result, list)