news 2026/10/1 17:23:29

4122张眼底图5类DR分级:VOC+YOLO双格式数据集实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
4122张眼底图5类DR分级:VOC+YOLO双格式数据集实战指南

简介:本资源为糖尿病肾病视网膜病变检测数据集,面向医学图像处理、眼底病变识别方向的算法工程师与深度学习研究者,可用于目标检测模型的训练、验证与竞赛实践。数据集采用Pascal VOC与YOLO双格式标注,包含4122张jpg图片,每张图片均配有对应的VOC格式xml文件与YOLO格式txt文件,标注类别共5类,分别为mild-DR、moderate-DR、normal、proliferation-DR、severe-DR,覆盖糖尿病视网膜病变的轻、中、重度及增殖期分级。压缩包为7z格式,内含2000个文件,以1999个xml标注文件和1个说明用txt为主,整体约44.31MB,目录结构清晰,便于直接接入主流检测框架进行训练与评估。目前已有139人学习下载,适合需要快速获取标注完备、类别均衡的眼底病变数据集的读者,可省去自行标注与格式转换的时间成本,专注于模型调优与实验对比。

1. 4122 张眼底图、5 类 DR 分级:这份 VOC+YOLO 双格式数据集到底能干什么

眼底糖网筛查这个方向,公开数据一直是个尴尬事。想跑检测模型,要么去啃几万张的竞赛集,标注格式还得自己转;要么找小样本凑合,类别又不够细。这份 4122 张的糖尿病肾病检测数据集,走的是另一条路——它把 5 个 DR 分级类别直接标好,同时给了 Pascal VOC 的 xml 和 YOLO 的 txt 两套标注,jpg 原图、xml、txt 三者数量严格对齐,都是 4122。类别名是mild-DR、moderate-DR、normal、proliferation-DR、severe-DR,从正常到增殖期一条线拉通。

它适合谁?一是想快速验证 YOLO 系列在自己环境里能不能跑通眼底检测的工程师,二是做医学图像课程设计、需要现成标注又不想从零标的学生,三是拿它当预训练或对比基线、再往私有数据上迁移的算法同学。不适合谁?指望它直接上临床的——4122 张的规模做研究够用,做产品远远不够。下面按「拿到手怎么用 → 参数怎么设 → 哪里会翻车」的顺序拆开讲。

2. 双格式标注怎么选:VOC 与 YOLO 的转换逻辑和目录结构

2.1 为什么同一批图要给两套标注

Pascal VOC 和 YOLO 的差别,本质是坐标表达方式不同。VOC 的 xml 存的是绝对像素坐标xmin/ymin/xmax/ymax,还带filename、size、object等一堆元信息;YOLO 的 txt 存的是归一化后的class_id cx cy w h,每行一个目标,没有图片尺寸信息,全靠训练时读原图反推。

给两套格式的好处很直接:VOC 适合做数据审查、可视化、转 COCO 或别的框架;YOLO 格式可以直接喂给 ultralytics 系的训练脚本,省掉转换步骤。但要注意,这份数据集不包含分割路径的 txt 文件,也就是说它只做检测框,不做实例分割。如果你的任务是分割,这份数据得自己补 mask,别指望现成的。

常见做法是:先用 VOC 的 xml 做一轮标注质量抽查,确认框没跑偏、类别没错,再转成 YOLO 格式训练。反过来也行,但 xml 里信息更全,审查阶段更顺手。

2.2 目录结构长什么样

解压后典型结构是这样(文件名以实际为准,这里按常见组织方式示意):

firc_shenbing_dataset/ ├── 使用前必读.txt ├── JPEGImages/ # 4122 张 jpg 原图 │ ├── firc_shenbing_1063.jpg │ └── ... ├── Annotations/ # 4122 个 VOC xml │ ├── firc_shenbing_1063.xml │ └── ... └── labels/ # 4122 个 YOLO txt ├── firc_shenbing_1063.txt └── ...

使用前必读.txt别跳过,里面通常写了类别顺序、命名规则、有没有空标注图。类别 id 的映射关系必须和 txt 里的数字对上,否则训练出来全是错类。这份数据的 5 类,按摘要给的顺序,常见映射是:

class_id类别名含义
0mild-DR轻度糖网
1moderate-DR中度糖网
2normal正常
3proliferation-DR增殖期糖网
4severe-DR重度糖网

提示:这个映射不是官方强制,有的整理者会按字母序或别的顺序排。动手前一定打开一个 txt 和一个 xml 对照,确认 id 和类别名的对应关系,别照搬上面的表。

2.3 用脚本核对三份文件是否对齐

拿到数据集第一件事不是训练,是核对。jpg、xml、txt 三者数量都是 4122,但数量相等不代表文件名一一对应。跑一段脚本查一遍:

import os img_dir = "firc_shenbing_dataset/JPEGImages" xml_dir = "firc_shenbing_dataset/Annotations" txt_dir = "firc_shenbing_dataset/labels" def stems(d, ext): return {os.path.splitext(f)[0] for f in os.listdir(d) if f.endswith(ext)} imgs = stems(img_dir, ".jpg") xmls = stems(xml_dir, ".xml") txts = stems(txt_dir, ".txt") print("图片数:", len(imgs)) print("xml 数:", len(xmls)) print("txt 数:", len(txts)) print("图片有但 xml 缺:", imgs - xmls) print("xml 有但图片缺:", xmls - imgs) print("图片有但 txt 缺:", imgs - txts)

逻辑说明:用文件名主干(去掉扩展名)做集合运算,能直接暴露「有图没标注」或「有标注没图」的情况。参数上,img_dir、xml_dir、txt_dir按你解压后的真实路径改。如果三个差集都为空,说明对齐没问题,可以进下一步;如果有差集,先手动处理掉再训练,否则 YOLO 训练时会因为找不到对应 label 直接报错或静默跳过。

3. 从零跑通 YOLO 训练:数据配置、类别文件和启动命令

3.1 生成 train/val 划分和 data.yaml

YOLO 训练不认 VOC 那套目录,需要按images/labels分 train、val 两套,再写一个data.yaml指向它们。先划分:

import os, random, shutil src_img = "firc_shenbing_dataset/JPEGImages" src_lbl = "firc_shenbing_dataset/labels" dst = "yolo_dataset" random.seed(42) names = [os.path.splitext(f)[0] for f in os.listdir(src_img) if f.endswith(".jpg")] random.shuffle(names) split = int(len(names) * 0.8) train_names, val_names = names[:split], names[split:] for subset, subset_names in [("train", train_names), ("val", val_names)]: os.makedirs(f"{dst}/images/{subset}", exist_ok=True) os.makedirs(f"{dst}/labels/{subset}", exist_ok=True) for n in subset_names: shutil.copy(f"{src_img}/{n}.jpg", f"{dst}/images/{subset}/{n}.jpg") shutil.copy(f"{src_lbl}/{n}.txt", f"{dst}/labels/{subset}/{n}.txt") print("train:", len(train_names), "val:", len(val_names))

逻辑说明:random.seed(42)固定随机种子,保证每次划分一致,方便复现。8:2 是常见比例,4122 张里约 3297 张训练、825 张验证。参数上,如果类别分布很不均(比如normal特别多、proliferation-DR特别少),建议改成按类别分层抽样,别纯随机,否则验证集里某些类可能一张都没有。

接着写data.yaml:

path: ./yolo_dataset train: images/train val: images/val names: 0: mild-DR 1: moderate-DR 2: normal 3: proliferation-DR 4: severe-DR

path是数据集根目录,train/val是相对路径。names的 id 必须和 txt 里的 class_id 严格一致,这是最容易翻车的地方——id 错一位,模型学出来的全是错的。

3.2 启动训练与关键参数

用 ultralytics 的 YOLOv8 起训,一条命令:

yolo detect train \ data=data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ lr0=0.01 \ patience=20 \ project=runs/dr \ name=baseline

参数逐个说:model=yolov8n.pt是最小的 nano 版,先用它验证流程通不通,别一上来就上 x 版,显存和时间都吃不消。imgsz=640是默认输入尺寸,眼底图如果分辨率很高,缩到 640 会丢小病灶细节,可以试 1024,但显存翻倍。batch=16按显存调,8G 显存跑 640 一般能到 16,跑 1024 就得降到 4 或 8。lr0=0.01是初始学习率,小数据集上偏大容易震荡,可以降到 0.001。patience=20是早停,20 轮没提升就停,省时间。

训练起来后盯两个东西:一是runs/dr/baseline/results.csv里的mAP50和mAP50-95,二是混淆矩阵。医学数据类别不平衡时,normal的 AP 往往虚高,真正要看的是proliferation-DR和severe-DR这两类的召回,它们样本少、最容易漏检。

3.3 验证与推理

训练完在验证集上跑一遍:

yolo detect val \ model=runs/dr/baseline/weights/best.pt \ data=data.yaml \ imgsz=640

单张图推理看效果:

yolo detect predict \ model=runs/dr/baseline/weights/best.pt \ source=firc_shenbing_dataset/JPEGImages/firc_shenbing_1063.jpg \ conf=0.25 \ save=True

conf=0.25是置信度阈值,医学场景宁可多报也别漏报的话,可以降到 0.1,代价是误检变多。这一步主要是肉眼确认框的位置和类别对不对,别只看数字指标。

4. 避坑与排查:这份数据集上最容易翻车的五件事

4.1 类别 id 对不上,训练全错

现象:训练 loss 正常下降,但验证时混淆矩阵里类别全乱,normal被预测成mild-DR。 原因:data.yaml里的names顺序和 txt 文件里的 class_id 不一致。有的整理者按字母序排,有的按严重程度排,摘要给的顺序不一定等于文件里的顺序。 解决:打开任意一个 txt,看第一列数字,再打开对应 xml 看object/name,两者对照确定映射。确认后写死到data.yaml,别凭记忆。

4.2 空标注图导致训练报错

现象:训练启动后报IndexError或某张图labels为空。 原因:4122 张里可能有极少数图没有目标框,对应的 txt 是空文件。YOLO 对空 label 的处理在不同版本里不一致,有的直接跳过,有的报错。 解决:先扫一遍空 txt:

import os empty = [f for f in os.listdir("firc_shenbing_dataset/labels") if os.path.getsize(os.path.join("firc_shenbing_dataset/labels", f)) == 0] print("空标注文件:", empty)

如果数量少,直接从训练集里剔除;如果数量多,说明这批图本身可能有问题,得回头查标注流程。

4.3 图片尺寸和坐标越界

现象:可视化时框跑到图外面,或者训练时警告coordinates out of range。 原因:VOC 转 YOLO 时归一化算错,或者原图被裁剪过但 xml 没更新。归一化公式是cx = (xmin+xmax)/2/width,w = (xmax-xmin)/width,分母用错就会越界。 解决:写个校验脚本,遍历所有 txt,检查每行后四个数是否都在 0~1 之间:

import os bad = [] for f in os.listdir("firc_shenbing_dataset/labels"): with open(f"firc_shenbing_dataset/labels/{f}") as fp: for i, line in enumerate(fp): parts = line.strip().split() if len(parts) != 5: bad.append((f, i, "字段数不对")); continue vals = list(map(float, parts[1:])) if any(v < 0 or v > 1 for v in vals): bad.append((f, i, "坐标越界")) print("异常行数:", len(bad))

4.4 类别不平衡导致小类漏检

现象:proliferation-DR和severe-DR的召回率极低,模型几乎全预测成normal和mild-DR。 原因:5 个类别在 4122 张里分布不均,重度样本天然少,标准交叉熵会让模型偏向多数类。 解决:训练时加类别权重,或用copy_paste、mosaic这类增强多造小类样本。ultralytics 里可以在data.yaml同级配cls权重,也可以换 focal loss。更直接的办法是过采样小类图片,但注意别过拟合。

4.5 验证集泄漏进训练集

现象:验证指标高得离谱,mAP50 到 0.95 以上,但换一批新图就崩。 原因:同一患者的左右眼、或同一张图的增强版本同时出现在 train 和 val 里,造成信息泄漏。 解决:划分时按患者 id 或图片前缀分组,别按单张随机分。这份数据文件名是firc_shenbing_xxxx这种编号,如果编号连续代表同一来源,就得按编号段划分,而不是逐张 shuffle。

5. 进阶:把 VOC 直接转 COCO、做分层抽样和指标复核

5.1 VOC 转 COCO 备用

有些框架(比如 mmdetection)吃 COCO 格式,转一份备用不亏:

import os, json import xml.etree.ElementTree as ET xml_dir = "firc_shenbing_dataset/Annotations" classes = ["mild-DR", "moderate-DR", "normal", "proliferation-DR", "severe-DR"] coco = {"images": [], "annotations": [], "categories": []} for i, c in enumerate(classes): coco["categories"].append({"id": i, "name": c}) ann_id = 1 for idx, f in enumerate(os.listdir(xml_dir)): tree = ET.parse(os.path.join(xml_dir, f)) root = tree.getroot() w = int(root.find("size/width").text) h = int(root.find("size/height").text) img_id = idx + 1 coco["images"].append({"id": img_id, "file_name": root.find("filename").text, "width": w, "height": h}) for obj in root.findall("object"): name = obj.find("name").text cls_id = classes.index(name) b = obj.find("bndbox") xmin = float(b.find("xmin").text); ymin = float(b.find("ymin").text) xmax = float(b.find("xmax").text); ymax = float(b.find("ymax").text) coco["annotations"].append({ "id": ann_id, "image_id": img_id, "category_id": cls_id, "bbox": [xmin, ymin, xmax - xmin, ymax - ymin], "area": (xmax - xmin) * (ymax - ymin), "iscrowd": 0}) ann_id += 1 json.dump(coco, open("coco_annotations.json", "w"))

逻辑说明:COCO 的 bbox 是[x, y, w, h],和 VOC 的[xmin, ymin, xmax, ymax]差一个减法,别写错。area用于评估时区分大小目标。参数上,classes顺序必须和 YOLO 的names一致,否则两套格式对不上。

5.2 分层抽样划分

前面提过纯随机划分在类别不平衡时会翻车,这里给个分层版本:

import os, random from collections import defaultdict lbl_dir = "firc_shenbing_dataset/labels" random.seed(42) # 按主类别分组 groups = defaultdict(list) for f in os.listdir(lbl_dir): stem = os.path.splitext(f)[0] with open(os.path.join(lbl_dir, f)) as fp: lines = fp.readlines() if not lines: continue main_cls = lines[0].split()[0] groups[main_cls].append(stem) train, val = [], [] for cls, items in groups.items(): random.shuffle(items) cut = int(len(items) * 0.8) train += items[:cut] val += items[cut:] print("train:", len(train), "val:", len(val))

逻辑说明:按每张图的主类别(第一个框的类别)分组,再在组内 8:2 划分,保证每个类在验证集里都有代表。参数上,如果一张图有多个类别,取第一个框只是近似,更严谨的做法是按类别出现次数加权,但工程上够用了。

5.3 指标复核:别只看 mAP

训练完拿到best.pt,除了 mAP,至少再看三样:每类的 precision/recall、混淆矩阵、以及按类别分组的 PR 曲线。医学检测里,severe-DR和proliferation-DR的召回比整体 mAP 重要得多,因为漏掉一个重度患者,代价远大于多报一个。我一般会在验证脚本里单独把这两类的 recall 打出来,低于 0.7 就回去查数据分布和增强策略,而不是急着调模型结构。

从那以后我每次拿到新数据集,都强制先跑一遍文件对齐校验和类别 id 对照,再动训练脚本。这份 4122 张、5 类的 VOC+YOLO 双格式数据,流程跑通不难,难的是别在 id 映射和划分泄漏上栽跟头。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 17:23:15

MediaPipe + KNN 健身动作计数:从骨骼提取到状态机实战

简介&#xff1a;这是一套基于MediaPipe与KNN分类算法的健身动作计数Python项目源码&#xff0c;面向具备一定Python基础、希望快速实现引体向上、深蹲、俯卧撑自动计数的开发者与健身应用爱好者。其核心思路是先提取人体关键点并归一化编码&#xff0c;再用k-NN完成姿态分类&a…

作者头像 李华
网站建设 2026/10/1 17:23:10

基于PINN的微分方程求解:PyTorch实现与避坑指南

简介&#xff1a;基于PINN的微分方程求解Python代码包&#xff0c;面向科研人员、工程师和拥有一定Python基础的学习者&#xff0c;系统展示物理信息神经网络求解常微分方程与偏微分问题的完整流程。内容覆盖常微分方程组、扩散方程、泊松方程、拉普拉斯方程、洛伦兹系统以及欧…

作者头像 李华
网站建设 2026/10/1 17:22:29

CNN图像分类实战:四大经典网络结构与训练部署全流程

简介&#xff1a;面向毕业设计及深度学习初学者&#xff0c;这份基于Python卷积神经网络CNN的图像分类系统源码&#xff0c;覆盖LeNet-5、AlexNet、GoogLeNet、ResNet等经典网络结构&#xff0c;并配有可直接使用的数据集与预训练模型。压缩包共25个文件&#xff0c;以13个py源…

作者头像 李华
网站建设 2026/10/1 17:22:19

用同态加密在密文上跑CNN:Python实现加密图像分类

简介&#xff1a;面向需要完成毕业设计、课程设计或工程实训的高校学生&#xff0c;以及希望探索隐私保护与深度学习结合方向的开发者。系统基于Python卷积神经网络&#xff0c;解决加密云端图像的隐私保护分类需求&#xff0c;尤其适合安全与AI交叉领域的初期项目。压缩包共20…

作者头像 李华
网站建设 2026/10/1 17:21:57

YOLO瓷砖裂缝检测数据集:从划分、标签到可视化验证全流程

简介&#xff1a;面向目标检测入门与工业质检场景的YOLO格式瓷砖缺陷数据集&#xff0c;包含约1700张标注图片&#xff0c;共裂缝、正常两个类别&#xff0c;适合用于瓷砖表面裂缝识别模型的训练与验证。图片与标签文本分别保存在不同目录&#xff0c;标注由LabelImg完成&#…

作者头像 李华
网站建设 2026/10/1 17:21:41

开源EMBO:基于STM32的示波器

很多人刚听见「STM32示波器」&#xff0c;第一反应就是板子焊个TFT屏&#xff0c;波形直接在设备端画完。EMBO根本不走这条路。 采样全在STM32片内做完&#xff0c;触发面板、波形渲染、FFT运算全扔给电脑处理。一块两三块钱的Blue Pill最小系统板&#xff0c;刷上对应固件插US…

作者头像 李华