GLM-OCR模型训练数据准备:Python脚本批量处理与标注文件生成
GLM-OCR模型训练数据准备:Python脚本批量处理与标注文件生成
1. 引言
想训练一个能看懂自家产品说明书、识别特定票据、或者读懂手写笔记的OCR模型吗?通用模型虽然强大,但面对五花八门的字体、独特的排版或者模糊的拍摄环境,往往就有点力不从心了。这时候,为自己的垂直场景定制一个GLM-OCR模型,就成了一个非常实际的选择。
而定制化训练的第一步,也是最关键、最耗时的一步,就是准备高质量的训练数据。这就像教一个孩子认字,你得先给他准备好清晰、多样的字帖。今天,我们就来聊聊怎么用Python脚本,把一堆原始图片,变成GLM-OCR模型能“吃”得下的标准“口粮”。
这篇文章的目标很直接:手把手带你搞定数据准备的整个流程。我们会从最基础的图片裁剪开始,到用一些简单技巧增强数据的多样性,最后生成模型需要的标注文件。整个过程我都会配上可以直接运行的Python代码,哪怕你刚接触Python,跟着做也能跑通。准备好了吗?我们开始吧。
2. 准备工作与环境搭建
在动手写脚本之前,我们得先把“厨房”收拾好,把需要的“食材”和“工具”备齐。
2.1 你需要准备什么
- 原始图片:这是你的核心素材。可以是手机拍的、扫描的,或者从网上下载的。建议先统一放到一个文件夹里,比如叫做
raw_images。 - 标注信息:每张图片里文字的位置和内容是什么?初期你可能需要手动标注一些,或者利用现有工具(如LabelImg、PPOCRLabel)先产生一个基础版本。我们假设你已经有了一份初步的标注,格式可能是每张图片对应一个
.txt文件,里面记录了文字框的坐标和文本内容。 - Python环境:确保你的电脑上安装了Python(3.7或以上版本)。打开命令行,输入
python --version检查一下。
2.2 安装必要的Python库
我们需要几个强大的帮手来处理图片和文件。打开你的命令行终端(Windows上是CMD或PowerShell,Mac/Linux上是Terminal),依次输入下面的命令来安装它们:
pip install Pillow opencv-python numpy tqdm简单解释一下这几个库是干什么的:
- Pillow (PIL):Python里处理图片的老牌主力,功能全面,接口友好。
- opencv-python (cv2):计算机视觉的瑞士军刀,在图像处理、增强方面非常高效。
- numpy:Python科学计算的基础,高效处理数组(我们的图片数据就是数组)。
- tqdm:一个能给你的循环加上美观进度条的小工具,处理大量文件时特别有用,让你知道程序跑到哪了。
安装成功后,我们就可以进入正题了。
3. 第一步:批量裁剪与区域提取
拿到一张包含文字的图片,我们通常不需要整张图,只需要把有文字的区域精准地“抠”出来。这一步能有效减少无关背景的干扰,让模型更专注于文字本身。
3.1 读取标注与基础裁剪
假设我们的标注文件是类似这样的image_001.txt:
x1,y1,x2,y2,x3,y3,x4,y4,text 100,50,300,50,300,150,100,150,产品名称 50,200,250,200,250,300,50,300,规格参数这表示有两个文本框,每个用四个点的坐标(通常是左上、右上、右下、左下)和文本内容来定义。
我们来写一个脚本,根据这些坐标把文字区域裁剪出来:
import os from PIL import Image import numpy as np from tqdm import tqdm def crop_text_regions(image_dir, label_dir, output_dir): """ 根据标注文件裁剪图片中的文字区域。 参数: image_dir: 原始图片文件夹路径 label_dir: 标注文件(.txt)文件夹路径 output_dir: 裁剪后图片的输出文件夹路径 """ # 创建输出目录 os.makedirs(output_dir, exist_ok=True) # 获取所有图片文件 image_files = [f for f in os.listdir(image_dir) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp'))] # 使用tqdm显示进度条 for img_file in tqdm(image_files, desc="正在裁剪图片"): img_path = os.path.join(image_dir, img_file) label_path = os.path.join(label_dir, os.path.splitext(img_file)[0] + '.txt') # 如果对应的标注文件不存在,则跳过 if not os.path.exists(label_path): continue # 打开图片 image = Image.open(img_path) img_width, img_height = image.size # 读取标注文件 with open(label_path, 'r', encoding='utf-8') as f: lines = f.readlines() for idx, line in enumerate(lines): parts = line.strip().split(',') if len(parts) < 9: # 确保有足够的坐标和文本 continue # 解析坐标,最后一部分是文本 coords = list(map(int, parts[:8])) text = parts[8] # 从8个坐标点中找出最小和最大的x、y值,确定裁剪矩形框 xs = coords[0::2] # 所有x坐标 ys = coords[1::2] # 所有y坐标 left, top = max(0, min(xs)), max(0, min(ys)) right, bottom = min(img_width, max(xs)), min(img_height, max(ys)) # 确保区域有效 if right <= left or bottom <= top: continue # 裁剪区域 region = image.crop((left, top, right, bottom)) # 生成输出文件名:原图名_序号_部分文本.jpg safe_text = text[:20].replace('/', '_').replace('\\', '_') # 防止非法字符 output_filename = f"{os.path.splitext(img_file)[0]}_{idx}_{safe_text}.jpg" output_path = os.path.join(output_dir, output_filename) # 保存裁剪后的图片 region.save(output_path) # 这里可以先不保存标注,我们后面统一生成 # 但可以记录下信息,比如把文本内容写在文件名里,或者存到一个临时列表 print(f"裁剪完成!图片已保存至: {output_dir}") # 使用示例 if __name__ == "__main__": crop_text_regions( image_dir='./raw_images', label_dir='./raw_labels', output_dir='./cropped_images' )运行这个脚本,你的cropped_images文件夹里就会充满一个个干净的文字区域小图了。
4. 第二步:数据增强——让数据“变”出花样
如果只有几百张裁剪图,模型很容易“过拟合”,也就是只认识你给的这些样本,换张类似的就不认识了。数据增强就是通过一些简单的图像变换,人工创造出更多的、多样化的训练样本,让模型变得更健壮。
4.1 几种实用的增强方法
我们主要介绍三种简单又有效的方法:旋转、模糊和噪声。我们用OpenCV来实现,因为它处理起来速度很快。
import cv2 import numpy as np import os from tqdm import tqdm import random def augment_image(image_path, output_dir, augmentations=['rotate', 'blur', 'noise']): """ 对单张图片进行数据增强,并保存增强后的版本。 参数: image_path: 输入图片路径 output_dir: 增强图片输出目录 augmentations: 需要进行的增强操作列表 """ img = cv2.imread(image_path) if img is None: return base_name = os.path.splitext(os.path.basename(image_path))[0] # 保存原始图片(作为增强的基准) cv2.imwrite(os.path.join(output_dir, f"{base_name}_orig.jpg"), img) # 1. 随机旋转(小角度) if 'rotate' in augmentations: angle = random.uniform(-10, 10) # 随机旋转-10到10度 height, width = img.shape[:2] center = (width // 2, height // 2) rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0) rotated = cv2.warpAffine(img, rotation_matrix, (width, height), borderMode=cv2.BORDER_REPLICATE) cv2.imwrite(os.path.join(output_dir, f"{base_name}_rot.jpg"), rotated) # 2. 高斯模糊(模拟轻微失焦) if 'blur' in augmentations: # 随机选择模糊核大小,必须是正奇数 ksize = random.choice([3, 5]) blurred = cv2.GaussianBlur(img, (ksize, ksize), 0) cv2.imwrite(os.path.join(output_dir, f"{base_name}_blur.jpg"), blurred) # 3. 添加高斯噪声(模拟图像传感器噪声) if 'noise' in augmentations: mean = 0 var = random.uniform(1, 10) # 随机噪声方差 sigma = var ** 0.5 gaussian = np.random.normal(mean, sigma, img.shape).astype(np.uint8) noisy = cv2.add(img, gaussian) cv2.imwrite(os.path.join(output_dir, f"{base_name}_noise.jpg"), noisy) # 可以组合增强,比如旋转+模糊 if 'rotate' in augmentations and 'blur' in augmentations: # 先旋转 angle = random.uniform(-5, 5) height, width = img.shape[:2] center = (width // 2, height // 2) M = cv2.getRotationMatrix2D(center, angle, 1.0) img_rot = cv2.warpAffine(img, M, (width, height), borderMode=cv2.BORDER_REPLICATE) # 再模糊 img_rot_blur = cv2.GaussianBlur(img_rot, (3, 3), 0) cv2.imwrite(os.path.join(output_dir, f"{base_name}_rot_blur.jpg"), img_rot_blur) def batch_augment(input_dir, output_dir): """ 批量处理一个文件夹内的所有图片。 """ os.makedirs(output_dir, exist_ok=True) image_files = [f for f in os.listdir(input_dir) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp'))] for img_file in tqdm(image_files, desc="正在进行数据增强"): img_path = os.path.join(input_dir, img_file) augment_image(img_path, output_dir) print(f"数据增强完成!增强后图片已保存至: {output_dir}") # 使用示例 if __name__ == "__main__": # 对裁剪后的图片进行增强 batch_augment( input_dir='./cropped_images', output_dir='./augmented_images' )运行后,augmented_images文件夹里的图片数量会是原来的好几倍。每张原始图都衍生出了旋转版、模糊版、带噪声的版本,甚至还有组合增强的版本。模型看到的数据多样性一下子就丰富起来了。
5. 第三步:生成模型所需的标注文件
数据准备好了,最后一步就是告诉模型每张图片对应什么文字。GLM-OCR等主流OCR模型通常接受特定的标注格式,比如每行一个“图片路径\t文本内容”的.txt文件,或者一个包含所有信息的.json文件。
5.1 生成标准格式的标注文件
我们需要遍历所有增强后的图片,根据它们的文件名(我们在裁剪时把文本信息嵌入了文件名)或者一个单独的映射关系,来生成最终的标注。
这里我们以生成一个简单的train.txt为例,格式为:image_path text_label。
import os import json from tqdm import tqdm def generate_annotation_txt(image_dir, output_txt_path): """ 生成标准的训练标注txt文件。 假设图片文件名格式为:原图名_序号_文本内容.jpg """ image_files = [f for f in os.listdir(image_dir) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp'))] annotations = [] for img_file in tqdm(image_files, desc="正在生成标注"): # 从文件名中提取文本标签(根据你的命名规则调整) # 例如:'doc_01_0_产品名称.jpg' -> '产品名称' try: # 去掉扩展名,按'_'分割,取最后一部分作为文本 # 注意:如果你的文本中包含'_',这种简单方法会出错,需要更稳健的解析或使用额外文件 base_name = os.path.splitext(img_file)[0] # 假设文件名结构是 {前缀}_{序号}_{文本} parts = base_name.split('_') if len(parts) >= 3: # 文本部分是序号之后的所有部分用'_'重新连接(防止原文本有_被分割) text_label = '_'.join(parts[2:]) else: text_label = base_name # 如果解析失败,用整个文件名(不含扩展名) # 构建相对路径或绝对路径 img_relative_path = os.path.join(image_dir, img_file) annotations.append(f"{img_relative_path}\t{text_label}") except Exception as e: print(f"处理文件 {img_file} 时出错: {e}") continue # 写入txt文件 with open(output_txt_path, 'w', encoding='utf-8') as f: f.write('\n'.join(annotations)) print(f"标注文件已生成: {output_txt_path}") print(f"共生成 {len(annotations)} 条标注。") def generate_annotation_json(image_dir, output_json_path): """ 生成JSON格式的标注文件,结构更灵活。 """ image_files = [f for f in os.listdir(image_dir) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp'))] data_list = [] for img_file in tqdm(image_files, desc="正在生成JSON标注"): try: base_name = os.path.splitext(img_file)[0] parts = base_name.split('_') text_label = '_'.join(parts[2:]) if len(parts) >= 3 else base_name img_path = os.path.join(image_dir, img_file) # 构建一个字典,可以包含更多信息,如图片尺寸等 data_item = { "filename": img_file, "text": text_label, "path": img_path # 可以添加 "width", "height" 等信息,如果需要的话 } data_list.append(data_item) except Exception as e: print(f"处理文件 {img_file} 时出错: {e}") continue # 写入JSON文件 with open(output_json_path, 'w', encoding='utf-8') as f: json.dump(data_list, f, ensure_ascii=False, indent=2) print(f"JSON标注文件已生成: {output_json_path}") # 使用示例 if __name__ == "__main__": # 生成TXT格式标注 generate_annotation_txt( image_dir='./augmented_images', output_txt_path='./annotations/train.txt' ) # 生成JSON格式标注(可选) generate_annotation_json( image_dir='./augmented_images', output_json_path='./annotations/train.json' )重要提示:上面从文件名提取文本的方法比较简陋。在实际项目中,更可靠的做法是维护一个独立的字典或文件,来映射每个裁剪后的图片文件与其对应的原始标注文本。你可以把第三步裁剪脚本中记录的文本信息(比如存到一个列表或字典里),传递给数据增强和标注生成步骤。
6. 总结
走完这一套流程,你就拥有了一套为GLM-OCR模型量身定制的训练数据了:经过裁剪、增强的图片,以及格式规范的标注文件。这个过程看似步骤不少,但一旦用脚本自动化起来,处理成百上千张图片也就是几分钟的事。
回顾一下关键点:裁剪让模型聚焦,增强让模型健壮,规范的标注让模型能正确学习。这些脚本只是一个起点,你可以根据自己数据的特性去调整,比如增加对比度调整、透视变换等更复杂的增强手段,或者处理更复杂的标注格式。
数据质量直接决定了模型效果的上限。花时间把数据准备这一步做扎实,后面的模型训练往往会事半功倍。接下来,你就可以拿着这些准备好的数据,投入到GLM-OCR的微调训练中去了。希望这篇教程能帮你扫清数据准备上的障碍,祝你训练出识别精准的专属OCR模型!
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。
