Files
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 11:59:26 +08:00

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")