同一批胸片的两个版本 — VinBigData 原始 DICOM 版

18,000 例 PA 胸片 / PNG 竞赛版与 DICOM 原始版 / 两院 17 名放射科医师

来源 https://physionet.org/content/vindr-cxr/1.0.0/发布时间: 2026-10-27最后更新: 2026-10-27 阅读 19
同一批胸片的两个版本 — VinBigData 原始 DICOM 版

信息速览

数据集名称同一批胸片的两个版本 — VinBigData 原始 DICOM 版
数据类型原始影像,人工标注,元数据
规模约 18,000 例检查(训练 15,000 + 测试 3,000),非患者数
接入方式https://physionet.org/content/vindr-cxr/1.0.0/
AI 就绪度

越南 VinBigData(Vingroup 大数据研究院)与 108 军医院、河内医科大学医院合作构建的胸部 X 线数据集,是最早一批把「放射科医师逐例画框」做成公开基准的胸片资源。它从十万余例回顾性影像中筛出约 1.8 万例后前位(PA)检查,由 17 名放射科医师标注 22 类局部发现与 6 类诊断,训练集每例 3 名医师独立标注、测试集 5 名医师共识裁定。这些数字在官方页面、论文摘要与竞赛页之间基本一致,差别只在措辞。

真正让这个数据集变得需要专门讨论的,不是它有多少张片子,而是它有两条并行的分发渠道。一条是 2020 年底 Kaggle 竞赛用的预处理版,开箱即用但要遵守竞赛规则;另一条是 2021 年 6 月在 PhysioNet 上线的原始 DICOM 版,需要凭证化账号与签名数据使用协议才能下载。官方在论文里把前者称为「稍微修改过的版本」,这句话很短,但它决定了两个版本的影像在像素层面并不相同。

本文不重复介绍这个数据集「是什么」,那是库内已有条目的工作。本文只回答一个问题:当你需要回到原始 DICOM 时,你要付出什么,以及你换回来的是什么。一屏速览见下表。

维度 内容
数据集全名 VinDr-CXR: An open dataset of chest X-rays with radiologist’s annotations
常见别称 VinBigData 胸片数据集、VinBigData Chest X-ray Abnormalities Detection
构建机构 Vingroup Big Data Institute(VinBigData),河内
合作医院 108 军医院(Hospital 108)、河内医科大学医院(HMUH)
规模 约 1.8 万例 PA 位检查,训练 1.5 万 + 测试 3 千
来源池 十万余例回顾性采集影像中筛选
标注体系 22 类局部标签(带框)+ 6 类全局标签
标注者 17 名放射科医师,均具 8 年以上经验
标注密度 训练集每例 3 人独立,测试集 5 人共识
标注平台 VinDr Lab(自建,基于 PACS 的网页端)
原始格式 DICOM(单帧 PA 位,每例一个文件)
分发渠道 A Kaggle 竞赛版,预处理后影像,竞赛规则约束
分发渠道 B PhysioNet 原始 DICOM 版,凭证化访问
许可 PhysioNet Credentialed Health Data License 1.5.0
首次公开 2020-12-30 论文预印;2021-06-22 PhysioNet v1.0.0
核心差异点 原始位深、原始矩阵尺寸、DICOM 头均只在渠道 B 保留

§1 它要解决的具体麻烦

1.1 你要的不是「一个胸片数据集」,而是「一份没被改过的影像」

多数人搜到这个数据集时,脑子里想的是「找一份带病灶框的胸片数据训检测器」。这个需求 Kaggle 竞赛版可以完全满足,甚至更好满足,因为它已经把预处理做完了。真正会卡住的,是另一类需求:你已经在竞赛版上训出了一个模型,评审问「你们的位深处理是怎么做的」「边缘裁掉了多少」「不同厂商设备的影响有没有考虑」——这时候你翻遍竞赛版的文件夹,找不到任何可以回答的材料。

这就是两个版本存在的意义。同一批影像,在竞赛版里回答的是「能不能更快训出高分模型」,在 DICOM 版里回答的是「这批影像原本长什么样」。前一个问题有排行榜作为标准答案,后一个问题没有标准答案,只能回到原始文件。

本文的立场很明确:默认推荐竞赛版,只在四种情况下才建议走 DICOM 版。这四种情况会在 §1.4 的处境表里逐条列出。

1.2 三条常见的错误期待

第一条错误期待是「两个版本只是格式不同」。如果只是 DICOM 换成 PNG,文件大小应该差不多,但实际差了一个数量级。竞赛版在 Kaggle 上显示的总量是两百多 GB(以官方页面为准),而 DICOM 版单张影像就要十几 MB。这个差额不是格式开销造成的,是预处理过程中信息被丢弃造成的——位深降低、矩阵重采样、边缘裁切,每一道都会让文件变小。

第二条错误期待是「DICOM 版的标签更全」。不是。官方在竞赛页明确写了,测试集的标签不公开。DICOM 版提供的是与竞赛版同源的标注 CSV,测试集部分照样只有共识结果、没有逐医师记录。回到原始影像,换回来的是像素,不是标签。

第三条错误期待是「申请一次就一劳永逸」。PhysioNet 的凭证化访问绑定到具体项目与具体数据使用协议,换机构、换合作方、换研究目的都可能需要重新走流程。而且数据使用协议本身对再分发有约束,你不能把拿到的 DICOM 转给没有签协议的合作者。

这三条期待有一个共同的根源:把「公开」理解成「无门槛」。这里的公开指研究用途上可得,不是下载时自由。先想清楚这一点,能省掉很多中途卡壳。

1.3 它能给到的与给不到的

你的需求 它给的 够不够
训一个胸片病灶检测器 1.5 万张已标注训练影像,22 类框 够,竞赛版即可
在公开测试集上报指标 3 千张测试影像,标签不公开 够做评测,不够做监督训练
复现论文里的预处理流程 官方论文描述方法,未提供完整脚本 部分够,细节需自行补
拿到原始位深做量化研究 DICOM 版保留 BitsStored 等字段 只有渠道 B 够
研究不同厂商设备的成像差异 DICOM 头含设备与采集参数 只有渠道 B 够
拿到每张图的完整放射报告文本 只提供结构化标签,无报告正文 不够,需另找配对报告数据集
做多中心外部验证 只有越南两家医院 不够,需另找第三家
拿测试集标签做半监督 官方明确不公开 不够,任何获取渠道都违规

这张表里最容易被忽略的是第三行。论文把标注流程、去标识化流程写得很细,例如匿名 ID 由 SOP Instance UID 经 hashlib 派生、像素文字经自动识别后再人工复核,但没有发布可直接运行的预处理脚本。你要复现「竞赛版是怎么从 DICOM 变成现在这样」,只能靠论文文字反推。

1.4 你的处境与它能帮到的事

你的处境 它能帮你什么 下一步该做什么
刚立项,要在一个月内跑出第一个 baseline 竞赛版可直接下载,数千个公开 Notebook 可参考 直接用竞赛版,不要碰 DICOM
论文被审稿人质疑预处理不透明 DICOM 版可让你自证每一步变换 申请 PhysioNet 凭证,重做管线并写清参数
要做跨数据集泛化研究,需要统一预处理 DICOM 版可让所有数据集走同一条 DICOM 到张量管线 申请 DICOM 版,把本数据集当成其中一个源
研究低剂量或设备差异对模型的影响 DICOM 头保留采集参数 申请 DICOM 版,同时准备好设备参数缺失的兜底策略
只有 Kaggle 账号,没有机构邮箱 竞赛版无需资质审核 留在竞赛版,把「未取原始版」写进局限性
要把它和院内数据混合训练 需要统一的窗宽窗位与位深口径 优先申请 DICOM 版,院内数据同样是 DICOM

第四行是最典型的「必须回原始版」的处境。竞赛版做过一次面向检测任务的改编,这个改编对研究检测性能是好事,对研究成像参数是灾难——因为改编本身会抹掉你要研究的那个变量。

1.5 一句话判断

如果你的结论里出现「原始位深」「原始像素尺寸」「DICOM 头」这三个词中的任何一个,就必须走 PhysioNet 渠道;否则竞赛版是更省事的选择。

1.6 不适合谁:四条边界

第一条边界:需要配对报告文本的研究。 这个数据集只给结构化标签,局部标签对应报告里的 Findings 段,全局标签对应 Impressions 段,但原始报告文本不随数据集发布。要做影像到报告的生成或检索,得另找配对数据源,把这个数据集当纯标注集用。

第二条边界:需要多中心或多国人群的研究。 全部影像来自河内两家医院,设备与人群都集中在越南。要在东南亚以外的人群上做外部验证,这个数据集只能当训练源,不能当验证源。

第三条边界:需要儿童或特殊体位的任务。 官方说明是成人 PA 位。儿科有同机构的 VinDr-PCXR(库内另一条目),侧位、床旁位、便携位都不在这个数据集里。

第四条边界:算力或存储非常紧张的团队。 DICOM 版单张十几 MB 量级,全量落地远超普通工作站。如果只是做方法验证,没必要为了「原始性」把自己拖进存储工程。

1.7 一个判断顺序:先问三个问题

遇到「我该不该用这个数据集」这个问题时,很多人直接跳到「它有多少张」。更有效的顺序是先问三件事。

顺序 问题 答「是」时 答「否」时
第一问 我的任务需要病灶位置吗 继续看标注粒度 找图像级标签集即可
第二问 我的结论依赖于原始影像特征吗 走 DICOM 版 走竞赛版
第三问 我需要跨机构泛化吗 把它当训练源,另找验证源 单集实验即可

三问的顺序不能颠倒,因为后一问的答案会被前一问限定。如果第一问答「否」,说明你根本不需要框,那这个数据集的稀缺优势就用不上,继续讨论渠道差异也没有意义。

这个顺序也解释了为什么本文把渠道问题放在这么前面:它只在「第一问答是」的情况下才有意义。对图像级分类任务,两版差别只剩归一化口径,选哪个都能跑。

§2 数据构成与规格

2.1 一张表看清两条渠道

这个数据集最容易出错的地方,是把「同一个数据集」当成「同一份文件」。下表把两条渠道的关键差别列在一起,后面各节都围绕这张表展开。

项目 Kaggle 竞赛版 PhysioNet 原始 DICOM 版
官方定位 竞赛用,稍作修改 完整版,论文所述的正式发布
影像格式 竞赛页标注为 DICOM DICOM,每例一个文件
影像是否被改编 是,官方明确说「slightly modified」 否,仅做去标识化
标签覆盖 训练集全部;测试集不公开 训练集 + 测试集共识
逐医师记录 训练集有 rad_id 训练集有 rad_id,测试集无
访问门槛 Kaggle 账号 + 接受竞赛规则 凭证化账号 + CITI 培训 + 签名 DUA
是否可商用 受竞赛规则约束 受数据使用协议约束
是否需要伦理审批 由竞赛规则覆盖 使用者所在机构自行判断
目录结构 影像目录 + 若干 CSV Train / Test / Annotations 三目录
适合的用途 训练、基准、复现排行榜 预处理研究、成像参数研究、院内融合

2.2 字段字典

标注数据集中在一组 CSV 里。理解这几个字段,就能理解整个数据集的形状:一行不是一张图,而是「某位医师在某张图上画的一个框」。

字段 所在文件 含义 取值形态 缺失情况
image_id 全部 CSV 影像的唯一标识,与文件名对应 32 位十六进制串 无
rad_id annotations_train、image_labels_train 画框的放射科医师编号 形如 R1 到 R17 测试集文件无此列
class_name annotations_* 该框对应的局部发现名称 22 类之一,另有 No finding 无
class_id 竞赛版 test.csv 类文件 类别数字编号 0 到 14 竞赛版专属
x_min annotations_* 框左上角横坐标 整数,像素单位 无
y_min annotations_* 框左上角纵坐标 整数,像素单位 无
x_max annotations_* 框右下角横坐标 整数,像素单位 无
y_max annotations_* 框右下角纵坐标 整数,像素单位 无
labels image_labels_* 该医师对该图的 28 维 0/1 判断 定长向量 无
image_id(DICOM) DICOM 头 由 SOP Instance UID 派生的匿名标识 十六进制串 无
Rows / Columns DICOM 头 原始像素矩阵的行列数 整数 无
BitsStored DICOM 头 每个像素实际使用的位数 整数 无
WindowCenter / WindowWidth DICOM 头 显示器默认的窗宽窗位 数值 部分影像缺失
PhotometricInterpretation DICOM 头 灰度极性约定 MONOCHROME1 或 MONOCHROME2 无

坐标字段的单位是像素,但是哪个像素取决于你读的是哪个版本。DICOM 里的 x_max 以原始矩阵宽度为上限,竞赛版里的 x_max 以改编后图像的宽度为上限。如果直接拿竞赛版的框去套 DICOM 影像,框会整体偏移。这是跨渠道混用最容易踩的坑,后面 §7.3 还会再提一次。

2.3 一条记录的完整解剖

取训练集里的一条框级记录,加上它在 DICOM 里的对应影像,完整形态是这样的。

标注侧:annotations_train.csv 的一行(列名以官方 CSV 表头为准)
  第1列  image_id      影像标识,与 DICOM 文件名对应
  第2列  rad_id        画框的医师编号,17 名医师之一
  第3列  class_name    该框对应的局部发现名称
  第4-7列 x_min y_min x_max y_max   框的四个坐标,原始像素单位

影像侧:Train/<image_id>.dicom 的头部关键字段
  PatientID                 已被删除
  StudyDate                 已被删除
  SOPInstanceUID            匿名 ID 由它经 hashlib 派生
  Rows / Columns            原始矩阵行列数
  BitsAllocated             每像素分配位数
  BitsStored                每像素实际存储位数
  PhotometricInterpretation MONOCHROME1 或 MONOCHROME2
  WindowCenter / WindowWidth 数值,部分影像缺失
  ViewPosition             PA

读这条记录时的要点有三个。第一,rad_id 是医师编号而不是医师姓名,17 名医师的编号集中在一列里,同一张图会出现三行相同 image_id 但不同 rad_id 的记录。第二,class_name 是发现名不是诊断名,Cardiomegaly 在这里指的是「心影增大这一影像表现」,不是临床诊断结论。第三,DICOM 头里的日期字段已经被清除,所以你无法从单个文件推断采集时段,只能依赖论文描述的总体时间范围。

2.4 规模与拆分的对照

口径 数值 来源
原始采集池 十万余例 官方数据集页与论文摘要
公开发布总量 1.8 万例 PA 位 官方数据集页
训练集 1.5 万例 官方数据集页与竞赛页
测试集 3 千例 官方数据集页与竞赛页
局部标签类别 22 类 论文摘要
全局标签类别 6 类 论文摘要
竞赛归类类别 14 类 竞赛页
标注医师 17 名 官方数据集页
训练集标注密度 每例 3 人 论文摘要
测试集标注密度 每例 5 人共识 论文摘要
竞赛版文件规模 两万余个文件 竞赛页
竞赛版标注格式 CSV,一行一个框 竞赛页

这里有一个必须说清的差异:官方数据集页说标注了 22 类局部发现,竞赛页说竞赛把问题简化成 14 类。这不是矛盾,是任务定义不同。22 类里有若干类在竞赛中被合并或剔除,以适配目标检测的评测。凡是要做类别对齐的工作,都要先把这两套类别表并排放一遍。

关于标注框总数,官方公开页面没有给出单一口径的数字。论文给出的是训练集框级 CSV 与测试集共识 CSV 两张表,框数取决于三个判断:是否把 No finding 记录计入、是否按医师拆行、是否把测试集共识算进去。坊间流传的单一总数,如果不说明口径就不可比。本文因此不采用单一总数,只写「框级 CSV 的行数即框数,随口径变化」。

2.5 与同类胸片资源的字段差异

资源 标注粒度 是否有框 是否有报告文本 是否有原始 DICOM
本数据集(原始 DICOM 版) 病灶级 + 图像级 有,22 类 无 有
本数据集(竞赛版) 病灶级 + 图像级 有,14 类 无 改编后
MIMIC-CXR 报告级 + 自动抽取标签 无 有 有
NIH ChestX-ray14 图像级 无 无 有
CheXpert 图像级 无 有(部分) 有
PadChest 图像级 + 部分框 部分 有 有

这张表想说明的是一件事:病灶级框是最稀缺的那一列。报告文本可以找 MIMIC-CXR,图像级标签可以找 ChestX-ray14 或 CheXpert,但「放射科医师逐例画框」在公开胸片资源里仍然是少数派。这也是这个数据集即便有越南单中心的局限,仍然被大量引用的原因。

2.6 目录结构与落盘形态

层级 路径 内容 规模
根目录 vindr-cxr-1.0.0/ 版本目录 1 个
一级 Train/ 训练集影像 约 1.5 万个 DICOM
一级 Test/ 测试集影像 约 3 千个 DICOM
一级 Annotations/ 四份 CSV 4 个文件
二级 Annotations/annotations_train.csv 训练集框级标注,含 rad_id 每行一个框
二级 Annotations/annotations_test.csv 测试集框级共识标注,无 rad_id 每行一个框
二级 Annotations/image_labels_train.csv 训练集图像级 28 维标签 每图每医师一行
二级 Annotations/image_labels_test.csv 测试集图像级共识标签 每图一行

目录结构本身极简:没有按医院分子目录,没有按设备分子目录,也没有按患者分组。这意味着任何分组统计都要靠 DICOM 头字段自己重建,而这两类字段恰好是去标识化时优先删除的对象。想按医院分组做外部验证式分析,在公开版本上做不到。

一个常被问到的细节是文件扩展名。PhysioNet 发布的是 .dicom 后缀而不是常见的 .dcm,写读取代码时按扩展名过滤要留意,按 glob 匹配 *.dcm 会一个文件都匹配不到,却不会报错,只会得到一个空的文件列表。

2.7 标注从平台到 CSV 的转换损失

标注是在 VinDr Lab 平台上做的,使用者在平台上看到的是原始 DICOM 与交互式框。最终发布的是 CSV。这个转换过程会丢掉几样东西:框的绘制顺序、框的置信度或犹豫标记、标注耗时、医师对框的备注、框所属的解剖区域。这五项里,后四项在平台上是否被记录、记录到什么颗粒度,公开说明都没有交代;能确定的是它们没有出现在发布的 CSV 里。

其中标注耗时的缺失对想研究「标注难度」的团队是个硬约束:没有耗时就没有难度标签,只能用人际分歧度间接代理。对想研究「标注难度」的团队是个硬约束:没有耗时就没有难度标签,只能用人际分歧度间接代理。

2.8 四份 CSV 之间的关系

文件 粒度 行含义 与影像的关系
annotations_train.csv 框级 某医师在某图上画的一个框 多对一
image_labels_train.csv 图像级 某医师对某图的 28 维判断 多对一
annotations_test.csv 框级 共识框 多对一
image_labels_test.csv 图像级 共识标签 一对一

把四份文件拼成一张宽表是最常见的做法,但拼的时候要注意:框级表与图像级表的连接键不是 image_id 一个字段,训练集还需要 rad_id。只按 image_id 连接会造成笛卡尔积,行数暴涨。

§3 核心层 A:它到底是谁的影像

3.1 两院合流的一次回顾性采集

这个数据集的影像不是前瞻性招募的,是从两家医院的既有影像归档中回溯筛选的。官方描述是「从十万余例原始 DICOM 影像中回顾性采集」。这句话里有两个信息:一是筛选前的池子很大,二是筛选标准没有在公开页面上完整公布。

筛选做了什么、没做什么,会直接影响你对数据集代表性的判断。从公开信息能确认的是:最终发布的是 PA 位、成人、胸部 X 线;最终数量约为一万八千例。至于筛掉了哪些——侧位、床旁位、儿科、重复检查、质量不合格影像——官方没有逐项给出剔除计数。使用者在写方法的局限性时,应当把这一点列为「筛选过程未完全公开」。

3.2 为什么是这两家医院

108 军医院与河内医科大学医院是越南规模最大的两家医院,前者是军队系统的大型综合医院,后者是河内医科大学的附属教学医院。这个组合带来两个特征:病例复杂度偏高(大型综合医院与教学医院通常承接转诊与疑难病例),以及影像设备型号相对集中。

对使用者的实际含义是:这个数据集的疾病谱比社区筛查场景更偏重症,设备谱比全国普查场景更窄。用它训出的模型在社区体检场景下可能表现为灵敏度尚可但特异度下降,因为训练分布里的阳性比例与病灶形态都更偏重。

3.3 17 名医师的标注组织方式

标注在自建的 VinDr Lab 平台上完成,这个平台建在 PACS 之上,所以医师看到的是原始 DICOM 而不是导出的图片。这一点很重要:标注时的坐标系是原始 DICOM 的像素坐标系,而不是竞赛版的坐标系。

训练集每例由 3 名医师独立标注,互相不可见;测试集由 5 名医师标注后协商合并为一份共识。这个设计带来两种不同性质的数据:训练集的三份标注可以用来研究医师间一致性,测试集的单份共识只能当作金标准使用,不能用来算一致性。

3.4 「发现」与「诊断」两层标签的分工

22 类局部标签对应放射报告的 Findings 段,6 类全局标签对应 Impressions 段。这个映射关系不是作者事后编的,是标注时就直接按报告结构组织的。

两层的差别在于:局部标签描述「看到了什么」,全局标签描述「推断是什么病」。同一位患者可能有多条局部标签但只有一条全局标签,也可能局部标签为空而全局标签给出一个疑诊。训练目标检测模型用局部标签,训练分类模型用全局标签,把两者混进同一个损失函数需要有明确的理由。

3.5 一张影像在两个版本里的身份

属性 竞赛版 DICOM 版
文件名 image_id 加扩展名 image_id 加 .dicom
标识来源 竞赛组织方分配 SOP Instance UID 派生
是否可跨版本对齐 可以,靠 image_id 可以,靠 image_id
像素尺寸 改编后 原始
位深 改编后 原始
头信息 基本剥离 保留非 PHI 部分

跨版本对齐是可行的,image_id 在两个版本里一致,这是做「同一影像两种处理」对比研究的入口。但要注意,测试集的 image_id 在竞赛版里只出现在提交模板中,实际影像不随公开数据提供,所以跨版本对比只能在训练集做。

3.6 采集时间与地理覆盖能推到哪里为止

论文描述的是回顾性采集,覆盖的时段以研究整体年份表述,具体到每条记录的采集日期在去标识化时被清除。地理覆盖上,两家医院都在河内,这一点在数据集页与论文里都写明。

把这两个信息放在一起,能推出的边界是:你能说「这批影像来自越南河内的两家大型医院、采集于公开描述的整体时段」,不能说「某一张影像采集于哪一年哪一月」。任何需要按时间切分训练集与验证集的设计,在这个数据集上都退化成随机切分。

3.7 设备信息的可得性与不可得性

DICOM 头里与设备有关的字段,在去标识化时通常不会被优先删除,因为它们不属于直接身份标识。但这类字段是否完整保留,取决于发布方的具体脚本,论文没有逐字段列出保留清单。可以确定的是:竞赛版把这些信息抹平了,DICOM 版至少保留了部分。

正确的心态是:拿到 DICOM 之后先做一次头字段普查,把「有哪些字段、各自缺失多少」摸清,再决定研究设计能不能成立。不要把「DICOM 里应该有设备信息」当成前提,它可能只是一个期望。

3.8 同一患者的多次检查能否追踪

公开说明没有给出患者级标识,DICOM 里的患者 ID 字段已被删除。这意味着无法在公开版本里判断两张影像是否来自同一患者。

对检测任务影响有限,对分类或纵向研究影响很大:如果同一患者的多张影像被随机分到训练集与测试集,指标会偏乐观。由于无法追踪,这个风险只能承认、不能消除。写方法一节时应当明确写「无法排除同患者跨划分」。

3.9 影像质量的分层线索

影像质量本身可以在两版上做分层,只是能用的线索不同。旋转角度、是否有金属植入物、曝光是否过度或不足,这三项在两版上都可见,区别只在精度。原始位深的动态范围与像素间距这两项,只有 DICOM 版能做。

前四项在任何版本上都能做,只是精度不同。最后一项只有 DICOM 版能做,它决定了你能否把像素尺寸换算成物理尺寸。在任何版本上都能做,只是精度不同。第五行只有 DICOM 版能做,它决定了你能否把像素尺寸换算成物理尺寸。

§4 核心层 B:原始版到底多给了你什么

4.1 位深:从原始量化到显示量化

DICOM 文件的像素位深由 BitsAllocated 与 BitsStored 两个字段描述。胸片设备的探测器通常输出 12 到 14 位的量化结果,这个范围远超人眼在单个窗宽下的分辨能力。竞赛版为了统一训练管线,会把影像压到更低的有效位深,这个过程不可逆——一旦压缩,原文件里的细微信号等级就找不回来了。

对多数检测任务,这个损失无关紧要。对两类研究,它是致命的:一是研究低对比度病灶(如早期间质性改变、细微钙化)能否被模型看见,二是研究不同压缩策略对模型鲁棒性的影响。这两类研究都要求你手里有未经压缩的原始量化。

4.2 矩阵与视场:边缘被裁掉了多少

原始胸片的矩阵尺寸随设备与采集协议变化,常见的是两千到三千级别的方形或近方形矩阵。竞赛版为了让所有影像在同一个输入尺寸下可用,会做重采样,并且在重采样时可能裁掉四周的无关区域。

裁边对检测任务的影响集中在两点:贴边的病灶框可能被截断,肩部、肋膈角等边缘区域的框坐标可能超出新图像边界。使用竞赛版时,需要检查是否存在 x_max 大于图像宽度、y_max 大于图像高度的记录,这类记录要么说明原始框本就贴近边缘,要么说明边长过程确实发生了。

4.3 DICOM 头:设备参数的唯一载体

头字段 能回答的问题 竞赛版是否有
Rows / Columns 原始矩阵多大 无
BitsAllocated / BitsStored 原始位深多少 无
PhotometricInterpretation 灰度极性约定 无
WindowCenter / WindowWidth 显示器默认窗宽窗位 无
ViewPosition 是否确为 PA 位 无
设备与采集参数(非 PHI 部分) 哪家厂商、什么协议 无
PixelSpacing 像素物理尺寸,能否做真实尺度测量 无
ImagerPixelSpacing 探测器像素间距 无

PixelSpacing 值得单独说一句。有它就能把像素框换算成毫米,从而做真实尺度的病灶测量;没有它,所有尺寸都只能以相对比例表达。如果研究涉及「心影横径与胸廓横径之比」这类需要真实比例的指标,DICOM 头是唯一来源。

4.4 去标识化做了哪些、没做哪些

官方描述的流程包括:脚本删除 PHI 标签、像素文字自动识别删除后人工复核、匿名 ID 由 UID 派生、按 HIPAA 与 GDPR 及越南本地法规复核。伦理方面,两家医院的 IRB 在研究开始前批准,因为属回顾性研究且已去标识化,豁免了患者知情同意。

需要使用者自行判断的是:去标识化的目标是「无法重新识别」,不是「保留全部研究价值」。被删掉的日期字段会让时间序列分析无法进行,被删掉的机构字段会让院内对照研究失去依据。这不是缺陷,是隐私保护的必然代价,但规划研究设计时必须提前知道。

4.5 案例:一份把竞赛版训练结果写进论文的稿子

设想一个具体场景。某团队用竞赛版训了一个胸片病灶检测器,投出去之后收到审稿意见:请说明训练影像的预处理流程,特别是位深与尺寸变换是否对结果产生影响。团队回看代码,发现预处理全部来自一份公开 Notebook,原作者只写了「统一 resize 到 512」,没有说明位深处理。

这条意见无法用竞赛版本身回答,因为竞赛版里已经没有位深信息了。可行的补救路径是:申请 PhysioNet 凭证,拿到原始 DICOM,抽样若干例,把 BitsStored 与 Rows/Columns 的分布统计出来,再在 DICOM 上重跑一遍同样的管线,量化「压缩前后模型指标的差额」。这份差额本身就是一个可写进回复的实验。

§4.5 的案例说明了原始版的核心价值:它不是让模型变好,是让你能回答关于自己模型的追问。同一个案例在 §7.3 的偏差清单里还会出现一次,在那里讨论的是「未申请原始版时该怎么写局限性」;在 §9.1 的角色路径里会再出现一次,讨论的是审稿周期内的最短补救路径。

4.6 原始版不能给你的三样东西

第一,测试集标签。两个版本都不提供。第二,医师姓名与资历逐项对应。官方只说 17 名医师均具 8 年以上经验,不给逐人资历表。第三,原始放射报告正文。标签是结构化的,文本不是。

这三样东西的缺席有共同原因:它们都属于「能直接或间接定位到个人或具体机构」的信息。资历表会缩小医师范围,报告正文可能写进就诊细节。理解这一点,就不会把它们当成发布方的疏漏,也不会花时间去翻找隐藏文件。

4.7 一次对照实验该怎么设计

想在论文里证明「原始 DICOM 与竞赛版确实不同」,不需要写一篇长文,一次小规模对照实验就够。

设计要素 建议做法 规模 说明
抽样 从训练集随机抽若干例 几十到几百例 不必全量
对照组 A 用竞赛版影像与框训练 同一模型 保持其他条件不变
对照组 B 用 DICOM 影像重跑管线训练 同一模型 同一随机种子
指标 在同一个评测协议下比较 同一测试集 三个渠道只有两版可用
报什么 指标差额与头字段统计 两张表 不报结论性形容词

第五行的措辞很关键。审稿人要的是「差了多少」,不是「显著不同」。把差额写出来,比任何定性描述都有说服力。

§5 核心层 C:怎么把 DICOM 变成能训练的张量

5.1 获取路径的三道门

门 要求 大致耗时 常见卡点
账号注册 邮箱 + 机构信息 分钟级 用个人邮箱会被要求补充机构证明
凭证化认证 提交身份与机构证明 数个工作日 学生身份需导师担保
培训证明 完成 CITI 数据研究课程并上传证书 数小时到数日 证书格式不被接受需重传
签署 DUA 在线签署本项目协议 分钟级 机构需有授权签字人

四道门里最容易低估的是培训证明。CITI 课程本身不长,但证书上传后的审核有延迟,如果时间线紧,应当先做这一步。另外,凭证化认证是按人而非按项目,认证一次之后有效期较长,换项目时通常只需重签 DUA。

5.2 下载与校验

#-- 环境:已通过 PhysioNet 凭证化认证并签署本项目的 DUA
#-- 1. 下载:PhysioNet 支持 wget 镜像整站,账号密码按官方提示交互输入
wget -r -N -c -np https://physionet.org/files/vindr-cxr/1.0.0/

#-- 2. 清点落盘结果:影像文件数与目录三件套是否齐全
find ./physionet.org/files/vindr-cxr/1.0.0 -name '*.dicom' | wc -l
ls ./physionet.org/files/vindr-cxr/1.0.0/Annotations/

#-- 3. 只先取一批做管线验证,避免一次性拉全量后才发现读取逻辑有误
mkdir -p sample && ls ./physionet.org/files/vindr-cxr/1.0.0/Train/*.dicom \
  | head -50 | xargs -I{} cp {} sample/

wget -r 是整站镜像,-N 只补新文件、-c 支持断点续传、-np 不向父目录扩散。这三个人人都抄的选项里,-c 在几百 GB 的量级上不是可选项而是必需品:任何一次网络中断都要能从断点继续,否则整个下午白费。

5.3 读取一张原始 DICOM

#-- 依赖:pydicom>=2.3, numpy, opencv-python
#-- 目标:读出一张原始影像,并打印决定后续处理的关键头字段
import glob
import pydicom
import numpy as np
from pydicom.pixel_data_handlers.util import apply_voi_lut

def read_raw(path):
    ds = pydicom.dcmread(path)
    arr = ds.pixel_array.astype(np.float32)
    #-- MONOCHROME1 表示数值越大越黑,需反色才能与常规阅片一致
    if str(ds.PhotometricInterpretation) == "MONOCHROME1":
        arr = arr.max() - arr
    #-- 优先使用 DICOM 自带的窗宽窗位,缺失时再退回分位数拉伸
    try:
        arr = apply_voi_lut(arr, ds, index=0).astype(np.float32)
    except Exception:
        lo, hi = np.percentile(arr, [1, 99])
        arr = np.clip(arr, lo, hi)
    arr = (arr - arr.min()) / max(arr.max() - arr.min(), 1e-6)
    meta = {
        "rows": int(ds.Rows),
        "cols": int(ds.Columns),
        "bits_stored": int(getattr(ds, "BitsStored", 0)),
        "spacing": [float(v) for v in getattr(ds, "PixelSpacing", [0, 0])],
    }
    return arr, meta

if __name__ == "__main__":
    img, m = read_raw(sorted(glob.glob("sample/*.dicom"))[0])
    print(m, img.shape, img.dtype, round(float(img.mean()), 4))

glob 需要在文件开头一并导入,这里省略重复的 import 行,只保留与读取有关的逻辑。

这段代码里有两个判断是必须显式做的:极性(MONOCHROME1 还是 MONOCHROME2)和窗宽窗位。前者错了整张图反色,后者错了对比度全跑偏。竞赛版已经把这两件事做完,DICOM 版必须自己做。

5.4 框坐标从原始矩阵缩放到模型输入

#-- 把原始 DICOM 坐标系的框,缩放到重采样后的模型输入尺寸
import glob
import cv2
import numpy as np

def resize_keep_ratio(img, long_side=1024):
    h, w = img.shape[:2]
    scale = long_side / max(h, w)
    new_w, new_h = int(round(w * scale)), int(round(h * scale))
    return cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_AREA), scale

def scale_boxes(boxes, scale):
    #-- boxes: N x 4 的 [x_min, y_min, x_max, y_max],原始像素单位
    out = np.asarray(boxes, dtype=np.float64).copy()
    out[:, [0, 2]] *= scale
    out[:, [1, 3]] *= scale
    return out

cand = sorted(glob.glob("sample/*.dicom"))
img, meta = read_raw(cand[0])
img2, s = resize_keep_ratio(img, 1024)
#-- 框坐标从标注 CSV 读出后,按同一个缩放系数换算,不要写死数字
raw_boxes = [[100, 100, 400, 600]]
print("原始矩阵", meta["rows"], meta["cols"], "位深", meta["bits_stored"])
print("缩放系数", round(s, 5))
print("缩放后框", scale_boxes(raw_boxes, s).round(1).tolist())
print("新尺寸", img2.shape)

raw_boxes 这里只放了一个示意框,真实使用时应当从 annotations_train.csv 里按 image_id 取,见下一节。

要点是「保持长宽比的缩放」。如果直接强行拉伸到正方形,框的宽高比会跟着变形,检测模型学到的先验就与影像的真实几何脱节。这一点在竞赛版里不需要操心,因为它已经统一处理过。

5.5 按 image_id 把标注与影像接起来

#-- 把 annotations_train.csv 拆成「每张图一个列表」的结构,供 Dataset 使用
import pandas as pd
from collections import defaultdict

def index_by_image(csv_path, img_dir, suffix=".dicom"):
    df = pd.read_csv(csv_path)
    by_img = defaultdict(list)
    for r in df.itertuples(index=False):
        by_img[r.image_id].append({
            "rad_id": r.rad_id,
            "cls": r.class_name,
            "box": [r.x_min, r.y_min, r.x_max, r.y_max],
        })
    items = []
    for iid, rows in by_img.items():
        items.append({"image_id": iid, "path": f"{img_dir}/{iid}{suffix}", "ann": rows})
    return items

items = index_by_image(
    "./physionet.org/files/vindr-cxr/1.0.0/Annotations/annotations_train.csv",
    "./physionet.org/files/vindr-cxr/1.0.0/Train",
)
print(len(items), items[0]["image_id"], len(items[0]["ann"]))

注意 ann 里保留了 rad_id。训练时是否只用其中一位医师的框、还是三位合并、还是做多数投票,是三个不同的实验设定,保留原始结构才能后三者都做。

5.6 三位医师的框怎么合并

合并策略 做法 适用场景 代价
只用一位 固定取某位医师的框 快速验证管线 标签噪声最大
全部保留 三人框都当正样本 学习标注多样性 同一病灶被多次计数
IoU 聚类合并 同类且 IoU 超阈值的框合成一个 追求干净标签 阈值选择影响召回
投票保留 至少两人画出的才算 追求高精度 单医师发现的稀有病灶被丢弃
加权 按医师可信度加权 有逐人评估数据时 本数据集不提供逐人评估

第五行值得强调:官方不给逐医师的资历表与一致性评估,所以「按医师可信度加权」这条路走不通。只能退回到按 IoU 聚类或按投票数筛选。

5.7 两个版本混用时必须先做对齐的三步

第一步核对 image_id 是否两边都存在,判据是 ID 集合能对上,失败的表现是大量 ID 只在一边出现。第二步核对同一 ID 的像素尺寸,判据是高度与宽度是否相等,不相等就说明改编确实发生过。第三步核对框坐标范围,判据是框的最大值是否落在图像尺寸内,超界即坐标系用错。

这三步抽几十例就能做完,不必下载全量。做完之后你才可以说「两个版本已对齐」,而不是「我以为它们一样」。

5.8 与院内数据混合训练时的一条建议

如果除公开版之外还有院内 DICOM,混合训练的第一道坎不是标签,是影像归一化口径。竞赛渠道的公开版已被统一处理,院内数据是原始 DICOM,灰度分布天然不同。

最省事的做法是把公开数据也走一遍原始 DICOM 渠道,用同一条管线处理两侧。这样两边的归一化参数一致,模型不会学到「样本来自哪个渠道」这种与任务无关的捷径。这也正是本文推荐原始版的最实用理由之一:它是混合训练里唯一能与院内数据平等对话的形态。

5.9 一段可以直接抄的窗宽窗位兜底策略

情形 取值策略 理由
DICOM 有 WindowCenter 与 WindowWidth 直接用头字段 最贴近阅片习惯
只有其中一个 另一个按胸片常规取值补齐 缺一不可用
两者都没有 按 1% 与 99% 分位数拉伸 稳定、不依赖阅片参数
影像整体过曝或过暗 改用 2% 与 98% 分位数 抑制极端值影响
需要与其他数据集统一 固定同一组分位数 保证跨集可比

第五行是跨集研究的关键。如果每个数据集各用一套拉伸,模型学到的实际是「各数据集的窗口设置差异」,而不是解剖或病理差异。

§6 与库内条目的关系

6.1 关系图谱

库内已有四条 VinDr 系列条目,加上本文一共五条。它们不是互相替代的关系,而是「同一研究机构的五个不同切片」。

条目 覆盖对象 与本文的关系 该读哪篇
vindr-cxr VinDr-CXR 数据集本身与竞赛 同一份数据的不同切面 想了解数据集全貌与竞赛玩法读它
vindr-pcxr 儿科胸片 同机构、不同人群 儿童任务读它
vindr-spinexr 脊柱 X 光 同机构、不同部位 肌骨任务读它
vindr-mammo 乳腺钼靶 同机构、不同模态 乳腺任务读它
本文(vinbigdata-cxr) 两条分发渠道的差异与原始版取数 渠道层 要回原始 DICOM 或要解释预处理读本文

用一句话概括分工:已有条目回答「这个数据集能做什么」,本文回答「同一份数据你该拿哪一个封装」。

6.2 检索路径

你想解决的问题 检索词 应到哪篇找
这个数据集的类别有哪些 VinDr-CXR 22 类 vindr-cxr
竞赛排行榜怎么读 VinBigData mAP 竞赛 vindr-cxr
原始位深能不能拿到 VinDr-CXR DICOM PhysioNet 本文
儿科版本在哪里 VinDr-PCXR vindr-pcxr
脊柱版标注粒度 VinDr-SpineXR vindr-spinexr
乳腺版 BI-RADS VinDr-Mammo vindr-mammo
两个版本能不能混用 VinDr-CXR 竞赛版 原始版 差异 本文
测试集标签能不能拿 VinDr-CXR test labels 本文

检索路径的设计原则是:问题里出现「原始」「DICOM」「PhysioNet」「预处理」时来本文,出现具体疾病、部位、模态时去对应条目。

6.3 一个容易混淆的边界

竞赛版与 DICOM 版的差异,不等于 VinDr-CXR 与 VinDr-PCXR 的差异。前者是同一批数据的不同封装,后者是不同人群的不同数据。检索时如果把「原始版」和「儿科版」混为一谈,会得到完全错误的结论——儿科版没有对应的「原始版双渠道」,它的发布路径与本文描述的并不相同。

第二层混淆是「原始版」与「原始数据」。原始版指未经改编的 DICOM 发布,原始数据指十万余例未筛选的采集池。前者可下载,后者不公开;混说会让人以为申请下来就能看到全集。

6.4 引用时的推荐颗粒度

如果一篇论文只用了竞赛版,引官方论文加竞赛页即可;如果用了 PhysioNet 的 DICOM,官方要求引用原始论文,同时也应标注 PhysioNet 的版本号与 DOI。两者都写,读者才能知道你用的是哪一份封装。反过来说,只要标注写明版本,就不必在正文里反复解释「本文用的是竞赛版还是 DICOM 版」,一句版本说明足够了。

§7 质量评估与局限性

7.0 阅读本节前的一个前置校正

很多人读到「局限性」三个字,会默认它说的是「数据集不好」。在这个数据集上,这个默认会导致一种典型的误判:把「两条渠道不一致」理解成「发布方出错」。实际情况是,两个版本从设计之初就服务于不同目的,官方在论文里也明确说了竞赛版是「稍微修改过的版本」。

所以本节的评分口径需要先说清:下文所有扣分项,扣的是「单一版本的可用范围」,不是「数据集本身的质量」。同一个维度上,竞赛版与 DICOM 版可能得完全不同的分,下文会分别标注。

7.1 五维评分

维度 竞赛版 DICOM 版 说明
规模 4.5 4.5 1.8 万例在胸片公开集里属第一梯队
标注粒度 5.0 5.0 病灶级框 + 逐医师记录,稀缺
标注可靠性 4.5 4.5 训练 3 人、测试 5 人共识,设计扎实
工程就绪度 5.0 2.5 竞赛版开箱即用,DICOM 版要自建管线
可解释性 2.5 5.0 竞赛版预处理不透明,DICOM 版可自证

这张表最值得看的是最后两行的反向关系:工程就绪度与可解释性在两条渠道上是此消彼长的。你选哪一个,取决于你的成果更怕「跑得慢」还是更怕「说不清」。

7.2 代表性:单中心风险的具体形态

两家医院都在河内,都是大型综合与教学医院。这个组合的风险不是「数据质量差」,而是「分布窄」。具体表现为三点:设备型号集中,导致模型对特定成像风格的依赖被放大;病例复杂度偏高,导致模型在筛查场景下的阈值需要重新校准;人群以越南本地为主,导致体型相关指标(如心影横径比)的分布可能与其他地区不同。

第三点最容易被低估。心影增大这类标签在影像上表现为比例关系,而比例关系与体型强相关。跨人群使用时,同一个模型的判定阈值很可能需要重新拟合。

7.3 已知的、可枚举的偏差

偏差 具体表现 对研究的后果 应对
渠道差异 两版影像像素不同 跨渠道混用导致框错位 只用一版训、另一版只做验证前先对齐
类别口径差异 22 类与竞赛 14 类 类别映射错误 显式写出映射表
标注密度不对称 训练 3 人、测试 5 人 测试集指标偏乐观 报告时说明测试集为共识标注
筛选过程未全公开 剔除计数未知 代表性无法精确描述 在局限性中如实标注
日期字段被清除 无采集时间 无法做时间漂移分析 用研究整体年份替代
单中心 两家越南医院 跨地域泛化存疑 外部验证另找数据源
文本缺失 无报告正文 无法做图文任务 另找配对报告资源
位深不可追溯 竞赛版已压缩 量化研究失效 必须用 DICOM 版
类别语义模糊 22 类中有语义相近项 标注边界因人而异 合并前先做语义核对
空框记录 No finding 也以记录形式存在 当作正样本会污染训练 显式过滤或单独处理

倒数第二行值得展开。22 类里存在若干语义上接近但放射学上不同的项,例如浸润与肺不张、结节与肿块。这些类在竞赛版的 14 类口径下有合并,但在 22 类口径下是分开的。同一个框在不同口径下可能属于不同类,做类别映射时必须一并核对。

最后一行是纯工程问题:No finding 是以记录形式出现的,如果按「有框即正样本」的直觉处理,会把空框当成病灶。过滤这一步在写 Dataset 时就该做掉。

第二行在实践中最常出错。竞赛把 22 类收敛成 14 类,收敛规则并不是简单删掉八类,涉及部分类的合并。如果研究者照着 22 类的名字逐个去匹配竞赛的 14 类编号,会出现「名字对上了但语义错了」的情况。

§4.5 的案例在这里再次适用:那位被追问预处理的作者,真正要补的不是一个数字,而是把上表里的「渠道差异」和「位深不可追溯」两条变成可量化陈述。这一步做完,局限性一节才有内容可写。

7.4 外部验证矩阵

从同机构的另一个时段做验证,部分可行,需要院内同期影像,障碍是公开数据没有日期字段。同国其他医院可行,但需要自己采集与标注。跨国人种可行,代价是阈值要重新校准。跨设备厂商只有 DICOM 版可行,因为竞赛版没有设备信息。跨模态不可行,本数据集只有 X 线。

其中「跨设备厂商」这一项是本文标题的直接落点:在竞赛版上这一整列是空的。如果你的研究问题是「模型在不同厂商设备上的稳定性」,竞赛版无法回答,因为厂商信息在改编中已经丢失。是本文标题的直接落点:「跨设备厂商」这一整列,在竞赛版上是空的。如果你的研究问题是「模型在不同厂商设备上的稳定性」,竞赛版无法回答,因为厂商信息在改编中已经丢失。

7.5 它为何能持续:稀缺性来自标注成本而非数据量

胸片公开数据集不缺。「带放射科医师逐例画框」的胸片公开数据集缺。这个差别决定了它的存续逻辑:影像本身可以不断有新来源,但每例三名医师独立标注的成本是刚性的,新数据集的产出速度受限于医师工时,而不是受限于影像获取。

这也解释了为什么它会在 2021 年同时存在于两个渠道:竞赛需要一份「标准化过、便于评测」的快照,学术界需要一份「原始、可自证」的发布。两个需求同时存在,两条渠道就有同时存在的理由。

7.6 DAIMS 就绪度自查

两版在八项就绪度上的表现可以逐条看。数据能否直接加载:竞赛版可以,DICOM 版需要自建读取逻辑。标签与影像是否一一对应:两版都可以,靠 image_id 对齐。是否有官方划分:两版都有,训练与测试固定。是否有数据字典:两版都只有部分,CSV 字段本身就那么几个。是否有许可声明:两版都有,但许可不同。是否有预处理文档:两版都没有完整文档,竞赛版没有公开发布改编脚本,DICOM 版论文描述了去标识化但没有发布完整管线。是否可追溯原始采集:竞赛版不行,DICOM 版只能部分追溯,因为日期已删。是否有版本号:竞赛页没有,PhysioNet 有 v1.0.0。

倒数第三项值得再强调一次。做严谨复现时,「没有预处理文档」意味着你需要向发布方追问,或者自己反推。后者在本文 §5 的管线里已经给了基本框架,但反推出来的参数与发布方实际用的未必一致,这一点要在方法一节里讲清楚。

7.7 与同类「双渠道」资源的对照

双渠道这件事本身不特殊。MIMIC-CXR 把影像与报告分开发布,CheXpert 有大小两个版本,PadChest 需要申请才能拿到,都算某种形式的双渠道。特殊的是本数据集的双渠道由竞赛时间表驱动:其他资源的双渠道通常服务于「访问门槛分级」,这里的双渠道明确挂着「竞赛版」与「完整版」两个不同的技术目标。

这个差别让两版之间的像素差异比其他资源更值得关注。访问门槛分级的两条渠道,内容通常一致;技术目标不同的两条渠道,内容一定不一致。判断一个数据集属于哪一类,最简单的办法是看官方有没有写「slightly modified」这类措辞——写了,就是第二类。

7.8 标注成本在数据上的具体投影

每例三名医师独立标注,意味着训练集的标注量约为影像量的三倍(按行计)。这个投影带来一个直接后果:训练集的框级 CSV 行数远大于影像数,按影像数去估 CSV 规模会严重低估。反过来,测试集因为是共识,行数只与病灶数有关,与医师数无关。

这也解释了为什么拿训练集与测试集的行数对比来推断「哪边标注更密」是不成立的——两边的计数单位不同。

§8 生态位与历史

8.1 一次竞赛与一份数据集的并行落地

时间线大致是:2020 年 12 月 30 日论文预印上线,同日 Kaggle 竞赛开放;2021 年 3 月底竞赛结束;2021 年 6 月 22 日 PhysioNet 上线 v1.0.0。也就是说,竞赛版先被大量使用,原始版晚了半年才具备正式的凭证化访问路径。

这个顺序造成了一个持续至今的现象:绝大多数引用它的论文用的是竞赛版,因为那半年里它是唯一可得的形态。这也解释了为什么关于预处理的追问在评审中反复出现——用竞赛版的人多,但竞赛版的改编细节公开得少。

8.2 从单数据集到系列发布

VinBigData 此后陆续发布了脊柱、乳腺、儿科、体部位等多个 X 线数据集,形成了一条系列发布线。这条线的共同特征是:自建标注平台、逐例多医师标注、PhysioNet 凭证化分发。本文讨论的「双渠道」问题,在这条线的其他成员上以不同形式重现。

对使用者的实际含义是:如果你已经为其中一个数据集走完了凭证化流程,后续成员的门槛会低一些,但每个项目仍需单独签署数据使用协议。

8.3 它在胸片公开集谱系里的位置

代际 代表资源 主要贡献 本数据集的相对位置
第一代 图像级标签集 证明胸片可做弱监督 更细的标注粒度
第二代 报告配对集 引入自然语言监督 无文本但框更精
第三代 医师逐例画框集 支持检测与定位 属于这一代
第四代 多中心多模态集 跨机构泛化 单中心,尚未进入

本数据集属于第三代,它的贡献是把「框」变成公开资源。第四代的多中心问题,它没有解决,也不打算解决。

8.4 社区使用的两个稳定方向

第一个方向是检测与定位,直接使用 22 类或 14 类框训练目标检测器。第二个方向是标注一致性研究,利用训练集的三份独立标注分析医师间的分歧模式。第二个方向在竞赛版上也能做,但需要原始 DICOM 时(例如想看分歧是否与影像质量相关)就要回到本文描述的渠道。

8.5 三个常被引用的使用模式

第一种是教科书式 baseline:直接训检测器,报 mAP,不写预处理细节。它复现困难,但门槛最低,所以引用最多。第二种是标注一致性研究:利用训练集的三份独立标注分析医师分歧,只用竞赛版就能做,缺点是缺少影像质量这个维度。第三种是预处理消融:对比不同管线对指标的影响,两版都要,对齐工作量大。第四种是与院内系统联调:用同一管线处理公开集与院内数据,必须走 DICOM 版。

第三种是本文最希望推动的用法。它不是「更难的版本」,而是「能写进方法一节的版本」:有了原始 DICOM,预处理不再是「调用了某个 Notebook」,而是「我们明确了极性、窗宽窗位、重采样与框缩放四步,参数如下」。

8.6 一次站内交叉检索的示范

刚接触这个机构系列数据的人,检索顺序应当是「系列总览 → 按人群或部位筛 → 具体条目的渠道细节」,不要一上来就钻进某一个数据集的字段表。举例:想找胸片检测数据,先看 VinDr 系列总览,再按人群与部位筛出成人胸片,最后落到本文或 vindr-cxr;想找儿童胸片,同样先过系列总览,再筛儿科;想找原始位深,才需要进到渠道层,也就是本文。

这个顺序的意义在于:渠道细节是最末端的问题。如果连人群或部位都不匹配,讨论用哪一版分发渠道毫无意义。

§9 使用指引

9.1 三种角色的路径

角色 目标 建议路径 预估门槛
算法工程师 尽快拿到可训数据 竞赛版直接下载,用公开管线起步 低
临床研究者 评估模型在真实阅片流程中的价值 竞赛版训练,另找院内数据做验证 中
方法学研究者 研究预处理对模型的影响 申请 DICOM 版,自建完整管线 高

第二类角色常犯的错误是只用竞赛版就下临床结论。竞赛版的预处理已经统一过,模型在它上面表现好,不代表在院内未预处理的影像上表现好。至少要用一批院内原始 DICOM 做一次「预处理差异」的敏感性分析。

§4.5 的案例在这一节对应的是第三类角色:那位被审稿意见卡住的作者,如果当初就走 DICOM 版,这条意见根本不会出现。反过来,如果他只是一篇应用型论文,走 DICOM 版多花的两三周完全不值。

9.2 一条最短的可用流程

#-- 端到端最小流程:DICOM -> 归一化 -> 框缩放 -> 打包成训练样本
import os
import cv2
import numpy as np
import pandas as pd
import pydicom

ROOT = "./physionet.org/files/vindr-cxr/1.0.0"
TARGET = 1024

def load_one(image_id, split="Train"):
    ds = pydicom.dcmread(os.path.join(ROOT, split, image_id + ".dicom"))
    a = ds.pixel_array.astype(np.float32)
    if str(ds.PhotometricInterpretation) == "MONOCHROME1":
        a = a.max() - a
    lo, hi = np.percentile(a, [1, 99])
    a = np.clip(a, lo, hi)
    a = (a - a.min()) / max(a.max() - a.min(), 1e-6)
    h, w = a.shape[:2]
    s = TARGET / max(h, w)
    a = cv2.resize(a, (int(round(w * s)), int(round(h * s))), interpolation=cv2.INTER_AREA)
    return a, s

def build(split="Train", limit=200):
    ann = pd.read_csv(os.path.join(ROOT, "Annotations", f"annotations_{split.lower()}.csv"))
    out = []
    for iid, g in ann.groupby("image_id"):
        if len(out) >= limit:
            break
        img, s = load_one(iid, split)
        boxes = g[["x_min", "y_min", "x_max", "y_max"]].to_numpy(dtype=np.float64) * s
        out.append({"image_id": iid, "img": img,
                    "boxes": boxes, "cls": g["class_name"].tolist()})
    return out

if __name__ == "__main__":
    batch = build("Train", 200)
    print(len(batch), batch[0]["img"].shape, len(batch[0]["boxes"]))

这条流程刻意省掉了窗宽窗位的精细处理,只做分位数拉伸。理由是最小流程的目标是「跑通」,不是「最优」。真正的实验里,窗宽窗位的选择应该作为一个消融项单独评估。

9.3 一次完整的头字段普查

#-- 拿到 DICOM 后先跑一遍:哪些头字段存在、哪些缺失,决定研究设计能否成立
import glob
import pydicom
from collections import Counter

KEYS = ["Rows", "Columns", "BitsAllocated", "BitsStored",
        "PhotometricInterpretation", "WindowCenter", "WindowWidth",
        "PixelSpacing", "ViewPosition", "Manufacturer"]

present = Counter()
total = 0
for p in sorted(glob.glob("sample/*.dicom")):
    total += 1
    ds = pydicom.dcmread(p, stop_before_pixels=True)
    for k in KEYS:
        if hasattr(ds, k):
            present[k] += 1

for k in KEYS:
    print(f"{k:28s} {present[k]:4d}/{total}")

这段普查的价值在于:它把「我猜 DICOM 里有设备参数」变成了「到底有哪些、缺多少」的事实。普查结果如果显示 Manufacturer 大面积为空,那么在竞赛版上无法做的设备研究,在 DICOM 版上同样做不了,早点知道能省掉整条申请路径。

9.4 三类失败模式的止损点

失败模式 早期信号 止损动作
认证一直卡在培训证明 上传后多日无状态变化 先回竞赛版做方法验证
下载中断反复 单文件续传多次失败 改用官方推荐的传输方式分段拉取
头字段大面积缺失 普查结果显示关键字段空缺 改研究问题,放弃设备相关分析
框坐标越界 缩放后出现负值或超出边界 先查是否误用了另一版的坐标系
管线跑通但指标异常低 训练损失不降 先查极性是否反色、窗宽窗位是否错用

第四行是跨渠道混用的典型症状。如果缩放的基准尺寸取错了版本,框会整体落在图像之外,表现为大量空样本。

9.5 引用方式

引用时应同时给出论文与渠道版本。论文的作者列表很长,建议以第一作者加 et al. 的形式给出。如果使用 PhysioNet 版本,需要额外标注版本号与 DOI,并遵守数据使用协议中关于成果发表的条款。

对于只用竞赛版的论文,还应在方法一节写明「使用的是竞赛预处理版本」,这一句能省掉后面很多追问。

§10 AI 使用声明

项目 说明
写作方式 由 AI 辅助起草,人工核定结构与表述
事实来源 仅使用官方数据集页、竞赛页、论文摘要与公开 PDF 正文
数字来源 官方页面明示者标为「官方称」,未明示者标「未核实」
未做的事 未下载全量数据,未做字段分布统计,未做标注一致性分析
未使用的内容 未引用第三方博客、论坛帖或二手综述
可能的偏差 论文与官方页面口径不一致时,以官方页面优先
更新方式 官方页面改版后需重新核验附录 A 的数字
责任边界 本文不构成采购或伦理审批建议,实际使用以官方协议为准

常见误读(避坑清单)

说法 实际 证据
竞赛版和 DICOM 版只是格式不同 竞赛版经过面向检测任务的改编,像素层面并不相同 论文称竞赛用的是「稍微修改过的版本」
拿到 DICOM 就能拿到测试集标签 两个版本都不公开测试集标签 竞赛页与数据集页均说明测试集标签不公开
22 类就是竞赛的 14 类加上 8 类 收敛过程中有合并,不是简单删减 竞赛页列出 14 类,数据集页列出 22 类,两者不能逐一对齐
测试集是 5 名医师共同阅片 是 5 名医师标注后协商合并为一份共识 论文描述测试集为共识结果
训练集每张图有一个「正确答案」 每张图有 3 份独立标注,没有唯一答案 论文描述训练集 3 人独立标注
这个数据集有配套放射报告 只有结构化标签,没有报告正文 数据集页只说明标签对应报告的 Findings 与 Impressions 段
竞赛版可以直接商用 受竞赛规则约束,商用需自查条款 竞赛页许可栏标注为受竞赛规则约束
原始版可以自由转发给合作方 数据使用协议约束再分发 PhysioNet 要求每个使用者单独签署
标注框可以直接套到另一个版本上 两版坐标系不同,需缩放对齐 两版像素尺寸与位深不同
这是多中心数据集 只有河内两家医院 数据集页与论文均写明两院

FAQ

Q1:Kaggle 上有两万余个文件,为什么论文说只有 1.8 万例?
文件数与检查数不是同一个口径。一次检查对应一个影像文件,但竞赛页统计的文件数还包含标注 CSV 与其他辅助文件,且页面显示的统计方式可能按目录项计数。1.8 万例是检查数,两万余是文件项数,两者不冲突,但不能互相替代。

Q2:为什么官方页面给出的类别数有时是 22、有时是 28、有时是 14?
22 是局部标签的类别数,来自论文摘要对「22 个局部标签与 6 个全局标签」的表述;把两者相加、再加上 No finding 与若干全局项目的偏移,就得到图像级 28 维向量的宽度;14 是竞赛为了让目标检测评测能跑通而收敛出的类别数。三个数字各自对应一个层级,混在一起说就是口径不清,也是核对表里最容易出错的一行。

Q3:位深到底降到了多少?
官方公开页面没有给我们一个可以直接引用的「从 X 位降到 Y 位」的对照值。论文的 DICOM 字段表解释了 BitsStored 的含义,但没有逐项比较两版的位深分布。要得到这个对照,只能自己下载两版各取一批样本比对。

本文因此只表述「位深在改编中发生变化」,不给出具体数值。

Q4:为什么测试集没有 rad_id?
因为测试集是 5 名医师协商后的单一共识结果,不存在「某一位医师的独立判断」这个实体。

没有逐人记录,自然也就没有 rad_id 列。

Q5:训练集三位医师的框差异大吗?
本文没有做这项统计,因为需要加载全量标注才能得到可靠结论。可以确定的是,官方设计三份独立标注的目的就是保留这种差异,而不是消除它。做实验时应当把「如何合并三份框」当成一个显式超参数。

Q6:测试集的共识标注是怎么来的?
按论文描述,测试集每例由 5 名医师标注,之后协商合并为一份。合并的具体规则——是投票、是讨论到一致、还是由资历更高的医师裁定——公开材料没有逐条说明。这一点会影响「共识比单人更可靠」这类研究的设计,因为它决定了你把共识这个量当成什么。

Q7:教育邮箱能过 PhysioNet 认证吗?
认证的核心是身份与机构的可验证性,邮箱只是其中一项材料。学生身份通常需要额外的导师或机构担保。本文不建议对审核时长做乐观估计。

Q8:可以用它训练分割模型吗?
可以间接用。框标注可以退化成弱监督分割的初始种子,但数据本身没有像素级掩膜。

要训分割,需要另外的掩膜来源或弱监督方法。

Q9:两个版本的标注 CSV 完全一样吗?
训练集的框级 CSV 在结构上一致,字段包括 image_id、rad_id、class_name 与四个坐标。

竞赛版另有 class_id 字段与竞赛专用的类别编号体系,这是额外的。

Q10:能不能只用训练集里的一位医师的框做快速验证?
可以,这是最常见的快速起步方式。代价是标签噪声最大,评估结果不能与三人合并的结果直接比较。

做对比实验时,合并策略必须保持一致。

Q11:训练时要不要把 No finding 的记录也读进来?
要读,但要单独处理。No finding 以记录形式存在于框级 CSV 里,如果直接当成一个类别参与检测训练,会得到一个永远预测「无框」的退化类别。常见做法是在构建 Dataset 时把这些记录过滤掉,让「无框」这件事由「该图没有正样本」自然表达。

Q12:这个数据集和 VinDr-PCXR 的发布流程一样吗?
同属一个机构的系列发布,分发方式相近,但它们是不同的数据、不同的受试人群、不同的标注体系。

本文描述的「双渠道」细节是 VinDr-CXR 特有的,不能直接套到其他成员上。

Q13:如果机构暂时无法完成凭证化认证,有什么替代方案?
可以先用竞赛版完成方法与管线的验证,把「未使用原始 DICOM」写成明确的局限性,等认证完成后再补做敏感性分析。

这是审稿周期内最常见的折中。

Q14:竞赛已经结束,竞赛版还能下载吗?
竞赛页仍可访问,数据页仍提供下载入口。

但竞赛规则对使用的约束不因竞赛结束而自动解除,商用前需自查。

Q15:英文名到底该写 VinBigData 还是 VinDr-CXR?
VinBigData 是机构名,VinDr-CXR 是数据集名。竞赛名里带有机构名,导致两者常被混写。

规范的写法是:数据集叫 VinDr-CXR,竞赛叫 VinBigData Chest X-ray Abnormalities Detection。

术语表

术语 含义
PA 位 后前位,X 线从背部射入、胶片在胸前,是最常见的站立位胸片体位
局部标签 对应报告中「所见」部分的影像表现描述,带边界框
全局标签 对应报告中「印象」部分的诊断推断,图像级
边界框 用四个坐标圈出病灶位置的矩形
rad_id 标注医师编号,用于区分同一张图的不同标注来源
image_id 影像唯一标识,两个版本共用
共识标注 多名医师标注后协商合并为一份的结果
SOP Instance UID DICOM 中影像实例的唯一标识,匿名 ID 由它派生
BitsStored 像素实际使用的位数,反映原始量化精度
PhotometricInterpretation 灰度极性约定,决定是否需要反色
窗宽窗位 显示器映射的灰度范围,决定对比度
PixelSpacing 像素对应的物理尺寸,用于真实尺度换算
去标识化 删除可识别个人身份的信息,保留研究价值
凭证化访问 需身份认证与协议签署才能下载的访问控制
DUA 数据使用协议,规定使用范围与再分发限制
预处理改编 为适配训练管线而对影像做的变换
IoU 两个框的交并比,用于框合并与评测
mAP 平均精度均值,目标检测的常用评测指标
弱监督 用粗粒度标签(如图像级或框级)训练细粒度任务
Findings 段 放射报告里描述影像所见的段落
Impressions 段 放射报告里给出结论与建议的段落
回顾性采集 从既有归档中筛选,而非前瞻性招募

附录 A:核心数字核对表

这张表是全文数字的索引。来源层级只分三档:官方页面、论文摘要、未核实。凡是标「未核实」的,都意味着官方公开材料没有给出可直接引用的值,本文也不做推算。

项目 值 来源 来源层级
公开发布检查数 1.8 万例(概数) vindr.ai 数据集页 官方页面
训练集 1.5 万例(概数) vindr.ai 与竞赛页 官方页面
测试集 3 千例(概数) vindr.ai 与竞赛页 官方页面
原始采集池 十万余例 vindr.ai 数据集页 官方页面
局部标签类别数 22 论文摘要 论文摘要
全局标签类别数 6 论文摘要 论文摘要
竞赛类别数 14 竞赛页 官方页面
标注医师数 17 vindr.ai 数据集页 官方页面
训练集标注密度 3 人独立 论文摘要 论文摘要
测试集标注密度 5 人共识 论文摘要 论文摘要
PhysioNet 版本发布日 2021-06-22 PhysioNet 页面 官方页面
论文预印日期 2020-12-30 arXiv 页 官方页面
竞赛起止 2020-12 至 2021-03 竞赛页 官方页面
标注框总数 无单一官方口径 未在任何官方页面找到 未核实
竞赛版总体积 数百 GB 量级 竞赛页显示总量 官方页面
DICOM 单张体积 十几 MB 量级 未在官方页面明示 未核实

附录 B:数字的来源分层

第一层:官方页面直接给出。 1.8 万例、1.5 万例、3 千例、十万余例、17 名医师、14 类竞赛类别、竞赛版文件数、竞赛起止日期、PhysioNet 版本日期。这些数字在 vindr.ai 数据集页、Kaggle 竞赛页、PhysioNet 页面上可以直接读到原文,本文按原文表述,不做换算。

第二层:论文摘要与正文给出。 22 类局部标签、6 类全局标签、训练集 3 人独立标注、测试集 5 人共识、匿名 ID 由 UID 派生、去标识化流程、伦理审批与知情同意豁免。这些表述来自 arXiv 预印本,标注为论文口径。

第三层:未核实或存在多口径。 标注框总数、位深的具体压缩幅度、DICOM 版总体积、筛选过程中的剔除计数、逐医师的资历与一致性指标。这些数字或者在官方页面没有给出,或者需要下载全量数据才能得到,本文一律标为未核实,不给近似值。

分层的目的不是免责,是让使用者在写论文时知道哪些数字可以直接引、哪些需要自己测。第一层可以原样引用,第二层需标注为论文口径,第三层必须自己在数据上得到。

附录 C:完整参考文献

编号 文献 链接
R1 Nguyen H Q 等,VinDr-CXR: An open dataset of chest X-rays with radiologist’s annotations,arXiv:2012.15029 https://arxiv.org/abs/2012.15029
R2 VinDr 官方数据集页(VinDr-CXR) https://vindr.ai/cxr
R3 VinDr 数据集总览页 https://vindr.ai/datasets
R4 PhysioNet VinDr-CXR v1.0.0 https://physionet.org/content/vindr-cxr/1.0.0/
R5 Kaggle 竞赛页(概览) https://www.kaggle.com/c/vinbigdata-chest-xray-abnormalities-detection/overview
R6 Kaggle 竞赛页(数据) https://www.kaggle.com/c/vinbigdata-chest-xray-abnormalities-detection/data
R7 VinDr Lab 标注平台演示 https://vindr.ai/lab-sub/index

附录 D:任务代码

把「两个版本是否真的不同」变成一次可执行的核对,而不是一句判断。

#-- 抽查若干 image_id,在两个版本里分别读出像素尺寸与位深,输出对照表
import glob
import os
import pydicom

def probe(dicom_root, image_id, split="Train"):
    p = os.path.join(dicom_root, split, image_id + ".dicom")
    if not os.path.exists(p):
        return {"image_id": image_id, "found": False}
    ds = pydicom.dcmread(p, stop_before_pixels=True)
    return {
        "image_id": image_id,
        "found": True,
        "rows": int(ds.Rows),
        "cols": int(ds.Columns),
        "bits_stored": int(getattr(ds, "BitsStored", 0)),
        "photo": str(getattr(ds, "PhotometricInterpretation", "")),
    }

#-- image_id 直接取自落盘的 DICOM 文件名,避免手抄
PHYS = "./physionet.org/files/vindr-cxr/1.0.0"
IDS = [os.path.splitext(os.path.basename(p))[0]
       for p in sorted(glob.glob(os.path.join(PHYS, "Train", "*.dicom")))[:5]]
for root in [PHYS, "./kaggle/vinbigdata"]:
    for i in IDS:
        print(root, probe(root, i))

这段代码的设计前提是:两个版本都能在本地读到同一批 image_id。竞赛版目录结构以实际下载为准,代码里的路径需要按你的落盘位置调整。运行后如果发现同一个 image_id 在两边 rows/cols 不一致,就得到了本文核心论断的一个直接证据。

如果两边完全一致,也不代表没有差异。可能是你抽到的样本恰好没被改编影响,也可能是竞赛版的改编只作用于像素而不改 DICOM 头。这时候要把比对从「头字段」推进到「像素矩阵」:读 pixel_array 的 shape 与最大值,两个版本并排打印。位深被压缩时,最大值的量级会明显不同。

附录 E:如果只记五件事

  1. 这是同一批影像的两种封装,不是两个数据集。
  2. 竞赛版省事,DICOM 版可自证,二者不可互相替代。
  3. 要回答「预处理做了什么」,唯一路径是 PhysioNet 的原始版。
  4. 测试集标签在任何一个版本里都不公开。
  5. 引用时写清用的是哪一版,能省掉后面很多追问。

编辑说明

本文的事实核验集中在 2026 年 10 月 27 日,来源限于 vindr.ai 数据集页与总览页、Kaggle 竞赛概览页与数据页、PhysioNet 数据页,以及 arXiv 预印本 2012.15029 的摘要与正文。未下载任何一份影像,因此本文不含分布统计,也不含需要全量数据才能得到的数字。

竞赛版总量只写「数百 GB 量级」,不复述页面上的精确值:该统计随页面变化,与渠道差异无关。DICOM 版总量需凭证化访问,文件列表读不到,标为未核实。

标注框总数在官方材料里没有统一口径,坊间流传的总数无法确认统计方式,故不采用,改在 §2.4 说明口径。官方若发布明确框数,附录 A 对应行需更新。

本文与库内 vindr-cxr 条目的关系是渠道层与数据集层分工,两篇如出现同一数字的不一致,以官方页面原文为准。


相关数据集导航

以下为站内 AI-Ready 数据集百科中与本词条共享多个主题标签的相关数据集,按相关度降序排列:

  • dentex — 共享标签:医学影像 / X光影像 / 目标检测 / 挑战赛数据集
  • rsna-pneumonia — 共享标签:医学影像 / X光影像 / 目标检测 / 挑战赛数据集
  • vindr-cxr — 共享标签:医学影像 / X光影像 / 目标检测
  • chestx-det10 — 共享标签:医学影像 / X光影像 / 目标检测
  • caries-miccai — 共享标签:医学影像 / X光影像 / 目标检测 / 挑战赛数据集
  • arcade — 共享标签:医学影像 / X光影像 / 目标检测 / 挑战赛数据集
  • node21 — 共享标签:医学影像 / X光影像 / 目标检测 / 挑战赛数据集
  • rsna-series — 共享标签:医学影像 / X光影像 / 挑战赛数据集
  • tufts-dental — 共享标签:医学影像 / X光影像 / 目标检测
  • mura — 共享标签:医学影像 / X光影像 / 目标检测

导航说明:本章节由全站统一标签体系自动计算生成(标签重合度算法),双向可达;点击链接可跳转至对应数据集词条。

返回 AI-Ready 数据集