Synthetic-CT-generation-from-MRI-using-3D-transformer-based-denoising-diffusion-model 完整教學
Repository: https://github.com/shaoyanpan/Synthetic-CT-generation-from-MRI-using-3D-transformer-based-denoising-diffusion-model Stars: 67 | Fork: 8 | License: MIT Tags: diffusion, 3D-transformer, MRI-CT 論文: Medical Physics (2024) | arXiv 預印本 主要語言: Python 3.8 | 框架: PyTorch + MONAI 最後更新: 2026-05-10
2. 核心架構
2.1 整體管線
flowchart TB
subgraph Input["輸入準備"]
MRI["MRI 影像
(Condition)"]
NOISE["高斯雜訊
x_T ~ N(0,I)"]
end
subgraph Forward["Forward Process
(訓練時)"]
CT_REAL["真實 CT 影像
x_0"]
ADD_NOISE["逐步加噪
q(x_t | x_0)"]
CT_REAL --> ADD_NOISE
NOISE_SCHED["Linear Beta Schedule
1000 steps"]
NOISE_SCHED --> ADD_NOISE
end
subgraph Model["3D Swin-Transformer UNet"]
direction TB
ENC1["Encoder Block 1
64ch, ResBlock x2"]
ENC2["Encoder Block 2
128ch, SwinTransformer"]
ENC3["Encoder Block 3
192ch, SwinTransformer"]
ENC4["Encoder Block 4
256ch, SwinTransformer"]
MID["Middle Block
256ch, SwinTransformer"]
DEC4["Decoder Block 4
256ch + Skip"]
DEC3["Decoder Block 3
192ch + Skip"]
DEC2["Decoder Block 2
128ch + Skip"]
DEC1["Decoder Block 1
64ch + Skip"]
ENC1 --> ENC2 --> ENC3 --> ENC4 --> MID
MID --> DEC4 --> DEC3 --> DEC2 --> DEC1
ENC4 -.->|skip| DEC4
ENC3 -.->|skip| DEC3
ENC2 -.->|skip| DEC2
ENC1 -.->|skip| DEC1
end
subgraph TimeEmb["Timestep Embedding"]
T["t (當前步驟)"]
SINCOS["Sinusoidal Encoding"]
MLP_T["MLP Projection"]
T --> SINCOS --> MLP_T
end
subgraph Reverse["Reverse Process
(推論時)"]
DENOISE["反覆去噪
p(x_{t-1} | x_t, MRI)"]
SYNTH_CT["合成 CT
x_0"]
DENOISE --> SYNTH_CT
end
MRI --> Model
ADD_NOISE --> Model
NOISE --> DENOISE
MRI --> DENOISE
MLP_T --> Model
Model --> |"預測 noise ε_θ"| Reverse
2.2 3D Swin Transformer 在 UNet 中的角色
傳統 DDPM UNet 在低解析度 feature map 使用 global self-attention,但這在 3D 體積中記憶體開銷極大。本專案以 3D Swin Transformer Block 取代:
flowchart LR
subgraph SwinBlock["Swin Transformer Block (3D)"]
direction TB
IN["Feature Map
(B, C, D, H, W)"]
RESHAPE["Reshape to Windows
(B*nW, ws^3, C)"]
WMSA["W-MSA
(Window Multi-head
Self-Attention)"]
SHIFT["Cyclic Shift
(shifted window)"]
SWMSA["SW-MSA
(Shifted Window
Multi-head SA)"]
MLP_BLK["MLP Block
(GELU + Dropout)"]
MERGE["Window Reverse
(B, C, D, H, W)"]
IN --> RESHAPE --> WMSA --> SHIFT --> SWMSA --> MLP_BLK --> MERGE
end
subgraph TimeInjection["Timestep Injection"]
TEMB["t embedding"]
SCALE_SHIFT["Scale + Shift
(AdaGN)"]
TEMB --> SCALE_SHIFT
end
SCALE_SHIFT --> WMSA
關鍵設計:
- Window Partition (視窗分割):將 3D feature map 分割為不重疊的局部視窗(例如 4x4x4),在視窗內做 self-attention。
- Shifted Window (位移視窗):交替使用標準視窗與位移後的視窗,讓跨視窗的資訊得以流通。
- 每層不同 window size:淺層用 [4,4,4](完整 3D),深層用 [4,4,2](配合 downsampling 後的 spatial 尺寸)。
2.3 Diffusion 數學流程
| 階段 | 公式 | 說明 |
|---|---|---|
| Forward | q(x_t | x_0) = N(x_t; sqrt(alpha_bar_t) * x_0, (1-alpha_bar_t) * I) | 逐步加入高斯雜訊 |
| Loss | L = E[w_t * || epsilon - epsilon_theta(x_t, t, MRI) ||^2] | 預測加入的噪音 |
| Reverse | p_theta(x_{t-1} | x_t, MRI) = N(mu_theta, sigma_theta) | 學習去噪分佈 |
| Sampling | x_{t-1} = mu_theta(x_t, t) + sigma_t * z | 迭代取樣(50 steps via respacing) |
3. 安裝與設定
3.1 環境需求
| 項目 | 需求 |
|---|---|
| Python | 3.8+ |
| CUDA | 11.1+(需 GPU 訓練) |
| GPU VRAM | 建議 24GB+(3D volumetric training) |
| 磁碟空間 | ~5GB(環境 + 資料) |
3.2 使用 Conda 安裝(原始方式)
1# 下載 repository
2git clone https://github.com/shaoyanpan/Synthetic-CT-generation-from-MRI-using-3D-transformer-based-denoising-diffusion-model.git
3cd Synthetic-CT-generation-from-MRI-using-3D-transformer-based-denoising-diffusion-model
4
5# 以 environment.yml 建立 Conda 環境
6conda env create -f environment.yml
7conda activate DL
注意:原始
environment.yml是 Windows 環境,包含大量非必要套件(如 Spyder、TensorFlow)。建議使用下方精簡版。
3.3 精簡安裝(建議)
1# 建立乾淨環境
2conda create -n synth-ct python=3.8 -y
3conda activate synth-ct
4
5# 核心套件
6conda install pytorch=1.9.1 cudatoolkit=11.1 -c pytorch -y
7pip install monai==0.7.0 nibabel pydicom scipy numpy==1.18.5
8pip install timm==0.4.12 einops matplotlib h5py
9pip install simpleitk medpy torchio
10
11# 驗證
12python -c "import torch; print(f'PyTorch {torch.__version__}, CUDA: {torch.cuda.is_available()}')"
13python -c "import monai; print(f'MONAI {monai.__version__}')"
3.4 資料組織
專案支援兩種資料格式:
方式 A:MATLAB .mat 檔案(專案預設)
1data/
2 imagesTr/ # 訓練集
3 patient_1.mat # dict: {'image': MRI_array, 'label': CT_array}
4 patient_2.mat
5 imagesTs/ # 測試集
6 patient_3.mat
7 imagesVal/ # 驗證集
8 patient_4.mat
方式 B:NIfTI .nii 檔案
1data/
2 imagesTr/ # 訓練 MRI
3 patient_1.nii.gz
4 labelsTr/ # 訓練 CT(ground truth)
5 patient_1.nii.gz
6 imagesTs/ # 測試 MRI
7 labelsTs/ # 測試 CT
8 imagesVal/ # 驗證 MRI
9 labelsVal/ # 驗證 CT
4. 使用方式與程式碼範例
4.1 範例一:建立 Diffusion 與網路模型
此範例展示如何初始化完整的 3D Transformer DDPM 管線。
1import torch
2from diffusion.Create_diffusion import create_gaussian_diffusion
3from diffusion.resampler import UniformSampler
4from network.Diffusion_model_transformer import SwinVITModel
5
6device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
7
8# === Step 1: 建立 Gaussian Diffusion ===
9# 訓練使用 1000 步,推論透過 timestep_respacing 壓縮到 50 步
10diffusion = create_gaussian_diffusion(
11 steps=1000, # 總擴散步數
12 learn_sigma=True, # 學習 variance(輸出 2 通道)
13 sigma_small=False,
14 noise_schedule='linear', # 線性 beta schedule
15 use_kl=False, # 使用 MSE loss(非 KL)
16 predict_xstart=False, # 預測 noise epsilon(非 x_0)
17 rescale_timesteps=True,
18 rescale_learned_sigmas=True,
19 timestep_respacing=[50], # 推論時壓縮為 50 步
20)
21
22# 均勻採樣器(訓練時隨機選取 timestep)
23schedule_sampler = UniformSampler(diffusion)
24
25# === Step 2: 建立 3D Swin-Transformer UNet ===
26img_size = (128, 128, 64) # 3D patch size (D, H, W)
27
28model = SwinVITModel(
29 image_size=img_size,
30 in_channels=2, # MRI (condition) + noisy CT
31 model_channels=64, # 基礎通道數
32 out_channels=2, # predicted noise + learned sigma
33 dims=3, # 3D convolutions
34 sample_kernel=([2,2,2], [2,2,1], [2,2,1], [2,2,1]), # downsampling kernels
35 num_res_blocks=[2, 2, 2, 2], # 每個解析度的 ResBlock 數量
36 attention_resolutions=(32, 16, 8),
37 dropout=0,
38 channel_mult=(1, 2, 3, 4), # 通道倍率: 64→128→192→256
39 num_classes=None, # 無 class conditioning
40 use_checkpoint=False,
41 use_fp16=False,
42 num_heads=[4, 4, 8, 16], # 每層 attention heads
43 window_size=[[4,4,4], [4,4,4], [4,4,2], [4,4,2]], # 3D window sizes
44 num_head_channels=64,
45 use_scale_shift_norm=True, # AdaGN (timestep-conditioned normalization)
46 resblock_updown=False,
47).to(device)
48
49print(f"模型參數量: {sum(p.numel() for p in model.parameters()):,}")
50# 預期約 ~50-80M 參數,視 img_size 而定
4.2 範例二:訓練迴圈
此範例展示一個完整的訓練 epoch,包含 loss 計算與反向傳播。
1import numpy as np
2from torch.utils.data import DataLoader, Dataset
3import scipy.io as sio
4
5# === 自定義 Dataset(MATLAB 格式)===
6class MRI_CT_Dataset(Dataset):
7 """載入 .mat 檔案,每個檔案包含 'image'(MRI)和 'label'(CT)"""
8 def __init__(self, data_dir, img_size=(128, 128, 64)):
9 import glob
10 self.files = sorted(glob.glob(f"{data_dir}/*.mat"))
11 self.img_size = img_size
12
13 def __len__(self):
14 return len(self.files)
15
16 def __getitem__(self, idx):
17 data = sio.loadmat(self.files[idx])
18 mri = data['image'].astype(np.float32) # MRI condition
19 ct = data['label'].astype(np.float32) # CT target
20
21 # 正規化到 [-1, 1]
22 mri = (mri - mri.mean()) / (mri.std() + 1e-8)
23 ct = (ct - ct.min()) / (ct.max() - ct.min() + 1e-8) * 2 - 1
24
25 # 轉為 tensor: (1, D, H, W)
26 mri = torch.tensor(mri).unsqueeze(0)
27 ct = torch.tensor(ct).unsqueeze(0)
28 return mri, ct
29
30# === 訓練迴圈 ===
31dataset = MRI_CT_Dataset("MRI_to_CT_brain_for_dosimetric/imagesTr")
32dataloader = DataLoader(dataset, batch_size=2, shuffle=True)
33
34optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4, weight_decay=0.01)
35
36num_epochs = 100
37for epoch in range(num_epochs):
38 model.train()
39 epoch_loss = 0.0
40
41 for batch_idx, (mri, ct) in enumerate(dataloader):
42 mri, ct = mri.to(device), ct.to(device)
43
44 # 從 schedule_sampler 抽取隨機 timestep
45 batch_size = ct.shape[0]
46 t, weights = schedule_sampler.sample(batch_size, device)
47
48 # 計算 diffusion training loss
49 # target = ct (ground truth CT), condition = mri
50 losses = diffusion.training_losses(
51 model,
52 x_start=ct, # 目標影像 (CT)
53 t=t, # 隨機 timestep
54 model_kwargs={"condition": mri} # MRI 作為條件
55 )
56
57 loss = (losses["loss"] * weights).mean()
58
59 optimizer.zero_grad()
60 loss.backward()
61 torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
62 optimizer.step()
63
64 epoch_loss += loss.item()
65
66 avg_loss = epoch_loss / len(dataloader)
67 if (epoch + 1) % 10 == 0:
68 print(f"Epoch [{epoch+1}/{num_epochs}], Loss: {avg_loss:.6f}")
69 # 定期儲存 checkpoint
70 torch.save({
71 'epoch': epoch,
72 'model_state_dict': model.state_dict(),
73 'optimizer_state_dict': optimizer.state_dict(),
74 'loss': avg_loss,
75 }, f"checkpoint_epoch_{epoch+1}.pt")
4.3 範例三:推論與合成 CT 生成
此範例展示如何使用訓練好的模型生成 Synthetic CT,搭配 MONAI 的 SlidingWindowInferer 處理任意大小的輸入體積。
1from monai.inferers import SlidingWindowInferer
2import matplotlib.pyplot as plt
3
4# === 載入已訓練的模型 ===
5checkpoint = torch.load("checkpoint_epoch_100.pt", map_location=device)
6model.load_state_dict(checkpoint['model_state_dict'])
7model.eval()
8
9# === 設定 SlidingWindowInferer ===
10# 因為訓練時使用固定 patch size,推論時用 sliding window 處理完整體積
11roi_size = (128, 128, 64) # 與訓練時的 img_size 一致
12sw_batch_size = 4 # 同時處理的 patch 數量
13overlap = 0.5 # patch 間 50% overlap(減少邊界偽影)
14
15inferer = SlidingWindowInferer(
16 roi_size=roi_size,
17 sw_batch_size=sw_batch_size,
18 overlap=overlap,
19 mode='constant' # overlap 區域的融合策略
20)
21
22# === 定義 diffusion sampling 函數 ===
23def diffusion_sampling(condition, model):
24 """
25 從純雜訊開始,以 MRI condition 引導,迭代去噪生成 Synthetic CT。
26 推論時使用 50 步 (timestep_respacing=[50]),而非完整 1000 步。
27 """
28 with torch.no_grad():
29 sampled = diffusion.p_sample_loop(
30 model,
31 shape=(
32 condition.shape[0], 1,
33 condition.shape[2], condition.shape[3], condition.shape[4]
34 ),
35 condition=condition,
36 clip_denoised=True, # 將輸出 clip 到 [-1, 1]
37 )
38 return sampled
39
40# === 對測試 MRI 執行推論 ===
41test_dataset = MRI_CT_Dataset("MRI_to_CT_brain_for_dosimetric/imagesTs")
42test_mri, test_ct_real = test_dataset[0]
43test_mri = test_mri.unsqueeze(0).to(device) # (1, 1, D, H, W)
44
45# SlidingWindowInferer 自動將大體積切成 patches 並合併結果
46synth_ct = inferer(test_mri, diffusion_sampling, model)
47
48# === 視覺化結果 ===
49slice_idx = synth_ct.shape[2] // 2 # 取中間 axial slice
50
51fig, axes = plt.subplots(1, 3, figsize=(15, 5))
52axes[0].imshow(test_mri[0, 0, slice_idx].cpu().numpy(), cmap='gray')
53axes[0].set_title('MRI (Input)')
54axes[1].imshow(synth_ct[0, 0, slice_idx].cpu().numpy(), cmap='gray')
55axes[1].set_title('Synthetic CT (Generated)')
56axes[2].imshow(test_ct_real[0, slice_idx].numpy(), cmap='gray')
57axes[2].set_title('Real CT (Ground Truth)')
58
59for ax in axes:
60 ax.axis('off')
61plt.tight_layout()
62plt.savefig('synth_ct_comparison.png', dpi=150, bbox_inches='tight')
63plt.show()
64
65# === 量化評估 ===
66from skimage.metrics import structural_similarity as ssim
67from skimage.metrics import peak_signal_noise_ratio as psnr
68
69synth_np = synth_ct[0, 0].cpu().numpy()
70real_np = test_ct_real[0].numpy()
71
72mae = np.mean(np.abs(synth_np - real_np))
73psnr_val = psnr(real_np, synth_np, data_range=2.0) # [-1, 1] range
74ssim_val = ssim(real_np, synth_np, data_range=2.0)
75
76print(f"MAE: {mae:.4f}")
77print(f"PSNR: {psnr_val:.2f} dB")
78print(f"SSIM: {ssim_val:.4f}")
5. 在生醫影像合成生態系中的定位
5.1 Domain 2-C:Cross-modal Medical Image Synthesis
本專案屬於 Bio-SDG 生態系 Domain 2(生物醫學影像合成)中的 Sub-domain C: Cross-modal Medical Image Synthesis (跨模態醫學影像合成),與以下專案形成互補:
| 專案 | 方法 | 維度 | 模態轉換 | 特色 |
|---|---|---|---|---|
| 本專案 (Synthetic-CT) | 3D DDPM + Swin Transformer | 3D 原生 | MRI→CT | 3D Transformer attention, learned sigma |
| medSynthesisV1 | Pix2Pix (cGAN) | 2D/3D | MRI→CT, CT→PET | 快速推論,無迭代 |
| medSynthesis | CycleGAN + edge loss | 2D | 多模態 | 不需配對資料 |
| conditional_DDPM | 標準 UNet DDPM | 2D | 通用條件生成 | 基礎 diffusion baseline |
5.2 GAN vs. Diffusion 在 MRI→CT 的比較
| 面向 | GAN (如 Pix2Pix, CycleGAN) | Diffusion (如本專案) |
|---|---|---|
| 影像品質 | 偶有 checkerboard artifacts | 更平滑、更高 PSNR/SSIM |
| 模式崩潰 (Mode Collapse) | 常見問題 | 理論上不存在 |
| 訓練穩定性 | 需謹慎調參 (discriminator/generator balance) | 相對穩定(單一 MSE loss) |
| 推論速度 | 單次 forward pass(毫秒級) | 50-1000 步迭代(秒~分鐘級) |
| 多樣性 | 單一輸出 | 可生成多個合理變體 |
| 3D 支援 | 記憶體受限,多為 2D slice | 本專案原生支援 3D |
5.3 生態系連結
1Domain 2 生態系定位:
2
3[A] 基礎平台層
4 MONAI / MONAI GenerativeModels ──→ 本專案直接使用 MONAI SlidingWindowInferer
5 nnUNet ──→ 資料組織格式相近
6
7[C] 跨模態合成(本專案所在)
8 medSynthesisV1 (GAN baseline) ──→ 本專案為其 Diffusion 升級版
9 medSynthesis (unpaired) ──→ 互補:paired vs. unpaired 場景
10
11[D] Diffusion 前沿
12 conditional_DDPM (2D baseline) ──→ 本專案為其 3D + Transformer 擴展
13 DenseDiffusion ──→ layout-guided,不同任務但共享 diffusion 基礎
14
15[F] 癌症影像
16 foundation-cancer-image-biomarker ──→ 合成 CT 可做為 downstream biomarker 輸入
6. 與其他工具的整合
6.1 與 MONAI 生態系整合
本專案已直接依賴 MONAI,可進一步整合:
1# 使用 MONAI Transforms 做資料前處理
2from monai.transforms import (
3 Compose, LoadImaged, EnsureChannelFirstd,
4 ScaleIntensityd, CropForegroundd, RandSpatialCropd
5)
6
7train_transforms = Compose([
8 LoadImaged(keys=["image", "label"]), # 載入 NIfTI
9 EnsureChannelFirstd(keys=["image", "label"]),
10 ScaleIntensityd(keys=["image"], minv=-1, maxv=1),
11 ScaleIntensityd(keys=["label"], minv=-1, maxv=1),
12 CropForegroundd(keys=["image", "label"], source_key="image"),
13 RandSpatialCropd(keys=["image", "label"], roi_size=(128, 128, 64)),
14])
6.2 與 MONAI GenerativeModels 整合
MONAI GenerativeModels 提供更完整的 Diffusion / Latent Diffusion 框架,可將本專案的 Swin-Transformer backbone 遷移過去:
1# 概念性整合:用 MONAI GenerativeModels 的 scheduler 替換手寫 diffusion
2from generative.networks.schedulers import DDPMScheduler
3
4scheduler = DDPMScheduler(
5 num_train_timesteps=1000,
6 beta_schedule="linear",
7 prediction_type="epsilon"
8)
9
10# 搭配本專案的 SwinVITModel 作為 denoiser
11# scheduler.add_noise() 取代手寫的 forward process
12# scheduler.step() 取代手寫的 reverse sampling
6.3 與 AIKT Pipeline 整合
在 AI-Knowledge Template 架構下,本專案可以:
- Paper-search 層:搜尋 MRI→CT synthetic imaging 最新文獻
- Paper-tutorial 層:將原始 Medical Physics 論文 + 本 repo 打包為內部教學
- ToolUniverse 整合:結合影像品質評估工具(PSNR/SSIM/FID 計算)
6.4 藥物開發應用場景
| 應用 | 說明 |
|---|---|
| Imaging Biomarker (影像生物標記) | 合成 CT 用於腫瘤體積量化,作為臨床試驗 endpoint |
| Virtual Clinical Trial (虛擬臨床試驗) | 搭配 VICTRE 框架,以合成影像做虛擬試驗模擬 |
| Privacy-preserving Data (隱私保護資料) | 生成不含 PHI 的合成影像,分享給多中心研究 |
| 放射治療計畫驗證 | MRI-only RT workflow 中,驗證合成 CT 劑量計算準確性 |
7. 優缺點分析
7.1 優點
| 面向 | 說明 |
|---|---|
| 3D 原生處理 | 直接操作 volumetric data,保留 slice 間空間連續性,避免 2D 方法的 inter-slice 不連續偽影 |
| Swin Transformer Attention | 相比 global attention,在 3D 資料上的記憶體效率大幅提升(window-based complexity) |
| Learned Sigma | 學習 variance 而非固定值,讓模型自適應不同組織區域的不確定性 |
| MONAI 整合 | SlidingWindowInferer 讓推論時不受 patch size 限制,可處理任意大小的 3D 體積 |
| 研究可重現性 | MIT License,附範例資料(雖為 over-smoothed),Jupyter Notebook 完整演示 |
| Diffusion 優勢 | 無 mode collapse,影像品質穩定,可生成多樣化變體 |
7.2 缺點與限制
| 面向 | 說明 |
|---|---|
| 推論速度慢 | 即便 respacing 到 50 步,3D 推論仍需數分鐘(vs. GAN 的毫秒級) |
| GPU 記憶體需求高 | 3D Transformer + Diffusion 在訓練時需 24GB+ VRAM,batch size 受限 |
| 資料受限 | 因隱私限制,僅提供 over-smoothed 的 MATLAB 範例資料,無法直接復現論文結果 |
| 程式碼品質 | 無單元測試、無 CI/CD、pycache 被 commit、environment.yml 包含大量非必要套件 |
| 文件不足 | 無 API 文件、無 docstring、部分程式碼有被註解掉的區塊 |
| 版本固定 | 鎖定 PyTorch 1.9.1 + Python 3.8,與現代環境(PyTorch 2.x)相容性未驗證 |
| 僅限 paired data | 需要配對的 MRI-CT 資料,不支援 unpaired(CycleGAN-style)訓練 |
7.3 藍海機會 (Blue Ocean Opportunities)
- Latent Diffusion 升級:將 pixel-space diffusion 改為 latent space(搭配 3D VAE),可大幅降低記憶體需求與加速推論,類似 Stable Diffusion 的做法。
- Multi-modal Conditioning:加入 text prompt(如放射科報告)或其他模態(PET、超音波)做多條件合成。
- Consistency Model / Flow Matching:以 Consistency Model 或 Rectified Flow 替換 DDPM sampling,將推論從 50 步壓縮到 1-4 步。
- 3D Foundation Model:結合大規模醫學影像預訓練(如 CT-CLIP、SAM-Med3D),建立跨模態 3D foundation model。
- GMP-grade Synthetic Data Pipeline:建立符合 GMP (Good Manufacturing Practice) 標準的合成影像生成 + 品質驗證管線,用於 FDA 送件的虛擬臨床試驗。
一行總結:本專案是首批將 3D Swin Transformer 整合進 DDPM 的 MRI→CT 合成實作,雖然程式碼工程品質有改善空間,但其 3D 原生 + Transformer attention + MONAI 整合的架構設計,為醫學影像 diffusion model 提供了堅實的研究 baseline,特別適合作為放射治療 synthetic CT 和隱私保護影像生成的起點。
Comments