简介:面向医学影像AI与计算机视觉学习者,这份数据集基于X胸透光片构建,采用COCO格式对1765张原始图片完成标注,覆盖新冠肺炎、正常、肺炎三种类别,可直接用于目标检测、图像分类、语义分割等任务的模型训练与效果验证。压缩包共1770个文件,包括1765张jpg原图、3个json标注文件以及2个txt说明文件,整体约61.41MB,目录结构清晰,便于导入MMDetection、Detectron2等主流框架。目前已有906人在CSDN学习或下载,适合医疗影像诊断研究、高校深度学习课程设计以及算法对比实验等场景。借助完整的标注信息,使用者可快速完成数据划分、样本平衡与模型微调,免去人工标注的繁杂流程,把精力集中在检测网络结构设计、损失函数优化与性能评估上。
1. 新冠肺炎检测数据集:先看这份COCO标注的X光胸透能用在哪
影像AI项目里,最难的不是模型,而是标注干净的数据。这份名为“新冠肺炎检测数据集”的资源,包含1765张原始X光胸透图片,已经用COCO格式完成了目标检测标注,能区分新冠肺炎、正常、肺炎三种状态。对做医疗影像检测、想快速验证YOLO系列或MMDetection流程的开发者来说,它省掉了收集图片和数据标注两件最耗时的事。拿到手就能直接转格式、训练、出指标,适合正在做肺部X光检测实验、需要一份带bbox标注的医学图像数据集来跑通训练管线的从业者。
2. 数据集结构与COCO JSON:1765张图、三分类标签是这样组织的
数据集的可靠程度,决定了后面训练顺不顺利。拿到压缩包后,建议先别急着训练,把目录结构和标注文件拆开看一遍,摸清图片和标注的对应关系。这一章我把这份数据集的目录特征、COCO JSON的字段含义、类别分布逐一拆开讲清楚。
2.1 目录与文件命名:从文件名反推数据集来源
解压后你会发现图片文件名长这样:
COVID19-8-_jpg.rf.d1e42a6f39394fad7a82891eb20d0f82.jpg COVID19-260-_jpg.rf.f73c9b0edecb542528268295e0214e45.jpg COVID19-175-_jpg.rf.a23ef5f24b9e58a4fd42ac52775d81eb.jpg这里有个关键信息:文件名里的rf是Roboflow平台导出时留下的标记。常见做法是,Roboflow导出的数据集会自带train、valid、test三个子目录,COCO标注文件通常以_annotations.coco.json的形式存放在各目录下,例如train/_annotations.coco.json。这个数据集之所以叫“1765张原始图片”,是因为图片内容未做裁剪增强,保留了原始胸片的画面范围。
文件名中间段的-8-、-260-这类数字是图片在原始采集库里的编号,_jpg则是数据早期从其他格式转换时遗留的后缀,不必特别处理。实际使用时,我一般会写一段脚本统一重命名,避免后续处理时因为文件名里的特殊字符产生编码问题,尤其是Windows环境直接解压容易出现路径解析故障。
2.2 读懂COCO标注JSON:images、annotations、categories三个数组
COCO标注格式的核心是JSON文件,里面有三个主要数组。我拆过不少数据集,绝大多数问题都出在这个JSON的某些字段与图片实际内容不匹配。先看一个典型的COCO JSON骨架:
{ "images": [ { "id": 1, "file_name": "COVID19-8-_jpg.rf.d1e42a6f39394fad7a82891eb20d0f82.jpg", "width": 512, "height": 512 } ], "annotations": [ { "id": 1, "image_id": 1, "category_id": 1, "bbox": [120, 80, 240, 300], "area": 72000, "iscrowd": 0 } ], "categories": [ {"id": 1, "name": "covid"}, {"id": 2, "name": "normal"}, {"id": 3, "name": "pneumonia"} ] }images数组里存的是每张图片的基本信息:id是全局唯一编号,file_name对应实际图片文件名,width和height是图片原始尺寸。如果训练时发现目标框位置偏了,先查这个字段和真实图片尺寸是否一致。
annotations数组是核心,每条记录对应一个标注框。image_id关联到images里的某张图,category_id关联到categories里的某个类别,bbox是[左上角x, 左上角y, 框宽, 框高]四元组,area是框面积。iscrowd为0表示这是一个普通目标框。
categories数组定义了类别编号和名称的映射。不同数据集的编号起点不一样,有的从0开始,有的从1开始,训练前必须确认清楚。
我习惯用一段Python脚本快速体检这个JSON:
import json from collections import Counter with open('train/_annotations.coco.json', 'r', encoding='utf-8') as f: coco = json.load(f) print('图片数量:', len(coco['images'])) print('标注框数量:', len(coco['annotations'])) print('类别定义:', coco['categories']) # 统计每张图的标注框数,找出空标注图 img_id_to_anns = Counter(a['image_id'] for a in coco['annotations']) empty_imgs = [img['file_name'] for img in coco['images'] if img['id'] not in img_id_to_anns] print('无标注图片数量:', len(empty_imgs)) if empty_imgs: print('示例:', empty_imgs[:5]) # 统计各类别bbox数量 cat_counter = Counter(a['category_id'] for a in coco['annotations']) print('各类别标注数量:', dict(cat_counter))这段脚本做三件事:输出总图片数、总标注框数、类别定义;找出没有任何标注的图片;统计每个类别的框数量。如果无标注图片占比过高,训练时会频繁出现无目标报错,要考虑是否剔除或补充标注。
实际拆包时我遇到过一种情况:图片有1765张,但标注框只有1300多个,说明有一部分图是纯背景或标注遗漏。这类图对训练不全是坏事,可以当作负样本,但比例失衡会让模型偏向误报。一般我建议不超过总量的15%。
2.3 三分类的标注细节:胸部X光片的边界框特点
这是一个三分类目标检测数据集,类别为:新冠肺炎、正常、肺炎。肺部X光片的目标检测和自然场景检测不太一样,bbox标注通常是围绕肺部区域或病灶阴影画的,边界相对模糊。
从标注实践看,X光胸片里的病灶框有两种画法:一种是框住整个肺野,把左右肺都包进去;另一种是只框住可见的磨玻璃阴影或实变区域。这个数据集更接近后者,因为要区分新冠肺炎和普通肺炎,靠的是病灶形态和位置差异,框住全肺野会丢失空间判别信息。
框的尺寸分布也不均匀。新冠病灶可能是一个小斑片,也可能是一大片实变。如果你的训练代码里用了固定的anchor尺寸,小目标召回率会很低。COCO格式里area字段就是为这个准备的,你可以直接用它来分析目标尺度分布:
import numpy as np areas = [a['area'] for a in coco['annotations']] areas = np.array(areas) print(f'最小面积: {areas.min():.1f}, 最大面积: {areas.max():.1f}') print(f'面积中位数: {np.median(areas):.1f}') print(f'小于32x32的框占比: {(areas < 1024).mean() * 100:.2f}%')尺寸分布统计决定了你要不要调整模型的anchor策略。如果在YOLOv8里用默认anchor,模型会自己学习,但前提是你给的训练数据框尺寸分布不能过于极端。用这份数据训练之前,我建议先跑一遍这个统计,心里有个底。
3. 把COCO转成YOLO格式并训练检测模型:转换脚本与训练参数
COCO JSON不能直接喂给YOLO系列训练器,YOLO系列使用的是归一化后的txt格式标签,每行一组类别和坐标。这一章我把格式转换、数据划分、训练参数这三步完整串起来,给出可直接运行的脚本和YOLOv8训练命令。
3.1 转换前的准备:装好依赖并核对标注完整性
环境需要Python 3.8以上,安装PyTorch和ultralytics。建议创建一个干净的虚拟环境,避免依赖冲突。安装命令如下:
pip install ultralytics opencv-python pycocotoolsultralytics是YOLOv8的训练和推理库,pycocotools用来做COCO格式的数据评估,opencv-python负责图片读取和尺寸校验。装完后先用上一章体检脚本确认标注JSON完好,特别是所有标注框的坐标都在图片范围内,不越界。
这一步容易踩坑的地方在于:有些导出的COCO JSON里,images字段的宽高是缩略图尺寸,但磁盘上的原始图片分辨率更高,训练时会直接按实际图片尺寸读取,导致坐标比例完全错位。校验方法很简单,随机抽几张图,用OpenCV读取真实宽高,与JSON里记录的宽高比对。不一致的话,转换脚本里就要以实际图片尺寸为准。
3.2 COCO转YOLO格式:一张图对应一个txt标签文件
YOLO格式的标签规则是:每张图片的名字去掉后缀后对应一个同名txt文件,放在labels/xxx/目录下。txt文件每一行表示一个目标框:
<类别id> <x_center> <y_center> <归一化宽> <归一化高>归一化指的是除以图片实际宽高。转换脚本如下:
import json import os import cv2 from pathlib import Path def coco_to_yolo(coco_json_path, image_root, label_root): with open(coco_json_path, 'r', encoding='utf-8') as f: coco = json.load(f) # 建立image_id到图片信息的映射 img_info = {img['id']: img for img in coco['images']} # 建立category_id到YOLO类别索引的映射,注意类别id从0开始 # 这里假设类别id连续且从1开始 cat_id_to_yolo = {cat['id']: idx for idx, cat in enumerate(coco['categories'])} print('类别映射:', cat_id_to_yolo) # 按image_id将标注框分组 anns_by_img = {} for ann in coco['annotations']: img_id = ann['image_id'] anns_by_img.setdefault(img_id, []).append(ann) label_root = Path(label_root) label_root.mkdir(parents=True, exist_ok=True) converted_count = 0 for img_id, anns in anns_by_img.items(): img = img_info[img_id] img_path = Path(image_root) / img['file_name'] # 读取图片真实宽高,防止JSON里的尺寸与实际不一致 if img_path.exists(): height, width = cv2.imread(str(img_path)).shape[:2] else: width, height = img['width'], img['height'] # 写入对应的txt标签文件 txt_path = label_root / (Path(img['file_name']).stem + '.txt') lines = [] for ann in anns: x, y, w, h = ann['bbox'] # bbox坐标归一化,并转为中心点格式 x_center = (x + w / 2) / width y_center = (y + h / 2) / height w_norm = w / width h_norm = h / height # 越界保护:YOLO标签要求坐标在0~1之间,超出则截断 x_center = min(max(x_center, 0.0), 1.0) y_center = min(max(y_center, 0.0), 1.0) w_norm = min(max(w_norm, 0.0), 1.0) h_norm = min(max(h_norm, 0.0), 1.0) cat_idx = cat_id_to_yolo[ann['category_id']] lines.append(f'{cat_idx} {x_center:.6f} {y_center:.6f} {w_norm:.6f} {h_norm:.6f}') if lines: txt_path.write_text('\n'.join(lines), encoding='utf-8') converted_count += 1 else: print(f'警告: {img["file_name"]} 无标注,跳过') print(f'转换完成,共生成 {converted_count} 个标签文件')这段脚本的设计逻辑有四个关键点。第一,读取图片真实宽高再归一化,保证坐标比例正确;这叫“以实际为准”。第二,类别id做了重映射,兼容COCO类别id从1开始、YOLO要求从0开始的问题。第三,坐标做了越界截断,避免标注框边缘超出图片范围导致训练报错。第四,无标注图片不生成标签文件,保证数据干净。
执行时把对应路径替换成你解压后的实际路径即可:
python coco_to_yolo.py3.3 数据划分与YAML配置文件
转换完成后,把图片和标签按7:2:1比例划分成train、val、test三份。划分时最忌讳直接random.shuffle整个列表,这样容易出现同一张图的数据泄漏到多个集合里。用train_test_split按文件名去重划分即可:
import os import random import shutil from pathlib import Path random.seed(42) image_files = list(Path('images').glob('*.jpg')) random.shuffle(image_files) train_ratio, val_ratio, test_ratio = 0.7, 0.2, 0.1 n = len(image_files) train_files = image_files[:int(n * train_ratio)] val_files = image_files[int(n * train_ratio):int(n * (train_ratio + val_ratio))] test_files = image_files[int(n * (train_ratio + val_ratio)):] def copy_files(file_list, img_dst, label_dst, label_src): img_dst.mkdir(parents=True, exist_ok=True) label_dst.mkdir(parents=True, exist_ok=True) for img_path in file_list: shutil.copy(img_path, img_dst / img_path.name) label_path = label_src / (img_path.stem + '.txt') if label_path.exists(): shutil.copy(label_path, label_dst / label_path.name) copy_files(train_files, Path('dataset/train/images'), Path('dataset/train/labels'), Path('labels')) copy_files(val_files, Path('dataset/val/images'), Path('dataset/val/labels'), Path('labels')) copy_files(test_files, Path('dataset/test/images'), Path('dataset/test/labels'), Path('labels')) print(f'train: {len(train_files)}, val: {len(val_files)}, test: {len(test_files)}')然后写YOLOv8的数据配置文件。在covid.yaml里定义训练和验证路径,以及三个类别的名称,注意顺序要和转换脚本里的类别索引一致:
path: /absolute/path/to/dataset train: train/images val: val/images test: test/images names: 0: covid 1: normal 2: pneumonia这里如果类别顺序乱了,比如把normal放到了0号位,整个训练就白费了。我建议把转换脚本里打印出来的类别映射和这个yaml里的names逐行核对一次。
3.4 训练命令与参数选择
数据路径配好之后,直接执行YOLOv8训练。第一次训练建议用yolov8n这种小模型跑通全流程,确认没有报错后再换大模型调精度:
yolo detect train \ data=covid.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ workers=4 \ patience=20 \ save=True参数含义如下:epochs是训练轮数,胸片检测任务100轮基本够收敛;imgsz是输入分辨率,X光胸片纹理细节多,有条件可以用640,显存不够降到512;batch是批大小,16GB显存的显卡建议设16以内;patience是早停轮数,验证集mAP连续20轮不涨就提前停止,节省时间。model用yolov8n.pt会在ImageNet预训练权重基础上做迁移学习,收敛速度明显快于随机初始化。
训练日志里重点看两个指标:box_loss是否持续下降,val mAP50是否稳步上升。如果训练了几轮后box_loss不降反升,大概率是标签出错了,停下来检查数据。训练完成后权重保存在runs/detect/train/weights/best.pt,后面做评估和推理都用这个文件。
4. 训练与标注的常见问题排查:从数据翻车到模型欠拟合
这个数据集我实际跑下来踩了不少坑,这里整理出几条最常见的问题,每一条都按“现象→原因→解决”来讲,避免你在同样的地方浪费时间。
4.1 转换后标签文件为空,训练时提示无目标
现象:训练日志里出现大量类似WARNING: no labels found in ...的警告,某个类别的目标数始终为0。
原因:COCO JSON里的images字段宽度高度与磁盘上图片真实尺寸不一致。Roboflow导出时经常会对图片做resize,而原始COCO JSON里记录的还是旧尺寸。转换脚本用img['width']做归一化时,所有坐标比例都是错的,生成的txt里全是负数或大于1的值,被过滤后就成了空标签。
解决:转换脚本改成cv2.imread读取真实宽高。如果确实不想在转换时读图,至少随机抽10张图比对JSON尺寸和实际尺寸,误差超过2%就要换用真实尺寸。从那以后我每次写转换脚本都强制检查这一步。
4.2 训练时图片加载就报错,提示图片不存在
现象:FileNotFoundError: ... does not exist,或者提示某张图片路径下找不到文件。
原因:数据划分时没有做统一重命名,图片在train和val里复用,或者文件名里的特殊字符在不同操作系统中表现不同。这个数据集的原始文件名里带-和_,Windows解压后路径拼接时没问题,但如果在Linux或者容器内挂载Windows分区,路径分隔符和大小写敏感问题就会暴露。
解决:在划分数据之前,先把所有图片统一重命名为纯数字编号,例如0001.jpg、0002.jpg,同时修改JSON里对应的file_name字段。重命名脚本很简单,但能避免一大半路径相关的问题。
4.3 三种类别数量悬殊,模型偏向正常类
现象:训练结束后,normal类别的精确率很高,但covid类别的召回率只有零点几,约等于没检出。
原因:数据分布不均衡。一份典型的胸片数据集中,正常样本数量往往远大于新冠和肺炎样本。模型在训练时看到大量正常样本,决策边界倾向于“宁可漏掉少数类也不误报多数类”。
解决:三类手段可以叠加。第一,在YOLO的yaml里给少样本类别增加loss权重;第二,做一个简单的过采样,让每个epoch里新冠图片重复出现两次;第三,数据增强时对新冠类样本用更强的随机翻转和光照扰动。实测下来,loss权重和过采样组合效果最稳定。
4.4 灰度X光图训练欠拟合,纹理特征学不出来
现象:训练loss下降缓慢,验证集mAP勉强到0.5就卡住不动了。
原因:X光胸片本质是灰度图,而数据集导出时可能被存成了三通道的伪彩色图或单通道灰度图。YOLO系列默认输入是RGB三通道,如果喂进去的是单通道灰度图,imgsz=640下信息量少了一个数量级,特征提取器学不到足够的纹理细节。
解决:在训练前统一把图片转成RGB三通道,并做一次CLAHE自适应直方图均衡增强。这个增强对X光片特别有效,能让肺野和病灶的对比度拉开。从实践看,加了CLAHE之后,新冠病灶的小斑片区域检出率会有明显提升。
4.5 图像分辨率过大导致显存溢出
现象:CUDA out of memory,4090显卡也撑不住。
原因:胸片原始图分辨率通常在2000x2000以上,YOLO默认的imgsz=640会把大图缩到640x640,显存不够是因为batch设太大,或者模型本身选了yolov8x。
解决:把batch降到4或8,imgsz改到512。如果还要用大模型,就得换显存更大的卡。做实验先用yolov8n跑通,调参数时再逐步增大。
5. 评估与进阶用法:用mAP验证结果,再做数据增强与模型压缩
训练完成后怎么衡量这份数据集的检出效果,以及怎么把模型调得更好用,是这一章要解决的事。评估阶段我会跳开官方说法,用一个吃过亏的人的角度来讲。
5.1 在验证集上评估:mAP、召回率与混淆矩阵
用最优权重跑一遍验证集和测试集:
yolo detect val \ data=covid.yaml \ model=runs/detect/train/weights/best.pt \ conf=0.25 \ iou=0.5 \ save_json=Trueconf是置信度阈值,低于这个值的框会被过滤;iou是NMS的IoU阈值,用于去重。save_json=True会输出预测结果的COCO格式JSON,方便用pycocotools计算更详细的指标。验证完成后去runs/detect/val/目录看指标文件。
胸片检测重点关注mAP50和mAP50-95两个指标里那个小的。mAP50只要框大概对得上就算命中,而mAP50-95要求框非常精准,差一点就算错。医疗影像落地场景里,目标框位置稍微偏一点都可能影响后续诊断判断,所以两个指标都要看。如果mAP50不错但mAP50-95很低,说明模型的定位精度不行,可以考虑增加回归loss权重。
测试集上再看一眼confusion_matrix.png。正常情况下,normal列的对角线数值远高于非对角线。如果covid被大规模误分类到normal里,说明两类样本的特征空间重叠度高,需要补充更多新冠样本的特征。
5.2 进阶:数据增强与迁移学习配置
YOLO的增强参数可以在yaml里直接配置。针对X光胸片,我建议这样配:
augment: mosaic: 0.5 flipud: 0.5 fliplr: 0.0 hsv_h: 0.0 hsv_s: 0.0 hsv_v: 0.2mosaic把四张图拼成一张,能让模型在小目标上见多识广;flipud上下翻转对胸片特别有用,因为肺叶上下形态有差异但没有方向性;fliplr水平翻转对胸片要慎用,心脏位置会左右颠倒,有些医生标注时会区分左右肺叶。
hsv_h和hsv_s建议设为0,因为X光片是灰度影像,色调和饱和度扰动没有意义,还会干扰模型。hsv_v可以保留一个小值,模拟不同曝光条件下的亮度差异。
如果想进一步提升精度而训练时间有限,可以在best.pt基础上继续训练:
yolo detect train \ data=covid.yaml \ model=runs/detect/train/weights/best.pt \ epochs=50 \ lr0=0.0001这种做法是冻结特征提取器、只微调检测头,学习率调低一个数量级,避免破坏已经学到的特征。
5.3 模型压缩与部署思路
实验阶段的模型通常不是最终交付形态。胸片检测如果要做成服务端接口,我一般会把YOLOv8的权重导出成TensorRT engine,推理速度能提升一到两倍:
yolo export \ model=runs/detect/train/weights/best.pt \ format=engine \ imgsz=640 \ half=True或者先用model=prune做通道剪枝,把参数整体减到原模型的三分之一再量化。压缩后的模型精度会掉一点,但mAP50一般不会低于训练时的90%,对大部分场景是可接受的。
我从那以后每次拿到新数据集,都先写一个统计脚本看三件事:各类别的bbox数量、框尺寸分布、无标注比例。这三项数据能预判训练过程中八成以上的问题。希望这些拆解和分析能帮到你,让你少走一次弯路。
本文还有配套的精品资源,点击获取