简介这份印章检测数据集面向计算机视觉入门与目标检测实践者尤其适合需要快速验证印章识别方案的学生、算法工程师及科研人员。数据集同时提供Pascal VOC与YOLO两种标注格式包含jpg原图、对应xml文件与yolo格式txt文件可直接接入主流检测框架训练省去格式转换环节。压缩包共632个文件以210张jpg图片、210个xml标注和212个txt标签为主整体约36.47MB体积轻量便于本地调试与迁移。标注类别仅seal一类共488个矩形框使用labelImg完成画框标注准确合理适合作为单类别检测的基线数据。目前已有918人学习下载可作为印章检测模型训练、数据增强实验或教学演示的实用素材帮助读者快速搭建训练流程并验证检测效果。1. 210 张印章图、488 个框小样本单类检测数据集怎么用才不翻车拿到一个只有 210 张图、1 个类别、488 个标注框的印章检测数据集第一反应往往是「这么点数据能训出什么」。但印章检测本身就是个典型的单类、强纹理、背景相对固定的任务210 张精标的图在迁移学习加持下完全够跑出一个能用的基线。这个数据集同时给了 Pascal VOC 的 xml 和 YOLO 的 txt 两套标注图片命名是firc_yz_176.jpg这种带序号的风格标注工具是 labelImg类别只有一个seal平均每张图 2.3 个框。它适合两类人一是想跑通 YOLO 训练全流程但不想花时间标数据的新手二是需要快速验证印章检测方案可行性的工程同学。真正决定成败的不是图片数量而是你怎么切分、怎么配增强、怎么读标注。2. VOC 与 YOLO 双格式标注的差异与转换校验2.1 两套标注到底差在哪VOC 格式的 xml 存的是绝对像素坐标结构是bndbox下的xmin/ymin/xmax/ymax还带filename、size、object等节点。YOLO 的 txt 存的是归一化后的中心点加宽高一行一个目标格式是class_id cx cy w h五个值全部除以图片宽高落在 0 到 1 之间。这个数据集两套都给全了省掉了自己写转换脚本的麻烦但正因为是两套独立文件必须先校验它们是否一一对应否则训练时会出现「图有标注但框错位」这种最难查的问题。常见做法是先做文件名集合比对再做框数量比对。下面这段脚本一次性把两个问题都查了import os import xml.etree.ElementTree as ET img_dir images # jpg 目录 xml_dir annotations # VOC xml 目录 txt_dir labels # YOLO txt 目录 imgs {os.path.splitext(f)[0] for f in os.listdir(img_dir) if f.endswith(.jpg)} xmls {os.path.splitext(f)[0] for f in os.listdir(xml_dir) if f.endswith(.xml)} txts {os.path.splitext(f)[0] for f in os.listdir(txt_dir) if f.endswith(.txt)} # 1. 三份文件名是否完全一致 print(仅图片有:, imgs - xmls, imgs - txts) print(仅xml有:, xmls - imgs) print(仅txt有:, txts - imgs) # 2. 逐张比对框数量 mismatch [] for name in sorted(imgs xmls txts): tree ET.parse(os.path.join(xml_dir, name .xml)) n_xml len(tree.findall(object)) with open(os.path.join(txt_dir, name .txt)) as f: n_txt len([l for l in f if l.strip()]) if n_xml ! n_txt: mismatch.append((name, n_xml, n_txt)) print(框数不一致:, mismatch)逻辑上先做集合差集能立刻暴露漏标、错命名、扩展名大小写这类问题再做逐张框数比对能抓到转换脚本丢框的情况。参数上img_dir、xml_dir、txt_dir按你解压后的实际目录改如果数据集把 jpg 和 xml 混在一个文件夹里把三个路径指向同一目录即可脚本靠扩展名区分。2.2 归一化坐标的边界坑YOLO 的归一化值理论上必须在 0 到 1 之间但 labelImg 导出时如果框贴到图片边缘偶尔会出现xmax等于图片宽度导致cx w/2略微超过 1 的情况。训练框架大多会 clip但个别版本会直接报错或静默丢弃该框。校验时顺手加一道范围检查bad [] for name in txts: with open(os.path.join(txt_dir, name .txt)) as f: for i, line in enumerate(f): vals list(map(float, line.split()[1:])) if any(v 0 or v 1 for v in vals): bad.append((name, i, vals)) print(越界框:, bad[:10], 共, len(bad))发现越界不要慌绝大多数是浮点误差级别的超出手动 clip 到[0,1]即可如果超出幅度很大说明转换时用错了图片尺寸得回去核对 xml 里的size节点和实际 jpg 分辨率是否一致。2.3 类别映射表要固定这个数据集只有seal一类YOLO 的class_id就是 0。但如果你后续要合并其他印章数据集务必先定好统一的类别顺序表写进data.yaml的names里别让不同来源的数据各用各的 id。单类任务里这个坑不明显一旦扩类就是灾难。格式坐标类型存储单位类别表示典型文件VOC左上右下绝对像素字符串名称firc_yz_176.xmlYOLO中心宽高归一化 0-1整数 idfirc_yz_176.txt3. 用 YOLOv8 跑通印章检测训练全流程3.1 目录结构与 data.yamlYOLOv8 对目录结构有约定最省事的组织方式如下seal_dataset/ ├── images/ │ ├── train/ # 约 168 张 │ └── val/ # 约 42 张 ├── labels/ │ ├── train/ │ └── val/ └── data.yaml按 8:2 切分210 张里训练 168、验证 42。切分脚本要保证图片和同名 txt 一起移动别只挪图片import os, random, shutil random.seed(42) src_img, src_lbl images, labels for split in [train, val]: os.makedirs(fseal_dataset/images/{split}, exist_okTrue) os.makedirs(fseal_dataset/labels/{split}, exist_okTrue) names [os.path.splitext(f)[0] for f in os.listdir(src_img) if f.endswith(.jpg)] random.shuffle(names) cut int(len(names) * 0.8) for i, n in enumerate(names): split train if i cut else val shutil.copy(f{src_img}/{n}.jpg, fseal_dataset/images/{split}/{n}.jpg) shutil.copy(f{src_lbl}/{n}.txt, fseal_dataset/labels/{split}/{n}.txt)random.seed(42)固定随机种子保证每次切分结果一致方便复现实验。cut控制训练集比例210 张这种小数据集建议验证集别低于 15%否则指标抖动会很大。data.yaml内容path: ./seal_dataset train: images/train val: images/val nc: 1 names: [seal]nc是类别数单类写 1names顺序必须和 txt 里的class_id对应这里只有 0 号对应seal。3.2 训练命令与关键参数环境装好后一条命令启动yolo detect train \ dataseal_dataset/data.yaml \ modelyolov8n.pt \ epochs150 \ imgsz640 \ batch16 \ lr00.01 \ patience30 \ projectruns/seal \ nameexp1modelyolov8n.pt用官方预训练权重做迁移小数据集千万别从零训210 张图从零训基本学不出东西。epochs150配合patience3030 轮验证指标不涨就早停避免过拟合。imgsz640是 YOLOv8 的默认输入尺寸印章通常占图比例不小640 够用如果印章很小可以提到 960但显存和速度要重新权衡。batch16在 8G 显存上跑 640 尺寸比较稳显存不够就降到 8 并同步把lr0调小到 0.005 左右。3.3 小样本下的增强策略210 张图最容易过拟合YOLOv8 默认开了 mosaic、HSV 抖动、随机翻转。印章检测有个特殊性印章文字方向有语义上下翻转flipud会把文字倒过来反而制造噪声。建议关掉垂直翻转保留水平翻转和轻微旋转yolo detect train \ dataseal_dataset/data.yaml \ modelyolov8n.pt \ epochs150 imgsz640 batch16 \ fliplr0.5 flipud0.0 \ degrees10.0 translate0.1 scale0.3 \ mosaic1.0 close_mosaic20flipud0.0关掉垂直翻转degrees10.0允许小角度旋转模拟拍摄倾斜close_mosaic20表示最后 20 轮关闭 mosaic让模型在接近真实分布的图上收敛。这几个参数是小样本印章任务里我一般会调的默认值直接跑往往验证集 mAP 上不去。提示训练前先用yolo detect train ... epochs1跑一轮确认数据能正常加载、没有报「no labels found」比训到一半才发现路径错要省事得多。4. 训练结果解读与常见报错排查4.1 看哪些指标、怎么判断过拟合训练完在runs/seal/exp1/下有results.csv和一堆曲线图。重点看三个metrics/mAP50、metrics/mAP50-95、train/box_loss和val/box_loss的走势。单类印章任务mAP50 能到 0.9 以上算正常如果只有 0.6 左右先怀疑标注质量而不是模型。判断过拟合看两条 loss 曲线训练 loss 持续下降但验证 loss 在某个 epoch 后开始上升就是过拟合信号此时要么加增强要么减 epoch。# 快速看最后 10 轮的指标 tail -n 10 runs/seal/exp1/results.csv4.2 典型报错对照报错信息原因处理No labels foundtxt 路径或命名不匹配检查 labels 目录与 images 同名同层级class label 1 exceeds nc1txt 里出现 id 1单类只能是 0回去查转换脚本Image Not Founddata.yaml 的 path 写错用绝对路径或确认相对基准目录验证 mAP 为 0验证集 txt 为空或全越界重跑第 2 章的校验脚本4.3 推理验证训完拿几张没参与训练的图测一下确认框的位置和置信度yolo detect predict \ modelruns/seal/exp1/weights/best.pt \ sourcetest_imgs/ \ conf0.25 \ saveTrueconf0.25是置信度门限印章检测误检通常不多可以适当调高到 0.4 减少虚警如果漏检多就降到 0.15。saveTrue会把画框结果存到runs/detect/predict/肉眼过一遍比看数字直观。5. 从 210 张到可用模型扩样与置信度调优技巧210 张的天花板是明显的想再往上走有两条路。第一条是扩样把训练好的模型拿去推理未标注的印章图挑置信度高的预测框转成 YOLO txt 做伪标签人工复核后并入训练集这是小数据集半自动扩样的标准玩法。伪标签脚本核心就一句model.predict后把boxes.xywhn按行写文件但一定要人工过一遍印章检测里模型把红色圆形 logo 误判成印章的情况不少。第二条是置信度门限的精细调优。别用默认 0.25 一把梭而是画一条 PR 曲线找拐点from ultralytics import YOLO model YOLO(runs/seal/exp1/weights/best.pt) metrics model.val(dataseal_dataset/data.yaml, conf0.001, iou0.6) # metrics.box 里含 precision/recall 曲线数据导出后找 F1 最大点 p, r metrics.box.p, metrics.box.r f1 [2 * pi * ri / (pi ri 1e-9) for pi, ri in zip(p, r)] best_conf metrics.box.ap_class_index # 结合曲线数据定位 print(F1 峰值附近对应门限:, max(f1))conf0.001是为了让验证时输出足够多的点来画完整曲线实际部署再取 F1 峰值对应的门限。印章检测对漏检比对误检敏感的场景比如票据审核门限往低取保召回对误检敏感的场景比如自动盖章计数门限往高取保精度。这个取舍没有标准答案取决于你的下游业务怎么用这些框。本文还有配套的精品资源点击获取
