新闻详情

新闻详情

首页 / 资讯中心 / 详情

3D-CT肺结节检测源码全解析:从数据预处理到两阶段训练实战

发布时间:2026/9/28 15:58:52来源:尧图网络
3D-CT肺结节检测源码全解析:从数据预处理到两阶段训练实战
简介基于Python的3D-CT影像肺结节检测算法完整项目包含源码、数据集与项目说明适合计算机、通信、人工智能、自动化等专业学生与从业者作为毕业设计、课程大作业或进阶学习的参考。项目源于个人毕设答辩评分98分代码经过调试测试可运行复现便于从数据处理、模型构建到检测评估全流程理解。资源共64个文件以38个py源码为主覆盖配置、数据预处理、分类器与检测器训练等模块另有zbak备份文件、csv表格数据、ipynb演示文档、png示意图、npy数据、sh训练脚本及md说明文件整体压缩包约9.61MB目录结构清晰便于按需查阅。已有57人浏览学习该资源通过源码中的演示notebook、项目说明文档以及标注/候选数据使用者可快速掌握肺结节检测的基本流程并在此基础上进行修改扩展实现个性化功能具备较高的学习借鉴价值。1. 3D-CT肺结节检测源码不只是一份代码是一套能跑的完整方案拿到一份3D-CT影像肺结节检测的毕设源码最怕的不是跑不起来而是不知道先点哪个文件。这个项目把肺结节检测拆成两条独立管线detector负责从CT体数据里找可疑结节的位置和大小classifier负责判断候选结节是良性还是恶性数据、预处理、训练、推理四个环节各有独立脚本。配套的candidates.csv、annotations.csv、labels.csv把从候选框生成到最终标签的链路串得很清楚对想用Python做医学影像课题的人来说是一份能直接跑起来当基线的完整数据集和源码组合。它适合三类人要交毕设或课程大作业、缺一个可运行实验的学生想学3D医学影像处理、但不清楚DICOM怎么变成网络输入的从业者以及想改造成自己课题、需要知道从哪里下手不翻车的工程师。下面按我实际跑通的顺序拆解。2. 两阶段架构与数据流detector、classifier和三个CSV之间的分工这套源码我拿到手第一反应是看文件多不多。解压之后顶层是detector、classifier、preprocessing、results四个目录外加一堆散落的.py文件。之所以这么组织是因为它没有走端到端的3D目标检测而是用了医学影像比赛里非常成熟的两阶段路子第一阶段用detector从CT里扫出候选结节的坐标和大小第二阶段用classifier逐个判断候选是良性还是恶性。这个设计的好处很实际——候选生成阶段可以用低阈值换高召回宁可多扫出一些可疑点也不放过真结节分类阶段再慢慢抠精度把假阳性压下去。对毕设场景来说两阶段更好讲故事也更好定位问题检测掉点了就去查detector分类不准了就改classifier互不干扰。2.1 从目录结构判断工作流再决定从哪个文件下手刚打开目录时.zbak后缀的文件容易吓到人比如config_training.py.zbak、utils.py.zbak。这些是保留的备份版本不用管直接用不带后缀的正式文件就行。真正要关注的是下面这张功能表文件职责main.py总入口串起数据加载、训练、推理data_detector.py加载detector训练样本读candidates.csvnet_detector.py检测网络结构定义trainval_detector.py检测分支的训练与验证循环data_classifier.py分类分支数据加载net_classifier.py分类网络结构定义trainval_classifier.py分类分支训练与验证split_combine.py3D体数据切块与推理结果拼接layers.py自定义网络层残差块、激活等config_training.py训练参数集中配置这里有个细节值得注意res18.py和res_pool.py放在detector目录下说明backbone用的是3D ResNet18池化层被单独拆出来封装。如果你做过2D的YOLO系列会发现3D医学影像不是简单的通道数加一显存占用和计算量都是另一个量级所以网络层拆分越细越方便在训练时逐层排查问题。2.2 三个CSV把数据流串起来先看数据再碰模型源码根目录下有candidates.csv、annotations.csv、labels.csv三个表格。这三个文件决定了整个训练样本怎么组织我用pandas先看一遍再动手import pandas as pd # 候选结节detector训练时的样本来源含坐标和直径 candidates pd.read_csv(candidates.csv) print(candidates:, candidates.shape) print(candidates.head()) # 医生标注金标准判断正负样本的依据 annotations pd.read_csv(annotations.csv) print(annotations:, annotations.shape) print(annotations.head()) # 最终标签候选与标注配准后的结果 labels pd.read_csv(labels.csv) print(labels:, labels.shape) print(labels.head())这段代码的逻辑很简单先看每个表的结构和行数确认坐标字段名是coordX、coordY、coordZ这类物理坐标直径字段通常是diameter_mm。注意单位——CSV里给的一般是毫米而网络输入是体素索引中间差着一个spacing换算这一步后面预处理章节会细说。annotations.csv里全是医生确认过的真结节candidates.csv里大部分是detector扫出来的假阳性候选labels.csv则是把两者在空间上做匹配之后生成的训练标签。看懂这个链路你才知道为什么训练要分两段跑。2.3 split_combine.py为什么3D网络不能整卷输入CT一个case常见尺寸是512×512×300甚至更大3D卷积网络直接整卷输入显存立刻爆掉。常见做法是训练时切成小patch喂进去推理时再用滑动窗口把整个CT扫一遍最后把概率图拼回原始尺寸。split_combine.py干的就是这件事。# split_combine.py 中常见的切块参数示意具体以项目内配置为准 patch_size [96, 96, 96] # 每块体素数越大越吃显存 stride [48, 48, 48] # 滑窗步长通常取patch一半保留重叠 margin [16, 16, 16] # 边缘余量用于消除拼接缝导致的伪影参数说明patch_size直接决定单次前向的内存占用96的三次方在12G显存上配合batch size 4是安全值stride设为patch一半相邻patch有50%重叠推理时重叠区域取平均能明显减少边界处的预测抖动margin是给patch四周补的上下文CNN感受野有限切在结节边缘时容易误判留一圈context能提升边界样本的鲁棒性。训练阶段和推理阶段的切块策略通常不一样训练时随机采样、推理时滑窗遍历这个区别在trainval_detector.py和test_detect.py里分别实现。3. 数据预处理把DICOM变成网络认识的数值这一步决定训练能不能收敛很多人在模型结构上花大量时间结果loss不降问题往往出在输入数据上。CT影像和自然图像不一样它存的是组织对X射线的衰减系数单位是HUHounsfield Unit范围从-1000多到3000多。直接把原始值送进网络卷积核学到的全是噪声。这个项目的preprocessing目录和dicom2raw.py、full_prep.py、step1.py就是来解决这个问题的。3.1 dicom2raw.pyHU窗口化与体素重采样dicom2raw.py名字很直白就是把DICOM格式的CT序列转成numpy数组。CT序列一个case可能包含几百张切片读取时用SimpleITK一次性读入最省事# dicom2raw.py 核心逻辑示意 import SimpleITK as sitk import numpy as np # 读入DICOM序列返回itk图像对象 itk_img sitk.ReadImage(case_0001/) # 物理体素间距单位毫米三个方向可能不一致 spacing itk_img.GetSpacing() print(spacing:, spacing) # 转为numpy数组shape为 (Z, H, W) ct_array sitk.GetArrayFromImage(itk_img) # HU值截断到肺窗范围再归一化到[-1, 1]这是呼吸科CT的常规窗位 ct_clip np.clip(ct_array, -1200, 600) ct_norm (ct_clip - (-1200)) / (600 - (-1200)) * 2 - 1逻辑说明sitk.ReadImage既可以读单张DICOM也可以读整个序列文件夹GetSpacing()拿到的是三个方向的物理间距必须保存下来后面坐标换算全依赖它。窗口化这步是关键——肺结节的灰度范围落在-1200到600HU之间把范围之外的值截断相当于把无关的骨骼和金属伪影直接丢掉网络只需要学肺组织内部的结构。归一化到[-1,1]是为了匹配常见预训练权重的输入分布如果你用的是自带Normalization的网络这步可以相应调整但截断窗口几乎不能省。3.2 full_prep.py与step1.py把标注坐标对齐到体素空间医生在标注软件里看到的坐标通常是物理坐标单位是毫米而网络操作的是体素索引。full_prep.py和step1.py里处理的就是这个换算。拿前面读到的spacing举个实例# 坐标换算示意物理坐标(mm) - 体素索引 phys_x, phys_y, phys_z 12.5, 30.2, 85.0 # 医生标注的结节中心 sx, sy, sz spacing # 例如 (0.7, 0.7, 1.0) voxel_x round(phys_x / sx) voxel_y round(phys_y / sy) voxel_z round(phys_z / sz) print(voxel:, voxel_x, voxel_y, voxel_z)逻辑说明物理坐标除以对应方向的spacing得到该点在体素网格中的位置。这里有三个坑要注意。第一方向顺序SimpleITK读出的数组shape是(Z, H, W)而物理坐标通常是(x, y, z)映射的时候要搞清楚哪个轴对应哪一维。第二用round还是floor我一般用四舍五入因为结节中心落在两个体素之间的概率不小向下取整会带来半个体素的系统性偏移。第三重采样之后spacing会变如果full_prep.py里把数据resample到了各向同性分辨率那么标注坐标也必须用新的spacing重新换算先resample后对齐坐标顺序不能反。3.3 valsplit.npy与数据缓存保证实验可复现的小细节根目录下的valsplit.npy是预先划分好的训练/验证索引。很多人在跑深度学习项目时忽略固定划分这件事每次shuffle结果都不一样调参时根本分不清是模型变好了还是数据划分变好了。这个项目把划分固定成文件是一个很值得保留的习惯import numpy as np # 加载预划分索引 split np.load(valsplit.npy, allow_pickleTrue) train_idx, val_idx split[0], split[1] print(train cases:, len(train_idx)) print(val cases:, len(val_idx))逻辑说明valsplit.npy保存的是case级别的索引数组训练和验证各自用一部分病例互不重叠。这样做的好处是你改任何参数之后训练集和验证集完全一致指标差异可以归因到模型改动上。预处理环节如果重跑注意不要覆盖这个文件否则之前所有实验的对比基准就全废了。4. 训练与推理config_training.py里的关键参数和完整跑法预处理做完数据就变成了可以直接喂网络的patch和标签。接下来要面对的是config_training.py——整个项目最值得逐行读的文件。它把所有训练相关的超参数集中在一起改模型配置、调学习率、换batch大小都不用去翻训练脚本。4.1 config_training.py决定训练结果的核心参数这个文件里值得优先关注的参数就几个我整理成一张表参数建议起点说明patch_size[96, 96, 96]越小越省显存小目标检测建议不低于64batch_size43D网络显存大户12G显存以下别硬上8base_lr1e-3Adam/AdamW家族常用SGD要下调到1e-2量级num_epochs100配合早停看验证集loss决定是否提前停det_thresh0.5detector输出的置信度阈值调低提高召回cls_thresh0.5classifier阈值调高降低假阳性参数说明patch_size和batch_size是显存预算的两个旋钮两者成反比关系显存不够优先降patch因为patch太小会损失空间上下文对肺结节这种小目标不利base_lr取决于优化器类型Adam家族用1e-3是安全起点SGD配合动量通常要更低det_thresh控制的是第一阶段的灵敏度不要一上来就设高第一遍跑通先用0.3左右看召回再慢慢往上调。4.2 run_training.sh与adapt_ckpt.py启动训练与预训练权重适配项目提供了run_training.sh脚本一键跑完整训练流程。常见的启动方式是# 一键训练先detector后classifier bash run_training.sh # 如果想单步调试可以分开执行 python trainval_detector.py --config config_training.py python trainval_classifier.py --config config_training.py逻辑说明run_training.sh一般会依次执行detector训练和classifier训练并把日志和权重写到results目录。分开跑的好处是detector没训练好之前classifier训练没有意义——输入的可疑区域质量直接决定分类性能。另一个文件adapt_ckpt.py是用来适配预训练权重的因为项目里有net_detector_3.py、net_classifier_3.py、net_classifier_4.py这些网络变体结构改过之后直接加载旧权重会报key不匹配# 把旧预训练权重转成当前结构可加载的格式 python adapt_ckpt.py --src pretrained_res18.pth --dst adapted.pth逻辑说明这个转换脚本做的事本质上是按名字匹配网络层的state_dict缺的层随机初始化多的层丢掉。医学影像数据量通常不够从零训一个深层3D网络预训练权重能明显加速收敛。如果加载时遇到missing keys或unexpected keys的报错先跑一遍adapt_ckpt.py再看不要硬着头皮忽略。4.3 推理test_detect.py输出prediction.csv训练完之后的推理脚本集中在detector和classifier目录下。test_detect.py负责跑detector生成候选test_classifier.py负责对候选做最终判定最终结果写到prediction.csv# 推理结果示意prediction.csv 的结构 import pandas as pd pred pd.read_csv(prediction.csv) print(pred.head()) # 通常包含病例ID、结节中心物理坐标、直径、恶性概率 # 按恶性概率降序排列方便后续做FROC评估逻辑说明prediction.csv是按case组织的预测结果每一行是一个检测到的结节字段包括坐标、尺寸和分类概率。提交或评估之前注意坐标单位必须和标注文件一致——如果labels.csv用的是毫米预测输出也必须是毫米否则后面算指标全对不上。demo.ipynb可以直接加载这份预测结果做可视化检查这也是第6章要展开的验证手段。5. 复现避坑指南我在跑这套源码时踩过的五个坑毕设项目最常见的问题不是模型不行而是环境、数据、权重三者之间各种隐性问题。这套源码我在复现和调改过程中踩过几个实实在在的坑按「现象 → 原因 → 解决」写出来希望你不用再绕一圈。坑一训练一开始就OOMbatch size降到1还是爆显存。现象跑trainval_detector.py不到一个step就报CUDA out of memory。原因patch_size没跟着显存调96³的patch在6G显存上本身就临界。解决先把patch_size降到[64, 64, 64]batch降到2跑通一个epoch确认能跑之后再逐步放大。另外检查是否同时加载了多个网络在一个进程里main.py如果同时初始化detector和classifier显存是叠加的。坑二loss一直在高位震荡不收敛。现象训练几十个epochloss曲线完全没有下降趋势。原因输入没有做HU窗口化原始值范围太大网络很难学到稳定特征。解决回到dicom2raw.py确认输出数值范围在[-1,1]之间并打印几个patch的min/max做校验。这是所有坑里最隐蔽的一个因为代码不报错只是悄悄不收敛。坑三检测结果出现大量假阳性一张CT扫出几百个结节。现象测试集上detector输出极多候选明显不符合常理。原因detector置信度阈值设得太低加上候选生成阶段本身就要高召回两个因素叠加。解决先看candidates.csv里的候选数量再用test_classifier.py过一遍分类器大部分假阳性的分类概率会非常低把cls_thresh从0.5提到0.7通常能砍掉一半以上假阳性。坑四预测坐标和标注对不上可视化时检测框整体偏移。现象在demo.ipynb里把预测结果叠加到CT切片上框的位置和实际结节明显错位。原因物理坐标转体素坐标时用错了分辨率或者DICOM重采样之后没有同步更新spacing。解决检查预处理阶段保存的spacing值和dicom2raw.py读取的一致坐标换算用四舍五入而不是截断并且确认Z轴方向没有翻转。CT的Z轴是从头到脚还是从脚到头不同数据源可能不一样这个需要可视化第一张切片来验证。坑五加载预训练权重时疯狂报错missing keys一长串。现象用adapt_ckpt.py转换后仍然有一堆层对不上。原因网络结构选择了带数字后缀的版本和预训练权重对应的结构不是同一个。解决先确认当前用的是net_detector.py还是net_detector_3.py找到预训练权重对应的版本必要时把adapt_ckpt.py里的层名映射表手动加几条override。这类问题没有捷径只能逐层比对state_dict的key。6. 验证与进阶用demo.ipynb可视化结果再做两点改进训练完不是终点能把你自己的case跑出可视化结果才算真正拿到了这个项目。demo.ipynb提供了现成的可视化链路加载一个CT case读入prediction.csv把检测框叠加到横断面切片上。常见的做法是在轴状位切片上画出检测框和概率值中间切片最能反映结节形态import matplotlib.pyplot as plt import numpy as np # 取第80张横断面切片叠加预测概率高于阈值的检测框 slice_idx 80 ct_slice ct_array[slice_idx, :, :] plt.figure(figsize(8, 8)) plt.imshow(ct_slice, cmapgray) plt.axis(off) plt.show()这段代码不复杂但验证价值很高。我会在每一类case里挑几个切片确认检测框中心和结节视觉中心一致框的大小与结节直径匹配。验证通过之后再谈进阶。我认为这个项目最有价值的改进点是两个方向。第一给detector加FROC评估医学影像检测的核心指标不是mAP而是FROC曲线——横轴是每case平均假阳性个数纵轴是敏感度。把prediction.csv按概率排序逐步降低阈值就能画出一条FROC这也是答辩评审最认可的评估方式。第二替换backboneres18.py是3D ResNet18你可以把res_pool.py里的池化策略改成自适应池化或者把分类分支换成注意力机制改动范围被网络定义文件隔离得比较干净不至于牵一发动全身。这套项目我从预处理一路跑到可视化最大的体感是两阶段的架构让每一步都能单独验证检测不对就查候选分类不对就查阈值不会像端到端模型那样一出问题就是黑匣子。从那以后我每次拿到新的医学影像数据集都强制自己先做一轮HU分布统计和坐标原点可视化确认数据没毛病再碰模型结构。这个习惯是从这个项目里带出来的希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

应届生产品经理面试全攻略:从 0 到 1 准备指南与 TaoToken 配置助力 2026/9/28 19:44:01

应届生产品经理面试全攻略:从 0 到 1 准备指南与 TaoToken 配置助力

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Windows下编译IEC 61850依赖库:TaoToken统一Key接入VS工程配置骨架 2026/9/28 19:44:01

Windows下编译IEC 61850依赖库:TaoToken统一Key接入VS工程配置骨架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
每天介绍一家新质生产力公司37 2026/9/28 19:44:01

每天介绍一家新质生产力公司37

https://mp.weixin.qq.com/s/Xe3JtKppkLaIOAHDZOP3mw

阅读更多 →
从0开始:小白也能学会的 Cline 配置 fetch MCP 服务器模块完整教程 2026/9/28 19:44:01

从0开始:小白也能学会的 Cline 配置 fetch MCP 服务器模块完整教程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
YOLO人体行为检测数据集实战:从标签格式校验到模型训练排错 2026/9/28 19:44:01

YOLO人体行为检测数据集实战:从标签格式校验到模型训练排错

简介:面向目标检测初学者、YOLO实践者及需要人体行为数据的计算机视觉课程设计,这份数据集提供了1000张真实场景下的人体行为图片,标注覆盖VOC、COCO、YOLO三种格式(分别对应xml、json、txt),可直接用于YOL…

阅读更多 →
Windows 安装 Codex 并接入自己的 API:CC Switch 配置与 settings.json 骨架详解 2026/9/28 19:43:55

Windows 安装 Codex 并接入自己的 API:CC Switch 配置与 settings.json 骨架详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉