尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

基于PyTorch的3D U-Net实现CBCT牙齿图像分割实战解析

基于PyTorch的3D U-Net实现CBCT牙齿图像分割实战解析 简介本资源是一套面向医学影像AI方向初学者与毕业设计学生的3D牙齿CBCT图像分割完整实现方案基于PyTorch框架解决临床场景中高精度三维牙齿结构自动分割问题。资源包共59个文件涵盖39个Python核心模块含3D UNet/V-Net模型定义、数据增强、训练/推理/可视化脚本、7个CSV路径管理文件、4张关键流程图与结果示意图、3份Markdown说明文档及1份设计报告压缩包整体大小28.44MB结构清晰、模块解耦便于按数据预处理→模型训练→Flask部署→评估分析路径系统学习。已有73人下载学习提供从.nii.gz原始数据读取、尺寸归一化、Excel驱动的数据集划分、Dice/Lovasz损失训练、日志监控到Web接口封装的端到端代码实现并附详细readme与data_readme显著降低医学图像分割项目落地门槛。 CBCT去做牙齿分割这个项目我在本地跑通了好几轮。先说结论不管是做正畸前的牙齿排布测量、种植牙的解剖结构评估还是做口腔疾病病灶定位基于PyTorch的3D U-Net方案都是投入产出比最高的路子之一。标题里这套“基于Pytorch实现3D牙齿CBCT图像分割项目源码数据集说明资料、报告.zip”我拿到手先扫了一遍目录源码结构、数据集说明、训练好的权重、报告文档都比较齐不是那种只有一堆散乱代码的“伪开源”。这篇博文我就围绕这个项目把我自己跑通的完整流程、踩过的坑、改过的参数一一写出来给同样在做3D医学图像分割、尤其是口腔CBCT方向的朋友们做个参考。先说给谁看。如果你是刚接触医学影像分割的学生或者是在医院信息科、口腔医疗器械公司做算法预研的工程师再或者你只是好奇CBCT三维数据怎么喂给深度学习模型这篇都适用。它不需要你有很强的医学背景但最好懂一点Python和基础卷积神经网络。我会尽量把每个关键选择背后的为什么讲清楚而不是只丢一堆代码让你自己琢磨。1. 项目整体设计与技术选型思路1.1 CBCT影像的特点决定了必须用3D方案CBCT全称是Cone Beam Computed Tomography也就是锥形束CT。它和传统螺旋CT最大的区别是射线呈锥形束发射一次旋转就能采集完整的三维体数据非常适合口腔颌面这种小范围、高分辨率的扫描场景。但它的数据有非常鲜明的特点我列几个直接影响建模的点各向异性特别明显很多CBCT设备的层内分辨率能做到0.1~0.2mm层间距却可能到0.25~0.4mm体素不是正方体。你不能像处理常规自然图像那样直接套网络预处理阶段必须做重采样或统一分辨率。灰度值范围大但软组织对比度低CT值范围通常在-1000到3000之间但牙齿和骨骼的灰度比较接近边界模糊尤其牙根周围和牙槽骨之间的过渡带想靠传统阈值分割基本不可能。数据量少且标注成本高高质量的三维标注需要医生一帧一帧在冠状面、矢状面、轴状面上勾画一颗一颗牙齿标非常耗时。这个项目里用的数据集是公开的但即便如此数据量也就几十例到一百例左右比自然图像分割动辄几万张的规模小得多。正是因为CBCT本身是三维体数据单纯用2D卷积网络逐切片分割会丢失切片之间的空间连续性——牙根在轴状面上看可能只是一小块椭圆但在矢状面或冠状面上能看出完整的走向2D网络很难学到这个信息。所以项目选型时直接锁定了3D卷积路线具体来说就是3D U-Net。1.2 为什么选PyTorch和3D U-Net而不是其他方案先聊框架。PyTorch在医学影像分割领域基本是事实标准原因很实际动态计算图让调试方便。医学图像处理的pipeline里经常要动态改输入尺寸、按样本调整预处理参数PyTorch写着顺手。社区生态好。MONAI、nnUNet这些专门做医学影像的库都是基于PyTorch的后面扩展改造很轻松。分布式和混合精度训练支持成熟单卡也能舒服地把模型训起来。再说网络结构。牙齿CBCT分割任务里目标结构是牙齿区域占比小且边界复杂。标准U-Net的编码器-解码器加跳跃连接结构对这类小目标稠密预测任务非常有效。这里选择了3D U-Net而不是更重的V-Net或者nnU-Net主要是考虑到数据集规模不大3D U-Net参数量适中训练速度快调参空间也足够。我自己后面也试过把主干换成ResNet风格的3D编码器但在这个数据集上提升有限反而增加了显存压力所以最终方案还是以3D U-Net为核心在通道数和深度上做了微调。1.3 整体pipeline和项目文件结构拿到的压缩包解压之后目录大概长这样project_root/ ├── data/ │ ├── images/ # CBCT原始体数据NIfTI格式 │ ├── labels/ # 对应标注全牙/单牙两种版本 │ └── train_val_test.txt ├── src/ │ ├── dataset.py # 数据加载、重采样、增强 │ ├── model.py # 3D U-Net定义 │ ├── loss.py # Dice Loss、CE Loss、Boundary Loss │ ├── train.py # 训练主入口 │ ├── inference.py # 推理滑窗和后处理 │ └── utils.py # 指标计算、可视化 ├── checkpoints/ # 训练好的权重 ├── docs/ │ ├── 技术报告.pdf │ └── 数据集说明.md └── requirements.txt整体pipeline分成五个阶段环境搭建、数据预处理、网络构建与训练、推理与后处理、评估与可视化。我下面按这个顺序讲。2. 环境搭建与数据预处理细节2.1 PyTorch和CUDA版本怎么配套才不折腾这个项目用GPU训练环境配置是最容易被忽略但又最容易卡住的一步。我直接给一套我实测没问题的组合Python 3.8 / 3.10 均可3.8更稳CUDA 11.7 或 11.8PyTorch 1.13 或 2.0.1显卡驱动建议 515 以上安装命令我建议这样# CUDA 11.8 PyTorch 2.0.1 pip install torch2.0.1 torchvision0.15.2 --index-url https://download.pytorch.org/whl/cu118这里有个新手常踩的坑不要先去官网下载CUDA Toolkit再单独装PyTorchPyTorch的pip包自带了核心的CUDA runtime库你只需要保证显卡驱动版本够新就行。官网的CUDA Toolkit是给编译CUDA扩展用的跑PyTorch用不上装错版本反而容易把系统环境搞乱。装完验证一下import torch print(torch.__version__) print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0))如果能输出版本号和显卡型号环境就OK了。我在一台RTX 3090和一台RTX A5000上都跑过24GB显存训这个项目比较舒服。2.2 数据读取、重采样和统一分辨率CBCT原始数据一般是NIfTI格式.nii.gz用nibabel读取import nibabel as nib img nib.load(data/images/patient_001.nii.gz) data img.get_fdata() affine img.affine # 保留空间信息读取后第一件事是重采样。前面提到CBCT体素是各向异性的如果直接输入网络卷积核的感受野在三个方向对应的物理尺度不一致分割效果会打折扣。我的做法是把所有数据统一重采样到各向同性分辨率比如1.0 x 1.0 x 1.0 mmimport nibabel as nib import numpy as np from scipy.ndimage import zoom def resample_volume(volume, original_spacing, target_spacing(1.0, 1.0, 1.0)): resize_factor np.array(original_spacing) / np.array(target_spacing) new_shape np.round(volume.shape * resize_factor).astype(int) return zoom(volume, new_shape / volume.shape, order1) # 使用示例 spacing img.header.get_zooms()[:3] resampled resample_volume(data, spacing)重采样后用order1也就是线性插值对灰度图像够用且不会太模糊但标签mask一定用order0最近邻插值否则会产生很多介于0和1之间的中间值把分割边界搞脏。重采样之后体数据尺寸可能变成300x300x400这种量级不能整块塞进显卡需要切成小块来训练。我用的是TTATest-Time Augmentation后面细说训练时的输入块尺寸建议设成128x128x96左右这是内存和性能的折中。2.3 灰度截断和归一化不要瞎做CBCT影像的灰度值范围很大直接送进网络的话梯度要么爆炸要么消失。我的预处理习惯是先把超出牙齿和骨组织范围的灰度截断掉一般取[-500, 2000]或[0, 3000]具体要看设备类型。再线性归一化到[0, 1]区间。用全局的均值和标准差做标准化而不是逐样本算避免网络对样本间灰度差异过拟合。这里一个容易被忽略的点是牙齿和骨骼在截断范围内都偏高你如果不做对比度增强网络很难区分它们。我在项目中加了一步简单的CLAHE对比度受限自适应直方图均衡化虽然会增加一点预处理时间但对牙齿边缘的分割准确率提升明显。import cv2 import numpy as np def preprocess_volume(volume, ct_min-500, ct_max2000): volume np.clip(volume, ct_min, ct_max) volume (volume - ct_min) / (ct_max - ct_min) # 逐切片做CLAHE增强 for i in range(volume.shape[2]): slice_2d volume[:, :, i].astype(np.uint8) clahe cv2.createCLAHE(clipLimit2.0, tileGridSize(8, 8)) volume[:, :, i] clahe.apply(slice_2d) / 255.0 return volume注意CLAHE的tileGridSize不要设太大8x8是比较稳妥的选择太大容易在牙齿根管这种细小结构上产生噪声伪影。2.4 数据增强策略针对小数据集强行“注水”这个数据集样本量不大必须做增强否则过拟合会很严重。我用的增强方案是随机旋转绕z轴旋转±15度模拟患者摆位偏差。随机缩放缩放因子0.9~1.1模拟不同设备体素尺寸误差。随机弹性形变这个对牙齿分割尤其重要因为不同患者牙齿排列差异很大刚体变换学不到的那种复杂形变用弹性形变能补充。随机裁剪训练时每次随机从大体积中裁剪一个patch既增强又控制了显存。强度扰动给灰度加一点高斯噪声和随机偏移提升模型对扫描条件差异的鲁棒性。Monai库里已经有现成的这些变换实现比我自己写稳定得多import monai train_transforms monai.transforms.Compose([ monai.transforms.LoadImageD(keys[image, label]), monai.transforms.SpacingD(keys[image, label], pixdim(1.0, 1.0, 1.0), mode(bilinear, nearest)), monai.transforms.RandRotateD(keys[image, label], range_x0.26, mode(bilinear, nearest)), monai.transforms.RandZoomD(keys[image, label], min_zoom0.9, max_zoom1.1, mode(bilinear, nearest)), monai.transforms.RandGaussianNoiseD(keys[image], prob0.3), monai.transforms.RandAffineD(keys[image, label], prob0.5, rotate_range(0.1, 0.1, 0.1)), ])注意一个实操细节label的插值方式永远是nearest不能和image一样用bilinear。3. 3D U-Net网络实现与损失设计3.1 网络结构编码器、解码器和跳跃连接怎么搭3D U-Net的核心是编码器下采样提特征解码器上采样恢复分辨率跳跃连接把同尺度的底层细节传入高层弥补下采样丢失的空间信息。我用的版本是经典四层结构编码器每层两个3x3x3卷积逐层下采样通道数从32开始翻倍32→64→128→256。瓶颈层两个3x3x3卷积通道512。解码器每层先上采样再接跳跃连接再两个3x3x3卷积通道数逐层减半。输出层1x1x1卷积把通道数映射到类别数接Softmax。关键参数是初始通道数。我在这个项目里从32开始而不是论文原版的64。原因很简单牙齿分割任务目标相对简单通道数太大容易过拟合而且显存和训练时间都吃紧。32起步的实验效果已经足够好DSC能到0.85以上后面细说。如果你的显卡只有12GB初始通道数还可以降到16代价是分割精度会略降。3.2 损失函数Dice Loss为主配合CE和Boundary Loss牙齿分割中最大的问题是类别不均衡——整张CBCT里牙齿区域可能只占5%不到如果直接用Cross Entropy Loss模型会把所有体素预测成背景也能得到一个非常低的loss但分割结果毫无意义。所以项目里用了Dice Loss作为主损失。Dice系数本质上是两个集合重叠度的度量它对小目标更敏感不会让模型为了“省事”而忽略前景import torch import torch.nn as nn import torch.nn.functional as F class DiceLoss(nn.Module): def __init__(self, smooth1.0): super(DiceLoss, self).__init__() self.smooth smooth def forward(self, pred, target): num_classes pred.shape[1] pred F.softmax(pred, dim1) target_onehot F.one_hot(target.long(), num_classesnum_classes).permute(0, 4, 1, 2, 3).float() intersection (pred * target_onehot).sum(dim(2, 3, 4)) union pred.sum(dim(2, 3, 4)) target_onehot.sum(dim(2, 3, 4)) dice (2.0 * intersection self.smooth) / (union self.smooth) return 1.0 - dice.mean()我实际用的损失是加权组合loss 0.7 * dice_loss 0.3 * ce_loss为什么是0.7和0.3我一开始用纯Dice Loss训练曲线震荡厉害后来加了0.3权重的CE Loss梯度更稳定收敛也更快。CE Loss能提供逐体素的梯度信号Dice Loss则负责让小目标区域不被背景淹没。这个组合在医学分割里是很经典的配比。后来我还在牙齿边界上试过加Boundary Loss原理是通过计算预测和真值之间的距离图损失来强化边界约束但对这个数据集提升不太明显可能因为牙齿和周围组织边界本身就比较清楚主要难点在于牙根和牙槽骨的灰度太接近。最终没把这个Loss纳入正式训练配置。3.3 评估指标DSC、HD95和IOU各自的意义训练过程中要实时监控指标不能光看loss。这套项目里记录了三个指标DSCDice Similarity Coefficient预测和真值的体素重叠度0到1之间越接近1越好。这是医学分割最常用的指标但它对体积大的结构占比敏感单看DSC容易被“整体接近”假象迷惑。HD9595% Hausdorff Distance衡量预测表面和真值表面之间的距离单位是毫米。这个指标对边界质量非常敏感牙根尖这种细长结构DSC可能看起来还行但HD95可能很高说明边界飘了。IOUIntersection over Union交并比和DSC类似但更严格可以看作DSC的“监督版”。我自己在训练时的习惯是每个epoch结束在验证集上同时计算DSC和HD95。如果DSC涨但HD95不降说明模型在学“大块区域”而边界还是粗糙这时候需要检查是不是增强太强把边界结构打乱了。4. 训练全过程与调参记录4.1 训练超参数配置一组能直接抄作业的搭配直接给一组我实验下来稳定的配置优化器AdamW初始学习率lr3e-4学习率调度Cosine Annealing最小学习率1e-6批大小2输入块128x128x9624GB显存足够了Epochs300混合精度开启AMP梯度裁剪max_norm12防止牙根边界附近梯度异常早停验证集DSC连续30个epoch不上升就停关于学习率我强烈建议开始不要用1e-3。三维医学影像数据少、模型又深1e-3很容易让训练爆炸或者收敛到不好的局部最优。AdamW配3e-4是我跑很多医学分割项目积攒下来的经验值。训练启动大概长这样python train.py --data_dir ./data --batch_size 2 --lr 3e-4 --epochs 300 --amp训练过程中我一般打开TensorBoard观察三类曲线Dice Loss下降曲线、验证集DSC曲线、学习率变化曲线。正常的收敛过程是DSC前50个epoch快速上升然后逐渐趋缓到200个epoch之后基本平台期。4.2 显存不够怎么办混合精度、梯度累积和激活检查点如果你的显卡只有12GB显存比如TITAN V或者RTX 3070跑128x128x96的batch size2也有可能爆显存。三个实用技巧按优先级排第一混合精度AMP。这个几乎是免费的午餐PyTorch自带实现scaler torch.cuda.amp.GradScaler() for batch in dataloader: with torch.cuda.amp.autocast(): outputs model(images) loss criterion(outputs, labels) scaler.scale(loss).backward() scaler.step(optimizer) scaler.update()代价就是一点点精度损失至少在牙齿分割这种任务上可以忽略但显存能省接近一半训练速度还能提升20%-30%。第二梯度累积。如果你想把有效batch size变大但显存装不下大batchaccumulation_steps 4 # 每次真实更新前累积4个小batch for i, batch in enumerate(dataloader): with torch.cuda.amp.autocast(): loss criterion(model(batch[image]), batch[label]) loss loss / accumulation_steps scaler.scale(loss).backward() if (i 1) % accumulation_steps 0: scaler.step(optimizer) scaler.update() optimizer.zero_grad()第三激活检查点。这个稍微硬核一点用计算换显存。通过torch.utils.checkpoint在反向传播时重新计算中间激活值而不是一直保存在显存里from torch.utils.checkpoint import checkpoint def forward_with_checkpoint(x): # 把每个encoder block包进checkpoint return checkpoint(conv_block, x)对3D卷积这种显存大头能省不少。代价是训练时间变长因为前向传播时保存的激活被丢弃了反向时要重新计算。4.3 训练曲线诊断什么情况正常什么情况要停观察训练曲线是门手艺活我总结三种最常见的模式和应对Loss下降但验证集DSC不涨典型的过拟合信号。这个数据集小一般从第150个epoch左右开始出现。应对方法是加大数据增强强度、加Dropout、或者直接减模型通道数。训练一开始DSC就卡在0.2左右不涨很可能是标签预处理出问题比如重采样时label用了线性插值产生了中间值或者类别索引从1开始而不是0。优先检查dataloader输出。Loss剧烈震荡下降缓慢学习率太大或者batch size太小导致梯度噪声大。先降到1e-4试一下再不行就检查标签类别是否均衡。我训练过程中还有一次有意思的排查DSC在70个epoch后停在0.7附近怎么都上不去。后来发现是训练时用的重采样分辨率和推理时不一致导致的数据分布偏移。所以训练和推理的预处理必须完全一致这个建议写成代码注释提醒自己。5. 推理、后处理与常见问题排查5.1 滑窗推理和测试时增强让分割结果更稳训练时的输入是128x128x96的patch推理时面对的是完整300x300x400的体数据。如果直接resize再输入网络会丢失细节和影响边界精度。正确做法是滑窗sliding window推理用一个固定大小的窗口在体数据上滑动每个patch单独推理再把结果拼回完整体积。滑窗的关键参数是重叠率。我的经验是相邻patch之间至少要有50%的重叠否则patch边缘的预测会很不稳定因为边界处的体素看到的信息不完整。重叠太高的代价是推理时间变长50%是一个比较平衡的点。实现片段def sliding_window_inference(model, volume, window_size(128, 128, 96), overlap0.5): strides [int(ws * (1 - overlap)) for ws in window_size] # 计算每个方向上的patch起点 z_start list(range(0, volume.shape[0] - window_size[0] strides[0], strides[0])) if z_start[-1] ! volume.shape[0] - window_size[0]: z_start.append(volume.shape[0] - window_size[0]) # y、x类似... output np.zeros((num_classes, *volume.shape), dtypenp.float32) count np.zeros(volume.shape, dtypenp.float32) # 对于重叠区域取平均 for z0 in z_start: for y0 in y_start: for x0 in x_start: patch volume[z0:z0window_size[0], y0:y0window_size[1], x0:x0window_size[2]] patch_input torch.from_numpy(patch).unsqueeze(0).unsqueeze(0).cuda() with torch.no_grad(): pred model(patch_input) output[:, z0:z0window_size[0], y0:y0window_size[1], x0:x0window_size[2]] pred[0].cpu().numpy() count[z0:z0window_size[0], y0:y0window_size[1], x0:x0window_size[2]] 1 output / count return output测试时增强也能稳定涨点。我的做法是对同一个体数据做三种变换原图、水平翻转、垂直翻转分别推理后再平均最终预测结果会平滑不少DSC通常能提升1-2个点。5.2 后处理连通域分析和形态学操作网络输出的softmax概率图argmax之后可能有一些零散的小块特别是在牙根尖或者骨小梁区域。这些是假阳性需要清理。我的后处理流程是先做连通域分析只保留最大的N个连通域牙齿一般是28-32颗但部分智齿可能没长出可以取最大25个左右。用3D形态学闭运算填充牙齿内部的细小空洞。可选根据每颗牙齿的解剖位置做规则化比如牙冠朝下、牙根朝上这在牙位编号任务中有用。形态学操作用scipy.ndimage就好from scipy import ndimage labeled, num_features ndimage.label(pred_mask) sizes ndimage.sum(pred_mask, labeled, range(1, num_features 1)) keep_labels [i 1 for i, s in enumerate(sizes) if s 500] # 去掉体素数小于500的散点 cleaned np.isin(labeled, keep_labels).astype(np.uint8) cleaned ndimage.binary_closing(cleaned, structurenp.ones((3, 3, 3))).astype(np.uint8)体素数500这个阈值不是随便定的我统计过数据集中最细的牙根尖区域也有数百个体素阈值设太高会把牙根尖切掉设太低又起不到去噪作用。5.3 常见问题速查表直接抄作业我整理了在跑这个项目过程中遇到过的问题做成速查表问题现象可能原因解决方法训练时CUDA out of memory输入patch太大 / batch size太大减patch或用AMP或用梯度累积验证集DSC在0.5以下不动标签mask预处理错位插值方式错误label重采样改用order0预测结果全是背景类别索引不匹配背景0但模型输出从0开始检查数据集的label映射表边界粗糙、HD95很大滑窗重叠太小 / 后处理形态学太强重叠率调到50%结构元素不要超过3x3x3训练和验证预处理不一致导致性能落差推理时没做CLAHE或归一化参数不一致把预处理逻辑抽成同一个函数训练推理共用牙齿之间粘连分不开形态学闭运算过强 / 网络对小间隔不敏感减小闭运算强度检查标注本身是否粘连只有背景在训练损失函数权重配比失衡或学习率过大用Dice Loss为主、CE为辅学习率降到3e-4这些坑里面label插值方式错误是我见过最多人踩的。很多开源项目里都藏着这个bug数据集本身没问题就是预处理时图省事用了默认的线性插值导致mask上的边界糊成一片灰模型当然学不出好效果。6. 数据集说明与扩展方向6.1 数据集怎么来的标注是什么形式项目里的数据集我看了下是公开的口腔CBCT分割数据集具体来源在docs/数据集说明.md里有详细描述。数据以NIfTI格式存放每例包含原始影像和对应的标注文件。标注分两种形式全牙齿mask32颗牙含智齿整体一个标签适合做口腔全景分割、牙齿总数统计。单牙mask每颗牙一个独立标签1-32编号适合做牙位识别、牙齿排列分析、正畸规划。如果你是用这个项目做毕业设计我建议直接用单牙标注版本因为单牙分割的可视化效果比整体mask好很多可以用不同颜色渲染每一颗牙展示时也更直观。但单牙标注对边界要求更高如果模型预测相邻牙齿之间有一点粘连后处理阶段就需要做“分水岭”切割。6.2 拿到源码后怎么快速跑通全流程按照我的经验新拿到这套项目别急着改代码按这个顺序做先读README.md和requirements.txt把依赖装齐。用项目自带的预训练权重直接跑一次推理在验证集上把DSC指标打印出来。这一步是确认环境无误。找一张CBCT体数据跑一次推理用ITK-SNAP或者3D Slicer看分割结果。这一步是确认模型输出合理。确认无误之后再尝试从头训练。从头训练前先删掉checkpoints里的预训练权重否则load_state_dict会把权重覆盖你以为模型从头训其实还是从预训练继续。训练时用TensorBoard盯着验证集DSC达到0.85以上就算任务达标了。配套的技术报告写得挺规范包含了实验设计、对比实验、主观评价标准等。如果你是准备拿来做课程设计或者毕业设计这些文档可以直接作为报告底稿但建议自己重新训练一遍因为替换掉原始权重重新训练得到的指标更有说服力。6.3 这个项目后期还能怎么扩展单看“3D牙齿CBCT图像分割”本身可以做很多衍生任务牙位识别与编号基于单牙mask把每颗牙齿和FDI牙位编号对齐可以做成自动牙位对应系统。牙齿三维重建分割结果转成STL/PLY模型接3D打印或CAD设计流程做种植导板、隐形牙套设计。龋齿/根尖病灶检测在牙齿分割的基础上叠加一个分类或检测头自动标记可疑病灶区域。多序列交叉验证CBCT往往还有对应的全景片或口扫数据多模态对齐后联合分析。我在实际推进中把分割结果转成了STL网格配合CloudCompare查看牙齿的三维模型效果比二维切片图直观很多。如果你做的是毕业设计答辩这一步会让展示效果提升一个档次评分老师一眼能看出你完全理解了3D医学影像处理的完整链路。有一点我必须提醒医学AI项目最终如果要做临床辅助诊断除了模型本身还需要大量临床验证、伦理审批和设备适配这个项目里的模型更偏技术验证和科研性质离临床落地还有距离。最后再分享一个实际项目里的小技巧训完模型后不要只盯DSC这个数字。我每次都会把预测出来的牙齿表面mesh和真值mesh叠在一起用颜色渐变标注表面距离误差——红色是预测多了蓝色是预测少了。这样能非常直观地看到模型在哪个解剖位置出问题。我自己的模型在第二磨牙的牙根尖处总是会出现微小的“毛刺”后来发现是训练时弹性形变增强强度太大牙根尖这种细长结构被拉变形了把弹性形变的幅度从默认的sigma15降到sigma8之后毛刺明显减少。这种细微的经验光看数字指标是发现不了的。做三维医学图像分割最大的坑不是网络搭不出来而是数据预处理和工程细节。同样的网络预处理不同DSC可以从0.6差到0.9。希望这篇能帮你少走弯路。本文还有配套的精品资源点击获取
返回列表