YOLO X Layout代码实例:批量处理文件夹内所有PNG/JPG文档图片并保存标注结果

yolo_x_layout文档理解模型

基于YOLO模型的文档版面分析工具,可识别文档中的文本、表格、图片、标题等11种元素类型。

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_FOLDEROUTPUT_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星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

您可能感兴趣的与本文相关的镜像

yolo_x_layout文档理解模型

yolo_x_layout文档理解模型

文本生成
Yolo

基于YOLO模型的文档版面分析工具,可识别文档中的文本、表格、图片、标题等11种元素类型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CyanWave34

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值