GLM-OCR 文档识别实战教程:从验证码、公式到车牌 OCR

GLM-OCR 文档识别实战教程:从验证码、公式到车牌 OCR

这篇教程是我根据近期学习 OCR、视觉语言模型和 Roboflow 数据集实践过程整理出来的,重点演示如何使用 GLM-OCR 处理多种真实 OCR 场景,包括验证码识别、LaTeX 公式识别、收据解析、日期戳提取、球衣号码识别、集装箱编号识别、轮胎编码识别和车牌识别。

GLM-OCR 是一个 0.9B 参数规模的视觉语言模型,专门面向光学字符识别(Optical Character Recognition,OCR)任务构建。它在 OmniDocBench V1.5 上取得 94.62 的成绩,并在该基准测试中排名第一。该模型架构通过跨模态连接器将 CogViT 视觉编码器与 GLM-0.5B 语言解码器连接起来,同时对视觉 token 进行下采样。

它支持最高 8K(7680x4320)分辨率的文档图像,并支持 8 种以上语言。模型内置三类能力:文本识别、LaTeX 公式识别和表格识别。通过自定义提示词,还可以从文档中抽取 JSON 等结构化数据。

如果你正在系统学习 OCR、视觉大模型和 Roboflow 相关实践,可以先收藏本文;配套 notebook、示例图片和后续同系列更新,我会整理成资料包,后面统一放到更方便获取的位置。


🧭 开始之前

先确认当前环境是否可以访问 GPU。可以使用 nvidia-smi 命令进行检查。如果在 Colab 中遇到 GPU 不可用的问题,可以进入 EditNotebook settingsHardware accelerator,将硬件加速器设置为 L4 GPU,然后点击 Save 保存。

!nvidia-smi

下面这段代码会根据运行环境自动选择 cudampscpu,并设置合适的数据类型。对于这类视觉语言模型,优先使用 GPU 会明显提升推理速度。

import torch

if torch.cuda.is_available():
    DEVICE = torch.device("cuda")
    DTYPE = torch.bfloat16 if torch.cuda.is_bf16_supported() else torch.float16
    gpu_name = torch.cuda.get_device_name()
elif hasattr(torch.backends, "mps") and torch.backends.mps.is_available():
    DEVICE = torch.device("mps")
    DTYPE = torch.float16
    gpu_name = "Apple Silicon (MPS)"
else:
    DEVICE = torch.device("cpu")
    DTYPE = torch.float32
    gpu_name = "CPU"

print(f"Device : {DEVICE} ({gpu_name})")
print(f"Dtype  : {DTYPE}")

🔑 配置 API 密钥

运行本教程需要两个密钥:

  • HuggingFace Token:用于下载 GLM-OCR 模型
  • Roboflow API Key:用于下载评测数据集

操作步骤如下:

  • 打开 HuggingFace Settings 页面,点击 Access Tokens,再点击 New Token 生成新的 token。
  • 进入 Roboflow Settings 页面,点击 Copy,复制你的私有 API Key。
  • 在 Colab 左侧面板中点击 Secrets(🔑)。
    • 将 HuggingFace Access Token 保存为 HF_TOKEN
    • 将 Roboflow API Key 保存为 ROBOFLOW_API_KEY

如果你不熟悉 Colab Secrets、HuggingFace Token 或 Roboflow API Key 的配置方式,可以在评论区说明卡住的位置;我后续会把环境配置清单和常见报错整理到同系列资料里。

import os
from google.colab import userdata

os.environ["HF_TOKEN"] = userdata.get("HF_TOKEN")
os.environ["ROBOFLOW_API_KEY"] = userdata.get("ROBOFLOW_API_KEY")

🔧 安装依赖

这里直接从 Hugging Face Transformers 的 GitHub 主分支安装,以便使用较新的模型支持能力。同时安装 roboflow,用于下载后续实验所需的数据集。

!pip install -q git+https://github.com/huggingface/transformers.git roboflow

🤖 加载 GLM-OCR 模型

这一部分会加载 GLM-OCR 的 processor 和模型本体。processor 负责把图像和文本 prompt 组织成模型输入,model 负责完成实际推理。

from transformers import AutoProcessor, AutoModelForImageTextToText

MODEL_ID = "zai-org/GLM-OCR"

processor = AutoProcessor.from_pretrained(MODEL_ID)

if DEVICE.type == "cuda":
    model = AutoModelForImageTextToText.from_pretrained(
        MODEL_ID,
        torch_dtype=DTYPE,
        device_map="auto",
    )
else:
    model = AutoModelForImageTextToText.from_pretrained(
        MODEL_ID,
        torch_dtype=DTYPE,
    ).to(DEVICE)

🛠️ 定义辅助函数

这一部分是整篇教程的核心:我们会封装 OCR 推理、数据集图片加载和结果可视化逻辑。后面的所有示例都会复用这三类函数。

流程可以理解为:读取图片 -> 构造 prompt -> 调用 GLM-OCR -> 解码文本 -> 在网格中展示图片和预测结果。

import json
import os
import textwrap
import random
from glob import glob

from tqdm import tqdm

import matplotlib.pyplot as plt
from PIL import Image

IMAGE_EXTENSIONS = (".jpg", ".jpeg", ".png", ".bmp", ".webp")


def run_ocr(image: Image.Image, prompt: str = "Text Recognition:") -> str:
    """在单张 PIL 图像上运行 GLM-OCR,并返回解码后的文本。"""
    messages = [
        {
            "role": "user",
            "content": [
                {"type": "image", "image": image},
                {"type": "text", "text": prompt},
            ],
        }
    ]
    inputs = processor.apply_chat_template(
        messages,
        tokenize=True,
        add_generation_prompt=True,
        return_dict=True,
        return_tensors="pt",
    ).to(model.device)
    inputs.pop("token_type_ids", None)
    generated_ids = model.generate(**inputs, max_new_tokens=8192)
    return processor.decode(
        generated_ids[0][inputs["input_ids"].shape[1]:],
        skip_special_tokens=True,
    ).strip()


def load_images(
    dataset_location: str,
    split: str = "test",
) -> list[dict]:
    """从 Roboflow 数据集目录中加载图像路径。

    会优先尝试指定的数据划分,然后依次回退到 valid 和 train。
    返回由字典组成的列表,每个字典包含一个 'image_path' 键。
    """
    for candidate in [split, "valid", "train"]:
        split_dir = os.path.join(dataset_location, candidate)
        if os.path.isdir(split_dir):
            paths = sorted([
                os.path.join(split_dir, f)
                for f in os.listdir(split_dir)
                if f.lower().endswith(IMAGE_EXTENSIONS)
            ])
            if paths:
                random.shuffle(paths)
                print(f"Loaded {len(paths)} images from {candidate} split")
                return [{"image_path": p} for p in paths]
    print(f"No images found in {dataset_location}")
    return []


def run_and_plot(
    entries: list[dict],
    prompt: str,
    n: int = 9,
    wrap_width: int = 40,
):
    """最多对 n 个样本运行 OCR,并以 3 列网格展示结果。"""
    entries = entries[:n]
    if not entries:
        print("No entries to display.")
        return

    cols = 3
    rows = (len(entries) + cols - 1) // cols
    fig, axes = plt.subplots(rows, cols, figsize=(3 * cols, 3 * rows))
    if rows == 1:
        axes = [axes] if cols == 1 else list(axes)
    else:
        axes = axes.flatten()

    for idx, entry in enumerate(tqdm(entries, desc="Running OCR")):
        if "image" in entry and isinstance(entry["image"], Image.Image):
            image = entry["image"]
        else:
            image = Image.open(entry["image_path"]).convert("RGB")

        prediction = run_ocr(image, prompt)

        axes[idx].imshow(image)
        axes[idx].axis("off")
        pred_wrapped = textwrap.fill(prediction, width=wrap_width).replace("$", "\\$")
        axes[idx].set_title(f"Pred: {pred_wrapped}", fontsize=8, loc="left", pad=8)

    for idx in range(len(entries), len(axes)):
        axes[idx].axis("off")

    plt.tight_layout()
    plt.show()

🔤 示例 1:验证码文本识别

第一个 OCR 场景是验证码识别。验证码图像通常包含 4 到 6 个字母数字字符,并带有噪声、形变和颜色变化。这个场景适合用来观察模型在短文本和干扰背景下的识别能力。

from roboflow import download_dataset

captcha_dataset = download_dataset(
    "https://universe.roboflow.com/roboflow-jvuqo/captcha-ocr-g5ol8/1",
    "jsonl",
)

captcha_entries = load_images(captcha_dataset.location)
run_and_plot(captcha_entries, prompt="Text Recognition:")

在这里插入图片描述

🧮 示例 2:LaTeX 公式识别

这个场景输入的是数学公式图片。我们通过提示词要求模型返回对应的 LaTeX 源码。对于论文、教材、试卷和公式截图整理,这类能力非常实用。

latex_dataset = download_dataset(
    "https://universe.roboflow.com/roboflow-jvuqo/latex-ocr-subset/3",
    "jsonl",
)

latex_entries = load_images(latex_dataset.location)
run_and_plot(latex_entries, prompt="Formula Recognition:")

在这里插入图片描述

🧾 示例 3:收据信息解析(结构化 JSON)

这个数据集包含印度尼西亚语收据图像。这里不是简单读取全部文本,而是通过自然语言问题让模型提取收据中的总价信息。类似思路也可以迁移到发票、订单、快递单和表单信息抽取任务。

receipt_dataset = download_dataset(
    "https://universe.roboflow.com/roboflow-jvuqo/cord-v2-receipts/1",
    "jsonl",
)

receipt_entries = load_images(receipt_dataset.location)
run_and_plot(receipt_entries, prompt="What is the total price on this receipt?")

在这里插入图片描述

📅 示例 4:日期戳提取

这个场景要求模型从图像中的日期戳提取日期,并统一整理成 YYYY-MM-DD 格式。相比直接 OCR,格式约束可以让输出更适合进入后续数据处理流程。

date_dataset = download_dataset(
    "https://universe.roboflow.com/roboflow-jvuqo/date-stamp-extraction/1",
    "jsonl",
)

date_entries = load_images(date_dataset.location)
DATE_PROMPT = "Extract the date from this image. Return it in YYYY-MM-DD format."

run_and_plot(date_entries, prompt=DATE_PROMPT)

在这里插入图片描述

🏀 示例 5:篮球球衣号码识别

这个场景输入的是篮球运动员图像,目标是提取球衣号码。球衣号码识别在体育视频分析、球员追踪和赛事数据统计中很常见。

jersey_dataset = download_dataset(
    "https://universe.roboflow.com/roboflow-jvuqo/basketball-jersey-numbers-ocr/7",
    "jsonl",
)

jersey_entries = load_images(jersey_dataset.location)
JERSEY_PROMPT = "Read jersey number."

run_and_plot(jersey_entries, prompt=JERSEY_PROMPT)

在这里插入图片描述

🚢 示例 6:集装箱序列号识别

这个数据集包含集装箱图像,其中有对应的序列号区域。集装箱编号识别是物流、港口、仓储和自动巡检中非常典型的 OCR 场景。

container_dataset = download_dataset(
    "https://universe.roboflow.com/kerofox/container-id-number/dataset/3",
    "coco",
)

container_entries = load_images(container_dataset.location)
run_and_plot(container_entries, prompt="Read only the container serial number.")

在这里插入图片描述

🛞 示例 7:轮胎编码识别

这个场景输入的是轮胎图像,图像中包含压印编码和数字。由于轮胎表面文字经常存在低对比度、弯曲、阴影和磨损,这类任务对 OCR 模型的鲁棒性要求更高。

tire_dataset = download_dataset(
    "https://universe.roboflow.com/bence-toth-h5oec/tire-serial/dataset/1",
    "coco",
)

tire_entries = load_images(tire_dataset.location)
run_and_plot(tire_entries, prompt="Read tire serial number.")

在这里插入图片描述

🚗 示例 8:车牌识别

最后一个场景是车牌识别。车牌 OCR 是计算机视觉中非常经典的工业应用,常见于停车场、园区出入口、交通监控和车辆管理系统。

plate_dataset = download_dataset(
    "https://universe.roboflow.com/roboflow-universe-projects/license-plate-recognition-rxg4e/dataset/1",
    "coco",
)

plate_entries = load_images(plate_dataset.location)
run_and_plot(plate_entries, prompt="Read license plate number.")

在这里插入图片描述


📌 小结

这篇教程从环境配置、模型加载、辅助函数封装开始,依次跑通了 8 类 OCR 场景。整体来看,GLM-OCR 不只是传统意义上的“读文字”,它还可以通过提示词完成更灵活的信息抽取,例如公式转 LaTeX、收据字段提取、日期格式标准化等。

如果你刚开始学习 OCR 或视觉语言模型,建议重点理解三件事:

  • prompt 会直接影响模型输出形式
  • OCR 结果最好尽量约束成后续程序容易处理的格式
  • 不同业务场景需要不同的 prompt,而不是只使用统一的“识别全部文字”

后续我会继续把类似的视觉大模型、OCR、目标检测和图像分割项目整理成系列教程。配套 notebook、示例图片和运行环境说明也会按主题逐步整理出来,适合刚入门计算机视觉和多模态模型的同学跟着实践。

📚 同系列教程汇总

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值