SynthText实战:从零构建自定义场景OCR数据集
1. 为什么你需要自己动手做OCR数据集做OCR项目最头疼的是什么十有八九是数据。公开数据集像ICDAR、COCO-Text虽然质量不错但场景太“通用”了。如果你的目标是识别街边小店的招牌、工厂设备上的铭牌或者古籍文献里的特殊字体这些通用数据集的泛化能力就捉襟见肘了。自己标注一张图几百个字符成本高、耗时长想想都头大。这时候合成数据就成了救命稻草。而SynthText就是合成数据生成领域的一个“老炮儿”工具虽然2016年就提出了但它的思路和效果至今依然非常实用。它核心做了一件事把文字“自然”地“贴”到真实的背景图片上生成带有逼真透视、光照、遮挡和模糊效果的场景文本图像。我最初接触SynthText是为了做一个商品包装盒上的文字识别项目。网上找不到足够多带复杂背景和艺术字体的包装盒图片自己拍和标注又不现实。用上SynthText后我用几百张包装盒背景图生成了几万张带标注的训练数据模型效果提升非常明显。这个过程里踩了不少坑也积累了很多实战经验。所以这篇文章就是带你从零开始走通用SynthText生成自定义场景OCR数据的完整流程。我们不只讲官方文档里有的更会重点分享那些官方没细说、但实际操作中一定会遇到的“坑”和解决方案比如如何处理自己的背景图、怎么生成深度图和分割图、如何合并数据以及如何支持中文竖排文本。目标就一个让你能真正用起来做出自己业务场景需要的数据。2. 理解SynthText的核心它到底是怎么“造”数据的在动手之前我们得先弄明白SynthText的工作原理。这样后面遇到问题你才知道从哪里下手调试而不是盲目试错。SynthText的合成过程可以想象成一个聪明的“贴图”过程但它贴得非常讲究。它需要三样核心原料背景图片就是你想要的场景图比如街道、室内、包装盒、文档背景。深度图一张和背景图同样尺寸的灰度图用来表示场景中每个像素点的“远近”。越亮的地方表示离镜头越近越暗的地方表示越远。SynthText利用这个信息让文字产生符合场景透视的形变。分割图同样尺寸的图用不同的整数值标记出图片中不同的物体区域比如天空是1建筑是2树木是3。SynthText用这个来判断文字应该“放”在哪个物体表面上以及如何被前景物体部分遮挡。有了这三样SynthText会从一个庞大的文本语料库默认是新闻语料中选取句子然后根据深度图和分割图的信息寻找那些平坦、连续且朝向合理的表面区域比如墙壁、地面、招牌平面。接着它会计算每个区域的“显著性”和“光滑度”优先把文字放在那些看起来适合放置文字、且不影响图片主体内容的区域。最关键的一步是渲染。它不是简单地把文字图片叠上去而是会模拟光照和混合效果颜色与对比度文字颜色会从背景区域采样并确保有足够的对比度让人眼能看清。透视形变根据深度信息对文字进行3D投影变换让它看起来像是印在了一个有透视的平面上。边界融合文字边缘会和背景进行Alpha混合避免生硬的“剪纸”感。模糊与噪声会模拟相机失焦或运动模糊并添加一些噪声让合成效果更真实。最终它不仅生成合成图片还会生成精确到字符级别和单词级别的边界框坐标以及对应的文本内容格式都保存在一个HDF5文件里直接就能用于训练像EAST、PSENet这类先进的场景文本检测模型或者CRNN、SATRN这类识别模型。所以整个自定义数据生成流程的主线就清晰了准备自己的背景图 - 为每张背景图生成对应的深度图和分割图 - 将这些信息打包成SynthText需要的dset.h5格式 - 运行合成脚本生成海量数据。下面我们就一步步拆解。3. 实战第一步准备你的专属背景图库背景图的质量直接决定了合成数据的上限。这里有几个从实战中总结的原则原则一场景要对口。你的背景图必须尽可能贴近你的目标应用场景。如果你做街景文字识别就去收集马路、店铺、路标的图片如果做文档OCR就收集各种纸质背景、扫描件纹理、屏幕截图。我建议建立一个分类文件夹比如/bg_imgs/street/,/bg_imgs/doc/,/bg_imgs/product/方便后期管理。原则二多样性与复杂性。不要只用干净、简单的背景。适当加入一些复杂场景比如光线不均逆光、阴影、有部分模糊、有复杂纹理木纹、砖墙的图片。这样合成出的数据才能让模型更鲁棒。我一开始只用干净的包装盒图结果模型一到反光强烈的实物照片上就崩了后来加入了大量不同光照条件的背景效果才好起来。原则三分辨率和尺寸。SynthText对输入图片尺寸没有硬性限制但建议统一处理一下。太大的图片如4K会极大增加深度图/分割图的计算时间且可能内存不足。太小的图片如小于256x256可能没有足够的空间放置文字。一个折中的做法是将图片的短边缩放到512像素左右长边按比例缩放。你可以用下面的Python脚本来批量处理import cv2 import os from pathlib import Path def resize_bg_images(input_dir, output_dir, target_short_side512): Path(output_dir).mkdir(parentsTrue, exist_okTrue) for img_name in os.listdir(input_dir): if img_name.lower().endswith((.png, .jpg, .jpeg)): img_path os.path.join(input_dir, img_name) img cv2.imread(img_path) if img is None: continue h, w img.shape[:2] # 计算缩放比例 scale target_short_side / min(h, w) new_h, new_w int(h * scale), int(w * scale) # 使用INTER_AREA插值适合缩小 img_resized cv2.resize(img, (new_w, new_h), interpolationcv2.INTER_AREA) output_path os.path.join(output_dir, img_name) cv2.imwrite(output_path, img_resized) print(fResized {img_name} to {new_h}x{new_w}) # 使用示例 resize_bg_images(./raw_bg_imgs, ./bg_imgs_resized)准备好背景图后我们就要为它们生成最重要的“地图”——深度图和分割图。4. 生成深度图让文字拥有“空间感”深度图是SynthText实现3D透视效果的关键。官方推荐使用一个基于Matlab的深度预测网络Liu et al.。但说实话对于大多数开发者在Windows上配置Matlab环境是一道坎。这里我分享两个更友好的路径。路径一使用更新的单目深度估计模型推荐现在有很多基于PyTorch或TensorFlow的轻量级深度估计模型比如MiDaS、DPT精度比老模型好且部署简单。我们可以用Python脚本批量处理。以下是一个使用MiDaS的示例import torch import cv2 import numpy as np from torchvision.transforms import Compose, Resize, ToTensor, Normalize import glob # 加载MiDaS模型 (小型版本速度较快) model_type DPT_Hybrid # 或 MiDaS_small midas torch.hub.load(intel-isl/MiDaS, model_type) device torch.device(cuda) if torch.cuda.is_available() else torch.device(cpu) midas.to(device) midas.eval() # 预处理变换 transform Compose([ Resize(384), ToTensor(), Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]), ]) def predict_depth(image_path, output_mat_path): img cv2.imread(image_path) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) input_batch transform(img).unsqueeze(0).to(device) with torch.no_grad(): prediction midas(input_batch) prediction torch.nn.functional.interpolate( prediction.unsqueeze(1), sizeimg.shape[:2], modebicubic, align_cornersFalse, ).squeeze() depth prediction.cpu().numpy() # 归一化到0-1并反转使得近处亮远处暗与SynthText预期一致 depth (depth - depth.min()) / (depth.max() - depth.min()) depth 1 - depth # 反转近亮远暗 # 保存为.mat文件 (需要scipy) from scipy import io io.savemat(output_mat_path, {depth: depth}) print(fDepth saved to {output_mat_path}) # 批量处理 bg_img_dir ./bg_imgs_resized for img_path in glob.glob(os.path.join(bg_img_dir, *.jpg)): mat_path img_path.replace(.jpg, _depth.mat) predict_depth(img_path, mat_path)这个方法省去了配置Matlab的麻烦利用现代深度学习模型效果通常更好。路径二坚持使用官方Matlab脚本备用如果你必须使用官方流程需要注意版本兼容性问题。官方代码依赖的MatConvNet库版本较老。你需要下载matconvnet-1.0-beta9版本并按照其官网指南编译。在Windows下你很可能需要安装并配置Visual Studio的C编译器给Matlab使用。编译成功后运行predict_depth.m脚本。记得修改脚本中的图片路径和输出路径。无论用哪种方法最终你会为每张背景图得到一个.mat文件里面存储着深度矩阵。接下来我们需要把所有.mat文件合并成一个SynthText能读取的depth.h5文件。5. 生成分割图定义文字的“可放置区域”分割图告诉SynthText哪里是天空不能放字哪里是墙壁可以放字。官方使用gPb-ucm算法并通过run_ucm.m这个Matlab脚本来生成。这个脚本同样需要在Linux环境下运行因为依赖一些编译工具。实战步骤在一台Ubuntu机器上安装Matlab例如R2016b或更高。克隆MCG代码库官方推荐的分割工具。将SynthText代码中prep_scripts目录下的run_ucm.m和floodFill.py准备好。修改run_ucm.m中的img_dir你的背景图路径和mcg_dirMCG代码路径。在Matlab中运行run_ucm.m。它会为每张图生成一个ucm.mat文件里面包含了超像素分割边界信息。接着运行Python脚本floodFill.py。这个脚本会读取ucm.mat进行区域填充和标记最终生成我们需要的seg_uint16.h5文件。你需要修改floodFill.py里的base_dir变量指向你存放ucm.mat文件的目录。注意floodFill.py脚本里用h5py读取.mat文件时如果之前保存用了-v7.3格式用scipy.io.loadmat可能会报错。这时直接使用h5py.File(‘xxx.mat’, ‘r’)来读取是最稳妥的。这是我在实践中遇到的一个典型坑点。生成seg_uint16.h5后我们手头就有了三样东西原始的JPG背景图、包含所有深度图的depth.h5、包含所有分割图的seg_uint16.h5。是时候把它们“组装”起来了。6. 合并数据打造专属的dset.h5文件SynthText的主合成脚本gen.py需要一个名为dset.h5的文件作为输入。这个文件是一个HDF5格式的数据库里面按组group存储了所有背景图的图像数据、深度数据和分割数据。官方提供了一个基础的dset.h5但我们要用自己的数据。我们需要写一个脚本把我们准备好的三个部分打包成一个新的dset.h5。下面是我修改和验证过的add_more_data.py脚本核心部分import h5py import os import cv2 import numpy as np def create_custom_dset(bg_img_dir, depth_h5_path, seg_h5_path, output_dset_path): bg_img_dir: 背景图片文件夹路径 depth_h5_path: 上一步生成的 depth.h5 文件路径 seg_h5_path: 上一步生成的 seg_uint16.h5 文件路径 output_dset_path: 输出的 dset.h5 文件路径 # 打开或创建输出文件 with h5py.File(output_dset_path, w) as db: # 创建三个组 db.create_group(image) db.create_group(depth) db.create_group(seg) # 打开深度和分割数据文件 with h5py.File(depth_h5_path, r) as depth_db, \ h5py.File(seg_h5_path, r) as seg_db: # 遍历背景图片 for img_name in os.listdir(bg_img_dir): if not img_name.lower().endswith((.png, .jpg, .jpeg)): continue img_path os.path.join(bg_img_dir, img_name) # 读取图片并转换为RGB格式SynthText内部使用RGB img_bgr cv2.imread(img_path) if img_bgr is None: print(fWarning: Could not read {img_path}, skipping.) continue img_rgb cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) # 在HDF5中存储图像数据 db[image].create_dataset(img_name, dataimg_rgb, compressiongzip) # 存储深度数据 (确保key名一致通常是去掉扩展名的文件名) key img_name # 或者 os.path.splitext(img_name)[0] if key in depth_db: db[depth].create_dataset(img_name, datadepth_db[key][:], compressiongzip) else: print(fWarning: Depth for {img_name} not found, skipping this image.) del db[image][img_name] # 删除已创建的图像数据 continue # 存储分割数据及属性 if mask in seg_db and img_name in seg_db[mask]: mask_ds seg_db[mask][img_name] db[seg].create_dataset(img_name, datamask_ds[:], compressiongzip) # 复制区域和标签属性 db[seg][img_name].attrs[area] mask_ds.attrs[area] db[seg][img_name].attrs[label] mask_ds.attrs[label] else: print(fWarning: Segmentation for {img_name} not found, skipping this image.) # 清理已创建的数据集 del db[image][img_name] del db[depth][img_name] continue print(fCustom dataset created successfully at {output_dset_path}) # 调用函数 create_custom_dset( bg_img_dir./bg_imgs_resized, depth_h5_path./preprocessed/depth.h5, seg_h5_path./preprocessed/seg_uint16.h5, output_dset_path./dset_custom.h5 )运行这个脚本后你就得到了一个完全由自定义背景图构成的dset_custom.h5文件。这是启动合成引擎的“燃料”。7. 启动合成引擎生成海量训练数据万事俱备只欠东风。现在我们可以运行SynthText的主程序gen.py来生成数据了。在运行前有几个关键参数需要根据你的需求调整修改数据源打开gen.py找到DB_FNAME变量大约在第30行将其路径指向你刚刚生成的dset_custom.h5。调整合成参数在gen.py文件头部或通过命令行NUM_IMG: 你总共想生成多少张合成图片。注意这个数字是“尝试”生成的数字可能因为背景图不合适而少于它。INSTANCE_PER_IMAGE: 每张背景图尝试生成多少个文本实例。默认是1到5个随机。如果你想要文字更密集的场景可以调高上限。MAX_TEXT每张图片上放置的最大单词数。这会影响文本的密集程度。TEXT_PLACEHOLDER: 文本语料库文件路径。默认是英文新闻。如果你想生成中文数据需要准备一个中文文本文件每行一段话并修改这里。调整好后在命令行运行cd /path/to/SynthText python gen.py程序就会开始运行。你会在终端看到进度日志。如果某张背景图计算失败比如找不到合适的区域放文字它会跳过并报一个警告比如“0 of 11”这意味着有背景图被跳过了。这是正常的取决于你的背景图复杂度。合成完成后你会得到一个巨大的SynthText.h5文件或者你指定的输出文件名。这个文件里包含了所有合成图片的图像数据、字符级边界框、单词级边界框和对应的文本字符串。8. 可视化与后处理看看我们生成了什么生成完数据第一件事肯定是看看效果。SynthText项目里提供了一个visualize_results.py脚本。运行它并指定你生成的.h5文件路径它会随机采样一些结果展示出来。python visualize_results.py --h5_file ./SynthText_custom.h5 --viz_dir ./samples/这个脚本会把图片保存到--viz_dir指定的目录并在图片上画出字符框(charBB)和单词框(wordBB)同时在终端打印出图片名和其中的文字内容。查看效果时要重点关注以下几点文字位置是否自然是否贴在了合理的表面上如地面、墙面而不是飘在空中或穿过物体。透视变形是否合理根据深度图文字的形变是否符合场景的透视规律。遮挡处理文字是否被前景物体由分割图定义合理地部分遮挡。清晰度与融合文字边缘是否过于生硬颜色和亮度是否与背景融合。如果发现大量不自然的合成结果可能需要回溯检查你的深度图或分割图质量。深度图预测不准会导致文字透视怪异分割图不准确会导致文字放在不该放的地方比如天空或人脸。9. 进阶技巧支持竖排文本与中文默认的SynthText是为水平拉丁文本设计的。但在很多中文场景、古籍或者一些特殊排版中我们需要竖排文本。社区里已经有人提出了解决方案参考官方Issue #114。核心是修改text_utils.py文件中的render_multiline函数。你需要修改文本布局的逻辑将水平行布局改为垂直列布局。这涉及到计算文本走向、换列逻辑等。修改后重新运行gen.py就能生成包含竖排文本的合成数据了。这对于训练能识别竖排文字的OCR模型至关重要。另外对于中文你还需要准备一个大的中文文本语料库如新闻、小说文本并处理好字体文件。SynthText支持指定字体目录你需要将一批中文字体如宋体、黑体、楷体等放入字体目录并在代码中指定以增加字体的多样性。10. 避坑指南与经验之谈走完整个流程你可能会遇到一些我踩过的坑这里集中列出来帮你节省时间坑1环境配置冲突。特别是Matlab老版本与新的系统库如gcc不兼容。如果深度图/分割图不是必须用官方工具强烈建议尝试我前面提到的PyTorch版深度估计和其他的分割工具如PyTorch版本的UCM算法复现可以彻底摆脱Matlab。坑2数据格式转换。从.mat到.h5的转换过程中一定要注意数据维度和数据类型。深度数据在gen.py中读取时默认期望是3通道的但很多深度估计模型输出是单通道。你需要根据代码中的注释通常是# depth depth[:,:,1]这一行决定是否要索引特定通道或复制通道来满足输入要求。坑3合成速度慢。SynthText的合成过程是CPU密集型的而且单线程。如果你有数万张背景图合成过程会非常漫长。一个优化思路是将你的dset.h5分成几个小份在多台机器上并行运行gen.py最后再合并结果。不过合并HDF5文件需要额外脚本。坑4生成的数据不均衡。如果你的背景图库中某类场景如室内特别多而另一类如户外少那么合成数据也会不均衡。最好在准备背景图阶段就有意识地平衡各类别的数量。坑5标注框的精度。SynthText生成的wordBB和charBB是四边形框这对于检测模型训练是友好的。但如果你训练的识别模型需要矩形框水平框可能需要进行一个最小外接矩形的转换。此外这些框是“透视图上的框”在评估某些使用水平矩形IoU的指标时需要注意。最后想说的是SynthText是一个强大的起点但它生成的毕竟是合成数据。要想模型在真实世界表现更好用合成数据预训练再用少量真实数据微调是一个被验证有效的策略。我自己的项目就是先用几万张合成数据训练一个基础模型然后再用几百张精心标注的真实数据微调最终达到了比只用真实数据好得多的效果。整个过程虽然步骤繁琐但当你看到自己生成的、贴合业务场景的海量数据并成功训练出模型时那种成就感是完全不一样的。希望这篇详细的实战指南能帮你扫清障碍顺利构建出你的自定义OCR数据集。

相关新闻

五款高效JavaScript代码保护工具横向评测:jscrambler、JShaman、jsfack、Ipa Guard与jjencode实战解析

五款高效JavaScript代码保护工具横向评测:jscrambler、JShaman、jsfack、Ipa Guard与jjencode实战解析

1. 为什么你的JavaScript代码需要“穿件隐身衣”? 你有没有过这样的经历?辛辛苦苦写了一个星期的前端特效,或者一个精巧的交互逻辑,结果上线没多久,别人打开浏览器开发者工具,轻轻一点“Sources”&#xff…

2026/7/3 18:05:43 阅读更多 →
NBTExplorer:Minecraft数据编辑的颠覆性效率革命

NBTExplorer:Minecraft数据编辑的颠覆性效率革命

NBTExplorer:Minecraft数据编辑的颠覆性效率革命 【免费下载链接】NBTExplorer A graphical NBT editor for all Minecraft NBT data sources 项目地址: https://gitcode.com/gh_mirrors/nb/NBTExplorer 你是否曾因Minecraft存档损坏而丢失数百小时的建造成果…

2026/7/4 2:04:31 阅读更多 →
PP-DocLayoutV3部署详解:基于Docker与GPU算力的高性能环境配置

PP-DocLayoutV3部署详解:基于Docker与GPU算力的高性能环境配置

PP-DocLayoutV3部署详解:基于Docker与GPU算力的高性能环境配置 如果你已经成功跑通了PP-DocLayoutV3的基础部署,恭喜你,你已经迈出了第一步。但要把这个强大的文档解析模型真正用起来,尤其是在生产环境中稳定、高效地提供服务&am…

2026/7/4 12:11:09 阅读更多 →

最新新闻

AI钓鱼攻击:从原理到防御,构建企业安全免疫系统

AI钓鱼攻击:从原理到防御,构建企业安全免疫系统

1. 项目概述:当钓鱼攻击披上AI的“羊皮” 如果你还认为钓鱼邮件是那种满屏错别字、用蹩脚英文催你点链接的“垃圾”,那你的安全观念可能还停留在五年前。我干了十多年网络安全,亲眼看着攻击手段从“广撒网”的群发垃圾邮件,进化到…

2026/7/4 12:14:52 阅读更多 →
如何永久保存微信聊天记录:免费开源工具让你的数字记忆永不丢失

如何永久保存微信聊天记录:免费开源工具让你的数字记忆永不丢失

如何永久保存微信聊天记录:免费开源工具让你的数字记忆永不丢失 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending…

2026/7/4 12:14:52 阅读更多 →
量子科技中的多样性与包容性实践

量子科技中的多样性与包容性实践

1. 量子科技领域为何需要关注多样性与包容性?量子计算、量子通信等量子科技正在重塑未来技术格局。与传统学科不同,量子科技本质上是一门高度交叉的领域,融合了物理学、计算机科学、材料学、工程学等多个学科。这种交叉性决定了其发展特别依赖…

2026/7/4 12:12:52 阅读更多 →
终极指南:3分钟解决Windows上iPhone USB网络共享驱动问题

终极指南:3分钟解决Windows上iPhone USB网络共享驱动问题

终极指南:3分钟解决Windows上iPhone USB网络共享驱动问题 【免费下载链接】Apple-Mobile-Drivers-Installer Powershell script to easily install Apple USB and Mobile Device Ethernet (USB Tethering) drivers on Windows! 项目地址: https://gitcode.com/gh_…

2026/7/4 12:10:51 阅读更多 →
SaToken实战:密码加密与会话查询的深度整合与应用

SaToken实战:密码加密与会话查询的深度整合与应用

1. 项目概述:为什么我们需要深度整合密码加密与会话查询? 在任何一个需要用户登录的现代Web应用中,安全都是悬在开发者头顶的达摩克利斯之剑。我们常常会陷入一种“头痛医头,脚痛医脚”的困境:用户注册时,我…

2026/7/4 12:10:51 阅读更多 →
Appium视觉测试实战:从像素对比到智能忽略的UI自动化回归方案

Appium视觉测试实战:从像素对比到智能忽略的UI自动化回归方案

1. 项目概述:为什么我们需要视觉测试?在移动应用自动化测试的征途上,我们常常会遇到一个令人头疼的问题:功能逻辑明明跑通了,按钮能点,数据能提交,但界面却“跑偏”了。可能是某个按钮在iOS 17上…

2026/7/4 12:08:51 阅读更多 →

日新闻

Memcached 1.6.43 发布:关键安全修复版本,多项问题得到解决

Memcached 1.6.43 发布:关键安全修复版本,多项问题得到解决

Memcached 1.6.43 正式发布,这是一个关键的安全修复版本,修复了多个方面的问题,还对部分功能进行了优化。 安全修复亮点 此次发布在安全修复上表现突出。binprot 避免了项目引用计数溢出,mcmc 因安全问题提升了上游版本号&#xf…

2026/7/4 0:04:29 阅读更多 →
终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案

终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案

终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案 【免费下载链接】HMCL A Minecraft Launcher which is multi-functional, cross-platform and popular 项目地址: https://gitcode.com/gh_mirrors/hm/HMCL HMCL(Hello Minecraft! Lau…

2026/7/4 0:06:29 阅读更多 →
KMX63与PIC18F66K40在嵌入式HMI中的硬件协同与低功耗设计

KMX63与PIC18F66K40在嵌入式HMI中的硬件协同与低功耗设计

1. KMX63与PIC18F66K40的硬件协同架构解析KMX63作为一款三轴加速度计和磁力计组合传感器,与PIC18F66K40微控制器的搭配堪称嵌入式HMI开发的黄金组合。这套硬件组合的核心优势在于KMX63提供的高精度运动感知能力与PIC18F66K40强大的信号处理能力形成了完美互补。KMX6…

2026/7/4 0:06:29 阅读更多 →

周新闻

月新闻