简介面向目标检测入门与印章识别场景的专用数据集包含210张真实印章图片提供Pascal VOC与YOLO两种主流标注格式共计488个矩形框标注类别仅seal一种。适合练习训练YOLO系列、Faster R-CNN等常见检测模型也可用于印章定位、文档自动化核验、票据审核等项目的初期验证。压缩包共632个文件主体为210张jpg原图、210个xml标注文件和212个txt标注文件并含少量辅助文件整体大小36.47MB结构简洁下载后即可按VOC或YOLO目录规范直接使用。数据集使用labelImg工具画框标注标注规则为矩形框坐标准确、类别一致便于用户快速开展模型训练、格式转换与评估流程。已有919人浏览学习。对于需要标准检测数据集完成课程设计或算法实验的开发者这份资源能省去自行采集标注的时间直接进入模型迭代阶段。1. 印章检测数据集210张单类别样本到底能做什么印章检测是文档自动化里常被低估的一类任务合同验真、财务票据归档、OA审批流里的关键页抽取都要先把印章位置找出来后续才能做印章内容识别或真伪比对。这个标题给的是210张图片、1个类别、同时提供VOC和YOLO两种格式的检测数据集属于典型的小样本单类别目标检测资源。别嫌数据量小印章目标在图像里结构固定、颜色显著、形态高度一致210张经过合理划分和增强完全能训出一个在真实文档上可用的检测模型。这套数据集解决的是两类人的问题一类是刚接触检测任务、想用现成标注数据跑通训练到推理全流程的开发者另一类是有自己的业务图像但不知道标注格式该怎么组织、需要参照样板来规整数据的工程师。配合VOC的XML标注和YOLO的TXT标注你既能直接喂给YOLO系列训练也能用标注工具打开做二次校验。下面我按「格式怎么选、转换怎么做、小数据怎么训、坑在哪、怎么扩成可用模型」的顺序把这条链路完整讲一遍。2. VOC与YOLO两种格式的差异和选型先搞懂再动手2.1 两种标注格式的存储结构和核心字段VOC格式是Pascal VOC项目定义的目标检测标注规范核心是每张图片对应一个同名XML文件标签信息嵌套在object节点里。看一个典型结构annotation folderseal_images/folder filenameIMG_001.jpg/filename size width1280/width height960/height depth3/depth /size object nameseal/name poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin312/xmin ymin228/ymin xmax587/xmax ymax502/ymax /bndbox /object /annotation这里xmin、ymin是框左上角坐标xmax、ymax是右下角坐标单位是像素坐标系原点在图像左上角。name字段就是类别名truncated表示目标是否被图像边界截断difficult表示目标是否难以辨认——这两个字段在转换时要特别注意很多工具默认丢弃difficult为1的框你不了解这个逻辑容易丢掉有效样本。YOLO格式则完全不同它把标注压缩成每张图一个TXT文件每一行描述一个目标格式是类别id、归一化后的中心点x坐标、中心点y坐标、归一化后的框宽w、框高h。归一化是相对图像宽高做的所以坐标全都落在0到1之间。0 0.351562 0.380208 0.214844 0.285417上面这行表示类别0即seal框中心在图像水平方向35.16%、垂直方向38.02%的位置框宽占整图宽度的21.48%高占整图高度的28.54%。YOLO格式没有保存图像尺寸信息所以训练时必须同时提供图像路径框架在读图的时候拿实际宽高来解析这些比例值。这也是为什么YOLO格式的标注文件不能脱离对应图片单独存在迁移到新机器时目录结构要保持完整。2.2 什么时候用VOC、什么时候用YOLO两种格式各有不可替代的场景。VOC格式的可读性最好XML里能塞很多附加信息除了检测框还能记录遮挡、截断、视角这些细粒度状态适合用在数据审计、多轮标注、以及需要人工核对标签质量的环节。标注工具如LabelImg、X-AnyLabeling原生支持VOC格式输出你拿它做二次修改也方便。YOLO格式是训练效率优先的产物。Ultralytics的YOLOv5/v8/v11训练管线、MMDetection里适配YOLO系列的配置标准数据格式都倾向TXT标注因为加载时一次读入全部归一化坐标省去了解析XML的开销。另外YOLO格式天然适合做在线数据增强因为坐标是比例值图像缩放、翻转后只需对坐标做对应数学变换不需要重新读标注文件。我一般建议的工作流是标注和存储用VOC训练前转成YOLO。这样保留原始像素坐标方便随时回查和重新划分数据集训练时转成YOLO格式获得最佳性能。这个数据集同时给了两种格式省去了自己转换的时间但你要清楚转换不是简单的格式替换坐标换算和特殊框过滤都有讲究下一章我把转换脚本和边界情况拆开讲。3. 从VOC的XML到YOLO的TXT转换脚本与四个边界坑3.1 一套可直接落地的转换脚本要把VOC格式转成YOLO格式核心是读取XML里的bndbox坐标和目标类别然后除以图像宽高得到归一化值。但转换过程远不止四则运算类别映射、difficult过滤、坐标越界都需要处理。下面这个脚本我跑过多套数据集直接复制改下路径就能用import os import xml.etree.ElementTree as ET # 类别列表顺序就是YOLO里类别id的编号不能乱 CLASSES [seal] # 单类别数据集只有印章 def convert_xml_to_yolo(xml_path, out_dir): tree ET.parse(xml_path) root tree.getroot() # 从XML里读图像宽高用于坐标归一化 size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) base_name os.path.splitext(os.path.basename(xml_path))[0] txt_path os.path.join(out_dir, base_name .txt) lines [] for obj in root.findall(object): name obj.find(name).text if name not in CLASSES: print(f[skip] 未知类别: {name} in {xml_path}) continue cls_id CLASSES.index(name) # difficult为1的框通常质量存疑直接跳过 difficult obj.find(difficult) if difficult is not None and int(difficult.text) 1: print(f[skip] difficult框: {base_name}) continue bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) # 坐标换算成YOLO格式中心点cx/cy和宽高w/h全部除以图像尺寸 cx (xmin xmax) / 2.0 / img_w cy (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h # 归一化坐标理论上在0~1之间越界一般是因为标注框超出了图像边界 cx min(max(cx, 0.0), 1.0) cy min(max(cy, 0.0), 1.0) w min(max(w, 0.0), 1.0) h min(max(h, 0.0), 1.0) line f{cls_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f} lines.append(line) with open(txt_path, w, encodingutf-8) as f: f.write(\n.join(lines)) def batch_convert(xml_dir, out_dir): os.makedirs(out_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue xml_path os.path.join(xml_dir, xml_file) convert_xml_to_yolo(xml_path, out_dir) print(f[done] {xml_file}) if __name__ __main__: batch_convert(path/to/voc_xmls, path/to/yolo_labels)我解释一下几个关键点。类别列表CLASSES的顺序决定了生成的TXT里类别id如果你以后要加新类别只能往这个列表尾部追加不能中间插入否则会用旧类别id训练过的模型全部报废这是必须守住的规则。difficult这个字段在XML里不一定始终存在所以我用了if判断而不是直接取很多标注工具产出不一致直接取字段会抛异常。最后是坐标限幅这段代码用min/max把归一化坐标约束在0到1之间。标注框如果正好压在图像边缘人工标注时很容易画出超出边界的框不处理的话训练时YOLO的loss计算会出现诡异结果常见表现是训练Loss能降但mAP一直在低位徘徊你查了很久发现是边界框的问题。3.2 转换过程中的四个边界坑第一个坑是类别名称和编号不匹配。标注的人可能在XML里写了中文类别名「印章」但你的CLASSES列表里是英文字符串转换出来的TXT全部指向未知类别。一旦发现目标类别变成负数或者训练时YOLO报class id out of range先查CLASSES映射表。处理方法是转换前先遍历所有XML统计unique类别名确保每个名字都能在CLASSES里找到对应项。第二个坑是图像尺寸和XML里记录的不一致。比如缩放处理过的图片XML里的width/height还是原始的转换出来的归一化坐标全部偏移训练时模型看到的框和标注框对不上。我做数据集清洗时常常跑一遍脚本把所有图片的真实尺寸和XML里的size字段比对一次发现不一样的直接把XML改掉。第三个坑是PNG带Alpha通道的情况。YOLO训练读图用的是OpenCV或者PILPNG的四通道图像在部分框架里被读成三通道像素坐标其实不变但颜色分布变了。印章数据集里有透明底的PNG时建议统一转成RGB的JPG避免训练和推理时读图结果不一致。第四个坑是转换后要立即做反向校验。转完不是就完事了随机抽几张图把TXT里的归一化坐标乘回图像宽高画框出来比对原标注看位置是否重合。我之前偷懒跳过这步训了两个小时发现数据增强视觉图里框全是错位的回查才知道是脚本里除以的尺寸变量写错了白白浪费半天算力。转换脚本本身不难但校验环节不能省。4. 用210张图训YOLO目录划分、配置文件与参数调节4.1 数据集目录组织与训练/验证划分拿到VOCYOLO双格式的210张图首先要做的是按YOLO的目录规范重新整理。Ultralytics YOLO对数据集目录的约定是images和labels两个主目录各自再分train和val子目录图片和对应的TXT标注文件名要完全一致后缀不同放在对应的子目录里。我习惯再单独留一个test目录放最终测试图这个目录不参与训练也不参与验证只在最后评估时用。dataset/ ├── images/ │ ├── train/ # 约160张 │ ├── val/ # 约40张 │ └── test/ # 约10张 └── labels/ ├── train/ ├── val/ └── test/划分比例我推荐80/15/5或者更保守的75/20/5。210张数据量不大验证集占比太低会导致mAP波动剧烈难以判断模型真实水平占比太高训练数据不够。划分时要注意打乱顺序不要把连续拍摄的同场景图像全分到训练集否则验证集全是没见过的新场景指标偏低反过来如果验证集和训练集高度相似指标虚高上线就露馅。我一般直接用Python的random.shuffle加固定随机种子来划分确保可复现。import os import random random.seed(42) # 固定种子复现划分结果 img_files os.listdir(dataset/images/all) random.shuffle(img_files) train_ratio 0.75 val_ratio 0.20 train_files img_files[:int(len(img_files) * train_ratio)] val_files img_files[int(len(img_files) * train_ratio): int(len(img_files) * (train_ratio val_ratio))] test_files img_files[int(len(img_files) * (train_ratio val_ratio)):] # 把文件名按训练/验证/测试分组写进对应images和labels目录 # 注意移动图片的同时要移动同名的txt标注文件划分完成之后检查一件事你移动的是图片和标注文件不要让某张图片在images目录里有labels目录里的同名txt丢了或者反了。210张图规模小手动检查一遍也花不了多少时间但漏一张标注文件训练时报错找半天才能定位到是哪个文件缺失。4.2 训练配置YAML写法和关键参数设置YOLO训练需要一个数据集配置文件指定图片路径和类别信息。210张1类别的数据集配置文件非常简单# seal_dataset.yaml path: /absolute/path/to/dataset # 数据集根目录的绝对路径 train: images/train # 相对path的训练图片路径 val: images/val # 相对path的验证图片路径 test: images/test # 相对path的测试图片路径可留空 nc: 1 # 类别数量只有seal一个类别 names: [seal] # 类别名列表顺序和TXT标注里的id对应配置文件里path建议写绝对路径最好不带中文和空格。我之前把数据集放在中文目录下YOLO训练时部分依赖库处理编码有问题报错信息还很隐晦查了半天才想到是路径的锅。改绝对路径后一切正常这种玄学问题遇一次长记性。训练参数方面数据量小模型复杂度要控制。用yolov5n.pt或者yolov8n.pt作为预训练权重起步不要一上来就上yolov8x210张图的规模撑不住大模型的参数量训练结果大概率严重过拟合。基础命令如下yolo detect train \ dataseal_dataset.yaml \ modelyolov8n.pt \ epochs100 \ batch16 \ imgsz640 \ patience20 \ device0epochs设100看起来不少但配合patience20做早停实际训练可能在30到50轮就会停下。patience的意思是连续20轮验证集mAP没有提升就自动终止训练这能有效防止小数据集上过拟合。batch16在210张图的规模下每轮迭代10步多一点梯度更新比较平稳。imgsz640是YOLO系列的默认输入尺寸印章如果原图分辨率高、目标占图比例小可以试试imgsz960但batch要相应调小显存占用会增大。训练过程中重点观察两个指标一个是验证集mAP0.5印章这类单类别目标检测任务0.85以上算基本可用另一个是训练集和验证集loss的差距如果训练loss持续下降但验证loss回升说明模型开始过拟合早停会帮你拦住。训练结束后best.pt就是验证集表现最好的权重后续推理都优先用它而不是最后一轮的last.pt。4.3 小样本训练的推理阈值策略训练完模型推理时的置信度阈值要针对业务场景单独调。印章检测不同场景容忍度差异很大做文档归档漏检一个印章会导致该文档没有被正确分拣影响后续流程所以要压低置信度阈值尽量多框出候选区域宁可多框不可漏做印章真伪比对多框出假阳性反而干扰比对引擎要抬高阈值保证框出的基本都是真印章。我常用的做法是拿训练集之外的真实文档图跑一遍批量推理画出置信度分布直方图找一个明显的分界点。印章目标特征明确模型置信度往往两极分化识别准的都在0.85以上误检的通常在0.3到0.5之间。这种情况下阈值取0.5到0.6之间非常稳妥。这个调参过程看的是实际分布而不是拍脑袋上线前一定要做翻车概率小很多。from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) results model.predict( sourcetest_images/, conf0.5, # 置信度阈值根据分布直方图调整 iou0.5, # NMS的IoU阈值控制重叠框的去留 saveTrue, )conf和iou两个参数作用完全不同。conf管的是「置信度低于这个值的框直接扔」iou管的是「两个框重叠比例高于这个值时只保留置信度更高的那个」。印章场景里同一枚印章通常不会被重复检测除非图像里有多个相似的红圈iou保持默认0.5就行。conf调高调低直接影响召回率和精确率的平衡点这是上线前必调的参数。5. 印章数据集的常见问题排查5条具体踩坑记录5.1 标注框坐标出现负数导致训练崩溃现象是训练过程中频繁弹出警告提示box坐标越界个别情况直接报错终止训练检查TXT标注发现坐标值出现了负数。原因有两个方向一是原始VOC标注里bndbox本身就存在负坐标人工标注时框拖到了图像外二是转换脚本在处理这类越界框时没有做限幅负数直接写进了TXT里。YOLO训练时读取近似的框会触发tensor维度错误看起来像数据加载器坏了实际源头是标注。解决方法是转换脚本里加上我前面写的min/max限幅逻辑同时转换完成后用一段检查代码扫描所有标签文件# 检查labels目录下所有txt是否有越界坐标 awk { if ($20 || $30 || $40 || $50 || $21 || $31 || $41 || $51) print FILENAME, $0 } labels/*/*.txt这个命令会把所有归一化坐标不在0到1范围内的行打印出来如果有输出就说明对应图片的标注需要翻修。小数据集翻修成本不高但漏掉一个越界框训练时的损失计算就可能出现异常值表现为Loss曲线突然跳高。5.2 mAP0.5高但实际检测效果差现象是验证集上mAP指标到了0.9以上看起来很漂亮但拿到真实业务图上跑漏检、误检一堆。这是小数据集训练里最容易遇到的假象。原因是验证集分布和训练集太像或者干脆是划分时没打乱顺序同场景的图像同时进了训练集和验证集模型等于记住了训练图换场景就不会泛化了。210张图本来就少如果原始采集集中在某几个合同模板或某几台扫描仪模型学的是背景特征而不是印章本身的特征。解决方法是严格按场景划分数据集同一个来源、同一批次、同一拍摄条件下的图片全部分在同一组划分维度是「场景」而不是「单张图」。先把图按来源分组再拿组做划分保证验证集里的场景在训练集里没出现过。如果210张里场景本身很少那就得靠第6章的数据扩增思路补足多样性否则再怎么划分模型也泛化不了。5.3 训练时类别数为0或报错class id out of range现象是运行训练命令后输出了DataLoader信息但马上报错class id out of range或者stats显示nc0。原因是labels目录下的TXT文件为空或者类别id和YAML里的nc对不上。空TXT文件表示该图没有任何标注目标训练时会跳过这张图如果全部为空就等于没有训练数据。class id超出范围通常是因为把不同数据集的TXT混在了一起比如某个标注文件的类别id写的是1但你的数据集只有nc1合法id只能是0。排查方法是按顺序检查三层先看labels目录下TXT文件数量是否和images目录下的图片数量一致再看TXT内容是否全部为空空文件的行数加起来应为210减去无目标图片数最后用代码统计所有类别的id分布确认只有0这一个id出现。这三个检查跑一遍问题基本能定位。5.4 图像格式混杂导致训练时读图失败现象是训练报错提示某张图片无法被解码定位后发现是图片后缀是.jpg但实际文件是PNG编码或者图片本身已经损坏。210张的小数据集合里混入几张坏图能直接影响训练效果因为YOLO训练是随机采样的坏图反复被采样到就会反复报错。原因是数据集经过多次拷贝、压缩、格式转换后部分图片的文件头和后缀不一致。比如微信传输过的图片会被自动转成jpg但内部编码可能还是webp还有部分扫描件是单通道灰度图但框架默认按三通道读。解决方法是训练前统一清洗一遍图像格式。我一般写一个预处理脚本用PIL打开每张图统一转为RGB模式的JPG质量设95同时修正后缀。这一步还能顺带把所有图片尺寸做了规范化处理避免参差不齐的分辨率影响batch训练。from PIL import Image import os for img_file in os.listdir(dataset/images/all): img_path os.path.join(dataset/images/all, img_file) img Image.open(img_path).convert(RGB) # 统一转成JPG并覆盖源文件 img.save(img_path.replace(.png, .jpg), JPEG, quality95)5.5 印章目标太小导致检测不到现象是训练完成大尺寸印章检测效果不错但图像里占比很小的印章总是漏检。真实合同场景中印章直径可能只有整图宽度的5%到10%在640×640输入下只有30到60个像素YOLO的检测头对极小目标天然不敏感。原因是多尺度特征融合对小目标分配的特征层级不够加上210张数据里小目标样本占比可能很少。模型对大目标过拟合小目标特征没学到。解决思路有三个方向一是训练时把imgsz提到960或1280小目标在输入图像里的像素占比变大模型更容易学到特征代价是显存和训练时间增加二是对小目标样本做过采样在数据加载时让包含小印章的图片有更高的被采样概率三是推理时做TTA或切图推理把大图切成若干小块每个小块单独检测再合并结果切图对小目标检测的提升非常明显。具体做法在下一章详细展开。6. 把210张扩成可落地的模型三种实用扩充手段6.1 基于原始数据的强增强不要一上来就合成数据先把手头210张的价值榨干。YOLO训练管线内置的Mosaic、MixUp、HSV增强在小数据集上能显著提升泛化能力但Mosaic要注意一个问题4张图拼成一张时原本就小的印章会被进一步缩小对小目标反而不利。我建议小数据集训练时把Mosaic概率从默认的1.0降下来比如0.5让模型在一半的迭代里看到完整比例的原始图另一半才叠加Mosaic增强。这个调整对验证集mAP的影响比较明显建议跑两版做对比哪个好用哪个。另外考虑朝向和翻转的物理合理性。印章在合同上什么角度都可能出现360度旋转增强是合理的不像文字检测领域怕字倒过来。用YOLO的旋转增强参数时要注意旋转后图像四角会有黑边部分实现会用灰色填充这种填充像素会让模型学到错误的边缘特征。我一般旋转增强的角度限制在±30度以内同时配合随机平移和缩放保证印章主体仍然完整出现在画面里。6.2 伪标签迭代用模型反哺数据集210张人工标注图训练出来的模型多少有可用性拿它对未标注的同类文档做批量推理把高置信度预测结果当作伪标签加入数据集再训练新模型这个过程可以迭代2到3轮小样本检测任务里效果明显。伪标签不是直接把模型输出写进数据集就完事要做两轮过滤第一轮用conf0.8的预测框筛选太低的不要第二轮做人工抽查每批伪标签随机抽20%叠在原图上肉眼确认框的精度发现有系统性的偏移比如框总是偏左半格就说明模型存在位置偏差这批伪标签质量不可靠。我通常会做两轮伪标签迭代从210张扩到400到500张的规模mAP一般能有6到10个点的提升。但第三轮往后收益快速递减因为模型产出的伪标签分布会固化模型的错误认知迭代越多越容易在错误方向上自我强化。两轮是安全边际。6.3 切图推理与工程化验证当检测目标还是不稳的时候不要慌着调模型结构切图推理往往最直接有效。把大幅扫描件按网格切成若干小块互相之间保留20%的重叠逐块送入模型检测最后把各块的结果映射回原图坐标做NMS融合。印章如果只有整图5%大小切成4×4后印章在其中的一块里就可能占到20%以上检测难度完全不在一个量级。切图推理的核心是坐标映射要算对我在代码里通常会维护一个偏移量记录每块图在原图中的起始坐标检测结果加上偏移再写回去。最终的NMS在原图尺度上做IoU阈值可以适当提高到0.6因为切图重叠区域里同一个印章可能被多块分别检出阈值太低会全部保留出现一个印章多个框的尴尬局面。上线前用一批完全没参与训练的真实图测一遍把置信度阈值定下来这个流程走完210张起步的印章检测模型就真正能落地了。这轮走下来你会发现210张1类别的小数据集不是瓶颈真正的瓶颈在格式转换是否严谨、划分是否合理、小目标预期是否管理到位。每一步踩的坑都用固定手段止损模型吃数据少反而更容易诊断问题出了状况回查链路很短。希望这篇笔记帮你在印章检测这条路上少走几趟弯路。本文还有配套的精品资源点击获取