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 數學流程

階段公式說明
Forwardq(x_t | x_0) = N(x_t; sqrt(alpha_bar_t) * x_0, (1-alpha_bar_t) * I)逐步加入高斯雜訊
LossL = E[w_t * || epsilon - epsilon_theta(x_t, t, MRI) ||^2]預測加入的噪音
Reversep_theta(x_{t-1} | x_t, MRI) = N(mu_theta, sigma_theta)學習去噪分佈
Samplingx_{t-1} = mu_theta(x_t, t) + sigma_t * z迭代取樣(50 steps via respacing)

3. 安裝與設定

3.1 環境需求

項目需求
Python3.8+
CUDA11.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 Transformer3D 原生MRI→CT3D Transformer attention, learned sigma
medSynthesisV1Pix2Pix (cGAN)2D/3DMRI→CT, CT→PET快速推論,無迭代
medSynthesisCycleGAN + edge loss2D多模態不需配對資料
conditional_DDPM標準 UNet DDPM2D通用條件生成基礎 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 架構下,本專案可以:

  1. Paper-search 層:搜尋 MRI→CT synthetic imaging 最新文獻
  2. Paper-tutorial 層:將原始 Medical Physics 論文 + 本 repo 打包為內部教學
  3. 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)

  1. Latent Diffusion 升級:將 pixel-space diffusion 改為 latent space(搭配 3D VAE),可大幅降低記憶體需求與加速推論,類似 Stable Diffusion 的做法。
  2. Multi-modal Conditioning:加入 text prompt(如放射科報告)或其他模態(PET、超音波)做多條件合成。
  3. Consistency Model / Flow Matching:以 Consistency Model 或 Rectified Flow 替換 DDPM sampling,將推論從 50 步壓縮到 1-4 步。
  4. 3D Foundation Model:結合大規模醫學影像預訓練(如 CT-CLIP、SAM-Med3D),建立跨模態 3D foundation model。
  5. 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 和隱私保護影像生成的起點。