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 不可用的问题,可以进入 Edit → Notebook settings → Hardware accelerator,将硬件加速器设置为 L4 GPU,然后点击 Save 保存。
!nvidia-smi
下面这段代码会根据运行环境自动选择 cuda、mps 或 cpu,并设置合适的数据类型。对于这类视觉语言模型,优先使用 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。
- 将 HuggingFace Access Token 保存为
如果你不熟悉 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、示例图片和运行环境说明也会按主题逐步整理出来,适合刚入门计算机视觉和多模态模型的同学跟着实践。
📚 同系列教程汇总
- Google Gemini 3.5 Flash 零样本目标检测教程:从提示词到可视化结果
- GLM-OCR 文档识别实战教程:从验证码、公式到车牌 OCR
- RF-DETR 目标检测训练中文教程:待整理
- YOLOv8 自定义目标检测训练中文教程:待整理
- SAM / SAM 2 图像分割中文教程:待整理
170

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



