brain-synthesis-lesion-segmentation 完整教學
Repository: https://github.com/harshitAgr/brain-synthesis-lesion-segmentation Stars: 40 | Fork: 33 | Language: Python (TensorFlow) Tags: brain, GAN, NVIDIA-DLI 論文: Medical Image Synthesis for Data Augmentation and Anonymization using Generative Adversarial Networks 最後更新: 2025-12-01
2. 核心架構
2.1 整體管線
graph TD
subgraph 資料準備
A[ISLES'18 原始 NIfTI] -->|預處理 Notebook| B[2D PNG 切片
或 3D PKL 體積]
end
subgraph 模型訓練
B --> C{選擇模式}
C -->|無條件| D[GAN 2D
gan2d.py]
C -->|條件式 2D| E[pix2pix 2D
pix2pix2d.py]
C -->|條件式 3D| F[pix2pix 3D
pix2pix3d.py]
end
subgraph Generator 架構
G[Input Image
CT Perfusion] --> H[U-Net Encoder
8 層 Downsample]
H --> I[Bottleneck
1x1x512]
I --> J[U-Net Decoder
7 層 Upsample + Skip]
J --> K[Output
合成病灶標籤]
end
subgraph Discriminator 架構
L[Input + Target
或 Input + Generated] --> M[PatchGAN
70x70 patches]
M --> N[Real / Fake
判別]
end
subgraph 後處理
E --> O[2D 預測 PNG]
F --> P[3D 預測 PKL]
O -->|merge_2d_test_to_nii.py| Q[NIfTI 輸出]
P -->|convert_3d_test_to_nii.py| Q
end
style G fill:#e1f5fe
style K fill:#c8e6c9
style Q fill:#fff3e0
2.2 Generator 詳細架構(U-Net + Skip Connections)
Generator(生成器)採用經典 U-Net(U 型網路)架構,包含:
- Encoder(編碼器):8 層
Downsampleblock,每層包含 Conv2D/Conv3D (stride=2) + BatchNorm + LeakyReLU,逐步將空間解析度從 256x256 壓縮到 1x1。 - Decoder(解碼器):7 層
Upsampleblock,每層包含 Conv2DTranspose/Conv3DTranspose (stride=2) + BatchNorm + Dropout(0.5, 前三層) + ReLU + Skip Connection(跳接連接),與 Encoder 對應層 concatenate。 - 最終輸出:Conv2DTranspose → Tanh activation,輸出 3 通道影像([-1, 1] 範圍)。
關鍵設計:前三層 Decoder 使用 Dropout (50%) 作為正則化(Regularization; 正則化),避免 GAN 對小資料集的 mode collapse(模式崩潰)。
2.3 Discriminator 詳細架構(PatchGAN)
Discriminator(判別器)採用 PatchGAN(Patch-based Discriminator; 基於影像區塊的判別器),在 70x70 pixel patch 上做真假判別:
- 接收
[input_image, target/generated]concatenated 的 6 通道輸入 - 4 層 Conv2D (stride=2) + BatchNorm + LeakyReLU 逐步下採樣
- 最終 Conv2D 輸出 1 通道 patch-level 判別結果
- 使用 ResNet-style 的
_IdentityBlock和_ConvBlock作為基礎 building block
2.4 損失函數設計
1Total Generator Loss = GAN Loss + λ * L1 Loss
- GAN Loss:Sigmoid cross-entropy,讓 Generator 騙過 Discriminator
- L1 Loss:生成影像與 ground truth 的逐像素絕對差異(pixel-wise absolute difference)
- λ = 100:L1 loss 權重(強調結構保真度 > 對抗訓練)
- Patch-based Discriminator Loss:針對 70x70 patch 採樣 1000 個 patch 分別判別,聚焦病灶區域
2.5 2D vs 3D 模式差異
| 特性 | pix2pix 2D | pix2pix 3D |
|---|---|---|
| 卷積層 | Conv2D / Conv2DTranspose | Conv3D / Conv3DTranspose |
| 輸入尺寸 | (bs, 256, 256, 3) | (bs, 24, 256, 256, 3) |
| Patch 尺寸 | 70x70 | 24x70x70 |
| 資料載入 | PNG 圖片 | PKL 序列化體積 |
| 輸出轉換 | merge_2d → NIfTI | convert_3d → NIfTI |
| Stride 設計 | 均勻 stride=2 | 非均勻 stride(z 軸 [1] 或 [3]) |
3D 版本的 stride 設計值得注意:z 軸(切片方向)僅在特定層使用非 1 的 stride,因為 ISLES'18 的 z 方向解析度(24 slices)遠低於 xy 平面(256x256),若全方向等比下採樣會導致 z 軸過早歸零。
3. 安裝與設定
3.1 環境需求
注意:本專案使用 TensorFlow 1.x Eager Execution API(
tf.contrib.eager、tf.enable_eager_execution()),不相容 TensorFlow 2.x。建議使用虛擬環境隔離。
| 套件 | 版本建議 | 用途 |
|---|---|---|
| Python | 3.6-3.7 | TF 1.x 相容 |
| TensorFlow (GPU) | 1.12-1.15 | 核心框架 |
| CUDA | 9.0-10.0 | GPU 加速 |
| cuDNN | 7.x | GPU 加速 |
| nibabel | >= 2.3 | NIfTI 讀寫 |
| scipy | < 1.3 | scipy.misc.imread / imsave |
| matplotlib | >= 2.x | 視覺化 |
| seaborn | >= 0.9 | 視覺化 |
| glob2 | >= 0.6 | 遞迴檔案搜尋 |
| Pillow (PIL) | >= 5.x | 影像處理 |
3.2 安裝步驟
1# 建議使用 conda 建立 TF 1.x 隔離環境
2conda create -n brain-synth python=3.7
3conda activate brain-synth
4
5# 安裝 TensorFlow 1.x GPU 版
6pip install tensorflow-gpu==1.15
7
8# 安裝相依套件
9pip install nibabel scipy==1.2.1 matplotlib seaborn glob2 Pillow
10
11# 複製專案
12git clone https://github.com/harshitAgr/brain-synthesis-lesion-segmentation.git
13cd brain-synthesis-lesion-segmentation
3.3 資料準備
取得 ISLES'18 資料
- 前往 ISLES'18 Challenge 註冊並下載資料
- 解壓縮至
datasets/ISLES2018/目錄
預處理(2D 模式)
使用 utils/isles18_sample_2d.ipynb:
1# 啟動 Jupyter Notebook 進行預處理
2jupyter notebook utils/isles18_sample_2d.ipynb
此 notebook 會將 NIfTI 格式的多序列 CTP 影像轉為 256x256 PNG 切片,分別存成 input image (Image A) 和 label (Image B) 的配對資料夾。
預處理(3D 模式)
使用 utils/isles18_sample_3d.ipynb,將多序列影像與標籤封裝成 PKL(Python Pickle)格式的 3D 體積。
3.4 目錄結構
1brain-synthesis-lesion-segmentation/
2├── gan2d.py # GAN 2D 訓練入口
3├── pix2pix2d.py # pix2pix 2D 訓練入口
4├── pix2pix3d.py # pix2pix 3D 訓練入口
5├── models/
6│ ├── gan2d.py # GAN Generator + Discriminator
7│ ├── pix2pix2d.py # pix2pix 2D U-Net Generator + PatchGAN
8│ ├── pix2pix3d.py # pix2pix 3D U-Net Generator + PatchGAN
9│ ├── blocks2d.py # ResNet-style 2D building blocks
10│ └── blocks3d.py # ResNet-style 3D building blocks
11├── data/
12│ ├── gan2d_loader.py # GAN 2D 資料載入器
13│ ├── pix2pix2d_loader.py # pix2pix 2D 配對資料載入器
14│ └── pix2pix3d_loader.py # pix2pix 3D 配對資料載入器
15└── utils/
16 ├── isles18_sample_2d.ipynb # 2D 預處理 notebook
17 ├── isles18_sample_3d.ipynb # 3D 預處理 notebook
18 ├── merge_2d_test_to_nii.py # 2D 預測結果 → NIfTI
19 └── convert_3d_test_to_nii.py # 3D 預測結果 → NIfTI
4. 使用方式與程式碼範例
4.1 範例一:無條件 GAN 2D 腦切片生成
這是最基礎的模式——從隨機雜訊生成 64x64 灰階腦部切片,用於理解 GAN 基本原理。
1# === 執行 GAN 2D 訓練 ===
2# 前置條件:已將腦部切片 PNG 存至 data_dir
3
4python gan2d.py --data_dir=./datasets/brain_slices_png/
5
6# --- 底層運作說明 ---
7# gan2d.py 內部流程:
8#
9# 1. 資料載入:data/gan2d_loader.py
10# - 讀取 data_dir 下所有 .png,resize 到 64x64,歸一化到 [-1, 1]
11# - dataset = tf.data.Dataset.from_tensor_slices(files)
12# - batch_size = 256, prefetch = 256 * 4
13#
14# 2. 模型初始化:models/gan2d.py
15# - Generator: noise(100) → Dense(8*8*64) → 5 層 Conv2DTranspose → tanh → (64,64,1)
16# - Discriminator: (64,64,1) → 5 層 Conv2D → Dense(1) → real/fake
17#
18# 3. 訓練迴圈(150 epochs):
19# - noise = tf.random_normal([256, 100])
20# - gen_loss = sigmoid_cross_entropy(ones, D(G(noise)))
21# - disc_loss = sigmoid_cross_entropy(ones, D(real)) + sigmoid_cross_entropy(zeros, D(G(noise)))
22# - 每 1 epoch 存圖,每 15 epoch 存 checkpoint
23
24# 4. 輸出:image_at_epoch_XXXX.png(4x4 grid 的生成結果)
學習重點:此模式展示了 GAN 的基礎架構,但無條件生成無法控制輸出內容。在醫學影像場景中,我們需要 條件式 生成——指定輸入影像後生成對應的標籤或合成影像。
4.2 範例二:pix2pix 2D 條件式影像合成(核心用法)
這是本專案最核心的模式——從 CT Perfusion 影像(多序列)合成對應的中風病灶分割標籤。
1# === pix2pix 2D 訓練 ===
2python pix2pix2d.py \
3 --data_dir=./datasets/isles18_2d/train/ \
4 --test_dir=./datasets/isles18_2d/test/ \
5 --output_file_dir=./output_2d/ \
6 --disc_dim_method=patch-based \
7 --swap_noise_imB_channel_13=True
8
9# --- 關鍵參數說明 ---
10# --data_dir : 訓練資料目錄(含 imageA/ 和 imageB/ 子目錄)
11# --test_dir : 測試資料目錄
12# --output_file_dir : 輸出目錄(訓練過程圖 + 測試預測)
13# --disc_dim_method : 判別器策略(patch-based 為 70x70 patch 採樣)
14# --swap_noise_imB_channel_13 : 通道交換雜訊增強(10% 機率觸發)
15
16# === 底層流程 ===
17#
18# 1. 資料載入(data/pix2pix2d_loader.py):
19# - load_image(imageA_path, imageB_path, is_train=True)
20# - 訓練時:resize 到 300x300 → random crop 到 256x256 → 50% 機率水平翻轉
21# - 測試時:直接 resize 到 256x256
22# - 歸一化到 [-1, 1]
23#
24# 2. 資料增強(pix2pix2d.py train()):
25# - swap_noise_imB_channel_13:10% 機率翻轉 target 通道順序 + 加入 Gaussian noise
26# - 這是為了防止 Generator 過度依賴特定通道順序
27#
28# 3. 訓練損失:
29# gen_total_loss = gan_loss + LAMBDA(100) * l1_loss
30# disc_loss = real_loss + generated_loss
31# + patch-based: 從病灶區域採樣 1000 個 70x70 patch 做額外判別
32#
33# 4. 測試推論:
34# prediction = generator(test_input, training=True) # BN 用 batch stats
35# prediction[prediction > 0.5] = 1 # 二值化閾值
36
37# === 測試結果轉 NIfTI ===
38python utils/merge_2d_test_to_nii.py
39# 將各切片 PNG 依 case 合併回 3D NIfTI 格式,供醫學影像軟體(如 ITK-SNAP)檢視
關鍵設計亮點:
- Patch-based Discriminator:不是對整張圖做一次真假判別,而是聚焦在病灶區域的 70x70 patch 上。因為中風病灶在整張腦部影像中通常只佔很小面積(<5%),全圖判別會讓 Discriminator 忽略病灶細節。
- 通道交換雜訊:CTP 多序列(CBF/CBV/MTT)在不同通道中呈現不同的灌注特徵,隨機交換通道並加 noise 可迫使 Generator 學習更泛化的特徵表示。
- 推論時 training=True:刻意使用 batch statistics 而非 accumulated statistics,因為訓練集太小,accumulated BN statistics 不可靠。
4.3 範例三:pix2pix 3D 全體積合成
3D 模式直接處理整個腦部 3D 體積,保留了切片間的空間連續性(Inter-slice Spatial Continuity; 切片間空間連續性)。
1# === pix2pix 3D 訓練 ===
2python pix2pix3d.py \
3 --data_dir=./datasets/isles18_3d/train/ \
4 --test_dir=./datasets/isles18_3d/test/ \
5 --output_file_dir=./output_3d/ \
6 --disc_dim_method=patch-based
7
8# === 底層差異(vs 2D) ===
9#
10# 1. 資料載入(data/pix2pix3d_loader.py):
11# - 從 PKL 載入 3D 體積(24 x 256 x 256 x channels)
12# - 訓練增強僅做水平翻轉(np.flip(image, 2))
13# - 3D random cropping 因計算量過大已在原始碼中註解掉
14#
15# 2. Generator 架構差異:
16# - Conv3D / Conv3DTranspose 取代 Conv2D / Conv2DTranspose
17# - 非均勻 stride 設計:
18# down4-down7: stride=[1,2,2] (z 軸不縮)
19# down8: stride=[3,2,2] (z 軸從 3→1)
20# up1: stride=[3,2,2] (z 軸從 1→3)
21# up2-up5: stride=[1,2,2] (z 軸不擴)
22# - 理由:z 解析度 24 << xy 解析度 256
23#
24# 3. Patch 尺寸:[24, 70, 70](整個 z 深度 x 70x70 xy patch)
25#
26# 4. 測試輸出為 PKL → 需轉換為 NIfTI:
27
28# === 測試結果轉 NIfTI ===
29python utils/convert_3d_test_to_nii.py
30# 從 PKL 載入 3D 預測,對齊原始 CT 空間資訊後存為 NIfTI
31# 重要:使用原始 CT 的 data dtype 和 affine matrix 確保空間一致性
3D 模式的實務考量:
- 記憶體需求顯著增加:24x256x256x3 的 float32 體積約 18 MB/sample,加上 Generator 中間層記憶體,單張 GPU 至少需 12 GB VRAM
BATCH_SIZE = 1是硬性限制,無法增大- 3D 模式的優勢在於保留了切片間的解剖連續性,避免 2D 模式中相鄰切片預測不一致的問題
5. 在生醫影像合成生態系中的定位
5.1 Domain 2 子領域歸屬
本專案屬於 Sub-domain E: Specialized Applications(特化應用),具體對應「腦部影像合成(Brain Image Synthesis; 腦部影像合成)」方向。
graph LR
subgraph "Domain 2: 生醫影像合成"
A["A. 平台工具
nnUNet / MONAI /
MONAI GenModels"]
B["B. 計算病理
CLAM / CONCH /
Patho-GAN"]
C["C. 跨模態合成
medSynthesisV1 /
medSynthesis /
Synthetic-CT"]
D["D. Diffusion 模型
conditional_DDPM /
DenseDiffusion /
SD-Messenger"]
E["E. 特化應用
VICTRE / medigan /
brain-synthesis"]
F["F. 癌症影像
foundation-cancer-
image-biomarker"]
end
E --> E1["brain-synthesis
★ 本專案"]
E --> E2["VICTRE
虛擬臨床試驗"]
E --> E3["medigan
統一生成 API"]
style E1 fill:#ffcc80,stroke:#e65100,stroke-width:3px
style E fill:#fff3e0
5.2 與同生態系專案的比較
| 維度 | brain-synthesis (本專案) | medSynthesisV1 | conditional_DDPM | medigan |
|---|---|---|---|---|
| 生成方法 | pix2pix (cGAN) | 3D U-Net cGAN | DDPM (Diffusion) | 多模型聚合 |
| 目標任務 | CT→病灶標籤 | MRI→CT 跨模態 | 無條件/條件式生成 | 統一 API |
| 維度支援 | 2D + 3D | 3D | 2D | 視後端而定 |
| 框架 | TensorFlow 1.x | PyTorch | PyTorch | PyTorch |
| 教學導向 | NVIDIA DLI workshop | 研究導向 | 研究導向 | 工程導向 |
| 隱私應用 | 匿名化合成 | 無 | 無 | 無 |
5.3 在藥物開發中的應用場景
影像生物標記驗證(Imaging Biomarker Validation; 影像生物標記驗證):合成帶有已知病灶的腦部影像,作為影像分析管線的 ground truth 基準。當開發中風治療藥物需要評估「治療後病灶體積縮小」這個療效指標時,合成影像可用於驗證量測演算法的準確性。
虛擬臨床試驗資料增強(Virtual Trial Data Augmentation; 虛擬試驗資料增強):在 Phase II 影像學終點評估中,若實際收案量不足,可使用合成資料輔助訓練自動化分析模型。但須注意:合成資料僅能用於工具開發,不能替代臨床證據。
隱私保護的多中心研究(Privacy-preserving Multi-site Studies; 隱私保護多中心研究):將各機構的影像風格轉為合成影像後分享,保留病灶解剖特徵但移除個人可識別資訊(PII; 個人可識別資訊)。
6. 與其他工具的整合
6.1 與 MONAI / nnUNet 的整合
本專案的合成影像可作為 MONAI 或 nnUNet 分割管線的額外訓練資料:
1# 概念範例:將 brain-synthesis 產出的合成資料注入 MONAI 訓練管線
2
3import nibabel as nib
4from monai.transforms import (
5 Compose, LoadImaged, EnsureChannelFirstd,
6 ScaleIntensityd, RandCropByPosNegLabeld
7)
8from monai.data import CacheDataset, DataLoader
9
10# 1. 收集真實資料 + 合成資料
11real_data = [
12 {"image": f"real_case_{i}/ct.nii", "label": f"real_case_{i}/label.nii"}
13 for i in range(63)
14]
15
16# brain-synthesis 產出的合成 NIfTI(由 merge_2d_test_to_nii.py 或 convert_3d_test_to_nii.py 生成)
17synthetic_data = [
18 {"image": f"synthetic_case_{i}/ct.nii", "label": f"synthetic_case_{i}/label.nii"}
19 for i in range(200) # 合成 200 例額外資料
20]
21
22# 2. 合併資料集(真實 + 合成)
23combined = real_data + synthetic_data
24
25# 3. 定義 MONAI transform pipeline
26transforms = Compose([
27 LoadImaged(keys=["image", "label"]),
28 EnsureChannelFirstd(keys=["image", "label"]),
29 ScaleIntensityd(keys=["image"]),
30 RandCropByPosNegLabeld(
31 keys=["image", "label"],
32 label_key="label",
33 spatial_size=(96, 96, 96),
34 pos=1, neg=1, num_samples=4
35 ),
36])
37
38# 4. 建立 DataLoader
39dataset = CacheDataset(data=combined, transform=transforms, cache_rate=1.0)
40loader = DataLoader(dataset, batch_size=2, shuffle=True)
41
42# 注意:合成資料的品質直接影響下游分割效能
43# 建議做法:先訓練 → 合成 → 人工品質審核 → 過濾 → 再注入訓練
6.2 與 AIKT Pipeline 的整合
1AIKT inbox/ ← brain-synthesis 論文筆記 + 合成影像品質報告
2AIKT projects/ ← 合成資料生成實驗管理
3 └── research-YYMMDD-brain-synth/
4 ├── models/ # 訓練好的 Generator checkpoint
5 ├── synthetic_nifti/ # 合成 NIfTI 輸出
6 ├── quality_report.md # FID / SSIM / Dice 評估
7 └── integration.md # 與下游管線整合紀錄
6.3 從 TensorFlow 1.x 遷移到 PyTorch 的考量
由於本專案使用已停止維護的 TensorFlow 1.x API,在實際應用中建議遷移至 PyTorch。核心映射如下:
| TF 1.x 元件 | PyTorch 對應 |
|---|---|
tf.keras.layers.Conv2D | torch.nn.Conv2d |
tf.keras.layers.Conv2DTranspose | torch.nn.ConvTranspose2d |
tf.keras.layers.BatchNormalization | torch.nn.BatchNorm2d |
tf.GradientTape() | loss.backward() + optimizer.step() |
tf.contrib.eager.defun | 不需要(PyTorch 預設 eager) |
tf.data.Dataset | torch.utils.data.DataLoader |
若需要 3D 版本,MONAI 已內建 pix2pix-style generator,可直接使用 monai.networks.nets.UNet 作為 Generator backbone。
7. 優缺點分析
7.1 優點
| 面向 | 說明 |
|---|---|
| 教學價值高 | 源自 NVIDIA DLI workshop,程式碼結構清晰、註解完整,是學習醫學影像 GAN 的優秀入門教材 |
| 2D + 3D 雙模式 | 同時提供 2D 切片和 3D 體積的實作,方便比較兩種方法的優劣 |
| Patch-based Discriminator | 針對醫學影像中「病灶佔比極小」的特性設計,聚焦在病灶區域做判別 |
| 資料增強策略完整 | 包含 random jittering、mirroring、通道交換雜訊等多種增強手段 |
| 端到端 Pipeline | 從預處理 Notebook 到 NIfTI 輸出轉換都有提供 |
| 雙重應用目標 | 同時解決 data augmentation 和 data anonymization |
7.2 缺點與限制
| 面向 | 說明 |
|---|---|
| 框架過時 | TensorFlow 1.x + tf.contrib.eager 已不受支持,TF 2.x 不相容 |
| 依賴已棄用 API | scipy.misc.imread/imsave 在 scipy >= 1.3 已移除 |
| 缺乏量化評估 | repo 中無 FID (Frechet Inception Distance; 弗雷謝起始距離)、SSIM (Structural Similarity; 結構相似度)、Dice Score 等標準評估指標的實作 |
| 無預訓練模型 | 未提供訓練好的 checkpoint,使用者需自行訓練 |
| 資料集取得門檻 | ISLES'18 需註冊申請,BRATS/ADNI 有嚴格的資料使用協議 |
| 無 License | 未宣告開源授權,法律使用有不確定性 |
| 記憶體效率 | 3D 模式 batch_size 被限制為 1,訓練效率受限 |
| 無自動化管線 | 缺少 Dockerfile、requirements.txt、Makefile 等標準化工程設施 |
7.3 與 Diffusion Model (DM) 方法的比較
本專案使用的 pix2pix (cGAN) 方法已是 2018 年的技術,當前醫學影像合成領域已逐步轉向 Diffusion Model。比較如下:
| 維度 | pix2pix (cGAN) | Diffusion Model (DDPM) |
|---|---|---|
| 訓練穩定性 | 較差(mode collapse 風險) | 較好(穩定收斂) |
| 影像品質 | 中等(容易出現棋盤格偽影) | 高(細節更豐富) |
| 推論速度 | 快(單次前向傳播) | 慢(需數百步反向擴散) |
| 條件控制 | 強(paired image-to-image) | 中等(需額外 conditioning 機制) |
| 記憶體需求 | 中等 | 高 |
| 3D 支援 | 本專案已有實作 | 仍在研究階段 |
7.4 藍海機會
- 3D 體積合成的 Diffusion 化:將本專案的 3D pix2pix 架構升級為 3D Latent Diffusion Model (LDM; 潛在擴散模型),結合 MONAI GenerativeModels 的 3D LDM 框架。
- 多模態腦部合成:CT→MRI→PET 的連鎖合成,一次生成多模態的配對資料,減少多模態研究中的 missing modality 問題。
- 隱私保護的聯邦合成(Federated Synthesis; 聯邦合成):在 Federated Learning 架構下訓練 Generator,各機構的原始資料不離開本地,只共享模型權重。
- 病灶可控合成(Lesion-controllable Synthesis; 病灶可控合成):整合 text-guided 或 layout-guided 機制,讓使用者指定病灶位置、大小和類型後生成對應影像。
一行摘要: brain-synthesis-lesion-segmentation 是 NVIDIA DLI workshop 的教學級 pix2pix cGAN 實作,提供 2D/3D 雙模式從 CT Perfusion 合成腦中風病灶標籤,架構清晰適合入門學習,但 TF 1.x 框架已過時,實務應用建議遷移至 PyTorch/MONAI 並考慮 Diffusion Model 替代方案。
Comments