YOLO X Layout代码实例:批量处理文件夹内所有PNG/JPG文档图片并保存标注结果
1. 什么是YOLO X Layout文档理解模型
YOLO X Layout不是简单的文字识别工具,而是一个专门针对文档图像的“视觉理解助手”。它不读文字内容,而是像一位经验丰富的排版设计师,一眼就能看出一张文档图里哪些是标题、哪些是正文段落、表格在哪里、图片占了多大位置、页眉页脚怎么分布——甚至能区分公式、脚注和列表项。
这个模型基于YOLO系列架构优化而来,但任务目标完全不同:它不做目标检测里的通用物体识别,而是聚焦在文档这一特殊场景下的11类语义区域划分。这意味着它对“文本块”的形状、密度、上下文位置高度敏感;对“表格”的边框结构、行列对齐有稳定判断;对“标题”与普通段落的字体大小、居中方式、前后空白等视觉线索能做出合理推断。
更关键的是,它不依赖OCR引擎,也不需要先做文字识别再分析布局。它是端到端的视觉感知模型——输入一张扫描件或手机拍的文档图,直接输出带坐标的区域分类结果。这对处理模糊、倾斜、低对比度的老旧文档尤其友好,也避免了OCR错误传导到布局分析环节的风险。
2. 为什么需要批量处理?单张上传太慢了
Web界面操作很直观:拖图、调阈值、点分析、看结果。但如果你手上有50份合同、200页实验报告、300张发票扫描件,一张张上传、等待、截图、手动保存标注框——不仅耗时,还极易出错:漏传、重复传、阈值没统一、结果没命名……实际工作中,这种“人肉流水线”根本不可持续。
批量处理不是锦上添花的功能,而是把YOLO X Layout从演示工具变成生产力工具的关键一步。它意味着你可以:
- 把一整个扫描文件夹扔进去,喝杯咖啡回来就拿到全部结构化结果;
- 后续无缝对接OCR提取文字、自动归档、信息抽取等下游任务;
- 在不同置信度下反复跑同一组数据,快速验证模型鲁棒性;
- 把布局分析嵌入自动化文档处理流程,比如邮件附件自动解析、扫描仪直连分析等。
下面我们就用纯Python实现一个轻量、可靠、可复用的批量处理脚本——不依赖Gradio前端,不启动Web服务,直接调用后端预测接口,全程命令行可控。
3. 批量处理核心代码详解
3.1 脚本设计思路
我们不重写模型推理逻辑,而是复用已部署的服务API(http://localhost:7860/api/predict)。这样做的好处非常明显:
- 零模型加载开销:服务已常驻内存,每次请求毫秒级响应;
- 配置完全一致:复用Web界面的所有预处理、后处理逻辑(如NMS、坐标归一化);
- 易于调试:请求/响应清晰可见,出错时可单独curl测试;
- 无环境冲突:Python脚本无需安装onnxruntime、CUDA等重型依赖。
整个流程分四步:遍历图片 → 构造请求 → 解析响应 → 保存结果。重点在于如何让结果“看得见、用得上”。
3.2 完整可运行脚本
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
YOLO X Layout 批量处理脚本
功能:遍历指定文件夹内所有PNG/JPG图片,调用本地API分析布局,保存可视化结果与JSON标注
作者:文档智能实践者
"""
import os
import cv2
import json
import requests
from pathlib import Path
from typing import List, Dict, Any
import numpy as np
# ================== 配置区(按需修改) ==================
API_URL = "http://localhost:7860/api/predict"
INPUT_FOLDER = "/path/to/your/documents" # ← 替换为你的图片文件夹路径
OUTPUT_FOLDER = "/path/to/save/results" # ← 替换为结果保存路径
CONF_THRESHOLD = 0.25 # 置信度阈值,与Web界面保持一致
VISUALIZE = True # 是否生成带框图(True/False)
SAVE_JSON = True # 是否保存结构化JSON(True/False)
# =======================================================
def load_image_as_bytes(image_path: str) -> bytes:
"""读取图片为二进制,适配requests文件上传"""
with open(image_path, "rb") as f:
return f.read()
def draw_layout_boxes(image: np.ndarray, predictions: List[Dict], class_names: List[str]) -> np.ndarray:
"""在原图上绘制检测框和类别标签"""
# 颜色映射:11类固定配色,保证可区分
colors = [
(0, 255, 0), # Caption - 绿色
(255, 165, 0), # Footnote - 橙色
(255, 0, 255), # Formula - 品红
(0, 191, 255), # List-item - 深天蓝
(128, 0, 128), # Page-footer - 紫色
(0, 128, 128), # Page-header - 蓝绿色
(255, 0, 0), # Picture - 红色
(255, 215, 0), # Section-header - 金色
(0, 255, 255), # Table - 青色
(255, 192, 203),# Text - 粉色
(138, 43, 226) # Title - 紫罗兰
]
h, w = image.shape[:2]
for pred in predictions:
x1, y1, x2, y2 = [int(x) for x in pred["bbox"]]
cls_id = int(pred["class_id"])
conf = float(pred["confidence"])
label = class_names[cls_id] if cls_id < len(class_names) else f"Class-{cls_id}"
# 绘制矩形框
color = colors[cls_id % len(colors)]
cv2.rectangle(image, (x1, y1), (x2, y2), color, 2)
# 绘制标签背景
text_size = cv2.getTextSize(f"{label} {conf:.2f}", cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1)[0]
cv2.rectangle(image, (x1, y1 - 20), (x1 + text_size[0] + 10, y1), color, -1)
# 绘制标签文字
cv2.putText(image, f"{label} {conf:.2f}", (x1 + 5, y1 - 5),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
return image
def process_single_image(image_path: str, output_dir: Path) -> bool:
"""处理单张图片:调用API → 解析响应 → 保存结果"""
try:
# 1. 读取图片
img_bytes = load_image_as_bytes(image_path)
filename = Path(image_path).stem
ext = Path(image_path).suffix.lower()
# 2. 构造API请求
files = {"image": (f"{filename}{ext}", img_bytes, f"image/{ext[1:]}")}
data = {"conf_threshold": CONF_THRESHOLD}
# 3. 发送请求(带超时,避免卡死)
response = requests.post(API_URL, files=files, data=data, timeout=120)
response.raise_for_status()
result = response.json()
# 4. 验证响应结构
if not isinstance(result, dict) or "predictions" not in result:
print(f" API响应异常({image_path}):缺少predictions字段")
return False
predictions = result["predictions"]
class_names = result.get("class_names", [
"Caption", "Footnote", "Formula", "List-item",
"Page-footer", "Page-header", "Picture", "Section-header",
"Table", "Text", "Title"
])
# 5. 保存JSON标注(结构化数据)
if SAVE_JSON:
json_path = output_dir / f"{filename}_layout.json"
with open(json_path, "w", encoding="utf-8") as f:
json.dump({
"image": str(Path(image_path).name),
"width": result.get("width", 0),
"height": result.get("height", 0),
"predictions": predictions,
"class_names": class_names,
"conf_threshold": CONF_THRESHOLD
}, f, ensure_ascii=False, indent=2)
print(f" JSON已保存:{json_path}")
# 6. 生成可视化图
if VISUALIZE:
# 读取原图用于绘制
img_cv2 = cv2.imread(image_path)
if img_cv2 is None:
print(f" 无法读取原图:{image_path}")
return False
# 绘制布局框
vis_img = draw_layout_boxes(img_cv2, predictions, class_names)
# 保存带框图
vis_path = output_dir / f"{filename}_layout_vis.jpg"
cv2.imwrite(str(vis_path), vis_img, [cv2.IMWRITE_JPEG_QUALITY, 95])
print(f" 可视化图已保存:{vis_path}")
print(f" 处理完成:{filename}{ext} | 检测到 {len(predictions)} 个区域")
return True
except requests.exceptions.Timeout:
print(f" 请求超时({image_path}):请检查服务是否运行正常")
return False
except requests.exceptions.ConnectionError:
print(f" 连接失败({image_path}):请确认 http://localhost:7860 是否可访问")
return False
except Exception as e:
print(f" 处理失败({image_path}):{str(e)}")
return False
def main():
"""主函数:批量处理入口"""
input_path = Path(INPUT_FOLDER)
output_path = Path(OUTPUT_FOLDER)
# 创建输出目录
output_path.mkdir(parents=True, exist_ok=True)
# 收集所有PNG/JPG文件(忽略大小写)
supported_exts = {".png", ".jpg", ".jpeg"}
image_files = [
f for f in input_path.rglob("*")
if f.is_file() and f.suffix.lower() in supported_exts
]
if not image_files:
print(f" 未在 {input_path} 中找到任何PNG/JPG文件")
return
print(f" 开始批量处理:共 {len(image_files)} 张图片")
print(f" API地址:{API_URL}")
print(f" 置信度阈值:{CONF_THRESHOLD}")
print("-" * 50)
success_count = 0
for i, img_path in enumerate(image_files, 1):
print(f"\n[{i}/{len(image_files)}] 正在处理:{img_path.name}")
if process_single_image(str(img_path), output_path):
success_count += 1
print("-" * 50)
print(f" 批量处理完成!成功 {success_count}/{len(image_files)} 张")
if success_count > 0:
print(f" 结果已保存至:{output_path.absolute()}")
print(" 提示:JSON文件含精确坐标,可用于后续OCR定位或数据标注")
if __name__ == "__main__":
main()
3.3 代码关键点说明
- 安全的文件遍历:使用
Path.rglob("*")递归查找,支持子文件夹;通过suffix.lower()统一处理.JPG和.jpg。 - 健壮的错误处理:捕获连接超时、服务不可达、响应格式异常等常见问题,并给出明确提示,避免脚本中断。
- 坐标精准还原:API返回的bbox已是像素坐标(非归一化),直接用于OpenCV绘图,无需额外转换。
- 可读性强的JSON结构:每个JSON包含原始文件名、图像尺寸、所有检测框(含类别ID、置信度、xyxy坐标)、类别名称列表,方便下游程序解析。
- 可视化友好配色:11类使用高对比度固定颜色,标题用紫罗兰、表格用青色、公式用品红,一眼可辨。
- 零配置运行:只需修改顶部4个变量,无需安装额外包(requests、opencv-python、numpy 已在服务环境中存在)。
4. 运行前准备与验证
4.1 确认服务已就绪
在运行脚本前,请务必验证服务状态:
# 检查端口是否监听
lsof -i :7860 || echo "端口7860未被占用"
# 测试API连通性(返回应为JSON,含"status":"success")
curl -X POST "http://localhost:7860/api/predict" \
-F "image=@/root/yolo_x_layout/test.png" \
-F "conf_threshold=0.25"
如果返回 Connection refused,请先启动服务:
cd /root/yolo_x_layout
nohup python app.py > layout.log 2>&1 &
4.2 快速验证脚本可用性
新建一个测试文件夹,放入1-2张文档图(如PDF转PNG的首页),然后修改脚本中的 INPUT_FOLDER 和 OUTPUT_FOLDER 为绝对路径:
INPUT_FOLDER = "/root/test_docs"
OUTPUT_FOLDER = "/root/test_results"
赋予执行权限并运行:
chmod +x batch_layout.py
./batch_layout.py
首次运行会看到类似输出:
开始批量处理:共 2 张图片
API地址:http://localhost:7860/api/predict
置信度阈值:0.25
--------------------------------------------------
[1/2] 正在处理:invoice_001.png
JSON已保存:/root/test_results/invoice_001_layout.json
可视化图已保存:/root/test_results/invoice_001_layout_vis.jpg
处理完成:invoice_001.png | 检测到 8 个区域
...
打开生成的 _layout_vis.jpg,你会看到每种元素都被清晰框出并标注——这才是真正“所见即所得”的文档理解。
5. 实用技巧与避坑指南
5.1 提升处理效率的3个方法
-
并行化处理(推荐):将
main()中的循环改为concurrent.futures.ThreadPoolExecutor,10线程可提升3-5倍速度(注意API服务本身并发能力):from concurrent.futures import ThreadPoolExecutor, as_completed with ThreadPoolExecutor(max_workers=8) as executor: futures = {executor.submit(process_single_image, str(p), output_path): p for p in image_files} for future in as_completed(futures): future.result() # 触发异常 -
跳过已处理文件:在
process_single_image开头加入检查:json_exists = (output_dir / f"{filename}_layout.json").exists() if json_exists and not FORCE_REPROCESS: print(f"⏩ 已存在,跳过:{filename}{ext}") return True -
批量压缩输出图:若只需存档,将可视化图质量从95降至75,体积减少40%且肉眼无差别。
5.2 常见问题排查清单
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
Connection refused | 服务未启动或端口错误 | ps aux | grep app.py 查进程;检查Docker是否映射 -p 7860:7860 |
Timeout | 图片过大(>5MB)或模型加载慢 | 缩小图片尺寸(cv2.resize预处理);改用YOLOX Tiny模型 |
JSON中predictions为空 | 置信度过高或图片质量差 | 降低CONF_THRESHOLD至0.15;检查图片是否全黑/过曝 |
| 可视化图颜色混乱 | class_names顺序与模型不匹配 | 硬编码11类名称(脚本中已提供),确保与API返回一致 |
| 中文路径报错 | Python 3.8+ 对中文路径支持良好,但Windows需注意 | 统一用str(Path(...))转换,避免直接字符串拼接 |
5.3 后续可扩展方向
- 对接OCR引擎:在JSON结果基础上,用PaddleOCR或EasyOCR对
Text/Title区域单独识别,实现“先定位、再识别”两步走; - 生成结构化Markdown:将
Section-header+Text+Table组合,自动生成带层级的文档摘要; - 异常检测报警:当某页
Picture数量为0但Text密度极高时,可能为扫描失败页,自动标记待人工复核; - Webhook通知:处理完成后向企业微信/钉钉发送完成消息,含统计摘要。
6. 总结:让文档理解真正落地
YOLO X Layout的价值,从来不在单张图的惊艳效果,而在于它能否成为你日常文档处理流水线中稳定可靠的一环。本文提供的批量脚本,没有炫技的深度学习代码,只有扎实的工程思维:复用现有服务、规避环境冲突、注重错误反馈、输出即用结果。
它不追求“全自动无人值守”,而是给你充分的控制权——你可以随时调整阈值、选择保存内容、查看每一步日志。真正的AI生产力,不是替代人,而是让人从重复劳动中解放出来,把精力留给更需要判断力的任务:比如审核识别结果、优化模板规则、设计业务流程。
当你把300页招标文件扔进文件夹,10分钟后得到300份带坐标的JSON和可视化图,那一刻你就知道:文档智能,已经不再是PPT里的概念。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

319


被折叠的 条评论
为什么被折叠?



