e904b667c6
Build/Publish Develop Docs / deploy (push) Failing after 1s
PaddleOCR Code Style Check / check-code-style (push) Failing after 1s
PaddleOCR PR Tests GPU / detect-changes (push) Failing after 1s
PaddleOCR PR Tests / detect-changes (push) Failing after 1s
PaddleOCR PR Tests GPU / test-pr-gpu (push) Has been cancelled
PaddleOCR PR Tests / test-pr (push) Has been cancelled
PaddleOCR PR Tests GPU / test-pr-gpu-impl (push) Has been cancelled
PaddleOCR PR Tests / test-pr-python (3.13) (push) Has been cancelled
PaddleOCR PR Tests / test-pr-python (3.8) (push) Has been cancelled
PaddleOCR PR Tests / test-pr-python (3.9) (push) Has been cancelled
134 lines
4.5 KiB
Python
134 lines
4.5 KiB
Python
# Copyright (c) 2025 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.
|
|
|
|
"""
|
|
Patches for paddlex layout parsing utilities to fix:
|
|
- Integer overflow in calculate_overlap_ratio when bounding box coordinates
|
|
are large (e.g. after doc_unwarping), causing RuntimeWarning and incorrect
|
|
overlap calculations.
|
|
- ValueError in calculate_minimum_enclosing_bbox when the bounding box list
|
|
is empty, which can happen when overflow causes all overlap matches to fail.
|
|
|
|
See: https://github.com/PaddlePaddle/PaddleOCR/issues/17503
|
|
"""
|
|
|
|
import logging
|
|
|
|
import numpy as np
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
_patched = False
|
|
|
|
|
|
def _fixed_calculate_overlap_ratio(bbox1, bbox2, mode="union"):
|
|
"""
|
|
Calculate the overlap ratio between two bounding boxes.
|
|
|
|
This version casts coordinates to float64 before computing areas to
|
|
prevent integer overflow when bounding box coordinates are large
|
|
(e.g. after document unwarping).
|
|
"""
|
|
bbox1 = np.array(bbox1, dtype=np.float64)
|
|
bbox2 = np.array(bbox2, dtype=np.float64)
|
|
|
|
x_min_inter = np.maximum(bbox1[0], bbox2[0])
|
|
y_min_inter = np.maximum(bbox1[1], bbox2[1])
|
|
x_max_inter = np.minimum(bbox1[2], bbox2[2])
|
|
y_max_inter = np.minimum(bbox1[3], bbox2[3])
|
|
|
|
inter_width = np.maximum(0, x_max_inter - x_min_inter)
|
|
inter_height = np.maximum(0, y_max_inter - y_min_inter)
|
|
|
|
inter_area = inter_width * inter_height
|
|
|
|
bbox1_area = abs((bbox1[2] - bbox1[0]) * (bbox1[3] - bbox1[1]))
|
|
bbox2_area = abs((bbox2[2] - bbox2[0]) * (bbox2[3] - bbox2[1]))
|
|
|
|
if mode == "union":
|
|
ref_area = bbox1_area + bbox2_area - inter_area
|
|
elif mode == "small":
|
|
ref_area = np.minimum(bbox1_area, bbox2_area)
|
|
elif mode == "large":
|
|
ref_area = np.maximum(bbox1_area, bbox2_area)
|
|
else:
|
|
raise ValueError(
|
|
f"Invalid mode {mode}, must be one of ['union', 'small', 'large']."
|
|
)
|
|
|
|
if ref_area == 0:
|
|
return 0.0
|
|
|
|
return inter_area / ref_area
|
|
|
|
|
|
def _fixed_calculate_minimum_enclosing_bbox(bboxes):
|
|
"""
|
|
Calculate the minimum enclosing bounding box for a list of bounding boxes.
|
|
|
|
This version returns a zero-area bounding box at the origin instead of
|
|
raising ValueError when the list is empty, allowing the caller to
|
|
continue without crashing.
|
|
"""
|
|
if not bboxes:
|
|
logger.debug(
|
|
"calculate_minimum_enclosing_bbox received an empty list; "
|
|
"returning a degenerate bounding box"
|
|
)
|
|
return np.array([0, 0, 0, 0])
|
|
|
|
bboxes_array = np.array(bboxes)
|
|
|
|
min_x = np.min(bboxes_array[:, 0])
|
|
min_y = np.min(bboxes_array[:, 1])
|
|
max_x = np.max(bboxes_array[:, 2])
|
|
max_y = np.max(bboxes_array[:, 3])
|
|
|
|
return np.array([min_x, min_y, max_x, max_y])
|
|
|
|
|
|
def apply_patches():
|
|
"""
|
|
Apply patches to paddlex layout parsing utilities to fix integer overflow
|
|
and empty bounding box errors.
|
|
|
|
This function is idempotent and safe to call multiple times.
|
|
"""
|
|
global _patched
|
|
if _patched:
|
|
return
|
|
|
|
try:
|
|
import paddlex.inference.pipelines.layout_parsing.utils as lp_utils
|
|
import paddlex.inference.pipelines.layout_parsing.pipeline_v2 as lp_pipeline
|
|
except ImportError:
|
|
logger.debug("paddlex layout parsing modules not available; skipping patches")
|
|
return
|
|
|
|
# Patch the utils module
|
|
lp_utils.calculate_overlap_ratio = _fixed_calculate_overlap_ratio
|
|
lp_utils.calculate_minimum_enclosing_bbox = _fixed_calculate_minimum_enclosing_bbox
|
|
|
|
# Also patch the references imported directly into pipeline_v2, since
|
|
# Python binds names at import time
|
|
if hasattr(lp_pipeline, "calculate_overlap_ratio"):
|
|
lp_pipeline.calculate_overlap_ratio = _fixed_calculate_overlap_ratio
|
|
if hasattr(lp_pipeline, "calculate_minimum_enclosing_bbox"):
|
|
lp_pipeline.calculate_minimum_enclosing_bbox = (
|
|
_fixed_calculate_minimum_enclosing_bbox
|
|
)
|
|
|
|
_patched = True
|
|
logger.debug("Applied layout parsing patches for issue #17503")
|