diff --git a/.github/workflows/gen_whl_to_pypi_rapid_layout.yml b/.github/workflows/gen_whl_to_pypi_rapid_layout.yml deleted file mode 100644 index 62d9374..0000000 --- a/.github/workflows/gen_whl_to_pypi_rapid_layout.yml +++ /dev/null @@ -1,81 +0,0 @@ -name: Push rapidocr_layout to pypi - -on: - push: - branches: [ main ] - paths: - - 'python/rapid_structure/rapid_layout/**' - - 'docs/doc_whl_rapid_layout.md' - - 'python/rapid_structure/setup_layout.py' - - '.github/workflows/gen_whl_to_pypi_rapid_layout.yml' - - 'python/rapid_structure/tests/test_layout.py' - -env: - RESOURCES_URL: https://github.com/RapidAI/RapidOCR/releases/download/v1.1.0/rapid_layout_models.zip - -jobs: - UnitTesting: - runs-on: ubuntu-latest - steps: - - name: Pull latest code - uses: actions/checkout@v3 - - - name: Set up Python 3.7 - uses: actions/setup-python@v4 - with: - python-version: '3.7' - architecture: 'x64' - - - name: Display Python version - run: python -c "import sys; print(sys.version)" - - - name: Unit testings - run: | - wget $RESOURCES_URL - ZIP_NAME=${RESOURCES_URL##*/} - DIR_NAME=${ZIP_NAME%.*} - unzip $DIR_NAME - cp $DIR_NAME/*.onnx python/rapid_structure/rapid_layout/models/ - cd python/rapid_structure - pip install -r requirements.txt - pytest tests/test_layout.py - - GenerateWHL_PushPyPi: - needs: UnitTesting - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - - name: Set SSH Environment - env: - DEPLOY_KEYS: ${{ secrets.GEN_PYTHON_SDK }} - run: | - mkdir -p ~/.ssh/ - echo "$DEPLOY_KEYS" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - chmod 700 ~/.ssh && chmod 600 ~/.ssh/* - - - name: Run setup - run: | - cd python/rapid_structure - pip install -r requirements.txt - wget $RESOURCES_URL - ZIP_NAME=${RESOURCES_URL##*/} - DIR_NAME=${ZIP_NAME%.*} - unzip $ZIP_NAME - mv $DIR_NAME/*.onnx rapid_layout/models/ - python setup_layout.py bdist_wheel ${{ github.event.head_commit.message }} - - # - name: Publish distribution 📦 to Test PyPI - # uses: pypa/gh-action-pypi-publish@v1.5.0 - # with: - # password: ${{ secrets.TEST_PYPI_API_TOKEN }} - # repository_url: https://test.pypi.org/legacy/ - # packages_dir: dist/ - - - name: Publish distribution 📦 to PyPI - uses: pypa/gh-action-pypi-publish@v1.5.0 - with: - password: ${{ secrets.RAPIDOCR_OPENVINO }} - packages_dir: python/rapid_structure/dist/ diff --git a/.github/workflows/gen_whl_to_pypi_rapid_orientation.yml b/.github/workflows/gen_whl_to_pypi_rapid_orientation.yml deleted file mode 100644 index cf6993f..0000000 --- a/.github/workflows/gen_whl_to_pypi_rapid_orientation.yml +++ /dev/null @@ -1,86 +0,0 @@ -name: Push rapidocr_orientation to pypi - -on: - push: - branches: [ main ] - paths: - - 'python/rapid_structure/rapid_orientation/**' - - 'docs/doc_whl_rapid_orientation.md' - - 'python/rapid_structure/setup_orientation.py' - - '.github/workflows/gen_whl_to_pypi_rapid_orientation.yml' - - 'python/rapid_structure/tests/test_orientation.py' - -env: - RESOURCES_URL: https://github.com/RapidAI/RapidOCR/releases/download/v1.1.0/rapid_orientation_models.zip - -jobs: - UnitTesting: - runs-on: ubuntu-latest - steps: - - name: Pull latest code - uses: actions/checkout@v3 - - - name: Set up Python 3.7 - uses: actions/setup-python@v4 - with: - python-version: '3.7' - architecture: 'x64' - - - name: Unit testings - run: | - wget $RESOURCES_URL - ZIP_NAME=${RESOURCES_URL##*/} - DIR_NAME=${ZIP_NAME%.*} - unzip $ZIP_NAME - rm python/rapid_structure/rapid_orientation/models/.gitkeep - mv $DIR_NAME/*.onnx python/rapid_structure/rapid_orientation/models/ - cd python/rapid_structure - pip install -r requirements.txt - cd tests - pytest test_orientation.py - - GenerateWHL_PushPyPi: - needs: UnitTesting - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - - name: Set up Python 3.7 - uses: actions/setup-python@v4 - with: - python-version: '3.7' - architecture: 'x64' - - - name: Set SSH Environment - env: - DEPLOY_KEYS: ${{ secrets.GEN_PYTHON_SDK }} - run: | - mkdir -p ~/.ssh/ - echo "$DEPLOY_KEYS" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - chmod 700 ~/.ssh && chmod 600 ~/.ssh/* - - - name: Run setup - run: | - cd python/rapid_structure - pip install -r requirements.txt - wget $RESOURCES_URL - ZIP_NAME=${RESOURCES_URL##*/} - DIR_NAME=${ZIP_NAME%.*} - unzip $ZIP_NAME - mv $DIR_NAME/*.onnx rapid_orientation/models/ - python setup_orientation.py bdist_wheel ${{ github.event.head_commit.message }} - - # - name: Publish distribution 📦 to Test PyPI - # uses: pypa/gh-action-pypi-publish@v1.5.0 - # with: - # password: ${{ secrets.TEST_PYPI_API_TOKEN }} - # repository_url: https://test.pypi.org/legacy/ - # packages_dir: python/rapid_structure/dist/ - - - name: Publish distribution 📦 to PyPI - uses: pypa/gh-action-pypi-publish@v1.5.0 - with: - password: ${{ secrets.RAPIDOCR_OPENVINO }} - packages_dir: python/rapid_structure/dist/ diff --git a/.github/workflows/gen_whl_to_pypi_rapid_table.yml b/.github/workflows/gen_whl_to_pypi_rapid_table.yml deleted file mode 100644 index 08e66fc..0000000 --- a/.github/workflows/gen_whl_to_pypi_rapid_table.yml +++ /dev/null @@ -1,88 +0,0 @@ -name: Push rapidocr_table to pypi - -on: - push: - branches: [ main ] - paths: - - 'python/rapid_structure/rapid_table/**' - - 'docs/doc_whl_rapid_table.md' - - 'python/rapid_structure/setup_table.py' - - '.github/workflows/gen_whl_to_pypi_rapid_table.yml' - - 'python/rapid_structure/tests/test_table.py' - -env: - RESOURCES_URL: https://github.com/RapidAI/RapidOCR/releases/download/v1.1.0/rapid_table_models.zip - -jobs: - UnitTesting: - runs-on: ubuntu-latest - steps: - - name: Pull latest code - uses: actions/checkout@v3 - - - name: Set up Python 3.7 - uses: actions/setup-python@v4 - with: - python-version: '3.7' - architecture: 'x64' - - - name: Display Python version - run: python -c "import sys; print(sys.version)" - - - name: Unit testings - run: | - wget $RESOURCES_URL - ZIP_NAME=${RESOURCES_URL##*/} - DIR_NAME=${ZIP_NAME%.*} - unzip $DIR_NAME - cp $DIR_NAME/*.onnx python/rapid_structure/rapid_table/models/ - cd python/rapid_structure - pip install -r requirements.txt - pip install rapidocr_onnxruntime - pytest tests/test_table.py - - GenerateWHL_PushPyPi: - needs: UnitTesting - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - - name: Set up Python 3.7 - uses: actions/setup-python@v4 - with: - python-version: '3.7' - architecture: 'x64' - - - name: Set SSH Environment - env: - DEPLOY_KEYS: ${{ secrets.GEN_PYTHON_SDK }} - run: | - mkdir -p ~/.ssh/ - echo "$DEPLOY_KEYS" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - chmod 700 ~/.ssh && chmod 600 ~/.ssh/* - - - name: Run setup - run: | - cd python/rapid_structure - pip install -r requirements.txt - wget $RESOURCES_URL - ZIP_NAME=${RESOURCES_URL##*/} - DIR_NAME=${ZIP_NAME%.*} - unzip $ZIP_NAME - mv $DIR_NAME/en_ppstructure_mobile_v2_SLANet.onnx rapid_table/models/ - python setup_table.py bdist_wheel ${{ github.event.head_commit.message }} - - # - name: Publish distribution 📦 to Test PyPI - # uses: pypa/gh-action-pypi-publish@v1.5.0 - # with: - # password: ${{ secrets.TEST_PYPI_API_TOKEN }} - # repository_url: https://test.pypi.org/legacy/ - # packages_dir: dist/ - - - name: Publish distribution 📦 to PyPI - uses: pypa/gh-action-pypi-publish@v1.5.0 - with: - password: ${{ secrets.RAPIDOCR_OPENVINO }} - packages_dir: python/rapid_structure/dist/ diff --git a/README.md b/README.md index 81909ca..7564b44 100755 --- a/README.md +++ b/README.md @@ -81,13 +81,14 @@ - [网页版OCR](https://github.com/RapidAI/RapidOCR/blob/main/ocrweb/README.md) - [Nuitka打包rapdocr_web](https://github.com/RapidAI/RapidOCR/wiki/Nuitka%E6%89%93%E5%8C%85rapidocr_web%E6%93%8D%E4%BD%9C%E6%96%87%E6%A1%A3) - [多语言网页版OCR](https://github.com/RapidAI/RapidOCR/blob/main/ocrweb_multi/README.md) -- [版面结构化](https://github.com/RapidAI/RapidOCR/blob/main/python/rapid_structure/README.md) - - [含文本的图像方向分类](https://github.com/RapidAI/RapidOCR/blob/main/python/rapid_structure/docs/README_Orientation.md) - - [版面分析](https://github.com/RapidAI/RapidOCR/blob/main/python/rapid_structure/docs/README_Layout.md) - - [表格还原](https://github.com/RapidAI/RapidOCR/blob/main/python/rapid_structure/docs/README_Table.md) +- [版面结构化-RapidStructure](https://github.com/RapidAI/RapidStructure) + - [含文本的图像方向分类](https://github.com/RapidAI/RapidStructure/blob/main/docs/README_Orientation.md) + - [版面分析](https://github.com/RapidAI/RapidStructure/blob/main/docs/README_Layout.md) + - [表格还原](https://github.com/RapidAI/RapidStructure/blob/main/docs/README_Table.md) - 衍生项目 - [RapidOCR HTTP服务/win32程序/易语言编写](https://github.com/Physton/RapidOCRServer) - [垂直项目](https://github.com/RapidAI/RapidOCR/blob/main/docs/related_projects.md) + - [RapidOCRPDF](https://github.com/RapidAI/RapidOCRPDF):解析PDF文件 - [RapidVideOCR](https://github.com/SWHL/RapidVideOCR):基于RapidOCR,提取视频中的硬字幕 - [LGPMA_Infer](https://github.com/SWHL/LGPMA_Infer): 表格结构还原 | [博客解读论文和源码](http://t.csdn.cn/QNN3S) - [文档图像矫正-PaperEdge](https://github.com/cvlab-stonybrook/PaperEdge) | [Demo](https://huggingface.co/spaces/SWHL/PaperEdgeDemo) diff --git a/docs/README_en.md b/docs/README_en.md index 083ddb8..eee78b9 100644 --- a/docs/README_en.md +++ b/docs/README_en.md @@ -73,13 +73,14 @@ - [Web OCR](https://github.com/RapidAI/RapidOCR/blob/main/ocrweb/README.md) - [Nuitka package rapdocr_web](https://github.com/RapidAI/RapidOCR/wiki/Nuitka%E6%89%93%E5%8C%85rapidocr_web%E6%93%8D%E4%BD%9C%E6%96%87%E6%A1%A3) - [Multi Web OCR](https://github.com/RapidAI/RapidOCR/blob/main/ocrweb_multi/README.md) -- [Structure](https://github.com/RapidAI/RapidOCR/blob/main/python/rapid_structure/README.md) - - [orientation](https://github.com/RapidAI/RapidOCR/blob/main/python/rapid_structure/docs/README_Orientation.md) - - [layout](https://github.com/RapidAI/RapidOCR/blob/main/python/rapid_structure/docs/README_Layout.md) - - [table recovery](https://github.com/RapidAI/RapidOCR/blob/main/python/rapid_structure/docs/README_Table.md) +- [RapidStructure](https://github.com/RapidAI/RapidStructure) + - [orientation](https://github.com/RapidAI/RapidStructure/blob/main/docs/README_Orientation.md) + - [layout](https://github.com/RapidAI/RapidStructure/blob/main/docs/README_Layout.md) + - [table recovery](https://github.com/RapidAI/RapidStructure/blob/main/docs/README_Table.md) - Derivatives - [RapidOCR HTTP service/win32 program/easy language writing](https://github.com/Physton/RapidOCRServer) - [Related projects](https://github.com/RapidAI/RapidOCR/blob/main/docs/related_projects.md) + - [RapidOCRPDF](https://github.com/RapidAI/RapidOCRPDF):extract PDF content. - [RapidVideOCR](https://github.com/SWHL/RapidVideOCR): Extract hard subtitles in videos based on RapidOCR - [LGPMA_Infer](https://github.com/SWHL/LGPMA_Infer): table structure restoration | [blog interpretation papers and source code](http://t.csdn.cn/QNN3S) - [Document Unwarping-PaperEdge](https://github.com/cvlab-stonybrook/PaperEdge) | [Demo](https://huggingface.co/spaces/SWHL/PaperEdgeDemo) diff --git a/docs/doc_whl_rapid_layout.md b/docs/doc_whl_rapid_layout.md deleted file mode 100644 index 29f40cf..0000000 --- a/docs/doc_whl_rapid_layout.md +++ /dev/null @@ -1,60 +0,0 @@ -## rapid-layout Package -

- - - PyPI -

- -### 1. Install package by pypi. -```bash -$ pip install rapid-layout -``` - -### 2. Run by script. -- RapidLayout has the default `model_path` value, you can set the different value of `model_path` to use different models, e.g. `layout_engine = RapidLayout(model_path='layout_publaynet.onnx')` -- See details, for [README_Layout](https://github.com/RapidAI/RapidOCR/blob/f133ff008a1c60edd6e0ed882da83873aa7b113a/python/rapid_structure/docs/README_Layout.md) . -- 📌 `layout.png` source: [link](https://github.com/RapidAI/RapidOCR/blob/f133ff008a1c60edd6e0ed882da83873aa7b113a/python/rapid_structure/test_images/layout.png) - -```python -import cv2 -from rapid_layout import RapidLayout - -layout_engine = RapidLayout() - -img = cv2.imread('layout.png') - -layout_res, elapse = layout_engine(img) -print(layout_res) -``` - -### 3. Run by command line. -- Usage: - ```bash - $ rapid_layout -h - usage: rapid_layout [-h] [-v] -img IMG_PATH [-m MODEL_PATH] - - optional arguments: - -h, --help show this help message and exit - -v, --vis Wheter to visualize the layout results. - -img IMG_PATH, --img_path IMG_PATH - Path to image for layout. - -m MODEL_PATH, --model_path MODEL_PATH - The model path used for inference. - ``` -- Example: - ```bash - $ rapid_layout -v -img layout.png - ``` - -### 4. Result. -- Return value. - ```python - [ - {'bbox': array([321.4160495, 91.53214898, 562.06141263, 199.85522603]), 'label': 'text'}, - {'bbox': array([58.67292211, 107.29000663, 300.25448676, 199.68142]), 'label': 'table_caption'} - ] - ``` -- Visualize result. -
- -
diff --git a/docs/doc_whl_rapid_orientation.md b/docs/doc_whl_rapid_orientation.md deleted file mode 100644 index dd751ed..0000000 --- a/docs/doc_whl_rapid_orientation.md +++ /dev/null @@ -1,51 +0,0 @@ -## rapid-orientation Package -

- - - PyPI -

- -### 1. Install package by pypi. -```bash -$ pip install rapid-orientation -``` - -### 2. Run by script. -- RapidOrientation has the default `model_path` value, you can set the different value of `model_path` to use different models, e.g. `orientation_engine = RapidOrientation(model_path='rapid_orientation.onnx')` -- See details, for [README_Layout](https://github.com/RapidAI/RapidOCR/blob/f133ff008a1c60edd6e0ed882da83873aa7b113a/python/rapid_structure/docs/README_Layout.md) . -- 📌 `layout.png` source: [link](https://github.com/RapidAI/RapidOCR/blob/f133ff008a1c60edd6e0ed882da83873aa7b113a/python/rapid_structure/test_images/layout.png) - -```python -import cv2 -from rapid_orientation import RapidOrientation - -orientation_engine = RapidOrientation() - -img = cv2.imread('test_images/layout.png') - -orientation_res, elapse = orientation_engine(img) -print(orientation_res) -``` - -### 3. Run by command line. -- Usage: - ```bash - $ rapid_orientation -h - usage: rapid_orientation [-h] -img IMG_PATH [-m MODEL_PATH] - - optional arguments: - -h, --help show this help message and exit - -img IMG_PATH, --img_path IMG_PATH - Path to image for layout. - -m MODEL_PATH, --model_path MODEL_PATH - The model path used for inference - ``` -- Example: - ```bash - $ rapid_orientation -img layout.png - ``` - -### 4. Result. -```python -# Return str, four types::0 | 90 | 180 | 270 -``` \ No newline at end of file diff --git a/docs/doc_whl_rapid_table.md b/docs/doc_whl_rapid_table.md deleted file mode 100644 index 6534b24..0000000 --- a/docs/doc_whl_rapid_table.md +++ /dev/null @@ -1,41 +0,0 @@ -## rapid-table Package -

- - -

- -### 1. Install package by pypi. -```bash -$ pip install rapid-table -``` - -### 2. Run by script. -- RapidTable has the default `model_path` value, you can set the different value of `model_path` to use different models, e.g. `table_engine = RapidTable(model_path='ch_ppstructure_mobile_v2_SLANet.onnx')` -- See details, for [README_Table](https://github.com/RapidAI/RapidOCR/blob/a87133a6fc3b8ee696e246028e529f7b2319d138/python/rapid_structure/docs/README_Table.md) . -- 📌 `table.jpg` source: [link](https://github.com/RapidAI/RapidOCR/blob/a87133a6fc3b8ee696e246028e529f7b2319d138/python/rapid_structure/test_images/table.jpg) - -```python -import cv2 -from rapid_table import RapidTable - -table_engine = RapidTable() - -img = cv2.imread('table.jpg') -table_html_str, _ = table_engine(img) -print(table_html_str) -``` - -### 3. Run by command line. -```bash -$ rapid_table -v -img table.jpg -``` - -### 4. Result. -- Return value. - ```html - <>
MethodsFPS
SegLink [26]70.086d>77.08.9
PixelLink [4]73.283.077.8
TextSnake [18]73.983.278.31.1
TextField [37]75.987.481.35.2
MSR[38]76.787.87.481.7
FTSN [3]77.187.682.0
LSE[30]81.784.282.9
CRAFT [2]78.288.282.98.6
MCN[16]798883
ATRR[35]82.185.283.6
PAN [34]83.884.484.130.2
DB[12]79.291.584.932.0
DRRG[41]82.3088.0585.08
Ours (SynText)80.688582.9712.68
Ours (MLT-17)84.5486.6285.5712.31
- ``` -- Visualize result. -
- <>
MethodsFPS
SegLink [26]70.086d>77.08.9
PixelLink [4]73.283.077.8
TextSnake [18]73.983.278.31.1
TextField [37]75.987.481.35.2
MSR[38]76.787.87.481.7
FTSN [3]77.187.682.0
LSE[30]81.784.282.9
CRAFT [2]78.288.282.98.6
MCN[16]798883
ATRR[35]82.185.283.6
PAN [34]83.884.484.130.2
DB[12]79.291.584.932.0
DRRG[41]82.3088.0585.08
Ours (SynText)80.688582.9712.68
Ours (MLT-17)84.5486.6285.5712.31
-
diff --git a/python/rapid_structure/README.md b/python/rapid_structure/README.md deleted file mode 100644 index 0db38a2..0000000 --- a/python/rapid_structure/README.md +++ /dev/null @@ -1,15 +0,0 @@ -## Rapid Structure -- 该部分的功能主要针对文档类图像,包括文档图像分类、版面分析和表格识别。 - -### [版面分析](https://github.com/RapidAI/RapidOCR/blob/main/python/rapid_structure/docs/README_Layout.md) - -### [表格识别](https://github.com/RapidAI/RapidOCR/blob/main/python/rapid_structure/docs/README_Table.md) - -### [文档方向分类](https://github.com/RapidAI/RapidOCR/blob/main/python/rapid_structure/docs/README_Orientation.md) - -### 整体结构 -```mermaid -flowchart LR - A[/文档图像/] --> B(文档方向分类 rapid_orientation) --> C(版面分析 rapid_layout) & D(表格识别 rapid_table) --> E(OCR识别 rapidocr_onnxruntime) - E --> F(结构化输出) -``` \ No newline at end of file diff --git a/python/rapid_structure/demo.py b/python/rapid_structure/demo.py deleted file mode 100644 index 4745930..0000000 --- a/python/rapid_structure/demo.py +++ /dev/null @@ -1,85 +0,0 @@ -# -*- encoding: utf-8 -*- -# @Author: SWHL -# @Contact: liekkaskono@163.com -import copy -from pathlib import Path - -import cv2 -import numpy as np - -from rapid_layout import RapidLayout -from rapid_orientation import RapidOrientation -from rapid_table import RapidTable - - -def vis_layout(img: np.ndarray, layout_res: list) -> None: - tmp_img = copy.deepcopy(img) - for v in layout_res: - bbox = np.round(v['bbox']).astype(np.int32) - label = v['label'] - - start_point = (bbox[0], bbox[1]) - end_point = (bbox[2], bbox[3]) - - cv2.rectangle(tmp_img, start_point, end_point, (0, 255, 0), 2) - cv2.putText(tmp_img, label, start_point, - cv2.FONT_HERSHEY_COMPLEX, 1, (0, 0, 255), 2) - - draw_img_save = Path("./inference_results/") - if not draw_img_save.exists(): - draw_img_save.mkdir(parents=True, exist_ok=True) - - image_save = str(draw_img_save / 'layout_result.jpg') - cv2.imwrite(image_save, tmp_img) - print(f'The infer result has saved in {image_save}') - - -def vis_table(table_res): - style_res = '''''' - prefix_table, suffix_table = table_res.split('') - new_table_res = f'{prefix_table}{style_res}{suffix_table}' - - draw_img_save = Path("./inference_results/") - if not draw_img_save.exists(): - draw_img_save.mkdir(parents=True, exist_ok=True) - - html_path = str(draw_img_save / 'table_result.html') - with open(html_path, 'w', encoding='utf-8') as f: - f.write(new_table_res) - print(f'The infer result has saved in {html_path}') - - -def demo_layout(): - layout_engine = RapidLayout() - - img = cv2.imread('test_images/layout.png') - - layout_res, _ = layout_engine(img) - - vis_layout(img, layout_res) - print(layout_res) - - -def demo_table(): - table_engine = RapidTable() - img = cv2.imread('test_images/table.jpg') - table_html_str, _ = table_engine(img) - - vis_table(table_html_str) - print(table_html_str) - - -def demo_orientation(): - orientation_engine = RapidOrientation() - img = cv2.imread('tests/test_files/img_rot180_demo.jpg') - cls_result, _ = orientation_engine(img) - print(cls_result) - - -if __name__ == '__main__': - demo_layout() - # demo_table() - # demo_orientation() diff --git a/python/rapid_structure/docs/README_Layout.md b/python/rapid_structure/docs/README_Layout.md deleted file mode 100644 index 7c83b37..0000000 --- a/python/rapid_structure/docs/README_Layout.md +++ /dev/null @@ -1,73 +0,0 @@ -## Rapid Layout -

- - - PyPI - -

- -#### 简介和说明 -- 该部分主要是做文档类图像的版面分析。模型来源:[PaddleOCR 版面分析](https://github.com/PaddlePaddle/PaddleOCR/blob/133d67f27dc8a241d6b2e30a9f047a0fb75bebbe/ppstructure/layout/README_ch.md) -- 具体来说,就是分析给定的文档类别图像(论文截图等),定位其中类别和位置,如标题、段落、表格和图片等各个部分。 -- 目前支持三种类别的版面分析模型:中文、英文和表格版面分析模型,具体可参见下面表格: - - | 模型类型 | 模型名称 | 模型大小 | 支持种类 | - |:---:|:---:|:---:|:---:| - | 表格 | `layout_table.onnx` | 7.06M | `table` | - | 英文 | `layout_publaynet.onnx` | 7.06M | `text title list table figure` | - | 中文 | `layout_cdla.onnx` | 7.07M | `text title figure figure_caption table table_caption`
`header footer reference equation` | -- 模型下载地址为:[百度网盘](https://pan.baidu.com/s/1PI9fksW6F6kQfJhwUkewWg?pwd=p29g) | [Google Drive](https://drive.google.com/drive/folders/1DAPWSN2zGQ-ED_Pz7RaJGTjfkN2-Mvsf?usp=sharing) - -#### 使用方式 -1. pip安装 - - 由于模型较小,预先将中文版面分析模型(`layout_cdla.onnx`)打包进了whl包内,如果做中文版面分析,可直接安装使用 - ```bash - $ pip install rapid-layout - ``` -2. python脚本运行 - ```python - import cv2 - from rapid_layout import RapidLayout - - # RapidLayout类提供model_path参数,可以自行指定上述3个模型,默认是layout_cdla.onnx - # layout_engine = RapidLayout(model_path='layout_publaynet.onnx') - layout_engine = RapidLayout() - - img = cv2.imread('test_images/layout.png') - - layout_res, elapse = layout_engine(img) - ``` - -3. 终端运行 - - 用法: - ```bash - $ rapid_layout -h - usage: rapid_layout [-h] [-v] -img IMG_PATH [-m MODEL_PATH] - - optional arguments: - -h, --help show this help message and exit - -v, --vis Wheter to visualize the layout results. - -img IMG_PATH, --img_path IMG_PATH - Path to image for layout. - -m MODEL_PATH, --model_path MODEL_PATH - The model path used for inference. - ``` - - 示例: - ```bash - $ rapid_layout -v -img test_images/layout.png - ``` - -4. 结果 - - 返回结果 - ```python - # bbox: [左上角x0,左上角y0, 右下角x0, 右上角x1] - # label: 类别 - [ - {'bbox': array([321.4160495, 91.53214898, 562.06141263, 199.85522603]), 'label': 'text'}, - {'bbox': array([58.67292211, 107.29000663, 300.25448676, 199.68142]), 'label': 'table_caption'} - ] - ``` - - 可视化结果 -
- -
diff --git a/python/rapid_structure/docs/README_Orientation.md b/python/rapid_structure/docs/README_Orientation.md deleted file mode 100644 index 19f2ac2..0000000 --- a/python/rapid_structure/docs/README_Orientation.md +++ /dev/null @@ -1,56 +0,0 @@ -## Rapid Orientation -

- - - PyPI - -

- -#### 简介和说明 -- 该部分主要是做含文字图像方向分类模型。模型来源:[PaddleClas 含文字图像方向分类模型](https://github.com/PaddlePaddle/PaddleClas/blob/177e4be74639c0960efeae2c5166d3226c9a02eb/docs/zh_CN/models/PULC/PULC_text_image_orientation.md) - - | 模型类型 | 模型名称 | 模型大小 | 支持种类 | - |:---:|:---:|:---:|:---:| - | 四方向分类 | `rapid_orientation.onnx` | 6.5M | `0 90 180 270`| - -#### 使用方式 -1. pip安装 - - 由于模型较小,已经将分类模型(`rapid_orientation.onnx`)打包进了whl包内: - ```bash - $ pip install rapid-orientation - ``` -2. python脚本运行 - ```python - import cv2 - from rapid_orientation import RapidOrientation - - orientation_engine = RapidOrientation() - - img = cv2.imread('test_images/layout.png') - - orientation_res, elapse = orientation_engine(img) - print(orientation_res) - ``` - -3. 终端运行 - - 用法: - ```bash - $ rapid_orientation -h - usage: rapid_orientation [-h] -img IMG_PATH [-m MODEL_PATH] - - optional arguments: - -h, --help show this help message and exit - -img IMG_PATH, --img_path IMG_PATH - Path to image for layout. - -m MODEL_PATH, --model_path MODEL_PATH - The model path used for inference. - ``` - - 示例: - ```bash - $ rapid_orientation -img test_images/layout.png - ``` - -4. 结果 - ```python - # 返回结果为str类型,有四类:0 | 90 | 180 | 270 - ``` diff --git a/python/rapid_structure/docs/README_Table.md b/python/rapid_structure/docs/README_Table.md deleted file mode 100644 index 08610bd..0000000 --- a/python/rapid_structure/docs/README_Table.md +++ /dev/null @@ -1,66 +0,0 @@ -## Rapid Table -

- - - PyPI - -

- -#### 简介和说明 -- 该部分主要是做文档类图像的表格结构还原。模型来源:[PaddleOCR 表格识别](https://github.com/PaddlePaddle/PaddleOCR/blob/133d67f27dc8a241d6b2e30a9f047a0fb75bebbe/ppstructure/table/README_ch.md) -- 具体来说,就是分析给定的表格图像,将表格还原为对应的html格式。 -- 目前支持两种类别的表格识别模型:中文和英文表格识别模型,具体可参见下面表格: - - | 模型类型 | 模型名称 | 模型大小 | - |:---:|:---:|:---:| - | 英文 | `en_ppstructure_mobile_v2_SLANet.onnx` | 7.3M | - | 中文 | `ch_ppstructure_mobile_v2_SLANet.onnx` | 7.4M | -- 模型下载地址为:[百度网盘](https://pan.baidu.com/s/1PI9fksW6F6kQfJhwUkewWg?pwd=p29g) | [Google Drive](https://drive.google.com/drive/folders/1DAPWSN2zGQ-ED_Pz7RaJGTjfkN2-Mvsf?usp=sharing) - -#### 使用方式 -1. pip安装 - - 由于模型较小,预先将英文表格识别模型(`en_ppstructure_mobile_v2_SLANet.onnx`)打包进了whl包内,如果做英文表格识别,可直接安装使用 - ```bash - $ pip install rapid-table - ``` -2. python脚本运行 - ```python - import cv2 - from rapid_table import RapidTable - - # RapidTable类提供model_path参数,可以自行指定上述2个模型,默认是en_ppstructure_mobile_v2_SLANet.onnx - # table_engine = RapidTable(model_path='ch_ppstructure_mobile_v2_SLANet.onnx') - table_engine = RapidTable() - - img = cv2.imread('test_images/table.jpg') - table_html_str, _ = table_engine(img) - print(table_html_str) - ``` -3. 终端运行 - - 用法: - ```bash - $ rapid_table -h - usage: rapid_table [-h] [-v] -img IMG_PATH [-m MODEL_PATH] - - optional arguments: - -h, --help show this help message and exit - -v, --vis Wheter to visualize the layout results. - -img IMG_PATH, --img_path IMG_PATH - Path to image for layout. - -m MODEL_PATH, --model_path MODEL_PATH - The model path used for inference. - ``` - - 示例: - ```bash - $ rapid_table -v -img test_images/table.jpg - ``` - -4. 结果 - - 返回结果 - ```html - <>
MethodsFPS
SegLink [26]70.086d>77.08.9
PixelLink [4]73.283.077.8
TextSnake [18]73.983.278.31.1
TextField [37]75.987.481.35.2
MSR[38]76.787.87.481.7
FTSN [3]77.187.682.0
LSE[30]81.784.282.9
CRAFT [2]78.288.282.98.6
MCN[16]798883
ATRR[35]82.185.283.6
PAN [34]83.884.484.130.2
DB[12]79.291.584.932.0
DRRG[41]82.3088.0585.08
Ours (SynText)80.688582.9712.68
Ours (MLT-17)84.5486.6285.5712.31
- ``` - - 可视化结果 -
- <>
MethodsFPS
SegLink [26]70.086d>77.08.9
PixelLink [4]73.283.077.8
TextSnake [18]73.983.278.31.1
TextField [37]75.987.481.35.2
MSR[38]76.787.87.481.7
FTSN [3]77.187.682.0
LSE[30]81.784.282.9
CRAFT [2]78.288.282.98.6
MCN[16]798883
ATRR[35]82.185.283.6
PAN [34]83.884.484.130.2
DB[12]79.291.584.932.0
DRRG[41]82.3088.0585.08
Ours (SynText)80.688582.9712.68
Ours (MLT-17)84.5486.6285.5712.31
-
\ No newline at end of file diff --git a/python/rapid_structure/rapid_layout/__init__.py b/python/rapid_structure/rapid_layout/__init__.py deleted file mode 100644 index 79e4a04..0000000 --- a/python/rapid_structure/rapid_layout/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from .rapid_layout import RapidLayout diff --git a/python/rapid_structure/rapid_layout/config.yaml b/python/rapid_structure/rapid_layout/config.yaml deleted file mode 100644 index 33a85d2..0000000 --- a/python/rapid_structure/rapid_layout/config.yaml +++ /dev/null @@ -1,24 +0,0 @@ -model_path: models/layout_cdla.onnx - -use_cuda: false -CUDAExecutionProvider: - device_id: 0 - arena_extend_strategy: kNextPowerOfTwo - cudnn_conv_algo_search: EXHAUSTIVE - do_copy_in_default_stream: true - -pre_process: - Resize: - size: [800, 608] - NormalizeImage: - std: [0.229, 0.224, 0.225] - mean: [0.485, 0.456, 0.406] - scale: 1./255. - order: hwc - ToCHWImage: - KeepKeys: - keep_keys: ['image'] - -post_process: - score_threshold: 0.5 - nms_threshold: 0.5 \ No newline at end of file diff --git a/python/rapid_structure/rapid_layout/models/.gitkeep b/python/rapid_structure/rapid_layout/models/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/python/rapid_structure/rapid_layout/rapid_layout.py b/python/rapid_structure/rapid_layout/rapid_layout.py deleted file mode 100644 index 178c015..0000000 --- a/python/rapid_structure/rapid_layout/rapid_layout.py +++ /dev/null @@ -1,95 +0,0 @@ -# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import argparse -import time -from pathlib import Path - -import cv2 -import numpy as np - -from .utils import (OrtInferSession, PicoDetPostProcess, create_operators, - read_yaml, transform, vis_layout) - -root_dir = Path(__file__).resolve().parent - - -class RapidLayout(): - def __init__(self, - model_path: str = None): - config_path = str(root_dir / 'config.yaml') - config = read_yaml(config_path) - if model_path is None: - model_path = str(root_dir / 'models' / 'layout_cdla.onnx') - config['model_path'] = model_path - - self.session = OrtInferSession(config) - labels = self.session.get_metadata()['character'].splitlines() - - self.preprocess_op = create_operators(config['pre_process']) - self.postprocess_op = PicoDetPostProcess(labels, - **config['post_process']) - - def __call__(self, img): - ori_im = img.copy() - data = {'image': img} - data = transform(data, self.preprocess_op) - img = data[0] - - if img is None: - return None, 0 - - img = np.expand_dims(img, axis=0) - img = img.copy() - - preds, elapse = 0, 1 - starttime = time.time() - - preds = self.session(img) - - score_list, boxes_list = [], [] - num_outs = int(len(preds) / 2) - for out_idx in range(num_outs): - score_list.append(preds[out_idx]) - boxes_list.append(preds[out_idx + num_outs]) - preds = dict(boxes=score_list, boxes_num=boxes_list) - post_preds = self.postprocess_op(ori_im, img, preds) - elapse = time.time() - starttime - return post_preds, elapse - - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument('-v', '--vis', action='store_true', - help='Wheter to visualize the layout results.') - parser.add_argument('-img', '--img_path', type=str, required=True, - help='Path to image for layout.') - parser.add_argument('-m', '--model_path', type=str, - default=str(root_dir / 'models' / 'layout_cdla.onnx'), - help='The model path used for inference.') - args = parser.parse_args() - - layout_engine = RapidLayout(args.model_path) - - img = cv2.imread(args.img_path) - layout_res, elapse = layout_engine(img) - print(layout_res) - - if args.vis: - img_path = Path(args.img_path) - save_path = img_path.resolve().parent / f'vis_{img_path.name}' - vis_layout(img, layout_res, str(save_path)) - - -if __name__ == '__main__': - main() diff --git a/python/rapid_structure/rapid_layout/utils.py b/python/rapid_structure/rapid_layout/utils.py deleted file mode 100644 index 70f3b2f..0000000 --- a/python/rapid_structure/rapid_layout/utils.py +++ /dev/null @@ -1,443 +0,0 @@ -# -*- encoding: utf-8 -*- -# @Author: SWHL -# @Contact: liekkaskono@163.com -import copy -import warnings -from pathlib import Path - -import yaml -import cv2 -import numpy as np -from onnxruntime import (get_available_providers, get_device, - SessionOptions, InferenceSession, - GraphOptimizationLevel) - - -class OrtInferSession(): - def __init__(self, config): - sess_opt = SessionOptions() - sess_opt.log_severity_level = 4 - sess_opt.enable_cpu_mem_arena = False - sess_opt.graph_optimization_level = GraphOptimizationLevel.ORT_ENABLE_ALL - - cuda_ep = 'CUDAExecutionProvider' - cpu_ep = 'CPUExecutionProvider' - cpu_provider_options = {"arena_extend_strategy": "kSameAsRequested",} - - EP_list = [] - if config['use_cuda'] and get_device() == 'GPU' \ - and cuda_ep in get_available_providers(): - EP_list = [(cuda_ep, config[cuda_ep])] - EP_list.append((cpu_ep, cpu_provider_options)) - - self._verify_model(config['model_path']) - self.session = InferenceSession(config['model_path'], - sess_options=sess_opt, - providers=EP_list) - - if config['use_cuda'] and cuda_ep not in self.session.get_providers(): - warnings.warn(f'{cuda_ep} is not avaiable for current env, the inference part is automatically shifted to be executed under {cpu_ep}.\n' - 'Please ensure the installed onnxruntime-gpu version matches your cuda and cudnn version, ' - 'you can check their relations from the offical web site: ' - 'https://onnxruntime.ai/docs/execution-providers/CUDA-ExecutionProvider.html', - RuntimeWarning) - - def __call__(self, input_content: np.ndarray) -> np.ndarray: - input_dict = dict(zip(self.get_input_names(), [input_content])) - try: - return self.session.run(self.get_output_names(), input_dict) - except Exception as e: - raise ONNXRuntimeError('ONNXRuntime inferece failed.') from e - - def get_input_names(self, ): - return [v.name for v in self.session.get_inputs()] - - def get_output_names(self,): - return [v.name for v in self.session.get_outputs()] - - def get_metadata(self): - meta_dict = self.session.get_modelmeta().custom_metadata_map - return meta_dict - - @staticmethod - def _verify_model(model_path): - model_path = Path(model_path) - if not model_path.exists(): - raise FileNotFoundError(f'{model_path} does not exists.') - if not model_path.is_file(): - raise FileExistsError(f'{model_path} is not a file.') - - -class ONNXRuntimeError(Exception): - pass - - -def transform(data, ops=None): - """ transform """ - if ops is None: - ops = [] - - for op in ops: - data = op(data) - if data is None: - return None - return data - - -def create_operators(op_param_dict): - ops = [] - for op_name, param in op_param_dict.items(): - if param is None: - param = {} - op = eval(op_name)(**param) - ops.append(op) - return ops - - -class Resize(): - def __init__(self, size=(640, 640), **kwargs): - self.size = size - - def resize_image(self, img): - resize_h, resize_w = self.size - ori_h, ori_w = img.shape[:2] # (h, w, c) - ratio_h = float(resize_h) / ori_h - ratio_w = float(resize_w) / ori_w - img = cv2.resize(img, (int(resize_w), int(resize_h))) - return img, [ratio_h, ratio_w] - - def __call__(self, data): - img = data['image'] - if 'polys' in data: - text_polys = data['polys'] - - img_resize, [ratio_h, ratio_w] = self.resize_image(img) - if 'polys' in data: - new_boxes = [] - for box in text_polys: - new_box = [] - for cord in box: - new_box.append([cord[0] * ratio_w, cord[1] * ratio_h]) - new_boxes.append(new_box) - data['polys'] = np.array(new_boxes, dtype=np.float32) - data['image'] = img_resize - return data - - -class NormalizeImage(): - def __init__(self, scale=None, mean=None, std=None, order='chw', **kwargs): - if isinstance(scale, str): - scale = eval(scale) - self.scale = np.float32(scale if scale is not None else 1.0 / 255.0) - mean = mean if mean is not None else [0.485, 0.456, 0.406] - std = std if std is not None else [0.229, 0.224, 0.225] - - shape = (3, 1, 1) if order == 'chw' else (1, 1, 3) - self.mean = np.array(mean).reshape(shape).astype('float32') - self.std = np.array(std).reshape(shape).astype('float32') - - def __call__(self, data): - img = np.array(data['image']) - assert isinstance(img, - np.ndarray), "invalid input 'img' in NormalizeImage" - data['image'] = ( - img.astype('float32') * self.scale - self.mean) / self.std - return data - - -class ToCHWImage(): - def __init__(self, **kwargs): - pass - - def __call__(self, data): - img = np.array(data['image']) - data['image'] = img.transpose((2, 0, 1)) - return data - - -class KeepKeys(): - def __init__(self, keep_keys, **kwargs): - self.keep_keys = keep_keys - - def __call__(self, data): - data_list = [] - for key in self.keep_keys: - data_list.append(data[key]) - return data_list - - -def read_yaml(yaml_path): - with open(yaml_path, 'rb') as f: - data = yaml.load(f, Loader=yaml.Loader) - return data - - -class PicoDetPostProcess(): - def __init__(self, - labels, - strides=[8, 16, 32, 64], - score_threshold=0.4, - nms_threshold=0.5, - nms_top_k=1000, - keep_top_k=100): - self.labels = labels - self.strides = strides - self.score_threshold = score_threshold - self.nms_threshold = nms_threshold - self.nms_top_k = nms_top_k - self.keep_top_k = keep_top_k - - def __call__(self, ori_img, img, preds): - scores, raw_boxes = preds['boxes'], preds['boxes_num'] - batch_size = raw_boxes[0].shape[0] - reg_max = int(raw_boxes[0].shape[-1] / 4 - 1) - out_boxes_num = [] - out_boxes_list = [] - results = [] - ori_shape, input_shape, scale_factor = self.img_info(ori_img, img) - - for batch_id in range(batch_size): - # generate centers - decode_boxes = [] - select_scores = [] - for stride, box_distribute, score in zip(self.strides, raw_boxes, - scores): - box_distribute = box_distribute[batch_id] - score = score[batch_id] - # centers - fm_h = input_shape[0] / stride - fm_w = input_shape[1] / stride - h_range = np.arange(fm_h) - w_range = np.arange(fm_w) - ww, hh = np.meshgrid(w_range, h_range) - ct_row = (hh.flatten() + 0.5) * stride - ct_col = (ww.flatten() + 0.5) * stride - center = np.stack((ct_col, ct_row, ct_col, ct_row), axis=1) - - # box distribution to distance - reg_range = np.arange(reg_max + 1) - box_distance = box_distribute.reshape((-1, reg_max + 1)) - box_distance = self.softmax(box_distance, axis=1) - box_distance = box_distance * np.expand_dims(reg_range, axis=0) - box_distance = np.sum(box_distance, axis=1).reshape((-1, 4)) - box_distance = box_distance * stride - - # top K candidate - topk_idx = np.argsort(score.max(axis=1))[::-1] - topk_idx = topk_idx[:self.nms_top_k] - center = center[topk_idx] - score = score[topk_idx] - box_distance = box_distance[topk_idx] - - # decode box - decode_box = center + [-1, -1, 1, 1] * box_distance - - select_scores.append(score) - decode_boxes.append(decode_box) - - # nms - bboxes = np.concatenate(decode_boxes, axis=0) - confidences = np.concatenate(select_scores, axis=0) - picked_box_probs = [] - picked_labels = [] - for class_index in range(0, confidences.shape[1]): - probs = confidences[:, class_index] - mask = probs > self.score_threshold - probs = probs[mask] - if probs.shape[0] == 0: - continue - subset_boxes = bboxes[mask, :] - box_probs = np.concatenate( - [subset_boxes, probs.reshape(-1, 1)], axis=1) - box_probs = self.hard_nms( - box_probs, - iou_threshold=self.nms_threshold, - top_k=self.keep_top_k, ) - picked_box_probs.append(box_probs) - picked_labels.extend([class_index] * box_probs.shape[0]) - - if len(picked_box_probs) == 0: - out_boxes_list.append(np.empty((0, 4))) - out_boxes_num.append(0) - - else: - picked_box_probs = np.concatenate(picked_box_probs) - - # resize output boxes - picked_box_probs[:, :4] = self.warp_boxes( - picked_box_probs[:, :4], ori_shape[batch_id]) - im_scale = np.concatenate([ - scale_factor[batch_id][::-1], scale_factor[batch_id][::-1] - ]) - picked_box_probs[:, :4] /= im_scale - # clas score box - out_boxes_list.append( - np.concatenate( - [ - np.expand_dims( - np.array(picked_labels), - axis=-1), np.expand_dims( - picked_box_probs[:, 4], axis=-1), - picked_box_probs[:, :4] - ], - axis=1)) - out_boxes_num.append(len(picked_labels)) - - out_boxes_list = np.concatenate(out_boxes_list, axis=0) - out_boxes_num = np.asarray(out_boxes_num).astype(np.int32) - - for dt in out_boxes_list: - clsid, bbox, score = int(dt[0]), dt[2:], dt[1] - label = self.labels[clsid] - result = {'bbox': bbox, 'label': label} - results.append(result) - return results - - def load_layout_dict(self, layout_dict_path): - with open(layout_dict_path, 'r', encoding='utf-8') as fp: - labels = fp.readlines() - return [label.strip('\n') for label in labels] - - def warp_boxes(self, boxes, ori_shape): - """Apply transform to boxes - """ - width, height = ori_shape[1], ori_shape[0] - n = len(boxes) - if n: - # warp points - xy = np.ones((n * 4, 3)) - xy[:, :2] = boxes[:, [0, 1, 2, 3, 0, 3, 2, 1]].reshape( - n * 4, 2) # x1y1, x2y2, x1y2, x2y1 - # xy = xy @ M.T # transform - xy = (xy[:, :2] / xy[:, 2:3]).reshape(n, 8) # rescale - # create new boxes - x = xy[:, [0, 2, 4, 6]] - y = xy[:, [1, 3, 5, 7]] - xy = np.concatenate( - (x.min(1), y.min(1), x.max(1), y.max(1))).reshape(4, n).T - # clip boxes - xy[:, [0, 2]] = xy[:, [0, 2]].clip(0, width) - xy[:, [1, 3]] = xy[:, [1, 3]].clip(0, height) - return xy.astype(np.float32) - else: - return boxes - - def img_info(self, ori_img, img): - origin_shape = ori_img.shape - resize_shape = img.shape - im_scale_y = resize_shape[2] / float(origin_shape[0]) - im_scale_x = resize_shape[3] / float(origin_shape[1]) - scale_factor = np.array([im_scale_y, im_scale_x], dtype=np.float32) - img_shape = np.array(img.shape[2:], dtype=np.float32) - - input_shape = np.array(img).astype('float32').shape[2:] - ori_shape = np.array((img_shape, )).astype('float32') - scale_factor = np.array((scale_factor, )).astype('float32') - return ori_shape, input_shape, scale_factor - - @staticmethod - def softmax(x, axis=None): - def logsumexp(a, axis=None, b=None, keepdims=False): - a_max = np.amax(a, axis=axis, keepdims=True) - - if a_max.ndim > 0: - a_max[~np.isfinite(a_max)] = 0 - elif not np.isfinite(a_max): - a_max = 0 - - tmp = np.exp(a - a_max) - - # suppress warnings about log of zero - with np.errstate(divide='ignore'): - s = np.sum(tmp, axis=axis, keepdims=keepdims) - out = np.log(s) - - if not keepdims: - a_max = np.squeeze(a_max, axis=axis) - out += a_max - return out - - return np.exp(x - logsumexp(x, axis=axis, keepdims=True)) - - def hard_nms(self, box_scores, iou_threshold, top_k=-1, - candidate_size=200): - """ - Args: - box_scores (N, 5): boxes in corner-form and probabilities. - iou_threshold: intersection over union threshold. - top_k: keep top_k results. If k <= 0, keep all the results. - candidate_size: only consider the candidates with the highest scores. - Returns: - picked: a list of indexes of the kept boxes - """ - scores = box_scores[:, -1] - boxes = box_scores[:, :-1] - picked = [] - indexes = np.argsort(scores) - indexes = indexes[-candidate_size:] - while len(indexes) > 0: - current = indexes[-1] - picked.append(current) - if 0 < top_k == len(picked) or len(indexes) == 1: - break - current_box = boxes[current, :] - indexes = indexes[:-1] - rest_boxes = boxes[indexes, :] - iou = self.iou_of(rest_boxes, np.expand_dims(current_box, axis=0),) - indexes = indexes[iou <= iou_threshold] - - return box_scores[picked, :] - - def iou_of(self, boxes0, boxes1, eps=1e-5): - """Return intersection-over-union (Jaccard index) of boxes. - Args: - boxes0 (N, 4): ground truth boxes. - boxes1 (N or 1, 4): predicted boxes. - eps: a small number to avoid 0 as denominator. - Returns: - iou (N): IoU values. - """ - overlap_left_top = np.maximum(boxes0[..., :2], boxes1[..., :2]) - overlap_right_bottom = np.minimum(boxes0[..., 2:], boxes1[..., 2:]) - - overlap_area = self.area_of(overlap_left_top, overlap_right_bottom) - area0 = self.area_of(boxes0[..., :2], boxes0[..., 2:]) - area1 = self.area_of(boxes1[..., :2], boxes1[..., 2:]) - return overlap_area / (area0 + area1 - overlap_area + eps) - - @staticmethod - def area_of(left_top, right_bottom): - """Compute the areas of rectangles given two corners. - Args: - left_top (N, 2): left top corner. - right_bottom (N, 2): right bottom corner. - Returns: - area (N): return the area. - """ - hw = np.clip(right_bottom - left_top, 0.0, None) - return hw[..., 0] * hw[..., 1] - - -def vis_layout(img: np.ndarray, layout_res: list, save_path: str) -> None: - font = cv2.FONT_HERSHEY_COMPLEX - font_scale = 1 - font_color = (0, 0, 255) - font_thickness = 1 - - tmp_img = copy.deepcopy(img) - for v in layout_res: - bbox = np.round(v['bbox']).astype(np.int32) - label = v['label'] - - start_point = (bbox[0], bbox[1]) - end_point = (bbox[2], bbox[3]) - - cv2.rectangle(tmp_img, start_point, end_point, (0, 255, 0), 2) - - (w, h), _ = cv2.getTextSize(label, font, font_scale, font_thickness) - put_point = start_point[0], start_point[1] + h - cv2.putText(tmp_img, label, put_point, font, font_scale, - font_color, font_thickness) - - cv2.imwrite(save_path, tmp_img) - print(f'The infer result has saved in {save_path}') diff --git a/python/rapid_structure/rapid_orientation/__init__.py b/python/rapid_structure/rapid_orientation/__init__.py deleted file mode 100644 index 336271c..0000000 --- a/python/rapid_structure/rapid_orientation/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# -*- encoding: utf-8 -*- -# @Author: SWHL -# @Contact: liekkaskono@163.com -from .rapid_orientation import RapidOrientation diff --git a/python/rapid_structure/rapid_orientation/config.yaml b/python/rapid_structure/rapid_orientation/config.yaml deleted file mode 100644 index 78847b2..0000000 --- a/python/rapid_structure/rapid_orientation/config.yaml +++ /dev/null @@ -1,16 +0,0 @@ -model_path: models/rapid_orientation.onnx - -use_cuda: false -CUDAExecutionProvider: - device_id: 0 - arena_extend_strategy: kNextPowerOfTwo - cudnn_conv_algo_search: EXHAUSTIVE - do_copy_in_default_stream: true - -PreProcess: - - ResizeImage: - resize_short: 256 - - CropImage: - size: 224 - - NormalizeImage: - - ToCHWImage: diff --git a/python/rapid_structure/rapid_orientation/models/.gitkeep b/python/rapid_structure/rapid_orientation/models/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/python/rapid_structure/rapid_orientation/rapid_orientation.py b/python/rapid_structure/rapid_orientation/rapid_orientation.py deleted file mode 100644 index 7694ab9..0000000 --- a/python/rapid_structure/rapid_orientation/rapid_orientation.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -*- encoding: utf-8 -*- -# @Author: SWHL -# @Contact: liekkaskono@163.com -import argparse -import time -from pathlib import Path -import cv2 - -import numpy as np -import yaml - -from .utils import OrtInferSession, create_operators - -root_dir = Path(__file__).resolve().parent - - -class RapidOrientation(): - def __init__(self, model_path: str = None): - config_path = str(root_dir / 'config.yaml') - config = self.read_yaml(config_path) - if model_path is None: - model_path = str(root_dir / 'models' / 'rapid_orientation.onnx') - config['model_path'] = model_path - - self.session = OrtInferSession(config) - self.labels = self.session.get_metadata()['character'].splitlines() - - self.preprocess_ops = create_operators(config["PreProcess"]) - - def __call__(self, images): - s = time.time() - for ops in self.preprocess_ops: - images = ops(images) - image = np.array(images)[None, ...] - - pred_output = self.session(image)[0] - - pred_output = pred_output.squeeze() - pred_idx = np.argmax(pred_output) - pred_txt = self.labels[pred_idx] - elapse = time.time() - s - return pred_txt, elapse - - @staticmethod - def read_yaml(yaml_path): - with open(yaml_path, 'rb') as f: - data = yaml.load(f, Loader=yaml.Loader) - return data - - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument('-img', '--img_path', type=str, required=True, - help='Path to image for layout.') - parser.add_argument('-m', '--model_path', type=str, - default=str(root_dir / 'models' / - 'rapid_orientation.onnx'), - help='The model path used for inference.') - args = parser.parse_args() - - orientation_engine = RapidOrientation(args.model_path) - - img = cv2.imread(args.img_path) - orientaion_result, _ = orientation_engine(img) - print(orientaion_result) - - -if __name__ == '__main__': - main() diff --git a/python/rapid_structure/rapid_orientation/utils.py b/python/rapid_structure/rapid_orientation/utils.py deleted file mode 100644 index f2a43c5..0000000 --- a/python/rapid_structure/rapid_orientation/utils.py +++ /dev/null @@ -1,169 +0,0 @@ -# -*- encoding: utf-8 -*- -# @Author: SWHL -# @Contact: liekkaskono@163.com -import importlib -from pathlib import Path -import warnings - -import cv2 -import numpy as np -from onnxruntime import (GraphOptimizationLevel, InferenceSession, - SessionOptions, get_available_providers, get_device) - - -class OrtInferSession(): - def __init__(self, config): - sess_opt = SessionOptions() - sess_opt.log_severity_level = 4 - sess_opt.enable_cpu_mem_arena = False - sess_opt.graph_optimization_level = GraphOptimizationLevel.ORT_ENABLE_ALL - - cuda_ep = 'CUDAExecutionProvider' - cpu_ep = 'CPUExecutionProvider' - cpu_provider_options = {"arena_extend_strategy": "kSameAsRequested", } - - EP_list = [] - if config['use_cuda'] and get_device() == 'GPU' \ - and cuda_ep in get_available_providers(): - EP_list = [(cuda_ep, config[cuda_ep])] - EP_list.append((cpu_ep, cpu_provider_options)) - - self._verify_model(config['model_path']) - self.session = InferenceSession(config['model_path'], - sess_options=sess_opt, - providers=EP_list) - - has_cuda_ep = cuda_ep not in self.session.get_providers() - if config['use_cuda'] and has_cuda_ep: - warnings.warn(f'{cuda_ep} is not avaiable for current env,' - f'the inference part is automatically shifted to ' - f'be executed under {cpu_ep}. ' - f'Please ensure the installed onnxruntime-gpu ' - f' version matches your cuda and cudnn version, ' - f'you can check their relations from the offical web site: ' - f'https://onnxruntime.ai/docs/execution-providers/CUDA-ExecutionProvider.html', - RuntimeWarning) - - def __call__(self, input_content: np.ndarray) -> np.ndarray: - input_dict = dict(zip(self.get_input_names(), [input_content])) - try: - return self.session.run(self.get_output_names(), input_dict) - except Exception as e: - raise ONNXRuntimeError('ONNXRuntime inferece failed.') from e - - def get_input_names(self, ): - return [v.name for v in self.session.get_inputs()] - - def get_output_names(self,): - return [v.name for v in self.session.get_outputs()] - - def get_metadata(self): - meta_dict = self.session.get_modelmeta().custom_metadata_map - return meta_dict - - @staticmethod - def _verify_model(model_path): - model_path = Path(model_path) - if not model_path.exists(): - raise FileNotFoundError(f'{model_path} does not exists.') - if not model_path.is_file(): - raise FileExistsError(f'{model_path} is not a file.') - - -class ONNXRuntimeError(Exception): - pass - - -def create_operators(params): - """ - create operators based on the config - - Args: - params(list): a dict list, used to create some operators - """ - assert isinstance(params, list), ('operator config should be a list') - mod = importlib.import_module(__name__) - ops = [] - for operator in params: - assert isinstance(operator, - dict) and len(operator) == 1, "yaml format error" - op_name = list(operator)[0] - param = {} if operator[op_name] is None else operator[op_name] - op = getattr(mod, op_name)(**param) - ops.append(op) - return ops - - -class ResizeImage(): - def __init__(self, size=None, resize_short=None): - if resize_short is not None and resize_short > 0: - self.resize_short = resize_short - self.w, self.h = None, None - elif size is not None: - self.resize_short = None - self.w = size if isinstance(size, int) else size[0] - self.h = size if isinstance(size, int) else size[1] - else: - raise ValueError("invalid params for ReisizeImage for '\ - 'both 'size' and 'resize_short' are None") - - def __call__(self, img: np.ndarray): - img_h, img_w = img.shape[:2] - - if self.resize_short: - percent = float(self.resize_short) / min(img_w, img_h) - w = int(round(img_w * percent)) - h = int(round(img_h * percent)) - else: - w = self.w - h = self.h - return cv2.resize(img, (w, h)) - - -class NormalizeImage(): - def __init__(self,): - self.scale = np.float32(1.0 / 255.0) - mean = [0.485, 0.456, 0.406] - std = [0.229, 0.224, 0.225] - - shape = 1, 1, 3 - self.mean = np.array(mean).reshape(shape).astype('float32') - self.std = np.array(std).reshape(shape).astype('float32') - - def __call__(self, img): - img = np.array(img).astype(np.float32) - img = (img * self.scale - self.mean) / self.std - return img.astype(np.float32) - - -class ToCHWImage(): - def __init__(self): - pass - - def __call__(self, img): - img = np.array(img) - return img.transpose((2, 0, 1)) - - -class CropImage(): - def __init__(self, size): - self.size = size - if isinstance(size, int): - self.size = (size, size) - - def __call__(self, img): - w, h = self.size - img_h, img_w = img.shape[:2] - - if img_h < h or img_w < w: - raise ValueError( - f"The size({h}, {w}) of CropImage must be greater than " - f"size({img_h}, {img_w}) of image." - ) - - w_start = (img_w - w) // 2 - h_start = (img_h - h) // 2 - - w_end = w_start + w - h_end = h_start + h - return img[h_start:h_end, w_start:w_end, :] diff --git a/python/rapid_structure/rapid_table/__init__.py b/python/rapid_structure/rapid_table/__init__.py deleted file mode 100644 index e4a5ed5..0000000 --- a/python/rapid_structure/rapid_table/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# -*- encoding: utf-8 -*- -# @Author: SWHL -# @Contact: liekkaskono@163.com -from .rapid_table import RapidTable diff --git a/python/rapid_structure/rapid_table/models/.gitkeep b/python/rapid_structure/rapid_table/models/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/python/rapid_structure/rapid_table/rapid_table.py b/python/rapid_structure/rapid_table/rapid_table.py deleted file mode 100644 index 27027e6..0000000 --- a/python/rapid_structure/rapid_table/rapid_table.py +++ /dev/null @@ -1,95 +0,0 @@ -# -*- encoding: utf-8 -*- -# @Author: SWHL -# @Contact: liekkaskono@163.com -import argparse -import copy -import time -from pathlib import Path - -import cv2 -import numpy as np -from rapidocr_onnxruntime import RapidOCR - -from .table_matcher import TableMatch -from .table_structure import TableStructurer - -root_dir = Path(__file__).resolve().parent - - -class RapidTable(): - def __init__(self, model_path: str = None): - self.ocr_sys = RapidOCR() - - if model_path is None: - model_path = str(root_dir / 'models' / 'en_ppstructure_mobile_v2_SLANet.onnx') - - self.table_structure = TableStructurer(model_path) - self.table_matcher = TableMatch() - - def __call__(self, img): - s = time.time() - dt_boxes, rec_res = self._ocr(copy.deepcopy(img)) - - structure_res, _ = self.table_structure(copy.deepcopy(img)) - pred_html = self.table_matcher(structure_res, dt_boxes, rec_res) - - elapse = time.time() - s - return pred_html, elapse - - def _ocr(self, img): - h, w = img.shape[:2] - - ocr_result, _ = self.ocr_sys(img) - dt_boxes, rec_res, scores = list(zip(*ocr_result)) - rec_res = list(zip(rec_res, scores)) - - r_boxes = [] - for box in dt_boxes: - box = np.array(box) - x_min = max(0, box[:, 0].min() - 1) - x_max = min(w, box[:, 0].max() + 1) - y_min = max(0, box[:, 1].min() - 1) - y_max = min(h, box[:, 1].max() + 1) - box = [x_min, y_min, x_max, y_max] - r_boxes.append(box) - dt_boxes = np.array(r_boxes) - return dt_boxes, rec_res - - -def vis_table(table_res: str, save_path: str) -> None: - style_res = '''''' - prefix_table, suffix_table = table_res.split('') - new_table_res = f'{prefix_table}{style_res}{suffix_table}' - with open(save_path, 'w', encoding='utf-8') as f: - f.write(new_table_res) - print(f'The infer result has saved in {save_path}') - - -def main() -> None: - parser = argparse.ArgumentParser() - parser.add_argument('-v', '--vis', action='store_true', - help='Wheter to visualize the layout results.') - parser.add_argument('-img', '--img_path', type=str, required=True, - help='Path to image for layout.') - parser.add_argument('-m', '--model_path', type=str, - default=str(root_dir / 'models' / 'en_ppstructure_mobile_v2_SLANet.onnx'), - help='The model path used for inference.') - args = parser.parse_args() - - rapid_table = RapidTable(args.model_path) - - img = cv2.imread(args.img_path) - table_html_str, elapse = rapid_table(img) - print(table_html_str) - - if args.vis: - img_path = Path(args.img_path) - save_path = img_path.resolve().parent / f'vis_{img_path.stem}.html' - vis_table(table_html_str, str(save_path)) - - -if __name__ == '__main__': - main() diff --git a/python/rapid_structure/rapid_table/table_matcher/__init__.py b/python/rapid_structure/rapid_table/table_matcher/__init__.py deleted file mode 100644 index 9bff7d7..0000000 --- a/python/rapid_structure/rapid_table/table_matcher/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# -*- encoding: utf-8 -*- -# @Author: SWHL -# @Contact: liekkaskono@163.com -from .matcher import TableMatch diff --git a/python/rapid_structure/rapid_table/table_matcher/matcher.py b/python/rapid_structure/rapid_table/table_matcher/matcher.py deleted file mode 100644 index d60ddb1..0000000 --- a/python/rapid_structure/rapid_table/table_matcher/matcher.py +++ /dev/null @@ -1,158 +0,0 @@ -# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -*- encoding: utf-8 -*- -import numpy as np - -from .utils import compute_iou, deal_bb, distance - - -class TableMatch(): - def __init__(self, filter_ocr_result=True, use_master=False): - self.filter_ocr_result = filter_ocr_result - self.use_master = use_master - - def __call__(self, structure_res, dt_boxes, rec_res): - pred_structures, pred_bboxes = structure_res - if self.filter_ocr_result: - dt_boxes, rec_res = self._filter_ocr_result(pred_bboxes, dt_boxes, - rec_res) - matched_index = self.match_result(dt_boxes, pred_bboxes) - pred_html, pred = self.get_pred_html(pred_structures, - matched_index, - rec_res) - return pred_html - - def match_result(self, dt_boxes, pred_bboxes): - matched = {} - for i, gt_box in enumerate(dt_boxes): - distances = [] - for j, pred_box in enumerate(pred_bboxes): - if len(pred_box) == 8: - pred_box = [ - np.min(pred_box[0::2]), np.min(pred_box[1::2]), - np.max(pred_box[0::2]), np.max(pred_box[1::2]) - ] - distances.append((distance(gt_box, pred_box), - 1. - compute_iou(gt_box, pred_box) - )) # compute iou and l1 distance - sorted_distances = distances.copy() - # select det box by iou and l1 distance - sorted_distances = sorted( - sorted_distances, key=lambda item: (item[1], item[0])) - if distances.index(sorted_distances[0]) not in matched.keys(): - matched[distances.index(sorted_distances[0])] = [i] - else: - matched[distances.index(sorted_distances[0])].append(i) - return matched - - def get_pred_html(self, pred_structures, matched_index, ocr_contents): - end_html = [] - td_index = 0 - for tag in pred_structures: - if '' in tag: - if '' == tag: - end_html.extend('') - if td_index in matched_index.keys(): - b_with = False - if '' in ocr_contents[matched_index[td_index][0]] \ - and len(matched_index[td_index]) > 1: - b_with = True - end_html.extend('') - for i, td_index_index in enumerate(matched_index[td_index]): - content = ocr_contents[td_index_index][0] - if len(matched_index[td_index]) > 1: - if len(content) == 0: - continue - - if content[0] == ' ': - content = content[1:] - - if '' in content: - content = content[3:] - - if '' in content: - content = content[:-4] - - if len(content) == 0: - continue - - if i != len(matched_index[td_index]) - 1 \ - and ' ' != content[-1]: - content += ' ' - end_html.extend(content) - if b_with: - end_html.extend('') - - if '' == tag: - end_html.append('') - else: - end_html.append(tag) - td_index += 1 - else: - end_html.append(tag) - return ''.join(end_html), end_html - - def get_pred_html_master(self, pred_structures, matched_index, - ocr_contents): - end_html = [] - td_index = 0 - for token in pred_structures: - if '' in token: - txt = '' - b_with = False - if td_index in matched_index.keys(): - if '' in ocr_contents[matched_index[td_index][ - 0]] and len(matched_index[td_index]) > 1: - b_with = True - for i, td_index_index in enumerate(matched_index[td_index]): - content = ocr_contents[td_index_index][0] - if len(matched_index[td_index]) > 1: - if len(content) == 0: - continue - if content[0] == ' ': - content = content[1:] - if '' in content: - content = content[3:] - if '' in content: - content = content[:-4] - if len(content) == 0: - continue - if i != len(matched_index[ - td_index]) - 1 and ' ' != content[-1]: - content += ' ' - txt += content - if b_with: - txt = '{}'.format(txt) - if '' == token: - token = '{}'.format(txt) - else: - token = '{}'.format(txt) - td_index += 1 - token = deal_eb_token(token) - end_html.append(token) - html = ''.join(end_html) - html = deal_bb(html) - return html, end_html - - def _filter_ocr_result(self, pred_bboxes, dt_boxes, rec_res): - y1 = pred_bboxes[:, 1::2].min() - new_dt_boxes = [] - new_rec_res = [] - - for box, rec in zip(dt_boxes, rec_res): - if np.max(box[1::2]) < y1: - continue - new_dt_boxes.append(box) - new_rec_res.append(rec) - return new_dt_boxes, new_rec_res diff --git a/python/rapid_structure/rapid_table/table_matcher/utils.py b/python/rapid_structure/rapid_table/table_matcher/utils.py deleted file mode 100644 index bb2b0cb..0000000 --- a/python/rapid_structure/rapid_table/table_matcher/utils.py +++ /dev/null @@ -1,244 +0,0 @@ -# copyright (c) 2022 PaddlePaddle Authors. All Rights Reserve. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -*- encoding: utf-8 -*- -# -*- encoding: utf-8 -*- -# @Author: SWHL -# @Contact: liekkaskono@163.com -import re - - -def deal_isolate_span(thead_part): - """ - Deal with isolate span cases in this function. - It causes by wrong prediction in structure recognition model. - eg. predict to rowspan="2">. - :param thead_part: - :return: - """ - # 1. find out isolate span tokens. - isolate_pattern = " rowspan=\"(\d)+\" colspan=\"(\d)+\">|" \ - " colspan=\"(\d)+\" rowspan=\"(\d)+\">|" \ - " rowspan=\"(\d)+\">|" \ - " colspan=\"(\d)+\">" - isolate_iter = re.finditer(isolate_pattern, thead_part) - isolate_list = [i.group() for i in isolate_iter] - - # 2. find out span number, by step 1 results. - span_pattern = " rowspan=\"(\d)+\" colspan=\"(\d)+\"|" \ - " colspan=\"(\d)+\" rowspan=\"(\d)+\"|" \ - " rowspan=\"(\d)+\"|" \ - " colspan=\"(\d)+\"" - corrected_list = [] - for isolate_item in isolate_list: - span_part = re.search(span_pattern, isolate_item) - spanStr_in_isolateItem = span_part.group() - # 3. merge the span number into the span token format string. - if spanStr_in_isolateItem is not None: - corrected_item = ''.format(spanStr_in_isolateItem) - corrected_list.append(corrected_item) - else: - corrected_list.append(None) - - # 4. replace original isolated token. - for corrected_item, isolate_item in zip(corrected_list, isolate_list): - if corrected_item is not None: - thead_part = thead_part.replace(isolate_item, corrected_item) - else: - pass - return thead_part - - -def deal_duplicate_bb(thead_part): - """ - Deal duplicate or after replace. - Keep one in a token. - :param thead_part: - :return: - """ - # 1. find out in . - td_pattern = "(.+?)|" \ - "(.+?)|" \ - "(.+?)|" \ - "(.+?)|" \ - "(.*?)" - td_iter = re.finditer(td_pattern, thead_part) - td_list = [t.group() for t in td_iter] - - # 2. is multiply in or not? - new_td_list = [] - for td_item in td_list: - if td_item.count('') > 1 or td_item.count('') > 1: - # multiply in case. - # 1. remove all - td_item = td_item.replace('', '').replace('', '') - # 2. replace -> , -> . - td_item = td_item.replace('', '').replace('', - '') - new_td_list.append(td_item) - else: - new_td_list.append(td_item) - - # 3. replace original thead part. - for td_item, new_td_item in zip(td_list, new_td_list): - thead_part = thead_part.replace(td_item, new_td_item) - return thead_part - - -def deal_bb(result_token): - """ - In our opinion, always occurs in text's context. - This function will find out all tokens in and insert by manual. - :param result_token: - :return: - """ - # find out parts. - thead_pattern = '(.*?)' - if re.search(thead_pattern, result_token) is None: - return result_token - thead_part = re.search(thead_pattern, result_token).group() - origin_thead_part = copy.deepcopy(thead_part) - - # check "rowspan" or "colspan" occur in parts or not . - span_pattern = "|||" - span_iter = re.finditer(span_pattern, thead_part) - span_list = [s.group() for s in span_iter] - has_span_in_head = True if len(span_list) > 0 else False - - if not has_span_in_head: - # not include "rowspan" or "colspan" branch 1. - # 1. replace to , and to - # 2. it is possible to predict text include or by Text-line recognition, - # so we replace to , and to - thead_part = thead_part.replace('', '')\ - .replace('', '')\ - .replace('', '')\ - .replace('', '') - else: - # include "rowspan" or "colspan" branch 2. - # Firstly, we deal rowspan or colspan cases. - # 1. replace > to > - # 2. replace to - # 3. it is possible to predict text include or by Text-line recognition, - # so we replace to , and to - - # Secondly, deal ordinary cases like branch 1 - - # replace ">" to "" - replaced_span_list = [] - for sp in span_list: - replaced_span_list.append(sp.replace('>', '>')) - for sp, rsp in zip(span_list, replaced_span_list): - thead_part = thead_part.replace(sp, rsp) - - # replace "" to "" - thead_part = thead_part.replace('', '') - - # remove duplicated by re.sub - mb_pattern = "()+" - single_b_string = "" - thead_part = re.sub(mb_pattern, single_b_string, thead_part) - - mgb_pattern = "()+" - single_gb_string = "" - thead_part = re.sub(mgb_pattern, single_gb_string, thead_part) - - # ordinary cases like branch 1 - thead_part = thead_part.replace('', '').replace('', - '') - - # convert back to , empty cell has no . - # but space cell( ) is suitable for - thead_part = thead_part.replace('', '') - # deal with duplicated - thead_part = deal_duplicate_bb(thead_part) - # deal with isolate span tokens, which causes by wrong predict by structure prediction. - # eg.PMC5994107_011_00.png - thead_part = deal_isolate_span(thead_part) - # replace original result with new thead part. - result_token = result_token.replace(origin_thead_part, thead_part) - return result_token - - -def deal_eb_token(master_token): - """ - post process with , , ... - emptyBboxTokenDict = { - "[]": '', - "[' ']": '', - "['', ' ', '']": '', - "['\\u2028', '\\u2028']": '', - "['', ' ', '']": '', - "['', '']": '', - "['', ' ', '']": '', - "['', '', '', '']": '', - "['', '', ' ', '', '']": '', - "['', '']": '', - "['', ' ', '\\u2028', ' ', '\\u2028', ' ', '']": '', - } - :param master_token: - :return: - """ - master_token = master_token.replace('', '') - master_token = master_token.replace('', ' ') - master_token = master_token.replace('', ' ') - master_token = master_token.replace('', '\u2028\u2028') - master_token = master_token.replace('', ' ') - master_token = master_token.replace('', '') - master_token = master_token.replace('', ' ') - master_token = master_token.replace('', - '') - master_token = master_token.replace('', - ' ') - master_token = master_token.replace('', '') - master_token = master_token.replace('', - ' \u2028 \u2028 ') - return master_token - - -def distance(box_1, box_2): - x1, y1, x2, y2 = box_1 - x3, y3, x4, y4 = box_2 - dis = abs(x3 - x1) + abs(y3 - y1) + abs(x4 - x2) + abs(y4 - y2) - dis_2 = abs(x3 - x1) + abs(y3 - y1) - dis_3 = abs(x4 - x2) + abs(y4 - y2) - return dis + min(dis_2, dis_3) - - -def compute_iou(rec1, rec2): - """ - computing IoU - :param rec1: (y0, x0, y1, x1), which reflects - (top, left, bottom, right) - :param rec2: (y0, x0, y1, x1) - :return: scala value of IoU - """ - # computing area of each rectangles - S_rec1 = (rec1[2] - rec1[0]) * (rec1[3] - rec1[1]) - S_rec2 = (rec2[2] - rec2[0]) * (rec2[3] - rec2[1]) - - # computing the sum_area - sum_area = S_rec1 + S_rec2 - - # find the each edge of intersect rectangle - left_line = max(rec1[1], rec2[1]) - right_line = min(rec1[3], rec2[3]) - top_line = max(rec1[0], rec2[0]) - bottom_line = min(rec1[2], rec2[2]) - - # judge if there is an intersect - if left_line >= right_line or top_line >= bottom_line: - return 0.0 - else: - intersect = (right_line - left_line) * (bottom_line - top_line) - return (intersect / (sum_area - intersect)) * 1.0 diff --git a/python/rapid_structure/rapid_table/table_structure/__init__.py b/python/rapid_structure/rapid_table/table_structure/__init__.py deleted file mode 100644 index a548391..0000000 --- a/python/rapid_structure/rapid_table/table_structure/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from .table_structure import TableStructurer diff --git a/python/rapid_structure/rapid_table/table_structure/table_structure.py b/python/rapid_structure/rapid_table/table_structure/table_structure.py deleted file mode 100644 index f63fcb3..0000000 --- a/python/rapid_structure/rapid_table/table_structure/table_structure.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import time -import numpy as np - -from .utils import (OrtInferSession, TableLabelDecode, TablePreprocess) - - -class TableStructurer(): - def __init__(self, model_path: str): - self.preprocess_op = TablePreprocess() - - self.session = OrtInferSession(model_path) - - self.character = self.session.get_metadata() - self.postprocess_op = TableLabelDecode(self.character) - - def __call__(self, img): - starttime = time.time() - data = {'image': img} - data = self.preprocess_op(data) - img = data[0] - if img is None: - return None, 0 - img = np.expand_dims(img, axis=0) - img = img.copy() - - outputs = self.session(img) - - preds = { - 'loc_preds': outputs[0], - 'structure_probs': outputs[1] - } - - shape_list = np.expand_dims(data[-1], axis=0) - post_result = self.postprocess_op(preds, [shape_list]) - - bbox_list = post_result['bbox_batch_list'][0] - - structure_str_list = post_result['structure_batch_list'][0] - structure_str_list = structure_str_list[0] - structure_str_list = [ - '', '', '' - ] + structure_str_list + ['
', '', ''] - elapse = time.time() - starttime - return (structure_str_list, bbox_list), elapse diff --git a/python/rapid_structure/rapid_table/table_structure/utils.py b/python/rapid_structure/rapid_table/table_structure/utils.py deleted file mode 100644 index 1d27c9b..0000000 --- a/python/rapid_structure/rapid_table/table_structure/utils.py +++ /dev/null @@ -1,349 +0,0 @@ -# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -*- encoding: utf-8 -*- -# @Author: SWHL -# @Contact: liekkaskono@163.com -from pathlib import Path - -import cv2 -import numpy as np -from onnxruntime import (GraphOptimizationLevel, InferenceSession, - SessionOptions) - - -class OrtInferSession(): - def __init__(self, onnx_path: str): - self._verify_model(onnx_path) - - sess_opt = SessionOptions() - sess_opt.log_severity_level = 4 - sess_opt.enable_cpu_mem_arena = False - sess_opt.graph_optimization_level = GraphOptimizationLevel.ORT_ENABLE_ALL - - cpu_ep = 'CPUExecutionProvider' - cpu_provider_options = {"arena_extend_strategy": "kSameAsRequested",} - EP_list = [(cpu_ep, cpu_provider_options)] - - self._verify_model(onnx_path) - self.session = InferenceSession(onnx_path, - sess_options=sess_opt, - providers=EP_list) - - def __call__(self, input_content: np.ndarray) -> np.ndarray: - input_dict = dict(zip(self.get_input_names(), [input_content])) - try: - return self.session.run(self.get_output_names(), input_dict) - except Exception as e: - raise ONNXRuntimeError('ONNXRuntime inferece failed.') from e - - def get_input_names(self, ): - return [v.name for v in self.session.get_inputs()] - - def get_output_names(self,): - return [v.name for v in self.session.get_outputs()] - - def get_metadata(self, key: str = 'character') -> list: - meta_dict = self.session.get_modelmeta().custom_metadata_map - content_list = meta_dict[key].splitlines() - return content_list - - @staticmethod - def _verify_model(model_path): - model_path = Path(model_path) - if not model_path.exists(): - raise FileNotFoundError(f'{model_path} does not exists.') - if not model_path.is_file(): - raise FileExistsError(f'{model_path} is not a file.') - - -class ONNXRuntimeError(Exception): - pass - - -class TableLabelDecode(): - """ """ - def __init__(self, - dict_character, - merge_no_span_structure=True, - **kwargs): - if merge_no_span_structure: - if "" not in dict_character: - dict_character.append("") - if "" in dict_character: - dict_character.remove("") - - dict_character = self.add_special_char(dict_character) - self.dict = {} - for i, char in enumerate(dict_character): - self.dict[char] = i - self.character = dict_character - self.td_token = ['', ''] - - def __call__(self, preds, batch=None): - structure_probs = preds['structure_probs'] - bbox_preds = preds['loc_preds'] - shape_list = batch[-1] - result = self.decode(structure_probs, bbox_preds, shape_list) - if len(batch) == 1: # only contains shape - return result - - label_decode_result = self.decode_label(batch) - return result, label_decode_result - - def decode(self, structure_probs, bbox_preds, shape_list): - """convert text-label into text-index. - """ - ignored_tokens = self.get_ignored_tokens() - end_idx = self.dict[self.end_str] - - structure_idx = structure_probs.argmax(axis=2) - structure_probs = structure_probs.max(axis=2) - - structure_batch_list = [] - bbox_batch_list = [] - batch_size = len(structure_idx) - for batch_idx in range(batch_size): - structure_list = [] - bbox_list = [] - score_list = [] - for idx in range(len(structure_idx[batch_idx])): - char_idx = int(structure_idx[batch_idx][idx]) - if idx > 0 and char_idx == end_idx: - break - - if char_idx in ignored_tokens: - continue - - text = self.character[char_idx] - if text in self.td_token: - bbox = bbox_preds[batch_idx, idx] - bbox = self._bbox_decode(bbox, shape_list[batch_idx]) - bbox_list.append(bbox) - structure_list.append(text) - score_list.append(structure_probs[batch_idx, idx]) - structure_batch_list.append([structure_list, np.mean(score_list)]) - bbox_batch_list.append(np.array(bbox_list)) - result = { - 'bbox_batch_list': bbox_batch_list, - 'structure_batch_list': structure_batch_list, - } - return result - - def decode_label(self, batch): - """convert text-label into text-index. - """ - structure_idx = batch[1] - gt_bbox_list = batch[2] - shape_list = batch[-1] - ignored_tokens = self.get_ignored_tokens() - end_idx = self.dict[self.end_str] - - structure_batch_list = [] - bbox_batch_list = [] - batch_size = len(structure_idx) - for batch_idx in range(batch_size): - structure_list = [] - bbox_list = [] - for idx in range(len(structure_idx[batch_idx])): - char_idx = int(structure_idx[batch_idx][idx]) - if idx > 0 and char_idx == end_idx: - break - if char_idx in ignored_tokens: - continue - structure_list.append(self.character[char_idx]) - - bbox = gt_bbox_list[batch_idx][idx] - if bbox.sum() != 0: - bbox = self._bbox_decode(bbox, shape_list[batch_idx]) - bbox_list.append(bbox) - structure_batch_list.append(structure_list) - bbox_batch_list.append(bbox_list) - result = { - 'bbox_batch_list': bbox_batch_list, - 'structure_batch_list': structure_batch_list, - } - return result - - def _bbox_decode(self, bbox, shape): - h, w, ratio_h, ratio_w, pad_h, pad_w = shape - bbox[0::2] *= w - bbox[1::2] *= h - return bbox - - def get_ignored_tokens(self): - beg_idx = self.get_beg_end_flag_idx("beg") - end_idx = self.get_beg_end_flag_idx("end") - return [beg_idx, end_idx] - - def get_beg_end_flag_idx(self, beg_or_end): - if beg_or_end == "beg": - idx = np.array(self.dict[self.beg_str]) - elif beg_or_end == "end": - idx = np.array(self.dict[self.end_str]) - else: - assert False, "unsupport type %s in get_beg_end_flag_idx" \ - % beg_or_end - return idx - - def add_special_char(self, dict_character): - self.beg_str = "sos" - self.end_str = "eos" - dict_character = dict_character - dict_character = [self.beg_str] + dict_character + [self.end_str] - return dict_character - - -class TablePreprocess(): - def __init__(self): - self.table_max_len = 488 - self.build_pre_process_list() - self.ops = self.create_operators() - - def __call__(self, data): - """ transform """ - if self.ops is None: - self.ops = [] - - for op in self.ops: - data = op(data) - if data is None: - return None - return data - - def create_operators(self,): - """ - create operators based on the config - - Args: - params(list): a dict list, used to create some operators - """ - assert isinstance(self.pre_process_list, list), ('operator config should be a list') - ops = [] - for operator in self.pre_process_list: - assert isinstance(operator, - dict) and len(operator) == 1, "yaml format error" - op_name = list(operator)[0] - param = {} if operator[op_name] is None else operator[op_name] - op = eval(op_name)(**param) - ops.append(op) - return ops - - def build_pre_process_list(self): - resize_op = {'ResizeTableImage': {'max_len': self.table_max_len, }} - pad_op = { - 'PaddingTableImage': { - 'size': [self.table_max_len, self.table_max_len] - } - } - normalize_op = { - 'NormalizeImage': { - 'std': [0.229, 0.224, 0.225], - 'mean': [0.485, 0.456, 0.406], - 'scale': '1./255.', - 'order': 'hwc' - } - } - to_chw_op = {'ToCHWImage': None} - keep_keys_op = {'KeepKeys': {'keep_keys': ['image', 'shape']}} - self.pre_process_list = [ - resize_op, normalize_op, pad_op, to_chw_op, keep_keys_op - ] - - -class ResizeTableImage(): - def __init__(self, max_len, resize_bboxes=False, infer_mode=False, - **kwargs): - super(ResizeTableImage, self).__init__() - self.max_len = max_len - self.resize_bboxes = resize_bboxes - self.infer_mode = infer_mode - - def __call__(self, data): - img = data['image'] - height, width = img.shape[0:2] - ratio = self.max_len / (max(height, width) * 1.0) - resize_h = int(height * ratio) - resize_w = int(width * ratio) - resize_img = cv2.resize(img, (resize_w, resize_h)) - if self.resize_bboxes and not self.infer_mode: - data['bboxes'] = data['bboxes'] * ratio - data['image'] = resize_img - data['src_img'] = img - data['shape'] = np.array([height, width, ratio, ratio]) - data['max_len'] = self.max_len - return data - - -class PaddingTableImage(): - def __init__(self, size, **kwargs): - super(PaddingTableImage, self).__init__() - self.size = size - - def __call__(self, data): - img = data['image'] - pad_h, pad_w = self.size - padding_img = np.zeros((pad_h, pad_w, 3), dtype=np.float32) - height, width = img.shape[0:2] - padding_img[0:height, 0:width, :] = img.copy() - data['image'] = padding_img - shape = data['shape'].tolist() - shape.extend([pad_h, pad_w]) - data['shape'] = np.array(shape) - return data - - -class NormalizeImage(): - """ normalize image such as substract mean, divide std - """ - def __init__(self, scale=None, mean=None, std=None, order='chw', **kwargs): - if isinstance(scale, str): - scale = eval(scale) - self.scale = np.float32(scale if scale is not None else 1.0 / 255.0) - mean = mean if mean is not None else [0.485, 0.456, 0.406] - std = std if std is not None else [0.229, 0.224, 0.225] - - shape = (3, 1, 1) if order == 'chw' else (1, 1, 3) - self.mean = np.array(mean).reshape(shape).astype('float32') - self.std = np.array(std).reshape(shape).astype('float32') - - def __call__(self, data): - img = np.array(data['image']) - assert isinstance(img, - np.ndarray), "invalid input 'img' in NormalizeImage" - data['image'] = ( - img.astype('float32') * self.scale - self.mean) / self.std - return data - - -class ToCHWImage(): - """ convert hwc image to chw image - """ - def __init__(self, **kwargs): - pass - - def __call__(self, data): - img = np.array(data['image']) - data['image'] = img.transpose((2, 0, 1)) - return data - - -class KeepKeys(): - def __init__(self, keep_keys, **kwargs): - self.keep_keys = keep_keys - - def __call__(self, data): - data_list = [] - for key in self.keep_keys: - data_list.append(data[key]) - return data_list diff --git a/python/rapid_structure/requirements.txt b/python/rapid_structure/requirements.txt deleted file mode 100644 index e6c2ae9..0000000 --- a/python/rapid_structure/requirements.txt +++ /dev/null @@ -1,8 +0,0 @@ -numpy>=1.21.6 -opencv_python>=4.6.0.66 -setuptools>=61.2.0 -PyYAML -pytest>=7.2.1 -pip>=22.3.1 -onnxruntime -wheel \ No newline at end of file diff --git a/python/rapid_structure/setup_layout.py b/python/rapid_structure/setup_layout.py deleted file mode 100644 index c6e1ca2..0000000 --- a/python/rapid_structure/setup_layout.py +++ /dev/null @@ -1,87 +0,0 @@ -# -*- encoding: utf-8 -*- -# @Author: SWHL -# @Contact: liekkaskono@163.com -import re -import subprocess -import sys -from pathlib import Path - -import setuptools - - -def get_latest_version(package_name): - output = subprocess.run(["pip", "index", "versions", package_name], - capture_output=True) - output = output.stdout.decode('utf-8') - if output: - return extract_version(output) - return None - - -def version_add_one(version, add_loc=-1): - if version: - version_list = version.split('.') - mini_version = str(int(version_list[add_loc]) + 1) - version_list[add_loc] = mini_version - new_version = '.'.join(version_list) - return new_version - return '0.0.1' - - -def get_readme(): - root_dir = Path(__file__).resolve().parent.parent.parent - readme_path = str(root_dir / 'docs' / 'doc_whl_rapid_layout.md') - with open(readme_path, 'r', encoding='utf-8') as f: - readme = f.read() - return readme - - -def extract_version(message: str) -> str: - pattern = r'\d+\.(?:\d+\.)*\d+' - matched_versions = re.findall(pattern, message) - if matched_versions: - return matched_versions[0] - return '' - - -MODULE_NAME = 'rapid_layout' -latest_version = get_latest_version(MODULE_NAME) -VERSION_NUM = version_add_one(latest_version) - -# 优先提取commit message中的语义化版本号,如无,则自动加1 -if len(sys.argv) > 2: - match_str = ' '.join(sys.argv[2:]) - matched_versions = extract_version(match_str) - if matched_versions: - VERSION_NUM = matched_versions -sys.argv = sys.argv[:2] - -setuptools.setup( - name=MODULE_NAME, - version=VERSION_NUM, - platforms="Any", - long_description=get_readme(), - long_description_content_type='text/markdown', - description='Tools for document layout analysis based ONNXRuntime.', - author="SWHL", - author_email="liekkaskono@163.com", - url="https://github.com/RapidAI/RapidOCR", - license='Apache-2.0', - include_package_data=True, - install_requires=["onnxruntime>=1.7.0", "PyYAML>=6.0", - "opencv_python>=4.5.1.48", "numpy>=1.21.6"], - packages=[MODULE_NAME, f'{MODULE_NAME}.models'], - package_data={'': ['layout_cdla.onnx', '*.yaml']}, - keywords=[ - 'ppstructure,layout,rapidocr,rapid_layout' - ], - classifiers=[ - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - ], - entry_points={ - 'console_scripts': [f'{MODULE_NAME}={MODULE_NAME}.{MODULE_NAME}:main'] - } -) diff --git a/python/rapid_structure/setup_orientation.py b/python/rapid_structure/setup_orientation.py deleted file mode 100644 index 64bdf5b..0000000 --- a/python/rapid_structure/setup_orientation.py +++ /dev/null @@ -1,87 +0,0 @@ -# -*- encoding: utf-8 -*- -# @Author: SWHL -# @Contact: liekkaskono@163.com -import re -import subprocess -import sys -from pathlib import Path - -import setuptools - - -def get_latest_version(package_name): - output = subprocess.run(["pip", "index", "versions", package_name], - capture_output=True) - output = output.stdout.decode('utf-8') - if output: - return extract_version(output) - return None - - -def version_add_one(version, add_loc=-1): - if version: - version_list = version.split('.') - mini_version = str(int(version_list[add_loc]) + 1) - version_list[add_loc] = mini_version - new_version = '.'.join(version_list) - return new_version - return '0.0.1' - - -def get_readme(): - root_dir = Path(__file__).resolve().parent.parent.parent - readme_path = str(root_dir / 'docs' / 'doc_whl_rapid_orientation.md') - with open(readme_path, 'r', encoding='utf-8') as f: - readme = f.read() - return readme - - -def extract_version(message: str) -> str: - pattern = r'\d+\.(?:\d+\.)*\d+' - matched_versions = re.findall(pattern, message) - if matched_versions: - return matched_versions[0] - return '' - - -MODULE_NAME = 'rapid_orientation' -latest_version = get_latest_version(MODULE_NAME) -VERSION_NUM = version_add_one(latest_version) - -# 优先提取commit message中的语义化版本号,如无,则自动加1 -if len(sys.argv) > 2: - match_str = ' '.join(sys.argv[2:]) - matched_versions = extract_version(match_str) - if matched_versions: - VERSION_NUM = matched_versions -sys.argv = sys.argv[:2] - -setuptools.setup( - name=MODULE_NAME, - version=VERSION_NUM, - platforms="Any", - long_description=get_readme(), - long_description_content_type='text/markdown', - description='Tools for classifying the direction of images containing text based ONNXRuntime.', - author="SWHL", - author_email="liekkaskono@163.com", - url="https://github.com/RapidAI/RapidOCR", - license='Apache-2.0', - include_package_data=True, - install_requires=["onnxruntime>=1.7.0", "PyYAML>=6.0", - "opencv_python>=4.5.1.48", "numpy>=1.21.6"], - packages=[MODULE_NAME, f'{MODULE_NAME}.models'], - package_data={'': ['*.onnx', '*.yaml']}, - keywords=[ - 'ppstructure,layout,rapidocr,rapid_orientation' - ], - classifiers=[ - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - ], - entry_points={ - 'console_scripts': [f'{MODULE_NAME}={MODULE_NAME}.{MODULE_NAME}:main'] - } -) diff --git a/python/rapid_structure/setup_table.py b/python/rapid_structure/setup_table.py deleted file mode 100644 index 50fa369..0000000 --- a/python/rapid_structure/setup_table.py +++ /dev/null @@ -1,91 +0,0 @@ -# -*- encoding: utf-8 -*- -# @Author: SWHL -# @Contact: liekkaskono@163.com -import re -import subprocess -import sys -from pathlib import Path - -import setuptools - - -def get_latest_version(package_name): - output = subprocess.run(["pip", "index", "versions", package_name], - capture_output=True) - output = output.stdout.decode('utf-8') - if output: - return extract_version(output) - return None - - -def version_add_one(version, add_loc=-1): - if version: - version_list = version.split('.') - mini_version = str(int(version_list[add_loc]) + 1) - version_list[add_loc] = mini_version - new_version = '.'.join(version_list) - return new_version - return '0.0.1' - - -def get_readme(): - root_dir = Path(__file__).resolve().parent.parent.parent - readme_path = str(root_dir / 'docs' / 'doc_whl_rapid_table.md') - with open(readme_path, 'r', encoding='utf-8') as f: - readme = f.read() - return readme - - -def extract_version(message: str) -> str: - pattern = r'\d+\.(?:\d+\.)*\d+' - matched_versions = re.findall(pattern, message) - if matched_versions: - return matched_versions[0] - return '' - - -MODULE_NAME = 'rapid_table' -latest_version = get_latest_version(MODULE_NAME) -VERSION_NUM = version_add_one(latest_version) - -# 优先提取commit message中的语义化版本号,如无,则自动加1 -if len(sys.argv) > 2: - match_str = ' '.join(sys.argv[2:]) - matched_versions = extract_version(match_str) - if matched_versions: - VERSION_NUM = matched_versions -sys.argv = sys.argv[:2] - -setuptools.setup( - name=MODULE_NAME, - version=VERSION_NUM, - platforms="Any", - long_description=get_readme(), - long_description_content_type='text/markdown', - description='Tools for parsing table structures based ONNXRuntime.', - author="SWHL", - author_email="liekkaskono@163.com", - url="https://github.com/RapidAI/RapidOCR", - license='Apache-2.0', - include_package_data=True, - install_requires=["onnxruntime>=1.7.0", "PyYAML>=6.0", - "opencv_python>=4.5.1.48", "numpy>=1.21.6", - "rapidocr_onnxruntime>=1.1.18"], - packages=[MODULE_NAME, - f'{MODULE_NAME}.models', - f'{MODULE_NAME}.table_matcher', - f'{MODULE_NAME}.table_structure'], - package_data={'': ['en_ppstructure_mobile_v2_SLANet.onnx']}, - keywords=[ - 'ppstructure,table,rapidocr,rapid_table' - ], - classifiers=[ - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - ], - entry_points={ - 'console_scripts': [f'{MODULE_NAME}={MODULE_NAME}.{MODULE_NAME}:main'] - } -) diff --git a/python/rapid_structure/test_images/layout.png b/python/rapid_structure/test_images/layout.png deleted file mode 100644 index 29a1dc5..0000000 Binary files a/python/rapid_structure/test_images/layout.png and /dev/null differ diff --git a/python/rapid_structure/test_images/table.jpg b/python/rapid_structure/test_images/table.jpg deleted file mode 100644 index 95fdf84..0000000 Binary files a/python/rapid_structure/test_images/table.jpg and /dev/null differ diff --git a/python/rapid_structure/tests/test_files/img_rot0_demo.jpg b/python/rapid_structure/tests/test_files/img_rot0_demo.jpg deleted file mode 100644 index 412d419..0000000 Binary files a/python/rapid_structure/tests/test_files/img_rot0_demo.jpg and /dev/null differ diff --git a/python/rapid_structure/tests/test_files/img_rot180_demo.jpg b/python/rapid_structure/tests/test_files/img_rot180_demo.jpg deleted file mode 100644 index f4725b9..0000000 Binary files a/python/rapid_structure/tests/test_files/img_rot180_demo.jpg and /dev/null differ diff --git a/python/rapid_structure/tests/test_layout.py b/python/rapid_structure/tests/test_layout.py deleted file mode 100644 index 757c427..0000000 --- a/python/rapid_structure/tests/test_layout.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- encoding: utf-8 -*- -# @Author: SWHL -# @Contact: liekkaskono@163.com -import sys -from pathlib import Path - -import cv2 - -root_dir = Path(__file__).resolve().parent.parent - -sys.path.append(str(root_dir)) - -from rapid_layout import RapidLayout - - -def test_layout(): - img_path = str(root_dir / 'test_images' / 'layout.png') - img = cv2.imread(img_path) - - layout_engine = RapidLayout() - - layout_res, elapse = layout_engine(img) - - assert len(layout_res) == 13 diff --git a/python/rapid_structure/tests/test_orientation.py b/python/rapid_structure/tests/test_orientation.py deleted file mode 100644 index f1c1dca..0000000 --- a/python/rapid_structure/tests/test_orientation.py +++ /dev/null @@ -1,28 +0,0 @@ -# -*- encoding: utf-8 -*- -# @Author: SWHL -# @Contact: liekkaskono@163.com -import sys -from pathlib import Path - -import cv2 -import pytest - -root_dir = Path(__file__).resolve().parent -sys.path.append(str(root_dir.parent)) - -from rapid_orientation import RapidOrientation -text_orientation = RapidOrientation() - - -@pytest.mark.parametrize( - 'img_path, result', - [ - (f'{root_dir}/test_files/img_rot0_demo.jpg', '0'), - (f'{root_dir}/test_files/img_rot180_demo.jpg', '180'), - ] -) -def test_img(img_path, result): - img = cv2.imread(str(img_path)) - pred_result, _ = text_orientation(img) - - assert pred_result == result diff --git a/python/rapid_structure/tests/test_table.py b/python/rapid_structure/tests/test_table.py deleted file mode 100644 index a73e203..0000000 --- a/python/rapid_structure/tests/test_table.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- encoding: utf-8 -*- -# @Author: SWHL -# @Contact: liekkaskono@163.com -import sys -from pathlib import Path - -import cv2 - -root_dir = Path(__file__).resolve().parent.parent - -sys.path.append(str(root_dir)) - -from rapid_table import RapidTable - - -def test_table(): - img_path = str(root_dir / 'test_images' / 'table.jpg') - img = cv2.imread(img_path) - - rapid_table = RapidTable() - - table_html_str, elapse = rapid_table(img) - assert table_html_str.count('') == 16