PaddleOCR 大图切片(Slice)操作实战:原理、参数调优与完整示例
【免费下载链接】PaddleOCR飞桨多语言OCR工具包(实用超轻量OCR系统,支持80+种语言识别,提供数据标注与合成工具,支持服务器、移动端、嵌入式及IoT设备端的训练与部署) Awesome multilingual OCR toolkits based on PaddlePaddle (practical ultra lightweight OCR system, support 80+ languages recognition, provide data annotation and synthesis tools, support training and deployment among server, mobile, embedded and IoT devices)项目地址: https://gitcode.com/paddlepaddle/PaddleOCR
PaddleOCR 在检测识别超大尺寸图像(如整页扫描文档、巨型海报、高分辨率工程图)时,直接送入模型容易受到det_limit_side_len等输入尺寸限制的影响,导致检测精度下降甚至失败。切片操作(slice operation)通过在原始大图上运行滑动窗口,把大图切成多个切片分别执行检测,再将切片级结果合并回图像级结果,从而实现对超大图像的完整 OCR 流程。本文将基于 切片操作文档 结合仓库源码,系统讲解切片机制的工作原理、四个核心参数的含义与推荐取值、底层合并算法,并给出可直接运行的完整示例。
一、为什么需要切片操作
PaddleOCR 的检测模型对输入图像有尺寸要求,通常由配置项det_limit_side_len控制(长边会先缩放后再送入模型)。当图像尺寸极大时,直接整图推理会带来两个问题:
- 超长文本行被截断:图像缩放到限制边长后,原本在一行内的长文本可能因为像素过密而无法被完整检测出来;
- 内存与耗时失控:超大图直接上采样或缩放推理,既影响精度也浪费算力。
切片操作将大图划分成若干较小的切片,每个切片独立运行检测与识别,规避了上述问题;随后通过坐标偏移与边界框合并,把散落在各切片中的零散检测结果重新组织成整图坐标系下的完整结果。
二、切片操作的基本用法
在 PaddleOCR 的 Python API 中,只需在调用ocr方法时传入slice字典参数即可启用切片:
ocr_inst = PaddleOCR(**ocr_settings) results = ocr_inst.ocr( img, det=True, rec=True, slice=slice, cls=False, bin=False, inv=False, alpha_color=False, )其中slice是一个包含四个用户可配置参数的字典:
slice = { 'horizontal_stride': h_stride, # 水平方向步幅 'vertical_stride': v_stride, # 垂直方向步幅 'merge_x_thres': x_thres, # 水平方向合并阈值 'merge_y_thres': y_thres, # 垂直方向合并阈值 }文档中以一张尺寸为6616x14886的超大图像为例,给出了推荐参数组合:
slice = {'horizontal_stride': 300, 'vertical_stride': 500, 'merge_x_thres': 50, 'merge_y_thres': 35}也就是说,该图像会按垂直步幅 500、水平步幅 300 被切成约ceil(14886/500) × ceil(6616/300) ≈ 30 × 23个切片,每个切片独立执行检测,再将结果合并。
三、切片机制的底层实现原理
1. 滑动窗口切片生成器
切片的核心生成逻辑位于 tools/infer/utility.py 的slice_generator函数。该函数是一个生成器,先根据步幅计算纵向与横向的切片数量:
vertical_num_slices = (image_h + vertical_stride - 1) // vertical_stride horizontal_num_slices = (image_w + horizontal_stride - 1) // horizontal_stride随后按行优先顺序逐块切出子图,并同时返回该切片在原图中的起始坐标v_start、h_start:
for v_slice_idx in range(vertical_num_slices): v_start = max(0, (v_slice_idx * vertical_stride)) v_end = min(((v_slice_idx + 1) * vertical_stride), image_h) vertical_slice = image[v_start:v_end, :] for h_slice_idx in range(horizontal_num_slices): h_start = max(0, (h_slice_idx * horizontal_stride)) h_end = min(((h_slice_idx + 1) * horizontal_stride), image_w) horizontal_slice = vertical_slice[:, h_start:h_end] yield (horizontal_slice, v_start, h_start)注意这里的切片是不重叠的:每个切片在水平/垂直方向上的长度恰好等于步幅(最后一个切片取图像剩余部分)。这种"步幅即切片尺寸"的设计意味着步幅决定了切片的粒度,而合并阈值负责把被切开的文本框重新拼回去。
2. 坐标偏移还原
切片检测得到的框坐标是相对切片左上角的局部坐标,必须还原到整图坐标系。这一步在 tools/infer/predict_system.py 的TextSystem.__call__中完成:
if slice: slice_gen = slice_generator( img, horizontal_stride=slice["horizontal_stride"], vertical_stride=slice["vertical_stride"], ) dt_slice_boxes = [] for slice_crop, v_start, h_start in slice_gen: dt_boxes, elapse = self.text_detector(slice_crop, use_slice=True) if dt_boxes.size: dt_boxes[:, :, 0] += h_start # 水平坐标偏移 dt_boxes[:, :, 1] += v_start # 垂直坐标偏移 dt_slice_boxes.append(dt_boxes) dt_boxes = np.concatenate(dt_slice_boxes)所有切片检测框经过坐标平移后拼接成一个完整的框集合,再交给merge_fragmented进行合并。识别阶段则复用常规流程:按合并后的框裁剪文本区域,送入识别器(text_recognizer)得到文本与置信度,最后通过drop_score过滤低分结果(见 predict_system.py)。
3. 合并阈值的工作方式
合并由 tools/infer/utility.py 的merge_boxes判断两个框是否应合并,判定条件为:
if ( abs(min_y1 - min_y2) <= y_threshold and abs(max_y1 - max_y2) <= y_threshold and abs(max_x1 - min_x2) <= x_threshold ):即两个框需要满足:上边界接近、下边界接近(纵向对齐),且 box1 的右边界到 box2 的左边界水平距离足够近——这正是"同一行文本被切片从中间切开"的典型形态。满足条件后,合并框取四边的极值构成新的外接矩形:
new_xmin = min(min_x1, min_x2) new_xmax = max(max_x1, max_x2) new_ymin = min(min_y1, min_y2) new_ymax = max(max_y1, max_y2)而 merge_fragmented 则对全部框执行迭代合并:每一轮遍历未访问的框,把可以合并的框吸收进当前合并框,直到一轮遍历中不再产生新合并(len(merged_boxes) == len(boxes))为止,从而支持跨多个切片的文本块逐级拼接。
4. 计算开销保护机制
slice_generator内置了maximum_slices=500的切片数量上限(见 utility.py)。如果按当前步幅计算的纵向或横向切片数达到 500,会直接断言报错并给出建议的最小步幅,防止切片数量过多导致计算成本失控:
if vertical_num_slices >= maximum_slices: recommended_vertical_stride = max(1, image_h // maximum_slices) + 1 assert False, ( f"Too computationally expensive with {vertical_num_slices} slices, " f"try a higher vertical stride (recommended minimum: {recommended_vertical_stride})" )这印证了文档中的警告:水平和垂直步幅不能低于一定限度,过低的值会产生太多切片,导致计算结果非常耗时。当步幅过小时,请根据报错信息中的 recommended minimum 增大步幅。
5. 检测器内部的极长宽比切片补充
值得一提的是,即使不显式传入slice参数,检测器在遇到极端长宽比图像时也会内部启用use_slice分支(见 tools/infer/predict_det.py):当高/宽 > 2且高度超过det_limit_side_len时按水平方向递归切分,当宽/高 > 3时按垂直方向递归切分,并对切分边界做"回滚缓冲"(MIN_BOUND_DISTANCE = 50)以避免文本块被切断。这说明 PaddleOCR 对长图处理有内外两层机制:检测器内部的自适应切分处理极端长宽比,外部显式切片操作则用于可控的、参数化的大图分块推理。
四、完整可运行示例
以下示例来自 快速开始文档 的切片用法,并补充了结果可视化:初始化启用角度分类的 OCR 实例后,对大图调用带slice的ocr方法,再把检测框与识别文本绘制回原图并保存:
from paddleocr import PaddleOCR from PIL import Image, ImageDraw, ImageFont # 初始化OCR引擎 ocr = PaddleOCR(use_angle_cls=True, lang="en") img_path = "./very_large_image.jpg" slice = {'horizontal_stride': 300, 'vertical_stride': 500, 'merge_x_thres': 50, 'merge_y_thres': 35} results = ocr.ocr(img_path, cls=True, slice=slice) # 加载图像 image = Image.open(img_path).convert("RGB") draw = ImageDraw.Draw(image) font = ImageFont.truetype("./doc/fonts/simfang.ttf", size=20) # 根据需要调整大小 # 处理并绘制结果 for res in results: for line in res: box = [tuple(point) for point in line[0]] # 找出边界框 box = [(min(point[0] for point in box), min(point[1] for point in box)), (max(point[0] for point in box), max(point[1] for point in box))] txt = line[1][0] draw.rectangle(box, outline="red", width=2) # 绘制矩形 draw.text((box[0][0], box[0][1] - 25), txt, fill="blue", font=font) # 在矩形上方绘制文本 # 保存结果 image.save("result.jpg")代码说明:
PaddleOCR(use_angle_cls=True, lang="en"):启用方向分类器,并将语言设为英文;如需识别中文可改为lang="ch";ocr.ocr(img_path, cls=True, slice=slice):cls=True表示在识别前对裁剪区域执行方向分类,slice传入切片参数;- 结果结构为
[[[box_points, (text, score)], ...], ...],box_points为四个角点坐标,(text, score)为识别文本与置信度; - 绘制时取角点坐标的最小/最大值构造轴对齐矩形,并把文本绘制在矩形上方。
五、参数调优建议
结合文档与源码,四个参数的调优遵循以下原则:
| 参数 | 作用 | 调优建议 |
|---|---|---|
horizontal_stride | 水平方向步幅,决定水平切片尺寸与数量 | 值越小切片越多、越耗时;值过大则长文本行可能超出切片宽度而被切断。建议从 300 起步,观察报错信息中的 recommended minimum |
vertical_stride | 垂直方向步幅,决定垂直切片尺寸与数量 | 同理,值越小越耗时;推荐从 500 起步。可参考 utility.py 中maximum_slices=500的保护逻辑 |
merge_x_thres | 水平合并阈值,同一直线上的相邻切片框水平距离小于该值则合并 | 应略大于文本行高量级,取 50 可覆盖大多数被切断的短行;过大可能误合并不同文本块 |
merge_y_thres | 垂直合并阈值,两个框的上下边界差小于该值才视为同一行 | 取 35 左右,保证同一切片边界上下两半的行对齐;过大会把不同行的文本错误拼接 |
提示:切片本身不重叠(步幅即切片边长),因此被切断的文本行完全依赖
merge_x_thres/merge_y_thres复原。如果发现长行被切成两段,优先增大合并阈值;如果切片数量触发 500 上限报错,则优先增大对应方向的步幅。
六、小结
切片操作是 PaddleOCR 处理超大图像的标准化方案:slice_generator负责滑动窗口切图,merge_fragmented负责按行对齐与水平邻近关系把碎片框合并回完整文本框,TextSystem统一完成坐标还原、裁剪识别与结果过滤。理解 slice_generator、merge_boxes 与 merge_fragmented 三个函数的行为,即可针对不同尺寸的文档图像快速定出合理的步幅与合并阈值,获得稳定可靠的整图 OCR 结果。
【免费下载链接】PaddleOCR飞桨多语言OCR工具包(实用超轻量OCR系统,支持80+种语言识别,提供数据标注与合成工具,支持服务器、移动端、嵌入式及IoT设备端的训练与部署) Awesome multilingual OCR toolkits based on PaddlePaddle (practical ultra lightweight OCR system, support 80+ languages recognition, provide data annotation and synthesis tools, support training and deployment among server, mobile, embedded and IoT devices)项目地址: https://gitcode.com/paddlepaddle/PaddleOCR
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考