
简介鼠类目标检测数据集整理587张左右jpg图片由labelImg工具标注完成同时输出VOC与YOLO两种格式的标注文件适用于需要训练检测模型、校验标注质量或研究不同标注格式差异的计算机视觉开发者和学生。压缩包内共1762个文件包含jpg原图、xml与txt标注文件三部分整体约149.16MB解压后图片与标注文件各按文件夹归类无需密码即可直接配合主流检测框架读取使用。标注过程强调边界框贴合目标、覆盖图中全部目标并做一致性校验因此标注质量相对可靠。类别统一为rat图片覆盖多种拍摄角度与场景可用于小目标检测实验、数据集格式转换练习及模型泛化能力评估。已有146人学习下载既帮助初学者快速理解VOC与YOLO标注数据的组织方式也为进阶用户提供现成的补充训练与验证数据。1. 鼠数据集 VOC 和 YOLO 格式目标标注587 张图拍的是一场入门复现很多人拿到这个“鼠数据集 VOC 和 yolo 格式目标标注 587 张左右”的压缩包第一反应是直接丢进训练脚本跑一遍。真正解压后却容易卡住三个文件夹里放着 jpg 图片、xml 标注和 txt 标注数量都是 587 左右看起来两份标注重复了到底该用哪一份其实这份数据集的定位是目标检测的标准弹药图片都是大鼠场景标注类别只有 rat 一类用 labelImg 拉矩形框完成VOC 和 YOLO 两种格式同时保留。它适合两类人——刚学 YOLO 系列检测、需要一套干净数据跑通全流程的入门者以及拿它做算法验证或基准测试的工程师。接下来我按“格式底层逻辑、数据体检、训练落地、避坑复查”的顺序拆开讲。2. 两种标注格式的底细XML 与 TXT 字段怎么逐项对齐VOC 和 YOLO 的标注不是“同一句话换成两种方言”而是坐标系和文件结构完全不同。VOC 把像素坐标写进 XML每个目标一个 object 块YOLO 用一行四个归一化数描述中心点、宽和高。理解这点后你就知道训练框架为什么只认 TXT也明白 labelImg 切换格式时到底在切什么。2.1 VOC 的 XML 在说什么filename、size 与 bndbox一份典型 XML 标注长这样从包里任选一张 rat_20220311_47.jpg 打开结构几乎一模一样annotation folderjpg/folder filenamerat_20220311_47.jpg/filename path/your/absolute/path/rat_20220311_47.jpg/path source databaseUnknown/database /source size width1920/width height1080/height depth3/depth /size object namerat/name difficult0/difficult bndbox xmin150/xmin ymin220/ymin xmax310/xmax ymax375/ymax /bndbox /object /annotation注意folder和path是 labelImg 写进去的现场路径机器 A 标注完放到机器 B这两个值往往对不上但这不影响训练。真正关键的是size和bndboxwidth 和 height 是图片原始分辨率bndbox 里 xmin、ymin、xmax、ymax 是像素坐标。一张图里如果有多只鼠就会出现多个object块训练时逐块解析就行。VOC 格式的通用性最好任何框架都能通过解析脚本消费这也是很多人保留 XML 当备份的原因。2.2 YOLO 的 TXT 为什么四列就够归一化坐标与 class_idTXT 的每行对应一个目标四个数字按顺序是class_id x_center y_center width height。class_id 从 0 开始这套数据只有 rat 一类所以所有行第一列都是 0。后面四列是归一化后的数值取值范围应在 0 到 1 之间。一个示例0 0.5012 0.6033 0.0880 0.1021 0 0.8127 0.4550 0.1224 0.1338第二行的 0.8127 代表目标中心点在图片宽度 81.27% 的位置0.4550 是高度方向 45.50% 处0.1224 是目标宽度占图片宽度的比例。这套坐标体系的核心好处是不管原图是 640 乘 480 还是 1920 乘 1080TXT 里都是 0 到 1 的小数训练时框架直接缩放处理不需要二次换算。VOC 转 YOLO 的标准公式也很简单x_center (xmin xmax) / 2.0 / width y_center (ymin ymax) / 2.0 / height box_width (xmax - xmin) / width box_height (ymax - ymin) / height其中 xmin、xmax、width 全部来自 XML 里的 bndbox 和 size。多数工具转格式时会保留 6 位小数精度足够。另外提醒一句这套数据是矩形框标注别拿去直接跑 yolo 实例分割分割需要的多边形点集不在这个包里。2.3 同图两种格式差在哪用脚本校验 XML 和 TXT 是否一致包里的 xml 和 txt 既然是同一批图片导出的训练前最好核一次免得其中一份缺行或错位。我一般用下面这个思路快速比对读取 XML 换算成归一化坐标再去读同名 TXT按浮点误差比对。import xml.etree.ElementTree as ET import glob import os xml_dir xml txt_dir txt def xml_to_yolo(xml_path): root ET.parse(xml_path).getroot() width int(root.find(./size/width).text) height int(root.find(./size/height).text) boxes [] for obj in root.findall(object): name obj.find(name).text bnd obj.find(bndbox) xmin float(bnd.find(xmin).text) ymin float(bnd.find(ymin).text) xmax float(bnd.find(xmax).text) ymax float(bnd.find(ymax).text) xc (xmin xmax) / 2.0 / width yc (ymin ymax) / 2.0 / height bw (xmax - xmin) / width bh (ymax - ymin) / height boxes.append((name, round(xc, 6), round(yc, 6), round(bw, 6), round(bh, 6))) return boxes for xml_path in glob.glob(os.path.join(xml_dir, *.xml)): base os.path.splitext(os.path.basename(xml_path))[0] txt_path os.path.join(txt_dir, base .txt) if not os.path.exists(txt_path): print(f缺少TXTs: {base}) continue xml_boxes xml_to_yolo(xml_path) with open(txt_path) as f: txt_lines [line.strip() for line in f if line.strip()] if len(xml_boxes) ! len(txt_lines): print(f目标数量不一致: {base}, xml{len(xml_boxes)}, txt{len(txt_lines)}) continue for box_xml, line in zip(xml_boxes, txt_lines): parts line.split() if parts[0] ! 0: print(f类别异常: {base}, 期望0, 实际{parts[0]})这段脚本不会修改任何文件只是把两份标注拉直了对齐。如果跑完没有任何输出说明这套数据里 XML 与 TXT 基本一致如果有“目标数量不一致”就以你准备用来训练的那份为准另一份只作参考。tolerance 我写在 round 到 6 位小数这个操作里labelImg 导出的两份文件一般不会有超过 1e-3 的差异超过这个量级就值得人工复查。2.4 文件对齐清点图片、xml、txt 三个目录谁缺了谁压缩包解压后常遇到的情况是图片 600 张、标注 587 份多出的几张是缩略图或误拷贝的图片。用集合差很直观from pathlib import Path jpg_dir Path(jpg) xml_dir Path(xml) txt_dir Path(txt) jpg_set {p.stem for p in jpg_dir.glob(*.jpg)} xml_set {p.stem for p in xml_dir.glob(*.xml)} txt_set {p.stem for p in txt_dir.glob(*.txt)} print(jpg数量:, len(jpg_set)) print(xml数量:, len(xml_set)) print(txt数量:, len(txt_set)) print(有图片没标注:, jpg_set - xml_set) print(有标注没图片:, xml_set - jpg_set) print(xml与txt不一致:, (xml_set - txt_set) | (txt_set - xml_set))这里的p.stem是去掉后缀后的文件名比如 rat_20220311_47。大小写问题要小心Windows 下复制文件偶尔会变成 .JPG脚本里建议统一比较 stem 的小写形式。真正训练前把这三组集合对齐能省掉后面排查数据加载报错的大把时间。3. 用 labelImg 复查 587 张标注目录结构、打开方式与体检脚本标注数据最大的风险不是“没标”而是“标了但有问题”。这份鼠数据集用的是 labelImg单个类别 rat看起来简单但恰恰因为只有一类批量检查反而容易漏掉坐标越界、空框这类毛病。3.1 为什么标注数据要“打开看一眼”质量决定模型上限模型训练的本质是从标注样本里学边界如果标注框比目标实际轮廓大 20%模型学出来的预测框也会跟着大 20%如果某张图里有只鼠漏标了模型会把那片区域当背景轻则漏检重则在附近误检。数据集的说明里特意写了三条标注规范框选尽可能贴合目标边界、尽量标注所有目标、标注完成后做一致性检查。这三条对应到实际训练效果分别是回归精度、召回率、以及多人标注时的框一致性。拿到数据后先花半小时抽样看 50 张图比训练完发现 mAP 上不去再回头查标注要高效得多。labelImg 之所以是首选是因为它支持 XML 和 TXT 两种模式直接切换打开一个目录就能连续翻图复查效率高。3.2 labelImg 打开方式与模式切换xml 和 txt 怎么对应同套图把压缩包解压后会得到三个文件夹分别是图片、xml 标注、txt 标注。用 labelImg 复查时我习惯直接打开图片文件夹然后手动切换保存格式先打开 labelImg菜单栏点“Open Dir”选中存放 jpg 的文件夹。左侧的按钮如果显示 PascalVOC点击后切换成 YOLO 模式。打开任意一张图片后按Ctrl R可以读取同名的 XML 标注按D翻下一张图。这里的关键对应关系是同一张 rat_20220311_47.jpg它的 xml 和 txt 文件名也必须是 rat_20220311_47.xml、rat_20220311_47.txt且放在各自目录下。labelImg 不会主动帮你把 XML 转换成 TXT切到 YOLO 模式后重新保存才会生成 txt。这个包里两份格式都有说明作者在两种模式下分别保存过解析时直接各取所需。3.3 批量体检脚本越界、空框、类别错一次扫完人工抽查看的是主观框质量脚本查的是客观硬伤。遍历所有 XML检查四个顶点坐标是否在图片尺寸范围内以及框是否被标成了反向框或零宽高。import glob import xml.etree.ElementTree as ET xml_list glob.glob(xml/*.xml) bad_cases [] total_box 0 class_count {} for xml_path in xml_list: root ET.parse(xml_path).getroot() width int(root.find(./size/width).text) height int(root.find(./size/height).text) for obj in root.findall(object): total_box 1 name obj.find(name).text class_count[name] class_count.get(name, 0) 1 bnd obj.find(bndbox) xmin int(float(bnd.find(xmin).text)) ymin int(float(bnd.find(ymin).text)) xmax int(float(bnd.find(xmax).text)) ymax int(float(bnd.find(ymax).text)) if xmin xmax or ymin ymax: bad_cases.append((xml_path, 空框或反向框)) if xmin 0 or ymin 0 or xmax width or ymax height: bad_cases.append((xml_path, f越界: {xmin},{ymin},{xmax},{ymax})) print(类别统计:, class_count) print(标注框总数:, total_box) print(异常数量:, len(bad_cases)) for case in bad_cases[:20]: print(case)这段脚本里int(float(...))是为了兼容 XML 里偶尔写出 150.0 这种浮点格式的坐标。类别统计应该只会输出 rat如果出现别的 class说明标注过程中有人改错了类别名。越界框对训练影响最大因为 YOLO 在加载标签时会按图片尺寸裁剪越界部分直接被截掉框中心点可能变成负坐标训练直接报错或静默跳样本。跑完脚本后我通常再补一个对 TXT 的巡检逐行确认四个值都在 0 到 1 之间且第三、四列不为 0。4. 避坑训练前最容易翻车的五个标注问题拿到这份鼠数据集以后真正编译和训练只有十分钟反而是标注文件的各种小毛病让人折腾一晚上。下面五条是我在类似数据集上反复遇到过的坑都按“现象、原因、解决”的顺序写清楚。4.1 图片名与 XML 名错位训练时一个目标都检不到现象是训练不报错loss 也往下降但验证集 precision 一直为 0模型输出的预测框全是空的。检查数据加载日志后发现框架按文件名去配对图片和标注时把 rat_20220311_47.jpg 配到了 rat_20220311_65.xml 上。原因多数是调整标注文件时手动重命名过图片或从压缩包解压过程中文件名编码出了问题。解决方法是强制做一次文件名集合对齐只保留图片名和标注名完全一致的文件把对不上的统一改成相同前缀。我处理这类数据集时第一步就跑第 2.4 节的集合差代码把不一致的文件先隔离到 tmp 目录再进入训练避免脏数据混进来。4.2 TXT 坐标大于 1框全部飞出图像边界现象是训练进程刚启动就提示标签格式错误或者训练后可视化验证集时图上出现一个巨大黑框占据整幅图。打开 TXT 一看某一行写的是0 856 402 92 121这明显是像素坐标直接塞进了 YOLO 标签文件里没有归一化。原因往往是用脚本转格式时漏除图片宽度和高度。解决方式是对全部 TXT 做范围扫描一旦发现第三或第四列大于 1.0就重新从 XML 换算覆盖生成 TXT而不是手工改一个值。这里要强调YOLO 的归一化公式里分母必须是原图宽高一旦原图是灰度图或通道数不是 3部分读取脚本还可能解析失败所以图片和标注都要确认来自同一份原始文件。4.3 class_id 从 1 开始单类数据集最常见的低级错误现象是训练时报index 1 is out of bounds for axis 0 with size 1或类似的类别越界错误。原因很直白这套数据只有 rat 一个类YOLO 的类别索引必须从 0 开始但有些资料会把类别序号从 1 编号于是 TXT 里第一列写成了 1而配置文件里names只有一行索引 1 自然越界。解决方法是确认全部 TXT 行首都是 0同时 data.yaml 里的names列表第一个元素就是 rat。我见过有人为了绕过这个报错在 names 里多加一行空字符串模型训练不报错了但 rat 的类别 mapping 全部错位推理结果全都要手动改标签得不偿失。4.4 缩略图和副本图片混进目录图片比标注多几十张现象是解压后统计 jpg 有 620 张xml 只有 587 个多出来的图片没有任何标注文件。原因多半是图片文件夹曾被同步工具生成过 Thumbs.db 或“xxx - 副本.jpg”也可能是别人选片时多拷了几张没有标注的图。解决方法是按第 2.4 节的集合差把无标注图片挑出来在训练配置里直接排除这些文件名不要继续放在同一个图片目录里。另外一个容易忽略的细节是图片文件夹里如果混入了 .png 或 .bmp而脚本只 glob*.jpg这些图会被静默跳过但 xml 文件可能还留着训练时解析不到对应图片同样会中断。4.5 图片分辨率跨度大小目标被吞得很厉害现象是模型训练完大尺寸的鼠检测得很好角落离得远、只占很小一块面积的鼠几乎全部漏检。原因是这套图片来源不统一有的图是 1920 乘 1080有的是 640 乘 480标注时框的像素面积差异可能有十倍以上。训练时 YOLO 默认把输入 resize 到 640 乘 640小目标会被极度压缩特征几乎消失。解决方法是先统计所有标注框的宽高分布如果小框占比明显训练参数里把 imgsz 提到 1280或者开启 mosaic 增强让模型在小目标上多学几个 epoch。这个坑不是标注错而是数据分布问题但它发生在训练前发现最划算等训练完再回头处理就要多花几个小时。5. 把数据送进 YOLO 训练目录编排、划分与 data.yaml 落地格式检查完接下来就是把三个文件夹整理成 YOLO 框架能直接消费的标准目录。这里我按最常见的做法走一遍图片和 txt 标注成对放进 train 和 val 两个子集xml 留在原目录当备份参考。5.1 先定划分策略train/val 随机打乱587 张左右的数据量不算大单类检测建议按 8:2 划分验证集留 100 多张足够看出模型能力。划分时一定要固定随机种子否则每次跑出来的训练集不一样前面模型的表现就没法对比了。我习惯用下面的脚本import random import shutil from pathlib import Path random.seed(42) jpg_dir Path(jpg) txt_dir Path(txt) train_img Path(dataset/images/train) val_img Path(dataset/images/val) train_lbl Path(dataset/labels/train) val_lbl Path(dataset/labels/val) for d in [train_img, val_img, train_lbl, val_lbl]: d.mkdir(parentsTrue, exist_okTrue) images list(jpg_dir.glob(*.jpg)) random.shuffle(images) val_count int(len(images) * 0.2) val_images images[:val_count] train_images images[val_count:] for split_imgs, split_img_dir, split_lbl_dir in [ (train_images, train_img, train_lbl), (val_images, val_img, val_lbl), ]: for img_path in split_imgs: label_path txt_dir / (img_path.stem .txt) if not label_path.exists(): print(跳过无标签图片:, img_path.name) continue shutil.copy(img_path, split_img_dir / img_path.name) shutil.copy(label_path, split_lbl_dir / label_path.name)这里的 random.seed(42) 是固定随机顺序42 只是习惯写的一个种子数换成其他数字也行关键是每次跑之前保持一致。val_count 取整后可能不是精确的 20%对 587 这个规模影响可以忽略。脚本用 copy 而不是 move是为了保留压缩包原目录结构万一后面想重新划分可以直接再跑一遍。5.2 编排目录结构并写 data.yaml让框架自动找到配对标签YOLO 系框架读取标注时有一套默认的配对逻辑图片在 images/train 下对应的 txt 必须在 labels/train 下文件名一致。整理后的目录结构如下dataset/ images/ train/ rat_20220311_47.jpg ... val/ rat_20220311_65.jpg ... labels/ train/ rat_20220311_47.txt ... val/ rat_20220311_65.txt ...对应的 data.yaml 写成这样path: ./dataset train: images/train val: images/val names: 0: ratpath是相对当前运行目录的数据集根路径也可以写成绝对路径但换机器后相对路径更省事。names必须和 TXT 里的第一列对应这里只有 0 对应 rat。有些朋友会额外加test:字段如果你的验证集已经承担测试职责不加也不影响。这个 yaml 文件要放在训练脚本能访问的位置我一般放在 dataset 同级目录下。5.3 冒烟测试用 20 张图跑 5 轮别一上来就全量训练第一次训练建议不要直接上全量 587 张跑几十轮先做冒烟测试确认数据链路通不通。我常用的命令是yolo detect train datadata.yaml modelyolov8n.pt epochs5 batch8 imgsz640model 选择 n 系列是为了图快反正只是验证数据加载。跑完看两个东西第一个是训练日志里有没有All 587 images loaded之类的提示确认图片数量不是 0第二个是第一个 epoch 结束后 loss 是否明显下降如果 loss 一直是 nan 或平稳不动大概率是标签里有非法值。冒烟测试通常几分钟就结束它解决的是一个很现实的问题不要等全量训练跑了一个小时才在排查数据读取。在资源充足、确定格式无误后再把 epochs 改回几十轮正式训练。这一步也顺带验证了 data.yaml 里路径书写正确常见翻车点是 path 多写了一层目录导致框架找不到任何图片。6. 训练后回查用预测框反过来挑标注漏检和错框训练流程跑完模型权重已经出来了但工作没有结束。我始终有一个习惯训练完必须拿验证集图片过一遍预测把预测框和原始标注叠在一起看这步花不了多少时间却能发现标注阶段埋下的雷。yolo detect predict modelruns/detect/train/weights/best.pt sourcedataset/images/val save_txtTrue save_confTruesave_txtTrue会生成每个验证图片对应的预测 txtsave_confTrue额外保存置信度。然后我拿预测 txt 和原标注 txt 做一次逐行比对按 IoU 粗略统计哪些框没匹配上。匹配不上的分两类一类是模型输出了多余框说明是误检另一类是原标注没有对应框但模型检出了清晰的大鼠这种基本就是标注漏标了。对漏标的图片回到 labelImg 里重新补一个框单独存成新版本数据集下次训练把它加进去。表格里我通常记录三个维度复查维度检查方法常见结论漏标预测框多出且置信度高原标注漏了目标边界不贴合预测框比原标注明显小一圈原标注框过大或过小重叠目标两只鼠贴在一起只标一个框需要拆成两个框处理边界不贴合时我一般不追求逐像素精确只要 IoU 在 0.85 左右就接受鼠标手标难免有上下几个像素的浮动。真正影响大的是漏标尤其这包数据里如果出现多只鼠叠着跑的情况标注时很容易只框了最显眼的一只训练出来的模型自然也会漏。最后一类重叠目标最麻烦因为训练脚本不会报错只有回看预测图时才能发现。从那以后我每次拿到新数据集都会强制走一遍“格式校验、批量体检、冒烟测试、预测回查”这条流程确认没有明显问题才敢往下游交付。希望帮到你。本文还有配套的精品资源点击获取