Initial commit

This commit is contained in:
Furen 2026-07-22 14:50:09 +08:00
commit c792f5eb0b
88 changed files with 15791 additions and 0 deletions

View file

@ -0,0 +1,143 @@
import json
import os
import numpy as np
import matplotlib.pyplot as plt
from typing import Dict, Tuple
# 設置中文字體支持
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
def load_data(file_path: str) -> Dict:
"""讀取JSON數據文件"""
try:
with open(file_path, 'r') as file:
return json.load(file)
except FileNotFoundError:
print(f"文件未找到: {file_path}")
return {}
except json.JSONDecodeError:
print(f"JSON解析錯誤: {file_path}")
return {}
def extract_scores(data: Dict, model: str, organ_id: str) -> Tuple[float, float]:
"""從數據中提取特定器官的分數"""
if "summary" not in data or organ_id not in data["summary"]:
return (0, 0)
scores = data["summary"][organ_id]
if model == '3D_fullres':
mean_dice = scores.get("mean_dice", 0)
std_dice = scores.get("std_dice", 0)
else:
mean_dice = scores.get("mean_dice", scores.get("mean", 0))
std_dice = scores.get("std_dice", scores.get("std", 0))
return (mean_dice, std_dice)
def create_plot(model_averages: Dict, model_stds: Dict, organs: Dict, output_path: str):
"""創建和保存圖表"""
plt.figure(figsize=(16, 5)) # Reduced figure size
plt.title('CT', fontsize=12, pad=10) # Changed title
plt.grid(True, axis='y', linestyle='--', alpha=0.7)
x = np.arange(len(organs)) * 1.5 # Reduced spacing between groups
width = 0.35 # Reduced bar width
colors = {
'2D': 'royalblue',
'3D_fullres': 'limegreen',
'3D_lowres': 'orange'
}
positions = {
'2D': x - width,
'3D_fullres': x,
'3D_lowres': x + width
}
for model, averages in model_averages.items():
bars = plt.bar(positions[model],
[averages[organ] * 100 for organ in organs.keys()],
width,
label=model,
color=colors[model])
# 調整數值標籤位置和大小
for j, bar in enumerate(bars):
height = bar.get_height()
organ = list(organs.keys())[j]
std = model_stds[model][organ] * 100
plt.text(bar.get_x() + bar.get_width()/2., height,
f'{height:.1f}±{std:.1f}%',
ha='center',
va='bottom',
fontsize=6, # Reduced font size
rotation=0) # Optional: rotate text if still crowded
plt.ylim(0, 100)
plt.ylabel('DSC (%)', fontsize=10)
plt.xlabel('OAR', fontsize=10)
plt.xticks(x, [organs[k] for k in organs.keys()], rotation=30, ha='right', fontsize=8)
plt.legend(loc='upper right', fontsize=8)
plt.tight_layout()
os.makedirs(os.path.dirname(output_path), exist_ok=True)
plt.savefig(output_path, dpi=300, bbox_inches='tight')
plt.close()
def print_results(model_averages: Dict, model_stds: Dict, organs: Dict):
"""打印數值結果"""
print("\n平均 Dice 分數 (百分比):")
for model in model_averages.keys():
print(f"\n{model}:")
for organ in organs.keys():
avg = model_averages[model][organ] * 100
std = model_stds[model][organ] * 100
print(f"{organs[organ]}: {avg:.1f}±{std:.1f}%")
def main():
# 定義路徑和器官映射
base_path = '/mnt/1248/onlylian/CT/Dice/test'
paths = {
'2D': os.path.join(base_path, 'CT_test_dice_results_2D.json'),
'3D_fullres': os.path.join(base_path, 'CT_test_dice_results_3D_fullres.json'),
'3D_lowres': os.path.join(base_path, 'CT_test_dice_results_3D_lowres.json')
}
organs = {
"1": "Brainstem",
"2": "Right_Eye",
"3": "Left_Eye",
"4": "Optic_Chiasm",
"5": "Right_Optic_Nerve",
"6": "Left_Optic_Nerve"
}
# 初始化數據存儲
model_dice_scores = {
model: {organ: None for organ in organs.keys()}
for model in paths.keys()
}
# 讀取和處理數據
for model, path in paths.items():
data = load_data(path)
for organ_id in organs.keys():
model_dice_scores[model][organ_id] = extract_scores(data, model, organ_id)
# 計算平均值和標準差
model_averages = {}
model_stds = {}
for model, scores in model_dice_scores.items():
model_averages[model] = {organ: scores[organ][0] if scores[organ] else 0 for organ in organs.keys()}
model_stds[model] = {organ: scores[organ][1] if scores[organ] else 0 for organ in organs.keys()}
# 創建圖表並保存
output_path = os.path.join(base_path, 'CT_test_dice_scores_comparison.png')
create_plot(model_averages, model_stds, organs, output_path)
# 打印結果
print_results(model_averages, model_stds, organs)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,306 @@
import json
import os
import numpy as np
import SimpleITK as sitk
from tqdm import tqdm
import time
def load_nifti(file_path):
"""加載 NIfTI 文件"""
return sitk.GetArrayFromImage(sitk.ReadImage(file_path))
def compute_dice(pred, gt):
"""計算Dice係數"""
intersection = np.sum(pred & gt)
sum_pred = np.sum(pred)
sum_gt = np.sum(gt)
if sum_pred == 0 and sum_gt == 0:
return 1.0
elif sum_pred == 0 or sum_gt == 0:
return 0.0
return 2.0 * intersection / (sum_pred + sum_gt)
def separate_structures(combined_mask, structure_name=""):
"""分離左右結構"""
# 使用SimpleITK的ConnectedComponent
sitk_image = sitk.GetImageFromArray(combined_mask)
labeled_components = sitk.ConnectedComponent(sitk_image)
labeled_array = sitk.GetArrayFromImage(labeled_components)
# 計算連通區域數量和大小
unique, counts = np.unique(labeled_array, return_counts=True)
if len(unique) < 3: # 背景算一個所以至少需要3個
print(f"警告:{structure_name} 未找到足夠的連通區域")
return None, None
# 排除背景標籤0並按大小排序
region_sizes = [(i, count) for i, count in zip(unique[1:], counts[1:])]
sorted_regions = sorted(region_sizes, key=lambda x: x[1], reverse=True)
if len(sorted_regions) < 2:
print(f"警告:{structure_name} 未找到兩個主要區域")
return None, None
# 獲取兩個最大區域
region1_mask = (labeled_array == sorted_regions[0][0])
region2_mask = (labeled_array == sorted_regions[1][0])
# 計算質心
def get_centroid(mask):
indices = np.where(mask)
return np.mean(indices[2]) # 使用x軸第三維度的平均值
centroid1 = get_centroid(region1_mask)
centroid2 = get_centroid(region2_mask)
# 根據x軸位置確定左右
if centroid1 > centroid2:
right = region1_mask
left = region2_mask
else:
right = region2_mask
left = region1_mask
return right.astype(np.uint8), left.astype(np.uint8)
def evaluate_case(gt_path, pred_path, labels):
"""評估單個案例"""
print(f"\n測試文件: {os.path.basename(pred_path)}")
# 加載圖像
start_time = time.time()
gt_img = load_nifti(gt_path)
pred_img = load_nifti(pred_path)
load_time = time.time() - start_time
print(f"圖像大小: {gt_img.shape}")
print(f"加載時間: {load_time:.2f}")
results = {}
total_time = 0
# 處理眼睛
start_time = time.time()
combined_gt_eye = ((gt_img == 2) | (gt_img == 3)).astype(np.uint8)
combined_pred_eye = ((pred_img == 2) | (pred_img == 3)).astype(np.uint8)
# 形態學處理眼睛
combined_pred_eye_sitk = sitk.GetImageFromArray(combined_pred_eye)
closing_filter = sitk.BinaryMorphologicalClosingImageFilter()
closing_filter.SetKernelRadius(2)
processed_pred_eye = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_eye_sitk))
# 計算眼睛的Dice
eye_dice = compute_dice(processed_pred_eye, combined_gt_eye)
eye_time = time.time() - start_time
total_time += eye_time
results['combined_eye'] = eye_dice
print(f"眼睛合併計算時間: {eye_time:.2f}秒, Dice: {eye_dice:.4f}")
# 分離眼睛
right_pred_eye, left_pred_eye = separate_structures(processed_pred_eye, "眼睛")
right_gt_eye, left_gt_eye = separate_structures(combined_gt_eye, "眼睛")
# 處理視神經
start_time = time.time()
combined_gt_nerve = ((gt_img == 5) | (gt_img == 6)).astype(np.uint8)
combined_pred_nerve = ((pred_img == 5) | (pred_img == 6)).astype(np.uint8)
# 形態學處理視神經
combined_pred_nerve_sitk = sitk.GetImageFromArray(combined_pred_nerve)
processed_pred_nerve = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_nerve_sitk))
# 計算視神經的Dice
nerve_dice = compute_dice(processed_pred_nerve, combined_gt_nerve)
nerve_time = time.time() - start_time
total_time += nerve_time
results['combined_nerve'] = nerve_dice
print(f"視神經合併計算時間: {nerve_time:.2f}秒, Dice: {nerve_dice:.4f}")
# 分離視神經
right_pred_nerve, left_pred_nerve = separate_structures(processed_pred_nerve, "視神經")
right_gt_nerve, left_gt_nerve = separate_structures(combined_gt_nerve, "視神經")
# 計算各個標籤的Dice
for label in labels:
start_time = time.time()
if label == 1: # Brainstem
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 2 and right_pred_eye is not None: # Right Eye
gt_binary = right_gt_eye
pred_binary = right_pred_eye
elif label == 3 and left_pred_eye is not None: # Left Eye
gt_binary = left_gt_eye
pred_binary = left_pred_eye
elif label == 4: # Optic Chiasm
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 5 and right_pred_nerve is not None: # Right Optic Nerve
gt_binary = right_gt_nerve
pred_binary = right_pred_nerve
elif label == 6 and left_pred_nerve is not None: # Left Optic Nerve
gt_binary = left_gt_nerve
pred_binary = left_pred_nerve
else:
continue
try:
dice = compute_dice(pred_binary, gt_binary)
compute_time = time.time() - start_time
total_time += compute_time
results[label] = dice
print(f"標籤 {label} 計算時間: {compute_time:.2f}秒, Dice: {dice:.4f}")
except Exception as e:
print(f"處理標籤 {label} 時出錯:{str(e)}")
results[label] = np.nan
continue
print(f"單個案例總計算時間: {total_time:.2f}")
return results
def evaluate_fold(gt_folder, pred_folder, labels):
"""評估單個fold的所有案例"""
fold_results = {label: [] for label in labels}
fold_results['combined_eye'] = []
fold_results['combined_nerve'] = []
pred_files = sorted([f for f in os.listdir(pred_folder) if f.endswith('.nii.gz')])
for pred_file in tqdm(pred_files, desc=f"Processing {os.path.basename(pred_folder)}"):
gt_path = os.path.join(gt_folder, pred_file)
pred_path = os.path.join(pred_folder, pred_file)
if not os.path.exists(gt_path):
print(f"找不到對應的真實標籤文件:{gt_path}")
continue
case_results = evaluate_case(gt_path, pred_path, labels)
# 收集結果
for key in case_results:
if not np.isnan(case_results[key]):
if key in fold_results:
fold_results[key].append(case_results[key])
return fold_results
def evaluate_model(gt_folder, base_pred_folder, model_name, labels, organ_names):
"""評估單個模型的所有fold"""
print(f"\n開始評估模型:{model_name}")
all_results = {label: [] for label in labels}
all_results['combined_eye'] = []
all_results['combined_nerve'] = []
fold_results = {}
# 處理 fold_0 到 fold_4
for fold in range(5):
print(f"\n處理 {model_name} - Fold {fold}")
pred_folder = os.path.join(base_pred_folder, f"fold_{fold}")
if not os.path.exists(pred_folder):
print(f"找不到預測文件夾:{pred_folder}")
continue
results = evaluate_fold(gt_folder, pred_folder, labels)
fold_results[f"fold_{fold}"] = results
# 合併結果
for key in results:
all_results[key].extend(results[key])
# 計算總平均值和標準差
summary = {}
for key in all_results:
if all_results[key]:
mean_dice = np.mean(all_results[key])
std_dice = np.std(all_results[key])
summary[key] = {
'mean_dice': float(mean_dice),
'std_dice': float(std_dice),
'n_samples': len(all_results[key])
}
else:
summary[key] = {
'mean_dice': np.nan,
'std_dice': np.nan,
'n_samples': 0
}
# 保存結果到指定路径
save_dir = "/mnt/1248/onlylian/CT/Dice/test"
os.makedirs(save_dir, exist_ok=True)
filename = os.path.join(save_dir, f'CT_test_dice_results_{model_name}.json')
model_results = {
'summary': summary,
'fold_results': fold_results
}
with open(filename, 'w') as f:
json.dump(model_results, f, indent=4)
print(f"\n{model_name}結果已保存到 {filename}")
# 打印結果
print(f"\n{model_name} 模型的 Dice 結果所有fold的平均值:")
# 打印器官結果
for label in labels:
if label in summary:
mean = summary[label]['mean_dice']
std = summary[label]['std_dice']
n = summary[label]['n_samples']
organ_name = organ_names.get(label, f"Label_{label}")
if not np.isnan(mean):
print(f"{organ_name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{organ_name}: 無法計算 (n={n})")
# 打印合併結果
print("\n合併結構的結果:")
for key in ['combined_eye', 'combined_nerve']:
if key in summary:
mean = summary[key]['mean_dice']
std = summary[key]['std_dice']
n = summary[key]['n_samples']
name = "合併眼睛" if key == 'combined_eye' else "合併視神經"
if not np.isnan(mean):
print(f"{name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{name}: 無法計算 (n={n})")
return summary
if __name__ == "__main__":
# 設置路徑和標籤
gt_folder = "/mnt/1248/onlylian/nnUNet/nnUNet_raw/Dataset555/labelsTs"
models = {
'2D': '/mnt/1248/onlylian/CT/output_predictions/2d',
'3D_fullres': '/mnt/1248/onlylian/CT/output_predictions/3d_fullres',
'3D_lowres': '/mnt/1248/onlylian/CT/output_predictions/3d_lowres'
}
labels = [1, 2, 3, 4, 5, 6]
organ_names = {
1: "Brainstem",
2: "Right_Eye",
3: "Left_Eye",
4: "Optic_Chiasm",
5: "Right_Optic_Nerve",
6: "Left_Optic_Nerve"
}
# 讓用戶選擇要運行哪個模型
print("可用的模型:")
for i, model_name in enumerate(models.keys(), 1):
print(f"{i}. {model_name}")
choice = input("\n請選擇要運行的模型 (輸入數字1-3): ")
try:
model_index = int(choice) - 1
model_names = list(models.keys())
selected_model = model_names[model_index]
evaluate_model(gt_folder, models[selected_model], selected_model, labels, organ_names)
except (ValueError, IndexError):
print("無效的選擇請輸入1-3之間的數字。")

View file

@ -0,0 +1,142 @@
import json
import os
import numpy as np
import matplotlib.pyplot as plt
from typing import Dict, Tuple
# 設置中文字體支持
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
def load_data(file_path: str) -> Dict:
"""讀取JSON數據文件"""
try:
with open(file_path, 'r') as file:
return json.load(file)
except FileNotFoundError:
print(f"文件未找到: {file_path}")
return {}
except json.JSONDecodeError:
print(f"JSON解析錯誤: {file_path}")
return {}
def extract_scores(data: Dict, model: str, organ_id: str) -> Tuple[float, float]:
"""從數據中提取特定器官的分數"""
if "summary" not in data or organ_id not in data["summary"]:
return (0, 0)
scores = data["summary"][organ_id]
if model == '3D_fullres':
mean_dice = scores.get("mean_dice", 0)
std_dice = scores.get("std_dice", 0)
else:
mean_dice = scores.get("mean_dice", scores.get("mean", 0))
std_dice = scores.get("std_dice", scores.get("std", 0))
return (mean_dice, std_dice)
def create_plot(model_averages: Dict, model_stds: Dict, organs: Dict, output_path: str):
"""創建和保存圖表"""
plt.figure(figsize=(16, 5)) # Reduced figure size
plt.title('CT', fontsize=12, pad=10) # Changed title
plt.grid(True, axis='y', linestyle='--', alpha=0.7)
x = np.arange(len(organs)) * 1.5 # Reduced spacing between groups
width = 0.35 # Reduced bar width
colors = {
'2D': 'royalblue',
'3D_fullres': 'limegreen',
'3D_lowres': 'orange'
}
positions = {
'2D': x - width,
'3D_fullres': x,
'3D_lowres': x + width
}
for model, averages in model_averages.items():
bars = plt.bar(positions[model],
[averages[organ] * 100 for organ in organs.keys()],
width,
label=model,
color=colors[model])
# 添加數值標籤
for j, bar in enumerate(bars):
height = bar.get_height()
organ = list(organs.keys())[j]
std = model_stds[model][organ] * 100
plt.text(bar.get_x() + bar.get_width()/2., height + 0.5,
f'{height:.1f}±{std:.1f}%',
ha='center',
va='bottom',
fontsize=6) # Reduced font size
plt.ylim(0, 100)
plt.ylabel('DC (%)', fontsize=10)
plt.xlabel('OAR', fontsize=10)
plt.xticks(x, [organs[k] for k in organs.keys()], rotation=30, ha='right', fontsize=8)
plt.legend(loc='upper right', fontsize=8)
plt.tight_layout()
os.makedirs(os.path.dirname(output_path), exist_ok=True)
plt.savefig(output_path, dpi=300, bbox_inches='tight')
plt.close()
def print_results(model_averages: Dict, model_stds: Dict, organs: Dict):
"""打印數值結果"""
print("\n平均 Dice 分數 (百分比):")
for model in model_averages.keys():
print(f"\n{model}:")
for organ in organs.keys():
avg = model_averages[model][organ] * 100
std = model_stds[model][organ] * 100
print(f"{organs[organ]}: {avg:.1f}±{std:.1f}%")
def main():
# 定義路徑和器官映射
base_path = '/mnt/1248/onlylian/CT/Dice/validation'
paths = {
'2D': os.path.join(base_path, 'CT_validation_dice_results_2D.json'),
'3D_fullres': os.path.join(base_path, 'CT_validation_dice_results_3D_fullres.json'),
'3D_lowres': os.path.join(base_path, 'CT_validation_dice_results_3D_lowres.json')
}
organs = {
"1": "Brainstem",
"2": "Right_Eye",
"3": "Left_Eye",
"4": "Optic_Chiasm",
"5": "Right_Optic_Nerve",
"6": "Left_Optic_Nerve"
}
# 初始化數據存儲
model_dice_scores = {
model: {organ: None for organ in organs.keys()}
for model in paths.keys()
}
# 讀取和處理數據
for model, path in paths.items():
data = load_data(path)
for organ_id in organs.keys():
model_dice_scores[model][organ_id] = extract_scores(data, model, organ_id)
# 計算平均值和標準差
model_averages = {}
model_stds = {}
for model, scores in model_dice_scores.items():
model_averages[model] = {organ: scores[organ][0] if scores[organ] else 0 for organ in organs.keys()}
model_stds[model] = {organ: scores[organ][1] if scores[organ] else 0 for organ in organs.keys()}
# 創建圖表並保存
output_path = os.path.join(base_path, 'CT_validation_dice_scores_comparison.png')
create_plot(model_averages, model_stds, organs, output_path)
# 打印結果
print_results(model_averages, model_stds, organs)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,622 @@
# import json
# import os
# import numpy as np
# import SimpleITK as sitk
# from tqdm import tqdm
# import time
# def load_nifti(file_path):
# """加載 NIfTI 文件"""
# return sitk.GetArrayFromImage(sitk.ReadImage(file_path))
# def compute_dice(pred, gt):
# """計算Dice係數"""
# intersection = np.sum(pred & gt)
# sum_pred = np.sum(pred)
# sum_gt = np.sum(gt)
# if sum_pred == 0 and sum_gt == 0:
# return 1.0
# elif sum_pred == 0 or sum_gt == 0:
# return 0.0
# return 2.0 * intersection / (sum_pred + sum_gt)
# def separate_structures(combined_mask, structure_name=""):
# """分離左右結構"""
# sitk_image = sitk.GetImageFromArray(combined_mask)
# labeled_components = sitk.ConnectedComponent(sitk_image)
# labeled_array = sitk.GetArrayFromImage(labeled_components)
# unique, counts = np.unique(labeled_array, return_counts=True)
# if len(unique) < 3:
# print(f"警告:{structure_name} 未找到足夠的連通區域")
# return None, None
# region_sizes = [(i, count) for i, count in zip(unique[1:], counts[1:])]
# sorted_regions = sorted(region_sizes, key=lambda x: x[1], reverse=True)
# if len(sorted_regions) < 2:
# print(f"警告:{structure_name} 未找到兩個主要區域")
# return None, None
# region1_mask = (labeled_array == sorted_regions[0][0])
# region2_mask = (labeled_array == sorted_regions[1][0])
# def get_centroid(mask):
# indices = np.where(mask)
# return np.mean(indices[2])
# centroid1 = get_centroid(region1_mask)
# centroid2 = get_centroid(region2_mask)
# if centroid1 > centroid2:
# right = region1_mask
# left = region2_mask
# else:
# right = region2_mask
# left = region1_mask
# return right.astype(np.uint8), left.astype(np.uint8)
# def evaluate_case(gt_path, pred_path, labels):
# """評估單個案例"""
# print(f"\n測試文件: {os.path.basename(pred_path)}")
# start_time = time.time()
# gt_img = load_nifti(gt_path)
# pred_img = load_nifti(pred_path)
# load_time = time.time() - start_time
# print(f"圖像大小: {gt_img.shape}")
# print(f"加載時間: {load_time:.2f}秒")
# results = {}
# total_time = 0
# # 處理眼睛
# start_time = time.time()
# combined_gt_eye = ((gt_img == 2) | (gt_img == 3)).astype(np.uint8)
# combined_pred_eye = ((pred_img == 2) | (pred_img == 3)).astype(np.uint8)
# combined_pred_eye_sitk = sitk.GetImageFromArray(combined_pred_eye)
# closing_filter = sitk.BinaryMorphologicalClosingImageFilter()
# closing_filter.SetKernelRadius(2)
# processed_pred_eye = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_eye_sitk))
# eye_dice = compute_dice(processed_pred_eye, combined_gt_eye)
# eye_time = time.time() - start_time
# total_time += eye_time
# results['combined_eye'] = eye_dice
# print(f"眼睛合併計算時間: {eye_time:.2f}秒, Dice: {eye_dice:.4f}")
# right_pred_eye, left_pred_eye = separate_structures(processed_pred_eye, "眼睛")
# right_gt_eye, left_gt_eye = separate_structures(combined_gt_eye, "眼睛")
# # 處理視神經
# start_time = time.time()
# combined_gt_nerve = ((gt_img == 5) | (gt_img == 6)).astype(np.uint8)
# combined_pred_nerve = ((pred_img == 5) | (pred_img == 6)).astype(np.uint8)
# combined_pred_nerve_sitk = sitk.GetImageFromArray(combined_pred_nerve)
# processed_pred_nerve = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_nerve_sitk))
# nerve_dice = compute_dice(processed_pred_nerve, combined_gt_nerve)
# nerve_time = time.time() - start_time
# total_time += nerve_time
# results['combined_nerve'] = nerve_dice
# print(f"視神經合併計算時間: {nerve_time:.2f}秒, Dice: {nerve_dice:.4f}")
# right_pred_nerve, left_pred_nerve = separate_structures(processed_pred_nerve, "視神經")
# right_gt_nerve, left_gt_nerve = separate_structures(combined_gt_nerve, "視神經")
# for label in labels:
# start_time = time.time()
# if label == 1: # Brainstem
# gt_binary = (gt_img == label).astype(np.uint8)
# pred_binary = (pred_img == label).astype(np.uint8)
# elif label == 2 and right_pred_eye is not None: # Right Eye
# gt_binary = right_gt_eye
# pred_binary = right_pred_eye
# elif label == 3 and left_pred_eye is not None: # Left Eye
# gt_binary = left_gt_eye
# pred_binary = left_pred_eye
# elif label == 4: # Optic Chiasm
# gt_binary = (gt_img == label).astype(np.uint8)
# pred_binary = (pred_img == label).astype(np.uint8)
# elif label == 5 and right_pred_nerve is not None: # Right Optic Nerve
# gt_binary = right_gt_nerve
# pred_binary = right_pred_nerve
# elif label == 6 and left_pred_nerve is not None: # Left Optic Nerve
# gt_binary = left_gt_nerve
# pred_binary = left_pred_nerve
# else:
# continue
# try:
# dice = compute_dice(pred_binary, gt_binary)
# compute_time = time.time() - start_time
# total_time += compute_time
# results[label] = dice
# print(f"標籤 {label} 計算時間: {compute_time:.2f}秒, Dice: {dice:.4f}")
# except Exception as e:
# print(f"處理標籤 {label} 時出錯:{str(e)}")
# results[label] = np.nan
# continue
# print(f"單個案例總計算時間: {total_time:.2f}秒")
# return results
# def evaluate_validation_fold(gt_base_folder, validation_folder, labels):
# """評估單個fold的驗證集"""
# fold_results = {label: [] for label in labels}
# fold_results['combined_eye'] = []
# fold_results['combined_nerve'] = []
# pred_files = sorted([f for f in os.listdir(validation_folder) if f.endswith('.nii.gz')])
# for pred_file in tqdm(pred_files, desc=f"Processing {os.path.basename(validation_folder)}"):
# patient_id = pred_file.split('.nii.gz')[0]
# gt_path = os.path.join(gt_base_folder, f"{patient_id}.nii.gz")
# pred_path = os.path.join(validation_folder, pred_file)
# if not os.path.exists(gt_path):
# print(f"找不到對應的真實標籤文件:{gt_path}")
# continue
# case_results = evaluate_case(gt_path, pred_path, labels)
# for key in case_results:
# if not np.isnan(case_results[key]):
# if key in fold_results:
# fold_results[key].append(case_results[key])
# return fold_results
# def evaluate_model(gt_folder, model_path, model_name, labels, organ_names, output_dir):
# """評估模型所有fold的驗證集"""
# print(f"\n開始評估模型{model_name}")
# all_results = {label: [] for label in labels}
# all_results['combined_eye'] = []
# all_results['combined_nerve'] = []
# fold_results = {}
# for fold in range(5):
# validation_folder = os.path.join(model_path, f"fold_{fold}", "validation")
# if not os.path.exists(validation_folder):
# print(f"找不到fold_{fold}的驗證資料夾:{validation_folder}")
# continue
# print(f"\n處理 {model_name} - Fold {fold} 驗證集")
# results = evaluate_validation_fold(gt_folder, validation_folder, labels)
# fold_results[f"fold_{fold}"] = results
# for key in results:
# all_results[key].extend(results[key])
# summary = {}
# for key in all_results:
# if all_results[key]:
# mean_dice = np.mean(all_results[key])
# std_dice = np.std(all_results[key])
# summary[key] = {
# 'mean_dice': float(mean_dice),
# 'std_dice': float(std_dice),
# 'n_samples': len(all_results[key])
# }
# else:
# summary[key] = {
# 'mean_dice': np.nan,
# 'std_dice': np.nan,
# 'n_samples': 0
# }
# model_results = {
# 'summary': summary,
# 'fold_results': fold_results
# }
# output_path = os.path.join(output_dir, 'CT', 'Dice', 'validation')
# os.makedirs(output_path, exist_ok=True)
# filename = os.path.join(output_path, f'dice_results_{model_name}.json')
# with open(filename, 'w') as f:
# json.dump(model_results, f, indent=4)
# print(f"\n{model_name}驗證集結果已保存到 {filename}")
# print(f"\n{model_name} 模型的驗證集 Dice 結果所有fold的平均值:")
# for label in labels:
# if label in summary:
# mean = summary[label]['mean_dice']
# std = summary[label]['std_dice']
# n = summary[label]['n_samples']
# organ_name = organ_names.get(label, f"Label_{label}")
# if not np.isnan(mean):
# print(f"{organ_name}: {mean:.4f} ± {std:.4f} (n={n})")
# else:
# print(f"{organ_name}: 無法計算 (n={n})")
# print("\n合併結構的結果:")
# for key in ['combined_eye', 'combined_nerve']:
# if key in summary:
# mean = summary[key]['mean_dice']
# std = summary[key]['std_dice']
# n = summary[key]['n_samples']
# name = "合併眼睛" if key == 'combined_eye' else "合併視神經"
# if not np.isnan(mean):
# print(f"{name}: {mean:.4f} ± {std:.4f} (n={n})")
# else:
# print(f"{name}: 無法計算 (n={n})")
# return summary
# if __name__ == "__main__":
# base_dir = "/mnt/1248/onlylian"
# gt_folder = os.path.join(base_dir, "nnUNet/nnUNet_raw/Dataset555/labelsTr")
# output_dir = base_dir
# models = {
# '2D': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset555/nnUNetTrainer__nnUNetPlans__2d"),
# '3D_fullres': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset555/nnUNetTrainer__nnUNetPlans__3d_fullres"),
# '3D_lowres': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset555/nnUNetTrainer__nnUNetPlans__3d_lowres")
# }
# labels = [1, 2, 3, 4, 5, 6]
# organ_names = {
# 1: "Brainstem",
# 2: "Right_Eye",
# 3: "Left_Eye",
# 4: "Optic_Chiasm",
# 5: "Right_Optic_Nerve",
# 6: "Left_Optic_Nerve"
# }
# print("可用的模型:")
# for i, model_name in enumerate(models.keys(), 1):
# print(f"{i}. {model_name}")
# choice = input("\n請選擇要運行的模型 (輸入數字1-3): ")
# try:
# model_index = int(choice) - 1
# model_names = list(models.keys())
# selected_model = model_names[model_index]
# evaluate_model(gt_folder, models[selected_model], selected_model, labels, organ_names, output_dir)
# except (ValueError, IndexError):
# print("無效的選擇請輸入1-3之間的數字。")
import json
import os
import numpy as np
import SimpleITK as sitk
from tqdm import tqdm
import time
import pandas as pd
def load_nifti(file_path):
"""加載 NIfTI 文件"""
return sitk.GetArrayFromImage(sitk.ReadImage(file_path))
def compute_dice(pred, gt):
"""計算Dice係數"""
intersection = np.sum(pred & gt)
sum_pred = np.sum(pred)
sum_gt = np.sum(gt)
if sum_pred == 0 and sum_gt == 0:
return 1.0
elif sum_pred == 0 or sum_gt == 0:
return 0.0
return 2.0 * intersection / (sum_pred + sum_gt)
def separate_structures(combined_mask, structure_name=""):
"""分離左右結構"""
sitk_image = sitk.GetImageFromArray(combined_mask)
labeled_components = sitk.ConnectedComponent(sitk_image)
labeled_array = sitk.GetArrayFromImage(labeled_components)
unique, counts = np.unique(labeled_array, return_counts=True)
if len(unique) < 3:
print(f"警告:{structure_name} 未找到足夠的連通區域")
return None, None
region_sizes = [(i, count) for i, count in zip(unique[1:], counts[1:])]
sorted_regions = sorted(region_sizes, key=lambda x: x[1], reverse=True)
if len(sorted_regions) < 2:
print(f"警告:{structure_name} 未找到兩個主要區域")
return None, None
region1_mask = (labeled_array == sorted_regions[0][0])
region2_mask = (labeled_array == sorted_regions[1][0])
def get_centroid(mask):
indices = np.where(mask)
return np.mean(indices[2])
centroid1 = get_centroid(region1_mask)
centroid2 = get_centroid(region2_mask)
if centroid1 > centroid2:
right = region1_mask
left = region2_mask
else:
right = region2_mask
left = region1_mask
return right.astype(np.uint8), left.astype(np.uint8)
def collect_image_stats(gt_path):
"""收集圖像大小信息"""
gt_img = load_nifti(gt_path)
return gt_img.shape
def evaluate_case(gt_path, pred_path, labels):
"""評估單個案例"""
print(f"\n測試文件: {os.path.basename(pred_path)}")
start_time = time.time()
gt_img = load_nifti(gt_path)
pred_img = load_nifti(pred_path)
load_time = time.time() - start_time
print(f"圖像大小: {gt_img.shape}")
print(f"加載時間: {load_time:.2f}")
results = {}
total_time = 0
# 處理眼睛
start_time = time.time()
combined_gt_eye = ((gt_img == 2) | (gt_img == 3)).astype(np.uint8)
combined_pred_eye = ((pred_img == 2) | (pred_img == 3)).astype(np.uint8)
combined_pred_eye_sitk = sitk.GetImageFromArray(combined_pred_eye)
closing_filter = sitk.BinaryMorphologicalClosingImageFilter()
closing_filter.SetKernelRadius(2)
processed_pred_eye = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_eye_sitk))
eye_dice = compute_dice(processed_pred_eye, combined_gt_eye)
eye_time = time.time() - start_time
total_time += eye_time
results['combined_eye'] = eye_dice
print(f"眼睛合併計算時間: {eye_time:.2f}秒, Dice: {eye_dice:.4f}")
right_pred_eye, left_pred_eye = separate_structures(processed_pred_eye, "眼睛")
right_gt_eye, left_gt_eye = separate_structures(combined_gt_eye, "眼睛")
# 處理視神經
start_time = time.time()
combined_gt_nerve = ((gt_img == 5) | (gt_img == 6)).astype(np.uint8)
combined_pred_nerve = ((pred_img == 5) | (pred_img == 6)).astype(np.uint8)
combined_pred_nerve_sitk = sitk.GetImageFromArray(combined_pred_nerve)
processed_pred_nerve = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_nerve_sitk))
nerve_dice = compute_dice(processed_pred_nerve, combined_gt_nerve)
nerve_time = time.time() - start_time
total_time += nerve_time
results['combined_nerve'] = nerve_dice
print(f"視神經合併計算時間: {nerve_time:.2f}秒, Dice: {nerve_dice:.4f}")
right_pred_nerve, left_pred_nerve = separate_structures(processed_pred_nerve, "視神經")
right_gt_nerve, left_gt_nerve = separate_structures(combined_gt_nerve, "視神經")
for label in labels:
start_time = time.time()
if label == 1: # Brainstem
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 2 and right_pred_eye is not None: # Right Eye
gt_binary = right_gt_eye
pred_binary = right_pred_eye
elif label == 3 and left_pred_eye is not None: # Left Eye
gt_binary = left_gt_eye
pred_binary = left_pred_eye
elif label == 4: # Optic Chiasm
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 5 and right_pred_nerve is not None: # Right Optic Nerve
gt_binary = right_gt_nerve
pred_binary = right_pred_nerve
elif label == 6 and left_pred_nerve is not None: # Left Optic Nerve
gt_binary = left_gt_nerve
pred_binary = left_pred_nerve
else:
continue
try:
dice = compute_dice(pred_binary, gt_binary)
compute_time = time.time() - start_time
total_time += compute_time
results[label] = dice
print(f"標籤 {label} 計算時間: {compute_time:.2f}秒, Dice: {dice:.4f}")
except Exception as e:
print(f"處理標籤 {label} 時出錯:{str(e)}")
results[label] = np.nan
continue
print(f"單個案例總計算時間: {total_time:.2f}")
return results
def evaluate_validation_fold(gt_base_folder, validation_folder, labels):
"""評估單個fold的驗證集"""
fold_results = {label: [] for label in labels}
fold_results['combined_eye'] = []
fold_results['combined_nerve'] = []
image_sizes = []
pred_files = sorted([f for f in os.listdir(validation_folder) if f.endswith('.nii.gz')])
for pred_file in tqdm(pred_files, desc=f"Processing {os.path.basename(validation_folder)}"):
patient_id = pred_file.split('.nii.gz')[0]
gt_path = os.path.join(gt_base_folder, f"{patient_id}.nii.gz")
pred_path = os.path.join(validation_folder, pred_file)
if not os.path.exists(gt_path):
print(f"找不到對應的真實標籤文件:{gt_path}")
continue
# 收集圖像大小信息
img_size = collect_image_stats(gt_path)
image_sizes.append({
'patient_id': patient_id,
'depth': img_size[0],
'height': img_size[1],
'width': img_size[2]
})
case_results = evaluate_case(gt_path, pred_path, labels)
for key in case_results:
if not np.isnan(case_results[key]):
if key in fold_results:
fold_results[key].append(case_results[key])
return fold_results, image_sizes
def evaluate_model(gt_folder, model_path, model_name, labels, organ_names, output_dir):
"""評估模型所有fold的驗證集"""
print(f"\n開始評估模型:{model_name}")
all_results = {label: [] for label in labels}
all_results['combined_eye'] = []
all_results['combined_nerve'] = []
fold_results = {}
all_image_sizes = []
for fold in range(5):
validation_folder = os.path.join(model_path, f"fold_{fold}", "validation")
if not os.path.exists(validation_folder):
print(f"找不到fold_{fold}的驗證資料夾:{validation_folder}")
continue
print(f"\n處理 {model_name} - Fold {fold} 驗證集")
results, image_sizes = evaluate_validation_fold(gt_folder, validation_folder, labels)
fold_results[f"fold_{fold}"] = results
all_image_sizes.extend(image_sizes)
for key in results:
all_results[key].extend(results[key])
# 保存圖像大小統計信息
df = pd.DataFrame(all_image_sizes)
stats_output_path = os.path.join(output_dir, 'CT', 'Stats')
os.makedirs(stats_output_path, exist_ok=True)
# 保存為Excel文件
excel_path = os.path.join(stats_output_path, f'image_sizes_{model_name}.xlsx')
df.to_excel(excel_path, index=False)
print(f"\n圖像大小統計已保存到 {excel_path}")
# 同時保存為txt文件
txt_path = os.path.join(stats_output_path, f'image_sizes_{model_name}.txt')
with open(txt_path, 'w') as f:
f.write("Patient ID, Depth, Height, Width\n")
for size in all_image_sizes:
f.write(f"{size['patient_id']}, {size['depth']}, {size['height']}, {size['width']}\n")
print(f"圖像大小統計已保存到 {txt_path}")
# 計算並打印統計摘要
print("\n圖像大小統計摘要:")
print(f"總樣本數: {len(df)}")
print("\n尺寸統計 (depth, height, width):")
print(f"最小值: ({df['depth'].min()}, {df['height'].min()}, {df['width'].min()})")
print(f"最大值: ({df['depth'].max()}, {df['height'].max()}, {df['width'].max()})")
print(f"平均值: ({df['depth'].mean():.1f}, {df['height'].mean():.1f}, {df['width'].mean():.1f})")
print(f"中位數: ({df['depth'].median()}, {df['height'].median()}, {df['width'].median()})")
summary = {}
for key in all_results:
if all_results[key]:
mean_dice = np.mean(all_results[key])
std_dice = np.std(all_results[key])
summary[key] = {
'mean_dice': float(mean_dice),
'std_dice': float(std_dice),
'n_samples': len(all_results[key])
}
else:
summary[key] = {
'mean_dice': np.nan,
'std_dice': np.nan,
'n_samples': 0
}
model_results = {
'summary': summary,
'fold_results': fold_results
}
output_path = os.path.join(output_dir, 'CT', 'Dice', 'validation')
os.makedirs(output_path, exist_ok=True)
filename = os.path.join(output_path, f'dice_results_{model_name}.json')
with open(filename, 'w') as f:
json.dump(model_results, f, indent=4)
print(f"\n{model_name}驗證集結果已保存到 {filename}")
print(f"\n{model_name} 模型的驗證集 Dice 結果所有fold的平均值:")
for label in labels:
if label in summary:
mean = summary[label]['mean_dice']
std = summary[label]['std_dice']
n = summary[label]['n_samples']
organ_name = organ_names.get(label, f"Label_{label}")
if not np.isnan(mean):
print(f"{organ_name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{organ_name}: 無法計算 (n={n})")
print("\n合併結構的結果:")
for key in ['combined_eye', 'combined_nerve']:
if key in summary:
mean = summary[key]['mean_dice']
std = summary[key]['std_dice']
n = summary[key]['n_samples']
name = "合併眼睛" if key == 'combined_eye' else "合併視神經"
if not np.isnan(mean):
print(f"{name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{name}: 無法計算 (n={n})")
return summary
if __name__ == "__main__":
base_dir = "/mnt/1248/onlylian"
gt_folder = os.path.join(base_dir, "nnUNet/nnUNet_raw/Dataset555/labelsTr")
output_dir = base_dir
models = {
'2D': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset555/nnUNetTrainer__nnUNetPlans__2d"),
'3D_fullres': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset555/nnUNetTrainer__nnUNetPlans__3d_fullres"),
'3D_lowres': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset555/nnUNetTrainer__nnUNetPlans__3d_lowres")
}
labels = [1, 2, 3, 4, 5, 6]
organ_names = {
1: "Brainstem",
2: "Right_Eye",
3: "Left_Eye",
4: "Optic_Chiasm",
5: "Right_Optic_Nerve",
6: "Left_Optic_Nerve"
}
print("可用的模型:")
for i, model_name in enumerate(models.keys(), 1):
print(f"{i}. {model_name}")
choice = input("\n請選擇要運行的模型 (輸入數字1-3): ")
try:
model_index = int(choice) - 1
model_names = list(models.keys())
selected_model = model_names[model_index]
evaluate_model(gt_folder, models[selected_model], selected_model, labels, organ_names, output_dir)
except (ValueError, IndexError):
print("無效的選擇請輸入1-3之間的數字。")

View file

@ -0,0 +1,141 @@
import nibabel as nib
import matplotlib.pyplot as plt
import numpy as np
import os
def load_nifti(file_path):
"""載入NIfTI檔案"""
return nib.load(file_path).get_fdata()
def create_colored_mask(data):
"""創建彩色遮罩"""
color_dict = {
1: [144/255, 238/255, 144/255], # Brainstem - 綠色
2: [255/255, 218/255, 150/255], # Right_Eye - 淺黃色
3: [205/255, 170/255, 125/255], # Left_Eye - 棕色
4: [135/255, 206/255, 235/255], # Optic_Chiasm - 藍色
5: [255/255, 99/255, 71/255], # Right_Optic_Nerve - 亮珊瑚紅
6: [255/255, 160/255, 122/255] # Left_Optic_Nerve - 淺珊瑚紅
}
mask = np.zeros((*data.shape, 3))
for label, color in color_dict.items():
mask_3d = np.dstack([data == label] * 3)
mask = np.where(mask_3d, color, mask)
return mask
def get_brain_bounds(image):
"""獲取腦部區域的邊界"""
mask = image > np.percentile(image, 1)
rows = np.any(mask, axis=1)
cols = np.any(mask, axis=0)
rmin, rmax = np.where(rows)[0][[0, -1]]
cmin, cmax = np.where(cols)[0][[0, -1]]
center_r = (rmin + rmax) // 2
center_c = (cmin + cmax) // 2
# 減小裁剪半徑以放大顯示區域
radius = int(max(rmax - rmin, cmax - cmin) * 0.55)
# 調整邊距
margin = int(radius * 0.15)
# 根據中心點和半徑設定新的邊界
rmin = max(center_r - radius - margin, 0)
rmax = min(center_r + radius + margin, image.shape[0])
cmin = max(center_c - radius - margin, 0)
cmax = min(center_c + radius + margin, image.shape[1])
return rmin, rmax, cmin, cmax
def check_organs_in_slice(slice_data):
"""檢查切片中存在的器官標籤"""
unique_labels = set(np.unique(slice_data))
if 0 in unique_labels:
unique_labels.remove(0)
organ_dict = {
1: "Brainstem",
2: "Right_Eye",
3: "Left_Eye",
4: "Optic_Chiasm",
5: "Right_Optic_Nerve",
6: "Left_Optic_Nerve"
}
return {organ_dict[label] for label in unique_labels if label in organ_dict}
def save_slice(image, ground_truth, prediction, slice_num, save_path):
"""保存指定切片的比較圖"""
plt.clf()
fig, axes = plt.subplots(1, 2, figsize=(25, 11))
plt.subplots_adjust(wspace=0.01)
img_slice = np.rot90(image[:, :, slice_num])
rmin, rmax, cmin, cmax = get_brain_bounds(img_slice)
# Ground Truth
gt_slice = np.rot90(ground_truth[:, :, slice_num])
gt_mask = create_colored_mask(gt_slice)
axes[0].imshow(img_slice[rmin:rmax, cmin:cmax], cmap='gray')
axes[0].imshow(gt_mask[rmin:rmax, cmin:cmax], alpha=0.5)
axes[0].set_title('Ground Truth', fontsize=32, pad=20, weight='bold')
axes[0].axis('off')
# Prediction
pred_slice = np.rot90(prediction[:, :, slice_num])
pred_mask = create_colored_mask(pred_slice)
axes[1].imshow(img_slice[rmin:rmax, cmin:cmax], cmap='gray')
axes[1].imshow(pred_mask[rmin:rmax, cmin:cmax], alpha=0.5)
axes[1].set_title('Prediction', fontsize=32, pad=20, weight='bold')
axes[1].axis('off')
plt.savefig(save_path, bbox_inches='tight', dpi=300, pad_inches=0.05)
plt.close()
print(f"已保存切片 {slice_num} 至: {save_path}")
if __name__ == "__main__":
# 載入影像檔案
image_path = '/mnt/1248/onlylian/nnUNet/nnUNet_raw/Dataset555/imagesTs/CT_081_0000.nii.gz'
gt_path = '/mnt/1248/onlylian/nnUNet/nnUNet_raw/Dataset555/labelsTs/CT_081.nii.gz'
pred_path = '/mnt/1248/onlylian/CT/output_predictions/2d/fold_3/CT_081.nii.gz'
# 設定保存路徑
save_dir = '/mnt/1248/onlylian/CT'
# 讀取影像
image = load_nifti(image_path)
ground_truth = load_nifti(gt_path)
prediction = load_nifti(pred_path)
# 檢查每個切片包含的器官
total_slices = image.shape[2]
for slice_num in range(total_slices):
gt_slice = ground_truth[:, :, slice_num]
pred_slice = prediction[:, :, slice_num]
# 獲取器官信息
gt_organs = check_organs_in_slice(gt_slice)
pred_organs = check_organs_in_slice(pred_slice)
if gt_organs or pred_organs:
print(f"\n切片 {slice_num}:")
print(f"Ground Truth 包含: {', '.join(sorted(gt_organs))}")
print(f"Prediction 包含: {', '.join(sorted(pred_organs))}")
# 讓用戶選擇要保存的切片
while True:
try:
slice_num = int(input("\n請輸入要保存的切片編號 (-1 退出): "))
if slice_num == -1:
break
if 0 <= slice_num < total_slices:
save_path = os.path.join(save_dir, f'comparison_CT_057.png')
save_slice(image, ground_truth, prediction, slice_num, save_path)
else:
print(f"切片編號必須在 0 到 {total_slices-1} 之間")
except ValueError:
print("請輸入有效的數字")

View file

@ -0,0 +1,287 @@
# import json
# import os
# import numpy as np
# import matplotlib.pyplot as plt
# from typing import Dict, Tuple
# # 設置中文字體支持
# plt.rcParams['font.sans-serif'] = ['SimHei']
# plt.rcParams['axes.unicode_minus'] = False
# def load_data(file_path: str) -> Dict:
# """讀取JSON數據文件"""
# try:
# with open(file_path, 'r') as file:
# return json.load(file)
# except FileNotFoundError:
# print(f"文件未找到: {file_path}")
# return {}
# except json.JSONDecodeError:
# print(f"JSON解析錯誤: {file_path}")
# return {}
# def extract_scores(data: Dict, model: str, organ_id: str) -> Tuple[float, float]:
# """從數據中提取特定器官的分數"""
# if "summary" not in data or organ_id not in data["summary"]:
# return (0, 0)
# scores = data["summary"][organ_id]
# if model == '3D_fullres':
# mean_dice = scores.get("mean_dice", 0)
# std_dice = scores.get("std_dice", 0)
# else:
# mean_dice = scores.get("mean_dice", scores.get("mean", 0))
# std_dice = scores.get("std_dice", scores.get("std", 0))
# return (mean_dice, std_dice)
# def create_plot(model_averages: Dict, model_stds: Dict, organs: Dict, output_path: str):
# """創建和保存圖表"""
# plt.figure(figsize=(16, 5)) # Reduced figure size
# plt.title('CT_CTC_T1_OAR', fontsize=12, pad=10) # Reduced title size and padding
# plt.grid(True, axis='y', linestyle='--', alpha=0.7)
# x = np.arange(len(organs)) * 1.5 # Reduced spacing between groups
# width = 0.35 # Reduced bar width
# colors = {
# '2D': 'royalblue',
# '3D_fullres': 'limegreen',
# '3D_lowres': 'orange'
# }
# positions = {
# '2D': x - width,
# '3D_fullres': x,
# '3D_lowres': x + width
# }
# for model, averages in model_averages.items():
# bars = plt.bar(positions[model],
# [averages[organ] * 100 for organ in organs.keys()],
# width,
# label=model,
# color=colors[model])
# # 調整數值標籤位置和大小
# for j, bar in enumerate(bars):
# height = bar.get_height()
# organ = list(organs.keys())[j]
# std = model_stds[model][organ] * 100
# plt.text(bar.get_x() + bar.get_width()/2., height,
# f'{height:.1f}±{std:.1f}%',
# ha='center',
# va='bottom',
# fontsize=6, # Reduced font size
# rotation=0) # Optional: rotate text if still crowded
# plt.ylim(0, 100)
# plt.ylabel('DC (%)', fontsize=10)
# plt.xlabel('OAR', fontsize=10)
# plt.xticks(x, [organs[k] for k in organs.keys()], rotation=30, ha='right', fontsize=8)
# plt.legend(loc='upper right', fontsize=8)
# plt.tight_layout()
# os.makedirs(os.path.dirname(output_path), exist_ok=True)
# plt.savefig(output_path, dpi=300, bbox_inches='tight')
# plt.close()
# def print_results(model_averages: Dict, model_stds: Dict, organs: Dict):
# """打印數值結果"""
# print("\n平均 Dice 分數 (百分比):")
# for model in model_averages.keys():
# print(f"\n{model}:")
# for organ in organs.keys():
# avg = model_averages[model][organ] * 100
# std = model_stds[model][organ] * 100
# print(f"{organs[organ]}: {avg:.1f}±{std:.1f}%")
# def main():
# # 定義路徑和器官映射
# base_path = '/mnt/1248/onlylian/CT_CTC_T1_OAR/Dice/test'
# paths = {
# '2D': os.path.join(base_path, 'CT_CTC_T1_OAR_test_dice_results_2D.json'),
# '3D_fullres': os.path.join(base_path, 'CT_CTC_T1_OAR_test_dice_results_3D_fullres.json'),
# '3D_lowres': os.path.join(base_path, 'CT_CTC_T1_OAR_test_dice_results_3D_lowres.json')
# }
# organs = {
# "1": "Brainstem",
# "2": "Right_Eye",
# "3": "Left_Eye",
# "4": "Optic_Chiasm",
# "5": "Right_Optic_Nerve",
# "6": "Left_Optic_Nerve"
# }
# # 初始化數據存儲
# model_dice_scores = {
# model: {organ: None for organ in organs.keys()}
# for model in paths.keys()
# }
# # 讀取和處理數據
# for model, path in paths.items():
# data = load_data(path)
# for organ_id in organs.keys():
# model_dice_scores[model][organ_id] = extract_scores(data, model, organ_id)
# # 計算平均值和標準差
# model_averages = {}
# model_stds = {}
# for model, scores in model_dice_scores.items():
# model_averages[model] = {organ: scores[organ][0] if scores[organ] else 0 for organ in organs.keys()}
# model_stds[model] = {organ: scores[organ][1] if scores[organ] else 0 for organ in organs.keys()}
# # 創建圖表並保存
# output_path = os.path.join(base_path, 'CT_CTC_T1_OAR_test_dice_scores_comparison.png')
# create_plot(model_averages, model_stds, organs, output_path)
# # 打印結果
# print_results(model_averages, model_stds, organs)
# if __name__ == "__main__":
# main()
import json
import os
import numpy as np
import matplotlib.pyplot as plt
from typing import Dict, Tuple
# 設置中文字體支持
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
def load_data(file_path: str) -> Dict:
"""讀取JSON數據文件"""
try:
with open(file_path, 'r') as file:
return json.load(file)
except FileNotFoundError:
print(f"文件未找到: {file_path}")
return {}
except json.JSONDecodeError:
print(f"JSON解析錯誤: {file_path}")
return {}
def extract_scores(data: Dict, model: str, organ_id: str) -> Tuple[float, float]:
"""從數據中提取特定器官的分數"""
if "summary" not in data or organ_id not in data["summary"]:
return (0, 0)
scores = data["summary"][organ_id]
if model == '3D_fullres':
mean_dice = scores.get("mean_dice", 0)
std_dice = scores.get("std_dice", 0)
else:
mean_dice = scores.get("mean_dice", scores.get("mean", 0))
std_dice = scores.get("std_dice", scores.get("std", 0))
return (mean_dice, std_dice)
def create_plot(model_averages: Dict, model_stds: Dict, organs: Dict, output_path: str):
"""創建和保存圖表"""
plt.figure(figsize=(16, 5)) # Reduced figure size
plt.title('CTC', fontsize=12, pad=10) # Changed title
plt.grid(True, axis='y', linestyle='--', alpha=0.7)
x = np.arange(len(organs)) * 1.5 # Reduced spacing between groups
width = 0.35 # Reduced bar width
colors = {
'2D': 'royalblue',
'3D_fullres': 'limegreen',
'3D_lowres': 'orange'
}
positions = {
'2D': x - width,
'3D_fullres': x,
'3D_lowres': x + width
}
for model, averages in model_averages.items():
bars = plt.bar(positions[model],
[averages[organ] * 100 for organ in organs.keys()],
width,
label=model,
color=colors[model])
# 調整數值標籤位置和大小
for j, bar in enumerate(bars):
height = bar.get_height()
organ = list(organs.keys())[j]
std = model_stds[model][organ] * 100
plt.text(bar.get_x() + bar.get_width()/2., height,
f'{height:.1f}±{std:.1f}%',
ha='center',
va='bottom',
fontsize=6, # Reduced font size
rotation=0) # Optional: rotate text if still crowded
plt.ylim(0, 100)
plt.ylabel('DC (%)', fontsize=10)
plt.xlabel('OAR', fontsize=10)
plt.xticks(x, [organs[k] for k in organs.keys()], rotation=30, ha='right', fontsize=8)
plt.legend(loc='upper right', fontsize=8)
plt.tight_layout()
os.makedirs(os.path.dirname(output_path), exist_ok=True)
plt.savefig(output_path, dpi=300, bbox_inches='tight')
plt.close()
def print_results(model_averages: Dict, model_stds: Dict, organs: Dict):
"""打印數值結果"""
print("\n平均 Dice 分數 (百分比):")
for model in model_averages.keys():
print(f"\n{model}:")
for organ in organs.keys():
avg = model_averages[model][organ] * 100
std = model_stds[model][organ] * 100
print(f"{organs[organ]}: {avg:.1f}±{std:.1f}%")
def main():
# 定義路徑和器官映射
base_path = '/mnt/1248/onlylian/CTC/Dice/test'
paths = {
'2D': os.path.join(base_path, 'CTC_test_dice_results_2D.json'),
'3D_fullres': os.path.join(base_path, 'CTC_test_dice_results_3D_fullres.json'),
'3D_lowres': os.path.join(base_path, 'CTC_test_dice_results_3D_lowres.json')
}
organs = {
"1": "Brainstem",
"2": "Right_Eye",
"3": "Left_Eye",
"4": "Optic_Chiasm",
"5": "Right_Optic_Nerve",
"6": "Left_Optic_Nerve"
}
# 初始化數據存儲
model_dice_scores = {
model: {organ: None for organ in organs.keys()}
for model in paths.keys()
}
# 讀取和處理數據
for model, path in paths.items():
data = load_data(path)
for organ_id in organs.keys():
model_dice_scores[model][organ_id] = extract_scores(data, model, organ_id)
# 計算平均值和標準差
model_averages = {}
model_stds = {}
for model, scores in model_dice_scores.items():
model_averages[model] = {organ: scores[organ][0] if scores[organ] else 0 for organ in organs.keys()}
model_stds[model] = {organ: scores[organ][1] if scores[organ] else 0 for organ in organs.keys()}
# 創建圖表並保存
output_path = os.path.join(base_path, 'CTC_test_dice_scores_comparison.png')
create_plot(model_averages, model_stds, organs, output_path)
# 打印結果
print_results(model_averages, model_stds, organs)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,306 @@
import json
import os
import numpy as np
import SimpleITK as sitk
from tqdm import tqdm
import time
def load_nifti(file_path):
"""加載 NIfTI 文件"""
return sitk.GetArrayFromImage(sitk.ReadImage(file_path))
def compute_dice(pred, gt):
"""計算Dice係數"""
intersection = np.sum(pred & gt)
sum_pred = np.sum(pred)
sum_gt = np.sum(gt)
if sum_pred == 0 and sum_gt == 0:
return 1.0
elif sum_pred == 0 or sum_gt == 0:
return 0.0
return 2.0 * intersection / (sum_pred + sum_gt)
def separate_structures(combined_mask, structure_name=""):
"""分離左右結構"""
# 使用SimpleITK的ConnectedComponent
sitk_image = sitk.GetImageFromArray(combined_mask)
labeled_components = sitk.ConnectedComponent(sitk_image)
labeled_array = sitk.GetArrayFromImage(labeled_components)
# 計算連通區域數量和大小
unique, counts = np.unique(labeled_array, return_counts=True)
if len(unique) < 3: # 背景算一個所以至少需要3個
print(f"警告:{structure_name} 未找到足夠的連通區域")
return None, None
# 排除背景標籤0並按大小排序
region_sizes = [(i, count) for i, count in zip(unique[1:], counts[1:])]
sorted_regions = sorted(region_sizes, key=lambda x: x[1], reverse=True)
if len(sorted_regions) < 2:
print(f"警告:{structure_name} 未找到兩個主要區域")
return None, None
# 獲取兩個最大區域
region1_mask = (labeled_array == sorted_regions[0][0])
region2_mask = (labeled_array == sorted_regions[1][0])
# 計算質心
def get_centroid(mask):
indices = np.where(mask)
return np.mean(indices[2]) # 使用x軸第三維度的平均值
centroid1 = get_centroid(region1_mask)
centroid2 = get_centroid(region2_mask)
# 根據x軸位置確定左右
if centroid1 > centroid2:
right = region1_mask
left = region2_mask
else:
right = region2_mask
left = region1_mask
return right.astype(np.uint8), left.astype(np.uint8)
def evaluate_case(gt_path, pred_path, labels):
"""評估單個案例"""
print(f"\n測試文件: {os.path.basename(pred_path)}")
# 加載圖像
start_time = time.time()
gt_img = load_nifti(gt_path)
pred_img = load_nifti(pred_path)
load_time = time.time() - start_time
print(f"圖像大小: {gt_img.shape}")
print(f"加載時間: {load_time:.2f}")
results = {}
total_time = 0
# 處理眼睛
start_time = time.time()
combined_gt_eye = ((gt_img == 2) | (gt_img == 3)).astype(np.uint8)
combined_pred_eye = ((pred_img == 2) | (pred_img == 3)).astype(np.uint8)
# 形態學處理眼睛
combined_pred_eye_sitk = sitk.GetImageFromArray(combined_pred_eye)
closing_filter = sitk.BinaryMorphologicalClosingImageFilter()
closing_filter.SetKernelRadius(2)
processed_pred_eye = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_eye_sitk))
# 計算眼睛的Dice
eye_dice = compute_dice(processed_pred_eye, combined_gt_eye)
eye_time = time.time() - start_time
total_time += eye_time
results['combined_eye'] = eye_dice
print(f"眼睛合併計算時間: {eye_time:.2f}秒, Dice: {eye_dice:.4f}")
# 分離眼睛
right_pred_eye, left_pred_eye = separate_structures(processed_pred_eye, "眼睛")
right_gt_eye, left_gt_eye = separate_structures(combined_gt_eye, "眼睛")
# 處理視神經
start_time = time.time()
combined_gt_nerve = ((gt_img == 5) | (gt_img == 6)).astype(np.uint8)
combined_pred_nerve = ((pred_img == 5) | (pred_img == 6)).astype(np.uint8)
# 形態學處理視神經
combined_pred_nerve_sitk = sitk.GetImageFromArray(combined_pred_nerve)
processed_pred_nerve = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_nerve_sitk))
# 計算視神經的Dice
nerve_dice = compute_dice(processed_pred_nerve, combined_gt_nerve)
nerve_time = time.time() - start_time
total_time += nerve_time
results['combined_nerve'] = nerve_dice
print(f"視神經合併計算時間: {nerve_time:.2f}秒, Dice: {nerve_dice:.4f}")
# 分離視神經
right_pred_nerve, left_pred_nerve = separate_structures(processed_pred_nerve, "視神經")
right_gt_nerve, left_gt_nerve = separate_structures(combined_gt_nerve, "視神經")
# 計算各個標籤的Dice
for label in labels:
start_time = time.time()
if label == 1: # Brainstem
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 2 and right_pred_eye is not None: # Right Eye
gt_binary = right_gt_eye
pred_binary = right_pred_eye
elif label == 3 and left_pred_eye is not None: # Left Eye
gt_binary = left_gt_eye
pred_binary = left_pred_eye
elif label == 4: # Optic Chiasm
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 5 and right_pred_nerve is not None: # Right Optic Nerve
gt_binary = right_gt_nerve
pred_binary = right_pred_nerve
elif label == 6 and left_pred_nerve is not None: # Left Optic Nerve
gt_binary = left_gt_nerve
pred_binary = left_pred_nerve
else:
continue
try:
dice = compute_dice(pred_binary, gt_binary)
compute_time = time.time() - start_time
total_time += compute_time
results[label] = dice
print(f"標籤 {label} 計算時間: {compute_time:.2f}秒, Dice: {dice:.4f}")
except Exception as e:
print(f"處理標籤 {label} 時出錯:{str(e)}")
results[label] = np.nan
continue
print(f"單個案例總計算時間: {total_time:.2f}")
return results
def evaluate_fold(gt_folder, pred_folder, labels):
"""評估單個fold的所有案例"""
fold_results = {label: [] for label in labels}
fold_results['combined_eye'] = []
fold_results['combined_nerve'] = []
pred_files = sorted([f for f in os.listdir(pred_folder) if f.endswith('.nii.gz')])
for pred_file in tqdm(pred_files, desc=f"Processing {os.path.basename(pred_folder)}"):
gt_path = os.path.join(gt_folder, pred_file)
pred_path = os.path.join(pred_folder, pred_file)
if not os.path.exists(gt_path):
print(f"找不到對應的真實標籤文件:{gt_path}")
continue
case_results = evaluate_case(gt_path, pred_path, labels)
# 收集結果
for key in case_results:
if not np.isnan(case_results[key]):
if key in fold_results:
fold_results[key].append(case_results[key])
return fold_results
def evaluate_model(gt_folder, base_pred_folder, model_name, labels, organ_names):
"""評估單個模型的所有fold"""
print(f"\n開始評估模型:{model_name}")
all_results = {label: [] for label in labels}
all_results['combined_eye'] = []
all_results['combined_nerve'] = []
fold_results = {}
# 處理 fold_0 到 fold_4
for fold in range(5):
print(f"\n處理 {model_name} - Fold {fold}")
pred_folder = os.path.join(base_pred_folder, f"fold_{fold}")
if not os.path.exists(pred_folder):
print(f"找不到預測文件夾:{pred_folder}")
continue
results = evaluate_fold(gt_folder, pred_folder, labels)
fold_results[f"fold_{fold}"] = results
# 合併結果
for key in results:
all_results[key].extend(results[key])
# 計算總平均值和標準差
summary = {}
for key in all_results:
if all_results[key]:
mean_dice = np.mean(all_results[key])
std_dice = np.std(all_results[key])
summary[key] = {
'mean_dice': float(mean_dice),
'std_dice': float(std_dice),
'n_samples': len(all_results[key])
}
else:
summary[key] = {
'mean_dice': np.nan,
'std_dice': np.nan,
'n_samples': 0
}
# 保存結果到指定路径
save_dir = "/mnt/1248/onlylian/CTC/Dice/test"
os.makedirs(save_dir, exist_ok=True)
filename = os.path.join(save_dir, f'CTC_test_dice_results_{model_name}.json')
model_results = {
'summary': summary,
'fold_results': fold_results
}
with open(filename, 'w') as f:
json.dump(model_results, f, indent=4)
print(f"\n{model_name}結果已保存到 {filename}")
# 打印結果
print(f"\n{model_name} 模型的 Dice 結果所有fold的平均值:")
# 打印器官結果
for label in labels:
if label in summary:
mean = summary[label]['mean_dice']
std = summary[label]['std_dice']
n = summary[label]['n_samples']
organ_name = organ_names.get(label, f"Label_{label}")
if not np.isnan(mean):
print(f"{organ_name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{organ_name}: 無法計算 (n={n})")
# 打印合併結果
print("\n合併結構的結果:")
for key in ['combined_eye', 'combined_nerve']:
if key in summary:
mean = summary[key]['mean_dice']
std = summary[key]['std_dice']
n = summary[key]['n_samples']
name = "合併眼睛" if key == 'combined_eye' else "合併視神經"
if not np.isnan(mean):
print(f"{name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{name}: 無法計算 (n={n})")
return summary
if __name__ == "__main__":
# 設置路徑和標籤
gt_folder = "/mnt/1248/onlylian/nnUNet/nnUNet_raw/Dataset666/labelsTs"
models = {
'2D': '/mnt/1248/onlylian/CTC/output_predictions/2d',
'3D_fullres': '/mnt/1248/onlylian/CTC/output_predictions/3d_fullres',
'3D_lowres': '/mnt/1248/onlylian/CTC/output_predictions/3d_lowres'
}
labels = [1, 2, 3, 4, 5, 6]
organ_names = {
1: "Brainstem",
2: "Right_Eye",
3: "Left_Eye",
4: "Optic_Chiasm",
5: "Right_Optic_Nerve",
6: "Left_Optic_Nerve"
}
# 讓用戶選擇要運行哪個模型
print("可用的模型:")
for i, model_name in enumerate(models.keys(), 1):
print(f"{i}. {model_name}")
choice = input("\n請選擇要運行的模型 (輸入數字1-3): ")
try:
model_index = int(choice) - 1
model_names = list(models.keys())
selected_model = model_names[model_index]
evaluate_model(gt_folder, models[selected_model], selected_model, labels, organ_names)
except (ValueError, IndexError):
print("無效的選擇請輸入1-3之間的數字。")

View file

@ -0,0 +1,142 @@
import json
import os
import numpy as np
import matplotlib.pyplot as plt
from typing import Dict, Tuple
# 設置中文字體支持
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
def load_data(file_path: str) -> Dict:
"""讀取JSON數據文件"""
try:
with open(file_path, 'r') as file:
return json.load(file)
except FileNotFoundError:
print(f"文件未找到: {file_path}")
return {}
except json.JSONDecodeError:
print(f"JSON解析錯誤: {file_path}")
return {}
def extract_scores(data: Dict, model: str, organ_id: str) -> Tuple[float, float]:
"""從數據中提取特定器官的分數"""
if "summary" not in data or organ_id not in data["summary"]:
return (0, 0)
scores = data["summary"][organ_id]
if model == '3D_fullres':
mean_dice = scores.get("mean_dice", 0)
std_dice = scores.get("std_dice", 0)
else:
mean_dice = scores.get("mean_dice", scores.get("mean", 0))
std_dice = scores.get("std_dice", scores.get("std", 0))
return (mean_dice, std_dice)
def create_plot(model_averages: Dict, model_stds: Dict, organs: Dict, output_path: str):
"""創建和保存圖表"""
plt.figure(figsize=(16, 5)) # Reduced figure size
plt.title('CTC', fontsize=12, pad=10) # Reduced title size and padding
plt.grid(True, axis='y', linestyle='--', alpha=0.7)
x = np.arange(len(organs)) * 1.5 # Reduced spacing between groups
width = 0.35 # Reduced bar width
colors = {
'2D': 'royalblue',
'3D_fullres': 'limegreen',
'3D_lowres': 'orange'
}
positions = {
'2D': x - width,
'3D_fullres': x,
'3D_lowres': x + width
}
for model, averages in model_averages.items():
bars = plt.bar(positions[model],
[averages[organ] * 100 for organ in organs.keys()],
width,
label=model,
color=colors[model])
# 添加數值標籤
for j, bar in enumerate(bars):
height = bar.get_height()
organ = list(organs.keys())[j]
std = model_stds[model][organ] * 100
plt.text(bar.get_x() + bar.get_width()/2., height + 0.5,
f'{height:.1f}±{std:.1f}%',
ha='center',
va='bottom',
fontsize=6) # Reduced font size
plt.ylim(0, 100)
plt.ylabel('DC (%)', fontsize=10)
plt.xlabel('OAR', fontsize=10)
plt.xticks(x, [organs[k] for k in organs.keys()], rotation=30, ha='right', fontsize=8)
plt.legend(loc='upper right', fontsize=8)
plt.tight_layout()
os.makedirs(os.path.dirname(output_path), exist_ok=True)
plt.savefig(output_path, dpi=300, bbox_inches='tight')
plt.close()
def print_results(model_averages: Dict, model_stds: Dict, organs: Dict):
"""打印數值結果"""
print("\n平均 Dice 分數 (百分比):")
for model in model_averages.keys():
print(f"\n{model}:")
for organ in organs.keys():
avg = model_averages[model][organ] * 100
std = model_stds[model][organ] * 100
print(f"{organs[organ]}: {avg:.1f}±{std:.1f}%")
def main():
# 定義路徑和器官映射
base_path = '/mnt/1248/onlylian/CTC/Dice/validation'
paths = {
'2D': os.path.join(base_path, 'CTC_validation_dice_results_2D.json'),
'3D_fullres': os.path.join(base_path, 'CTC_validation_dice_results_3D_fullres.json'),
'3D_lowres': os.path.join(base_path, 'CTC_validation_dice_results_3D_lowres.json')
}
organs = {
"1": "Brainstem",
"2": "Right_Eye",
"3": "Left_Eye",
"4": "Optic_Chiasm",
"5": "Right_Optic_Nerve",
"6": "Left_Optic_Nerve"
}
# 初始化數據存儲
model_dice_scores = {
model: {organ: None for organ in organs.keys()}
for model in paths.keys()
}
# 讀取和處理數據
for model, path in paths.items():
data = load_data(path)
for organ_id in organs.keys():
model_dice_scores[model][organ_id] = extract_scores(data, model, organ_id)
# 計算平均值和標準差
model_averages = {}
model_stds = {}
for model, scores in model_dice_scores.items():
model_averages[model] = {organ: scores[organ][0] if scores[organ] else 0 for organ in organs.keys()}
model_stds[model] = {organ: scores[organ][1] if scores[organ] else 0 for organ in organs.keys()}
# 創建圖表並保存
output_path = os.path.join(base_path, 'CTC_validation_dice_scores_comparison.png')
create_plot(model_averages, model_stds, organs, output_path)
# 打印結果
print_results(model_averages, model_stds, organs)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,288 @@
import os
import time
import numpy as np
import SimpleITK as sitk
from tqdm import tqdm
import json
def load_nifti(file_path):
"""加載 NIfTI 文件"""
return sitk.GetArrayFromImage(sitk.ReadImage(file_path))
def compute_dice(pred, gt):
"""計算Dice係數"""
intersection = np.sum(pred & gt)
sum_pred = np.sum(pred)
sum_gt = np.sum(gt)
if sum_pred == 0 and sum_gt == 0:
return 1.0
elif sum_pred == 0 or sum_gt == 0:
return 0.0
return 2.0 * intersection / (sum_pred + sum_gt)
def separate_structures(combined_mask, structure_name=""):
"""分離左右結構"""
sitk_image = sitk.GetImageFromArray(combined_mask)
labeled_components = sitk.ConnectedComponent(sitk_image)
labeled_array = sitk.GetArrayFromImage(labeled_components)
unique, counts = np.unique(labeled_array, return_counts=True)
if len(unique) < 3:
print(f"警告:{structure_name} 未找到足夠的連通區域")
return None, None
region_sizes = [(i, count) for i, count in zip(unique[1:], counts[1:])]
sorted_regions = sorted(region_sizes, key=lambda x: x[1], reverse=True)
if len(sorted_regions) < 2:
print(f"警告:{structure_name} 未找到兩個主要區域")
return None, None
region1_mask = (labeled_array == sorted_regions[0][0])
region2_mask = (labeled_array == sorted_regions[1][0])
def get_centroid(mask):
indices = np.where(mask)
return np.mean(indices[2])
centroid1 = get_centroid(region1_mask)
centroid2 = get_centroid(region2_mask)
if centroid1 > centroid2:
right = region1_mask
left = region2_mask
else:
right = region2_mask
left = region1_mask
return right.astype(np.uint8), left.astype(np.uint8)
def evaluate_case(gt_path, pred_path, labels):
"""評估單個案例"""
print(f"\n測試文件: {os.path.basename(pred_path)}")
start_time = time.time()
gt_img = load_nifti(gt_path)
pred_img = load_nifti(pred_path)
load_time = time.time() - start_time
print(f"圖像大小: {gt_img.shape}")
print(f"加載時間: {load_time:.2f}")
results = {}
total_time = 0
# 處理眼睛
start_time = time.time()
combined_gt_eye = ((gt_img == 2) | (gt_img == 3)).astype(np.uint8)
combined_pred_eye = ((pred_img == 2) | (pred_img == 3)).astype(np.uint8)
combined_pred_eye_sitk = sitk.GetImageFromArray(combined_pred_eye)
closing_filter = sitk.BinaryMorphologicalClosingImageFilter()
closing_filter.SetKernelRadius(2)
processed_pred_eye = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_eye_sitk))
eye_dice = compute_dice(processed_pred_eye, combined_gt_eye)
eye_time = time.time() - start_time
total_time += eye_time
results['combined_eye'] = eye_dice
print(f"眼睛合併計算時間: {eye_time:.2f}秒, Dice: {eye_dice:.4f}")
right_pred_eye, left_pred_eye = separate_structures(processed_pred_eye, "眼睛")
right_gt_eye, left_gt_eye = separate_structures(combined_gt_eye, "眼睛")
# 處理視神經
start_time = time.time()
combined_gt_nerve = ((gt_img == 5) | (gt_img == 6)).astype(np.uint8)
combined_pred_nerve = ((pred_img == 5) | (pred_img == 6)).astype(np.uint8)
combined_pred_nerve_sitk = sitk.GetImageFromArray(combined_pred_nerve)
processed_pred_nerve = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_nerve_sitk))
nerve_dice = compute_dice(processed_pred_nerve, combined_gt_nerve)
nerve_time = time.time() - start_time
total_time += nerve_time
results['combined_nerve'] = nerve_dice
print(f"視神經合併計算時間: {nerve_time:.2f}秒, Dice: {nerve_dice:.4f}")
right_pred_nerve, left_pred_nerve = separate_structures(processed_pred_nerve, "視神經")
right_gt_nerve, left_gt_nerve = separate_structures(combined_gt_nerve, "視神經")
for label in labels:
start_time = time.time()
if label == 1: # Brainstem
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 2 and right_pred_eye is not None: # Right Eye
gt_binary = right_gt_eye
pred_binary = right_pred_eye
elif label == 3 and left_pred_eye is not None: # Left Eye
gt_binary = left_gt_eye
pred_binary = left_pred_eye
elif label == 4: # Optic Chiasm
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 5 and right_pred_nerve is not None: # Right Optic Nerve
gt_binary = right_gt_nerve
pred_binary = right_pred_nerve
elif label == 6 and left_pred_nerve is not None: # Left Optic Nerve
gt_binary = left_gt_nerve
pred_binary = left_pred_nerve
else:
continue
try:
dice = compute_dice(pred_binary, gt_binary)
compute_time = time.time() - start_time
total_time += compute_time
results[label] = dice
print(f"標籤 {label} 計算時間: {compute_time:.2f}秒, Dice: {dice:.4f}")
except Exception as e:
print(f"處理標籤 {label} 時出錯:{str(e)}")
results[label] = np.nan
continue
print(f"單個案例總計算時間: {total_time:.2f}")
return results
def evaluate_validation_fold(gt_base_folder, validation_folder, labels):
"""評估單個fold的驗證集"""
fold_results = {label: [] for label in labels}
fold_results['combined_eye'] = []
fold_results['combined_nerve'] = []
pred_files = sorted([f for f in os.listdir(validation_folder) if f.endswith('.nii.gz')])
for pred_file in tqdm(pred_files, desc=f"Processing {os.path.basename(validation_folder)}"):
patient_id = pred_file.split('.nii.gz')[0]
gt_path = os.path.join(gt_base_folder, f"{patient_id}.nii.gz")
pred_path = os.path.join(validation_folder, pred_file)
if not os.path.exists(gt_path):
print(f"找不到對應的真實標籤文件:{gt_path}")
continue
case_results = evaluate_case(gt_path, pred_path, labels)
for key in case_results:
if not np.isnan(case_results[key]):
if key in fold_results:
fold_results[key].append(case_results[key])
return fold_results
def evaluate_model(gt_folder, model_path, model_name, labels, organ_names, output_dir):
"""評估模型所有fold的驗證集"""
print(f"\n開始評估模型:{model_name}")
all_results = {label: [] for label in labels}
all_results['combined_eye'] = []
all_results['combined_nerve'] = []
fold_results = {}
for fold in range(5):
validation_folder = os.path.join(model_path, f"fold_{fold}", "validation")
if not os.path.exists(validation_folder):
print(f"找不到fold_{fold}的驗證資料夾:{validation_folder}")
continue
print(f"\n處理 {model_name} - Fold {fold} 驗證集")
results = evaluate_validation_fold(gt_folder, validation_folder, labels)
fold_results[f"fold_{fold}"] = results
for key in results:
all_results[key].extend(results[key])
summary = {}
for key in all_results:
if all_results[key]:
mean_dice = np.mean(all_results[key])
std_dice = np.std(all_results[key])
summary[key] = {
'mean_dice': float(mean_dice),
'std_dice': float(std_dice),
'n_samples': len(all_results[key])
}
else:
summary[key] = {
'mean_dice': np.nan,
'std_dice': np.nan,
'n_samples': 0
}
model_results = {
'summary': summary,
'fold_results': fold_results
}
output_path = os.path.join(output_dir, 'CTC', 'Dice', 'validation')
os.makedirs(output_path, exist_ok=True)
filename = os.path.join(output_path, f'dice_results_{model_name}.json')
with open(filename, 'w') as f:
json.dump(model_results, f, indent=4)
print(f"\n{model_name}驗證集結果已保存到 {filename}")
print(f"\n{model_name} 模型的驗證集 Dice 結果所有fold的平均值:")
for label in labels:
if label in summary:
mean = summary[label]['mean_dice']
std = summary[label]['std_dice']
n = summary[label]['n_samples']
organ_name = organ_names.get(label, f"Label_{label}")
if not np.isnan(mean):
print(f"{organ_name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{organ_name}: 無法計算 (n={n})")
print("\n合併結構的結果:")
for key in ['combined_eye', 'combined_nerve']:
if key in summary:
mean = summary[key]['mean_dice']
std = summary[key]['std_dice']
n = summary[key]['n_samples']
name = "合併眼睛" if key == 'combined_eye' else "合併視神經"
if not np.isnan(mean):
print(f"{name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{name}: 無法計算 (n={n})")
return summary
if __name__ == "__main__":
base_dir = "/mnt/1248/onlylian"
gt_folder = os.path.join(base_dir, "nnUNet/nnUNet_raw/Dataset666/labelsTr")
output_dir = base_dir
models = {
'2D': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset666/nnUNetTrainer__nnUNetPlans__2d"),
'3D_fullres': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset666/nnUNetTrainer__nnUNetPlans__3d_fullres"),
'3D_lowres': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset666/nnUNetTrainer__nnUNetPlans__3d_lowres")
}
labels = [1, 2, 3, 4, 5, 6]
organ_names = {
1: "Brainstem",
2: "Right_Eye",
3: "Left_Eye",
4: "Optic_Chiasm",
5: "Right_Optic_Nerve",
6: "Left_Optic_Nerve"
}
print("可用的模型:")
for i, model_name in enumerate(models.keys(), 1):
print(f"{i}. {model_name}")
choice = input("\n請選擇要運行的模型 (輸入數字1-3): ")
try:
model_index = int(choice) - 1
model_names = list(models.keys())
selected_model = model_names[model_index]
evaluate_model(gt_folder, models[selected_model], selected_model, labels, organ_names, output_dir)
except (ValueError, IndexError):
print("無效的選擇請輸入1-3之間的數字。")

View file

@ -0,0 +1,180 @@
import nibabel as nib
import matplotlib.pyplot as plt
import numpy as np
import os
def load_nifti(file_path):
"""載入NIfTI檔案"""
return nib.load(file_path).get_fdata()
def create_colored_mask(data):
"""創建彩色遮罩"""
color_dict = {
1: [144/255, 238/255, 144/255], # Brainstem - 綠色
2: [255/255, 218/255, 150/255], # Right_Eye - 淺黃色
3: [205/255, 170/255, 125/255], # Left_Eye - 棕色
4: [135/255, 206/255, 235/255], # Optic_Chiasm - 藍色
5: [255/255, 99/255, 71/255], # Right_Optic_Nerve - 亮珊瑚紅
6: [255/255, 160/255, 122/255] # Left_Optic_Nerve - 淺珊瑚紅
}
mask = np.zeros((*data.shape, 4))
for label, color in color_dict.items():
organ_mask = (data == label)
if np.any(organ_mask):
mask[organ_mask] = [*color, 1.0]
return mask
def get_brain_bounds(image):
"""獲取腦部區域的邊界,並調整顯示位置以避開底部基架"""
# 基本閾值處理
mask = image > np.percentile(image, 1)
# 獲取非零區域的邊界
rows = np.any(mask, axis=1)
cols = np.any(mask, axis=0)
# 確保有足夠的點
if not np.any(rows) or not np.any(cols):
return 0, image.shape[0], 0, image.shape[1]
row_indices = np.where(rows)[0]
col_indices = np.where(cols)[0]
# 獲取初始邊界
rmin, rmax = row_indices[[0, -1]]
cmin, cmax = col_indices[[0, -1]]
# 計算中心點,但略微上移以避開基架
center_r = (rmin + rmax) // 2 - int(image.shape[0] * 0.15) # 上移15%
center_c = (cmin + cmax) // 2
# 計算顯示範圍
height = image.shape[0]
width = image.shape[1]
display_size = int(min(height, width) * 0.7) # 使用70%的圖像大小
# 設置新的邊界,確保不會超出圖像範圍
rmin = max(center_r - display_size//2, 0)
rmax = min(center_r + display_size//2, height - int(height * 0.2)) # 留出底部空間
cmin = max(center_c - display_size//2, 0)
cmax = min(center_c + display_size//2, width)
# 如果上邊界太小,適當下移整個顯示區域
if rmin < height * 0.1:
shift = int(height * 0.1) - rmin
rmin += shift
rmax += shift
return rmin, rmax, cmin, cmax
def apply_brain_window(image, window_width=80, window_level=40):
"""應用brain window設置"""
window_min = window_level - window_width/2
window_max = window_level + window_width/2
windowed = np.clip(image, window_min, window_max)
windowed = (windowed - window_min) / (window_max - window_min)
return windowed
def check_organs_in_slice(slice_data):
"""檢查切片中存在的器官標籤"""
unique_labels = set(np.unique(slice_data))
if 0 in unique_labels:
unique_labels.remove(0)
organ_dict = {
1: "Brainstem",
2: "Right_Eye",
3: "Left_Eye",
4: "Optic_Chiasm",
5: "Right_Optic_Nerve",
6: "Left_Optic_Nerve"
}
return {organ_dict[label] for label in unique_labels if label in organ_dict}
def save_slice(image, ground_truth, prediction, slice_num, save_path):
"""保存指定切片的比較圖"""
plt.clf()
# Create figure with 1 row and 3 columns
fig, axes = plt.subplots(1, 3, figsize=(24, 8))
plt.subplots_adjust(wspace=0.01)
img_slice = np.rot90(image[:, :, slice_num])
rmin, rmax, cmin, cmax = get_brain_bounds(img_slice)
# 使用brain window設置
img_slice = apply_brain_window(img_slice)
# Original Image
axes[0].imshow(img_slice[rmin:rmax, cmin:cmax], cmap='gray')
axes[0].set_title('Original Image', fontsize=32, pad=20, weight='bold')
axes[0].axis('off')
# Ground Truth
gt_slice = np.rot90(ground_truth[:, :, slice_num])
gt_mask = create_colored_mask(gt_slice)
axes[1].imshow(img_slice[rmin:rmax, cmin:cmax], cmap='gray', zorder=1)
axes[1].imshow(gt_mask[rmin:rmax, cmin:cmax], zorder=2)
axes[1].set_title('Ground Truth', fontsize=32, pad=20, weight='bold')
axes[1].axis('off')
# Prediction
pred_slice = np.rot90(prediction[:, :, slice_num])
pred_mask = create_colored_mask(pred_slice)
axes[2].imshow(img_slice[rmin:rmax, cmin:cmax], cmap='gray', zorder=1)
axes[2].imshow(pred_mask[rmin:rmax, cmin:cmax], zorder=2)
axes[2].set_title('Prediction', fontsize=32, pad=20, weight='bold')
axes[2].axis('off')
plt.savefig(save_path, bbox_inches='tight', dpi=300, pad_inches=0.05)
plt.close()
print(f"已保存切片 {slice_num} 至: {save_path}")
if __name__ == "__main__":
base_dir = "/mnt/1248/onlylian"
image_path = '/mnt/1248/onlylian/nnUNet/nnUNet_raw/Dataset666/imagesTs/CTC_081_0000.nii.gz'
gt_path = '/mnt/1248/onlylian/nnUNet/nnUNet_raw/Dataset666/labelsTs/CTC_081.nii.gz'
pred_path = '/mnt/1248/onlylian/CTC/output_predictions/3d_fullres/fold_4/CTC_081.nii.gz'
save_dir = os.path.join(base_dir, "CTC")
os.makedirs(save_dir, exist_ok=True)
print("正在載入影像...")
image = load_nifti(image_path)
ground_truth = load_nifti(gt_path)
prediction = load_nifti(pred_path)
print("影像載入完成")
print("\n分析切片中的器官...")
total_slices = image.shape[2]
for slice_num in range(total_slices):
gt_slice = ground_truth[:, :, slice_num]
pred_slice = prediction[:, :, slice_num]
gt_organs = check_organs_in_slice(gt_slice)
pred_organs = check_organs_in_slice(pred_slice)
if gt_organs or pred_organs:
print(f"\n切片 {slice_num}:")
print(f"Ground Truth 包含: {', '.join(sorted(gt_organs))}")
print(f"Prediction 包含: {', '.join(sorted(pred_organs))}")
print("\n開始切片選擇和保存過程...")
while True:
try:
slice_num = int(input("\n請輸入要保存的切片編號 (-1 退出): "))
if slice_num == -1:
print("程序結束")
break
if 0 <= slice_num < total_slices:
save_path = os.path.join(save_dir, f'comparison_CTC_081_{slice_num:03d}.png')
save_slice(image, ground_truth, prediction, slice_num, save_path)
else:
print(f"切片編號必須在 0 到 {total_slices-1} 之間")
except ValueError:
print("請輸入有效的數字")
except Exception as e:
print(f"發生錯誤: {str(e)}")

View file

@ -0,0 +1,180 @@
import nibabel as nib
import matplotlib.pyplot as plt
import numpy as np
import os
def load_nifti(file_path):
"""載入NIfTI檔案"""
return nib.load(file_path).get_fdata()
def create_colored_mask(data):
"""創建彩色遮罩"""
color_dict = {
1: [144/255, 238/255, 144/255], # Brainstem - 綠色
2: [255/255, 218/255, 150/255], # Right_Eye - 淺黃色
3: [205/255, 170/255, 125/255], # Left_Eye - 棕色
4: [135/255, 206/255, 235/255], # Optic_Chiasm - 藍色
5: [255/255, 99/255, 71/255], # Right_Optic_Nerve - 亮珊瑚紅
6: [255/255, 160/255, 122/255] # Left_Optic_Nerve - 淺珊瑚紅
}
mask = np.zeros((*data.shape, 4))
for label, color in color_dict.items():
organ_mask = (data == label)
if np.any(organ_mask):
mask[organ_mask] = [*color, 1.0]
return mask
def get_brain_bounds(image):
"""獲取腦部區域的邊界,並調整顯示位置以避開底部基架"""
# 基本閾值處理
mask = image > np.percentile(image, 1)
# 獲取非零區域的邊界
rows = np.any(mask, axis=1)
cols = np.any(mask, axis=0)
# 確保有足夠的點
if not np.any(rows) or not np.any(cols):
return 0, image.shape[0], 0, image.shape[1]
row_indices = np.where(rows)[0]
col_indices = np.where(cols)[0]
# 獲取初始邊界
rmin, rmax = row_indices[[0, -1]]
cmin, cmax = col_indices[[0, -1]]
# 計算中心點,但略微上移以避開基架
center_r = (rmin + rmax) // 2 - int(image.shape[0] * 0.15) # 上移15%
center_c = (cmin + cmax) // 2
# 計算顯示範圍
height = image.shape[0]
width = image.shape[1]
display_size = int(min(height, width) * 0.7) # 使用70%的圖像大小
# 設置新的邊界,確保不會超出圖像範圍
rmin = max(center_r - display_size//2, 0)
rmax = min(center_r + display_size//2, height - int(height * 0.2)) # 留出底部空間
cmin = max(center_c - display_size//2, 0)
cmax = min(center_c + display_size//2, width)
# 如果上邊界太小,適當下移整個顯示區域
if rmin < height * 0.1:
shift = int(height * 0.1) - rmin
rmin += shift
rmax += shift
return rmin, rmax, cmin, cmax
def apply_brain_window(image, window_width=80, window_level=40):
"""應用brain window設置"""
window_min = window_level - window_width/2
window_max = window_level + window_width/2
windowed = np.clip(image, window_min, window_max)
windowed = (windowed - window_min) / (window_max - window_min)
return windowed
def check_organs_in_slice(slice_data):
"""檢查切片中存在的器官標籤"""
unique_labels = set(np.unique(slice_data))
if 0 in unique_labels:
unique_labels.remove(0)
organ_dict = {
1: "Brainstem",
2: "Right_Eye",
3: "Left_Eye",
4: "Optic_Chiasm",
5: "Right_Optic_Nerve",
6: "Left_Optic_Nerve"
}
return {organ_dict[label] for label in unique_labels if label in organ_dict}
def save_slice(image, ground_truth, prediction, slice_num, save_path):
"""保存指定切片的比較圖"""
plt.clf()
# Create figure with 1 row and 3 columns
fig, axes = plt.subplots(1, 3, figsize=(24, 8))
plt.subplots_adjust(wspace=0.01)
img_slice = np.rot90(image[:, :, slice_num])
rmin, rmax, cmin, cmax = get_brain_bounds(img_slice)
# 使用brain window設置
img_slice = apply_brain_window(img_slice)
# Original Image
axes[0].imshow(img_slice[rmin:rmax, cmin:cmax], cmap='gray')
axes[0].set_title('Original Image', fontsize=32, pad=20, weight='bold')
axes[0].axis('off')
# Ground Truth
gt_slice = np.rot90(ground_truth[:, :, slice_num])
gt_mask = create_colored_mask(gt_slice)
axes[1].imshow(img_slice[rmin:rmax, cmin:cmax], cmap='gray', zorder=1)
axes[1].imshow(gt_mask[rmin:rmax, cmin:cmax], zorder=2)
axes[1].set_title('Ground Truth', fontsize=32, pad=20, weight='bold')
axes[1].axis('off')
# Prediction
pred_slice = np.rot90(prediction[:, :, slice_num])
pred_mask = create_colored_mask(pred_slice)
axes[2].imshow(img_slice[rmin:rmax, cmin:cmax], cmap='gray', zorder=1)
axes[2].imshow(pred_mask[rmin:rmax, cmin:cmax], zorder=2)
axes[2].set_title('Prediction', fontsize=32, pad=20, weight='bold')
axes[2].axis('off')
plt.savefig(save_path, bbox_inches='tight', dpi=300, pad_inches=0.05)
plt.close()
print(f"已保存切片 {slice_num} 至: {save_path}")
if __name__ == "__main__":
base_dir = "/mnt/1248/onlylian"
image_path = '/mnt/1248/onlylian/nnUNet/nnUNet_raw/Dataset666/imagesTs/CTC_001_0000.nii.gz'
gt_path = os.path.join(base_dir, "nnUNet/nnUNet_raw/Dataset666/labelsTs/CTC_001.nii.gz")
pred_path = '/mnt/1248/onlylian/CTC/output_predictions/2d/fold_0/CTC_001.nii.gz'
save_dir = os.path.join(base_dir, "CTC")
os.makedirs(save_dir, exist_ok=True)
print("正在載入影像...")
image = load_nifti(image_path)
ground_truth = load_nifti(gt_path)
prediction = load_nifti(pred_path)
print("影像載入完成")
print("\n分析切片中的器官...")
total_slices = image.shape[2]
for slice_num in range(total_slices):
gt_slice = ground_truth[:, :, slice_num]
pred_slice = prediction[:, :, slice_num]
gt_organs = check_organs_in_slice(gt_slice)
pred_organs = check_organs_in_slice(pred_slice)
if gt_organs or pred_organs:
print(f"\n切片 {slice_num}:")
print(f"Ground Truth 包含: {', '.join(sorted(gt_organs))}")
print(f"Prediction 包含: {', '.join(sorted(pred_organs))}")
print("\n開始切片選擇和保存過程...")
while True:
try:
slice_num = int(input("\n請輸入要保存的切片編號 (-1 退出): "))
if slice_num == -1:
print("程序結束")
break
if 0 <= slice_num < total_slices:
save_path = os.path.join(save_dir, f'comparison_CTC_001_{slice_num:03d}.png')
save_slice(image, ground_truth, prediction, slice_num, save_path)
else:
print(f"切片編號必須在 0 到 {total_slices-1} 之間")
except ValueError:
print("請輸入有效的數字")
except Exception as e:
print(f"發生錯誤: {str(e)}")

View file

@ -0,0 +1,170 @@
import json
import os
import numpy as np
import matplotlib.pyplot as plt
from typing import Dict, Tuple, List
import pandas as pd
# 使用更通用的字體設置
plt.rcParams['font.family'] = 'DejaVu Sans'
plt.rcParams['axes.unicode_minus'] = False
def load_data(file_path: str) -> Dict:
"""Load JSON data file"""
try:
with open(file_path, 'r') as file:
return json.load(file)
except FileNotFoundError:
print(f"File not found: {file_path}")
return {}
except json.JSONDecodeError:
print(f"JSON decoding error: {file_path}")
return {}
def extract_scores(data: Dict, model: str, organ_id: str) -> Tuple[float, float]:
"""Extract scores for a specific organ from the data"""
if "summary" not in data or organ_id not in data["summary"]:
return (0, 0)
scores = data["summary"][organ_id]
if model == '3D_fullres':
mean_dice = scores.get("mean_dice", 0)
std_dice = scores.get("std_dice", 0)
else:
mean_dice = scores.get("mean_dice", scores.get("mean", 0))
std_dice = scores.get("std_dice", scores.get("std", 0))
return (mean_dice, std_dice)
def create_subplot(ax, model_averages: Dict, model_stds: Dict, organs: Dict, modality: str):
"""Create a subplot for each modality"""
ax.set_title(modality, fontsize=12, pad=10)
ax.grid(True, axis='y', linestyle='--', alpha=0.7)
x = np.arange(len(organs)) * 1.5
width = 0.35
colors = {
'2D': 'royalblue',
'3D_fullres': 'limegreen',
'3D_lowres': 'orange'
}
positions = {
'2D': x - width,
'3D_fullres': x,
'3D_lowres': x + width
}
for model, averages in model_averages.items():
ax.bar(positions[model],
[averages[organ] * 100 for organ in organs.keys()],
width,
label=model,
color=colors[model])
ax.set_ylim(0, 100)
ax.set_ylabel('DSC (%)', fontsize=10)
ax.set_xlabel('OAR', fontsize=10)
ax.set_xticks(x)
ax.set_xticklabels([organs[k] for k in organs.keys()], rotation=30, ha='right', fontsize=8)
if modality == 'NC-CT':
ax.legend(loc='upper right', fontsize=8)
def create_results_table(all_results: Dict[str, Dict], organs: Dict, output_path: str):
"""Create and save results table"""
for model_type in ['2D', '3D_fullres', '3D_lowres']:
data = []
for modality, results in all_results.items():
for organ_id in organs.keys():
mean, std = results[model_type][organ_id]
data.append({
'Modality': modality,
'OAR': organs[organ_id],
'DSC': f"{mean*100:.2f} ± {std*100:.2f}"
})
df = pd.DataFrame(data)
pivot_df = df.pivot(index='OAR', columns='Modality', values='DSC')
filename = f"{output_path}_{model_type}_results.csv"
pivot_df.to_csv(filename)
def process_modality(base_path: str, modality: str, organs: Dict) -> Tuple[Dict, Dict]:
"""Process data for a single modality"""
paths = {
'2D': os.path.join(base_path, f'{modality}_test_dice_results_2D.json'),
'3D_fullres': os.path.join(base_path, f'{modality}_test_dice_results_3D_fullres.json'),
'3D_lowres': os.path.join(base_path, f'{modality}_test_dice_results_3D_lowres.json')
}
model_dice_scores = {
model: {organ: None for organ in organs.keys()}
for model in paths.keys()
}
for model, path in paths.items():
data = load_data(path)
for organ_id in organs.keys():
model_dice_scores[model][organ_id] = extract_scores(data, model, organ_id)
model_averages = {}
model_stds = {}
for model, scores in model_dice_scores.items():
model_averages[model] = {organ: scores[organ][0] if scores[organ] else 0 for organ in organs.keys()}
model_stds[model] = {organ: scores[organ][1] if scores[organ] else 0 for organ in organs.keys()}
return model_averages, model_stds
def main():
base_dir = '/mnt/1248/onlylian'
organs = {
"1": "Brainstem",
"2": "Right_Eye",
"3": "Left_Eye",
"4": "Optic_Chiasm",
"5": "Right_Optic_Nerve",
"6": "Left_Optic_Nerve"
}
modalities = {
'NC-CT': 'CT',
'CE-CT': 'CTC',
'T1W': 'T1',
'CE-T1W': 'T1C',
'T2W': 'T2',
'T2-FLAIR': 'FLAIR',
'CT/CE-T1W': 'CT_CTC_T1_OAR'
}
all_results = {}
# Process all modalities including CT_CTC_T1_OAR
fig, axes = plt.subplots(4, 2, figsize=(16, 24))
axes = axes.ravel()
for i, (display_name, folder_name) in enumerate(modalities.items()):
if display_name == 'CT/CE-T1W':
base_path = os.path.join(base_dir, folder_name, 'Dice/test')
else:
base_path = os.path.join(base_dir, folder_name, 'Dice/test')
model_averages, model_stds = process_modality(base_path, folder_name, organs)
create_subplot(axes[i], model_averages, model_stds, organs, display_name)
all_results[display_name] = {
model: {organ_id: (model_averages[model][organ_id], model_stds[model][organ_id])
for organ_id in organs.keys()}
for model in model_averages.keys()
}
plt.tight_layout()
plot_output_path = os.path.join(base_dir, 'multimodal_comparison.png')
plt.savefig(plot_output_path, dpi=300, bbox_inches='tight')
plt.close()
# Save results table
table_output_path = os.path.join(base_dir, 'Dice_scores')
create_results_table(all_results, organs, output_path=table_output_path)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,306 @@
import os
import time
import numpy as np
import SimpleITK as sitk
from tqdm import tqdm
import json
def load_nifti(file_path):
"""加載 NIfTI 文件"""
return sitk.GetArrayFromImage(sitk.ReadImage(file_path))
def compute_dice(pred, gt):
"""計算Dice係數"""
intersection = np.sum(pred & gt)
sum_pred = np.sum(pred)
sum_gt = np.sum(gt)
if sum_pred == 0 and sum_gt == 0:
return 1.0
elif sum_pred == 0 or sum_gt == 0:
return 0.0
return 2.0 * intersection / (sum_pred + sum_gt)
def separate_structures(combined_mask, structure_name=""):
"""分離左右結構"""
# 使用SimpleITK的ConnectedComponent
sitk_image = sitk.GetImageFromArray(combined_mask)
labeled_components = sitk.ConnectedComponent(sitk_image)
labeled_array = sitk.GetArrayFromImage(labeled_components)
# 計算連通區域數量和大小
unique, counts = np.unique(labeled_array, return_counts=True)
if len(unique) < 3: # 背景算一個所以至少需要3個
print(f"警告:{structure_name} 未找到足夠的連通區域")
return None, None
# 排除背景標籤0並按大小排序
region_sizes = [(i, count) for i, count in zip(unique[1:], counts[1:])]
sorted_regions = sorted(region_sizes, key=lambda x: x[1], reverse=True)
if len(sorted_regions) < 2:
print(f"警告:{structure_name} 未找到兩個主要區域")
return None, None
# 獲取兩個最大區域
region1_mask = (labeled_array == sorted_regions[0][0])
region2_mask = (labeled_array == sorted_regions[1][0])
# 計算質心
def get_centroid(mask):
indices = np.where(mask)
return np.mean(indices[2]) # 使用x軸第三維度的平均值
centroid1 = get_centroid(region1_mask)
centroid2 = get_centroid(region2_mask)
# 根據x軸位置確定左右
if centroid1 > centroid2:
right = region1_mask
left = region2_mask
else:
right = region2_mask
left = region1_mask
return right.astype(np.uint8), left.astype(np.uint8)
def evaluate_case(gt_path, pred_path, labels):
"""評估單個案例"""
print(f"\n測試文件: {os.path.basename(pred_path)}")
# 加載圖像
start_time = time.time()
gt_img = load_nifti(gt_path)
pred_img = load_nifti(pred_path)
load_time = time.time() - start_time
print(f"圖像大小: {gt_img.shape}")
print(f"加載時間: {load_time:.2f}")
results = {}
total_time = 0
# 處理眼睛
start_time = time.time()
combined_gt_eye = ((gt_img == 2) | (gt_img == 3)).astype(np.uint8)
combined_pred_eye = ((pred_img == 2) | (pred_img == 3)).astype(np.uint8)
# 形態學處理眼睛
combined_pred_eye_sitk = sitk.GetImageFromArray(combined_pred_eye)
closing_filter = sitk.BinaryMorphologicalClosingImageFilter()
closing_filter.SetKernelRadius(2)
processed_pred_eye = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_eye_sitk))
# 計算眼睛的Dice
eye_dice = compute_dice(processed_pred_eye, combined_gt_eye)
eye_time = time.time() - start_time
total_time += eye_time
results['combined_eye'] = eye_dice
print(f"眼睛合併計算時間: {eye_time:.2f}秒, Dice: {eye_dice:.4f}")
# 分離眼睛
right_pred_eye, left_pred_eye = separate_structures(processed_pred_eye, "眼睛")
right_gt_eye, left_gt_eye = separate_structures(combined_gt_eye, "眼睛")
# 處理視神經
start_time = time.time()
combined_gt_nerve = ((gt_img == 5) | (gt_img == 6)).astype(np.uint8)
combined_pred_nerve = ((pred_img == 5) | (pred_img == 6)).astype(np.uint8)
# 形態學處理視神經
combined_pred_nerve_sitk = sitk.GetImageFromArray(combined_pred_nerve)
processed_pred_nerve = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_nerve_sitk))
# 計算視神經的Dice
nerve_dice = compute_dice(processed_pred_nerve, combined_gt_nerve)
nerve_time = time.time() - start_time
total_time += nerve_time
results['combined_nerve'] = nerve_dice
print(f"視神經合併計算時間: {nerve_time:.2f}秒, Dice: {nerve_dice:.4f}")
# 分離視神經
right_pred_nerve, left_pred_nerve = separate_structures(processed_pred_nerve, "視神經")
right_gt_nerve, left_gt_nerve = separate_structures(combined_gt_nerve, "視神經")
# 計算各個標籤的Dice
for label in labels:
start_time = time.time()
if label == 1: # Brainstem
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 2 and right_pred_eye is not None: # Right Eye
gt_binary = right_gt_eye
pred_binary = right_pred_eye
elif label == 3 and left_pred_eye is not None: # Left Eye
gt_binary = left_gt_eye
pred_binary = left_pred_eye
elif label == 4: # Optic Chiasm
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 5 and right_pred_nerve is not None: # Right Optic Nerve
gt_binary = right_gt_nerve
pred_binary = right_pred_nerve
elif label == 6 and left_pred_nerve is not None: # Left Optic Nerve
gt_binary = left_gt_nerve
pred_binary = left_pred_nerve
else:
continue
try:
dice = compute_dice(pred_binary, gt_binary)
compute_time = time.time() - start_time
total_time += compute_time
results[label] = dice
print(f"標籤 {label} 計算時間: {compute_time:.2f}秒, Dice: {dice:.4f}")
except Exception as e:
print(f"處理標籤 {label} 時出錯:{str(e)}")
results[label] = np.nan
continue
print(f"單個案例總計算時間: {total_time:.2f}")
return results
def evaluate_fold(gt_folder, pred_folder, labels):
"""評估單個fold的所有案例"""
fold_results = {label: [] for label in labels}
fold_results['combined_eye'] = []
fold_results['combined_nerve'] = []
pred_files = sorted([f for f in os.listdir(pred_folder) if f.endswith('.nii.gz')])
for pred_file in tqdm(pred_files, desc=f"Processing {os.path.basename(pred_folder)}"):
gt_path = os.path.join(gt_folder, pred_file)
pred_path = os.path.join(pred_folder, pred_file)
if not os.path.exists(gt_path):
print(f"找不到對應的真實標籤文件:{gt_path}")
continue
case_results = evaluate_case(gt_path, pred_path, labels)
# 收集結果
for key in case_results:
if not np.isnan(case_results[key]):
if key in fold_results:
fold_results[key].append(case_results[key])
return fold_results
def evaluate_model(gt_folder, base_pred_folder, model_name, labels, organ_names):
"""評估單個模型的所有fold"""
print(f"\n開始評估模型:{model_name}")
all_results = {label: [] for label in labels}
all_results['combined_eye'] = []
all_results['combined_nerve'] = []
fold_results = {}
# 處理 fold_0 到 fold_4
for fold in range(5):
print(f"\n處理 {model_name} - Fold {fold}")
pred_folder = os.path.join(base_pred_folder, f"fold_{fold}")
if not os.path.exists(pred_folder):
print(f"找不到預測文件夾:{pred_folder}")
continue
results = evaluate_fold(gt_folder, pred_folder, labels)
fold_results[f"fold_{fold}"] = results
# 合併結果
for key in results:
all_results[key].extend(results[key])
# 計算總平均值和標準差
summary = {}
for key in all_results:
if all_results[key]:
mean_dice = np.mean(all_results[key])
std_dice = np.std(all_results[key])
summary[key] = {
'mean_dice': float(mean_dice),
'std_dice': float(std_dice),
'n_samples': len(all_results[key])
}
else:
summary[key] = {
'mean_dice': np.nan,
'std_dice': np.nan,
'n_samples': 0
}
# 保存結果到指定路径
save_dir = "/mnt/1248/onlylian/CT_CTC_T1_OAR/Dice/test"
os.makedirs(save_dir, exist_ok=True)
filename = os.path.join(save_dir, f'CT_CTC_T1_OAR_test_dice_results_{model_name}.json')
model_results = {
'summary': summary,
'fold_results': fold_results
}
with open(filename, 'w') as f:
json.dump(model_results, f, indent=4)
print(f"\n{model_name}結果已保存到 {filename}")
# 打印結果
print(f"\n{model_name} 模型的 Dice 結果所有fold的平均值:")
# 打印器官結果
for label in labels:
if label in summary:
mean = summary[label]['mean_dice']
std = summary[label]['std_dice']
n = summary[label]['n_samples']
organ_name = organ_names.get(label, f"Label_{label}")
if not np.isnan(mean):
print(f"{organ_name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{organ_name}: 無法計算 (n={n})")
# 打印合併結果
print("\n合併結構的結果:")
for key in ['combined_eye', 'combined_nerve']:
if key in summary:
mean = summary[key]['mean_dice']
std = summary[key]['std_dice']
n = summary[key]['n_samples']
name = "合併眼睛" if key == 'combined_eye' else "合併視神經"
if not np.isnan(mean):
print(f"{name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{name}: 無法計算 (n={n})")
return summary
if __name__ == "__main__":
# 設置路徑和標籤
gt_folder = "/mnt/1248/onlylian/nnUNet/nnUNet_raw/Dataset221_OAR/labelsTs"
models = {
'2D': '/mnt/1248/onlylian/CT_CTC_T1_OAR/output_predictions/2d',
'3D_fullres': '/mnt/1248/onlylian/CT_CTC_T1_OAR/output_predictions/3d_fullres',
'3D_lowres': '/mnt/1248/onlylian/CT_CTC_T1_OAR/output_predictions/3d_lowres'
}
labels = [1, 2, 3, 4, 5, 6]
organ_names = {
1: "Brainstem",
2: "Right_Eye",
3: "Left_Eye",
4: "Optic_Chiasm",
5: "Right_Optic_Nerve",
6: "Left_Optic_Nerve"
}
# 讓用戶選擇要運行哪個模型
print("可用的模型:")
for i, model_name in enumerate(models.keys(), 1):
print(f"{i}. {model_name}")
choice = input("\n請選擇要運行的模型 (輸入數字1-3): ")
try:
model_index = int(choice) - 1
model_names = list(models.keys())
selected_model = model_names[model_index]
evaluate_model(gt_folder, models[selected_model], selected_model, labels, organ_names)
except (ValueError, IndexError):
print("無效的選擇請輸入1-3之間的數字。")

View file

@ -0,0 +1,286 @@
# import json
# import os
# import numpy as np
# import matplotlib.pyplot as plt
# from typing import Dict, Tuple
# # 設置中文字體支持
# plt.rcParams['font.sans-serif'] = ['SimHei']
# plt.rcParams['axes.unicode_minus'] = False
# def load_data(file_path: str) -> Dict:
# """讀取JSON數據文件"""
# try:
# with open(file_path, 'r') as file:
# return json.load(file)
# except FileNotFoundError:
# print(f"文件未找到: {file_path}")
# return {}
# except json.JSONDecodeError:
# print(f"JSON解析錯誤: {file_path}")
# return {}
# def extract_scores(data: Dict, model: str, organ_id: str) -> Tuple[float, float]:
# """從數據中提取特定器官的分數"""
# if "summary" not in data or organ_id not in data["summary"]:
# return (0, 0)
# scores = data["summary"][organ_id]
# if model == '3D_fullres':
# mean_dice = scores.get("mean_dice", 0)
# std_dice = scores.get("std_dice", 0)
# else:
# mean_dice = scores.get("mean_dice", scores.get("mean", 0))
# std_dice = scores.get("std_dice", scores.get("std", 0))
# return (mean_dice, std_dice)
# def create_plot(model_averages: Dict, model_stds: Dict, organs: Dict, output_path: str):
# """創建和保存圖表"""
# plt.figure(figsize=(18, 6))
# plt.title('CT_CTC_T1_OAR', fontsize=14, pad=15)
# plt.grid(True, axis='y', linestyle='--', alpha=0.7)
# x = np.arange(len(organs)) * 2.0
# width = 0.5
# colors = {
# '2D': 'royalblue',
# '3D_fullres': 'limegreen',
# '3D_lowres': 'orange'
# }
# positions = {
# '2D': x - width,
# '3D_fullres': x,
# '3D_lowres': x + width
# }
# for model, averages in model_averages.items():
# bars = plt.bar(positions[model],
# [averages[organ] * 100 for organ in organs.keys()],
# width,
# label=model,
# color=colors[model])
# # 添加數值標籤
# for j, bar in enumerate(bars):
# height = bar.get_height()
# organ = list(organs.keys())[j]
# std = model_stds[model][organ] * 100
# plt.text(bar.get_x() + bar.get_width()/2., height + 0.5,
# f'{height:.1f}±{std:.1f}%',
# ha='center',
# va='bottom',
# fontsize=8)
# plt.ylim(0, 100)
# plt.ylabel('DC (%)', fontsize=12)
# plt.xlabel('OAR', fontsize=12)
# plt.xticks(x, [organs[k] for k in organs.keys()], rotation=45, ha='right')
# plt.legend(loc='upper right')
# plt.tight_layout()
# # 確保輸出目錄存在
# os.makedirs(os.path.dirname(output_path), exist_ok=True)
# plt.savefig(output_path, dpi=300, bbox_inches='tight')
# plt.close()
# def print_results(model_averages: Dict, model_stds: Dict, organs: Dict):
# """打印數值結果"""
# print("\n平均 Dice 分數 (百分比):")
# for model in model_averages.keys():
# print(f"\n{model}:")
# for organ in organs.keys():
# avg = model_averages[model][organ] * 100
# std = model_stds[model][organ] * 100
# print(f"{organs[organ]}: {avg:.1f}±{std:.1f}%")
# def main():
# # 定義路徑和器官映射
# base_path = '/mnt/1248/onlylian/CT_CTC_T1_OAR/Dice/validation'
# paths = {
# '2D': os.path.join(base_path, 'CT_CTC_T1_OAR_validation_dice_results_2D.json'),
# '3D_fullres': os.path.join(base_path, 'CT_CTC_T1_OAR_validation_dice_results_3D_fullres.json'),
# '3D_lowres': os.path.join(base_path, 'CT_CTC_T1_OAR_validation_dice_results_3D_lowres.json')
# }
# organs = {
# "1": "Brainstem",
# "2": "Right_Eye",
# "3": "Left_Eye",
# "4": "Optic_Chiasm",
# "5": "Right_Optic_Nerve",
# "6": "Left_Optic_Nerve"
# }
# # 初始化數據存儲
# model_dice_scores = {
# model: {organ: None for organ in organs.keys()}
# for model in paths.keys()
# }
# # 讀取和處理數據
# for model, path in paths.items():
# data = load_data(path)
# for organ_id in organs.keys():
# model_dice_scores[model][organ_id] = extract_scores(data, model, organ_id)
# # 計算平均值和標準差
# model_averages = {}
# model_stds = {}
# for model, scores in model_dice_scores.items():
# model_averages[model] = {organ: scores[organ][0] if scores[organ] else 0 for organ in organs.keys()}
# model_stds[model] = {organ: scores[organ][1] if scores[organ] else 0 for organ in organs.keys()}
# # 創建圖表並保存
# output_path = os.path.join(base_path, 'CT_CTC_T1_OAR_validation_dice_scores_comparison.png')
# create_plot(model_averages, model_stds, organs, output_path)
# # 打印結果
# print_results(model_averages, model_stds, organs)
# if __name__ == "__main__":
# main()
import json
import os
import numpy as np
import matplotlib.pyplot as plt
from typing import Dict, Tuple
# 設置中文字體支持
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
def load_data(file_path: str) -> Dict:
"""讀取JSON數據文件"""
try:
with open(file_path, 'r') as file:
return json.load(file)
except FileNotFoundError:
print(f"文件未找到: {file_path}")
return {}
except json.JSONDecodeError:
print(f"JSON解析錯誤: {file_path}")
return {}
def extract_scores(data: Dict, model: str, organ_id: str) -> Tuple[float, float]:
"""從數據中提取特定器官的分數"""
if "summary" not in data or organ_id not in data["summary"]:
return (0, 0)
scores = data["summary"][organ_id]
if model == '3D_fullres':
mean_dice = scores.get("mean_dice", 0)
std_dice = scores.get("std_dice", 0)
else:
mean_dice = scores.get("mean_dice", scores.get("mean", 0))
std_dice = scores.get("std_dice", scores.get("std", 0))
return (mean_dice, std_dice)
def create_plot(model_averages: Dict, model_stds: Dict, organs: Dict, output_path: str):
"""創建和保存圖表"""
plt.figure(figsize=(16, 5)) # Reduced figure size
plt.title('CT_CTC_T1_OAR', fontsize=12, pad=10) # Reduced title size and padding
plt.grid(True, axis='y', linestyle='--', alpha=0.7)
x = np.arange(len(organs)) * 1.5 # Reduced spacing between groups
width = 0.35 # Reduced bar width
colors = {
'2D': 'royalblue',
'3D_fullres': 'limegreen',
'3D_lowres': 'orange'
}
positions = {
'2D': x - width,
'3D_fullres': x,
'3D_lowres': x + width
}
for model, averages in model_averages.items():
bars = plt.bar(positions[model],
[averages[organ] * 100 for organ in organs.keys()],
width,
label=model,
color=colors[model])
# 添加數值標籤
for j, bar in enumerate(bars):
height = bar.get_height()
organ = list(organs.keys())[j]
std = model_stds[model][organ] * 100
plt.text(bar.get_x() + bar.get_width()/2., height + 0.5,
f'{height:.1f}±{std:.1f}%',
ha='center',
va='bottom',
fontsize=6) # Reduced font size
plt.ylim(0, 100)
plt.ylabel('DC (%)', fontsize=10)
plt.xlabel('OAR', fontsize=10)
plt.xticks(x, [organs[k] for k in organs.keys()], rotation=30, ha='right', fontsize=8)
plt.legend(loc='upper right', fontsize=8)
plt.tight_layout()
os.makedirs(os.path.dirname(output_path), exist_ok=True)
plt.savefig(output_path, dpi=300, bbox_inches='tight')
plt.close()
def print_results(model_averages: Dict, model_stds: Dict, organs: Dict):
"""打印數值結果"""
print("\n平均 Dice 分數 (百分比):")
for model in model_averages.keys():
print(f"\n{model}:")
for organ in organs.keys():
avg = model_averages[model][organ] * 100
std = model_stds[model][organ] * 100
print(f"{organs[organ]}: {avg:.1f}±{std:.1f}%")
def main():
# 定義路徑和器官映射
base_path = '/mnt/1248/onlylian/CT_CTC_T1_OAR/Dice/validation'
paths = {
'2D': os.path.join(base_path, 'CT_CTC_T1_OAR_validation_dice_results_2D.json'),
'3D_fullres': os.path.join(base_path, 'CT_CTC_T1_OAR_validation_dice_results_3D_fullres.json'),
'3D_lowres': os.path.join(base_path, 'CT_CTC_T1_OAR_validation_dice_results_3D_lowres.json')
}
organs = {
"1": "Brainstem",
"2": "Right_Eye",
"3": "Left_Eye",
"4": "Optic_Chiasm",
"5": "Right_Optic_Nerve",
"6": "Left_Optic_Nerve"
}
# 初始化數據存儲
model_dice_scores = {
model: {organ: None for organ in organs.keys()}
for model in paths.keys()
}
# 讀取和處理數據
for model, path in paths.items():
data = load_data(path)
for organ_id in organs.keys():
model_dice_scores[model][organ_id] = extract_scores(data, model, organ_id)
# 計算平均值和標準差
model_averages = {}
model_stds = {}
for model, scores in model_dice_scores.items():
model_averages[model] = {organ: scores[organ][0] if scores[organ] else 0 for organ in organs.keys()}
model_stds[model] = {organ: scores[organ][1] if scores[organ] else 0 for organ in organs.keys()}
# 創建圖表並保存
output_path = os.path.join(base_path, 'CT_CTC_T1_OAR_validation_dice_scores_comparison.png')
create_plot(model_averages, model_stds, organs, output_path)
# 打印結果
print_results(model_averages, model_stds, organs)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,602 @@
# import os
# import time
# import numpy as np
# import SimpleITK as sitk
# from tqdm import tqdm
# import json
# def load_nifti(file_path):
# """加載 NIfTI 文件"""
# return sitk.GetArrayFromImage(sitk.ReadImage(file_path))
# def compute_dice(pred, gt):
# """計算Dice係數"""
# intersection = np.sum(pred & gt)
# sum_pred = np.sum(pred)
# sum_gt = np.sum(gt)
# if sum_pred == 0 and sum_gt == 0:
# return 1.0
# elif sum_pred == 0 or sum_gt == 0:
# return 0.0
# return 2.0 * intersection / (sum_pred + sum_gt)
# def separate_structures(combined_mask, structure_name=""):
# """分離左右結構"""
# sitk_image = sitk.GetImageFromArray(combined_mask)
# labeled_components = sitk.ConnectedComponent(sitk_image)
# labeled_array = sitk.GetArrayFromImage(labeled_components)
# unique, counts = np.unique(labeled_array, return_counts=True)
# if len(unique) < 3:
# print(f"警告:{structure_name} 未找到足夠的連通區域")
# return None, None
# region_sizes = [(i, count) for i, count in zip(unique[1:], counts[1:])]
# sorted_regions = sorted(region_sizes, key=lambda x: x[1], reverse=True)
# if len(sorted_regions) < 2:
# print(f"警告:{structure_name} 未找到兩個主要區域")
# return None, None
# region1_mask = (labeled_array == sorted_regions[0][0])
# region2_mask = (labeled_array == sorted_regions[1][0])
# def get_centroid(mask):
# indices = np.where(mask)
# return np.mean(indices[2])
# centroid1 = get_centroid(region1_mask)
# centroid2 = get_centroid(region2_mask)
# if centroid1 > centroid2:
# right = region1_mask
# left = region2_mask
# else:
# right = region2_mask
# left = region1_mask
# return right.astype(np.uint8), left.astype(np.uint8)
# def evaluate_case(gt_path, pred_path, labels):
# """評估單個案例"""
# print(f"\n測試文件: {os.path.basename(pred_path)}")
# start_time = time.time()
# gt_img = load_nifti(gt_path)
# pred_img = load_nifti(pred_path)
# load_time = time.time() - start_time
# print(f"圖像大小: {gt_img.shape}")
# print(f"加載時間: {load_time:.2f}秒")
# results = {}
# total_time = 0
# # 處理眼睛
# start_time = time.time()
# combined_gt_eye = ((gt_img == 2) | (gt_img == 3)).astype(np.uint8)
# combined_pred_eye = ((pred_img == 2) | (pred_img == 3)).astype(np.uint8)
# combined_pred_eye_sitk = sitk.GetImageFromArray(combined_pred_eye)
# closing_filter = sitk.BinaryMorphologicalClosingImageFilter()
# closing_filter.SetKernelRadius(2)
# processed_pred_eye = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_eye_sitk))
# eye_dice = compute_dice(processed_pred_eye, combined_gt_eye)
# eye_time = time.time() - start_time
# total_time += eye_time
# results['combined_eye'] = eye_dice
# print(f"眼睛合併計算時間: {eye_time:.2f}秒, Dice: {eye_dice:.4f}")
# right_pred_eye, left_pred_eye = separate_structures(processed_pred_eye, "眼睛")
# right_gt_eye, left_gt_eye = separate_structures(combined_gt_eye, "眼睛")
# # 處理視神經
# start_time = time.time()
# combined_gt_nerve = ((gt_img == 5) | (gt_img == 6)).astype(np.uint8)
# combined_pred_nerve = ((pred_img == 5) | (pred_img == 6)).astype(np.uint8)
# combined_pred_nerve_sitk = sitk.GetImageFromArray(combined_pred_nerve)
# processed_pred_nerve = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_nerve_sitk))
# nerve_dice = compute_dice(processed_pred_nerve, combined_gt_nerve)
# nerve_time = time.time() - start_time
# total_time += nerve_time
# results['combined_nerve'] = nerve_dice
# print(f"視神經合併計算時間: {nerve_time:.2f}秒, Dice: {nerve_dice:.4f}")
# right_pred_nerve, left_pred_nerve = separate_structures(processed_pred_nerve, "視神經")
# right_gt_nerve, left_gt_nerve = separate_structures(combined_gt_nerve, "視神經")
# for label in labels:
# start_time = time.time()
# if label == 1: # Brainstem
# gt_binary = (gt_img == label).astype(np.uint8)
# pred_binary = (pred_img == label).astype(np.uint8)
# elif label == 2 and right_pred_eye is not None: # Right Eye
# gt_binary = right_gt_eye
# pred_binary = right_pred_eye
# elif label == 3 and left_pred_eye is not None: # Left Eye
# gt_binary = left_gt_eye
# pred_binary = left_pred_eye
# elif label == 4: # Optic Chiasm
# gt_binary = (gt_img == label).astype(np.uint8)
# pred_binary = (pred_img == label).astype(np.uint8)
# elif label == 5 and right_pred_nerve is not None: # Right Optic Nerve
# gt_binary = right_gt_nerve
# pred_binary = right_pred_nerve
# elif label == 6 and left_pred_nerve is not None: # Left Optic Nerve
# gt_binary = left_gt_nerve
# pred_binary = left_pred_nerve
# else:
# continue
# try:
# dice = compute_dice(pred_binary, gt_binary)
# compute_time = time.time() - start_time
# total_time += compute_time
# results[label] = dice
# print(f"標籤 {label} 計算時間: {compute_time:.2f}秒, Dice: {dice:.4f}")
# except Exception as e:
# print(f"處理標籤 {label} 時出錯:{str(e)}")
# results[label] = np.nan
# continue
# print(f"單個案例總計算時間: {total_time:.2f}秒")
# return results
# def evaluate_validation_fold(gt_base_folder, validation_folder, labels):
# """評估單個fold的驗證集"""
# fold_results = {label: [] for label in labels}
# fold_results['combined_eye'] = []
# fold_results['combined_nerve'] = []
# pred_files = sorted([f for f in os.listdir(validation_folder) if f.endswith('.nii.gz')])
# for pred_file in tqdm(pred_files, desc=f"Processing {os.path.basename(validation_folder)}"):
# # 從預測文件名中提取病人ID
# patient_id = pred_file.split('.nii.gz')[0]
# # 構建對應的ground truth路徑
# gt_path = os.path.join(gt_base_folder, f"{patient_id}.nii.gz")
# pred_path = os.path.join(validation_folder, pred_file)
# if not os.path.exists(gt_path):
# print(f"找不到對應的真實標籤文件:{gt_path}")
# continue
# case_results = evaluate_case(gt_path, pred_path, labels)
# for key in case_results:
# if not np.isnan(case_results[key]):
# if key in fold_results:
# fold_results[key].append(case_results[key])
# return fold_results
# def evaluate_model(gt_folder, model_path, model_name, labels, organ_names, output_dir):
# """評估模型所有fold的驗證集"""
# print(f"\n開始評估模型{model_name}")
# all_results = {label: [] for label in labels}
# all_results['combined_eye'] = []
# all_results['combined_nerve'] = []
# fold_results = {}
# # 遍歷每個fold
# for fold in range(5):
# validation_folder = os.path.join(model_path, f"fold_{fold}", "validation")
# if not os.path.exists(validation_folder):
# print(f"找不到fold_{fold}的驗證資料夾:{validation_folder}")
# continue
# print(f"\n處理 {model_name} - Fold {fold} 驗證集")
# results = evaluate_validation_fold(gt_folder, validation_folder, labels)
# fold_results[f"fold_{fold}"] = results
# for key in results:
# all_results[key].extend(results[key])
# # 計算總平均值和標準差
# summary = {}
# for key in all_results:
# if all_results[key]:
# mean_dice = np.mean(all_results[key])
# std_dice = np.std(all_results[key])
# summary[key] = {
# 'mean_dice': float(mean_dice),
# 'std_dice': float(std_dice),
# 'n_samples': len(all_results[key])
# }
# else:
# summary[key] = {
# 'mean_dice': np.nan,
# 'std_dice': np.nan,
# 'n_samples': 0
# }
# # 保存結果
# model_results = {
# 'summary': summary,
# 'fold_results': fold_results
# }
# # 確保輸出目錄存在
# os.makedirs(output_dir, exist_ok=True)
# # 使用完整路徑保存結果
# filename = os.path.join(output_dir, f'CT_CTC_T1_OAR_validation_dice_results_{model_name}.json')
# with open(filename, 'w') as f:
# json.dump(model_results, f, indent=4)
# print(f"\n{model_name}驗證集結果已保存到 {filename}")
# # 打印結果
# print(f"\n{model_name} 模型的驗證集 Dice 結果所有fold的平均值:")
# # 打印器官結果
# for label in labels:
# if label in summary:
# mean = summary[label]['mean_dice']
# std = summary[label]['std_dice']
# n = summary[label]['n_samples']
# organ_name = organ_names.get(label, f"Label_{label}")
# if not np.isnan(mean):
# print(f"{organ_name}: {mean:.4f} ± {std:.4f} (n={n})")
# else:
# print(f"{organ_name}: 無法計算 (n={n})")
# # 打印合併結果
# print("\n合併結構的結果:")
# for key in ['combined_eye', 'combined_nerve']:
# if key in summary:
# mean = summary[key]['mean_dice']
# std = summary[key]['std_dice']
# n = summary[key]['n_samples']
# name = "合併眼睛" if key == 'combined_eye' else "合併視神經"
# if not np.isnan(mean):
# print(f"{name}: {mean:.4f} ± {std:.4f} (n={n})")
# else:
# print(f"{name}: 無法計算 (n={n})")
# return summary
# if __name__ == "__main__":
# # 設置路徑
# base_dir = "/mnt/1248/onlylian"
# gt_folder = os.path.join(base_dir, "nnUNet/nnUNet_raw/Dataset221_OAR/labelsTr")
# output_dir = base_dir # 設置輸出目錄為 /mnt/1248/onlylian
# models = {
# '2D': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset221_OAR/nnUNetTrainer__nnUNetPlans__2d"),
# '3D_fullres': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset221_OAR/nnUNetTrainer__nnUNetPlans__3d_fullres"),
# '3D_lowres': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset221_OAR/nnUNetTrainer__nnUNetPlans__3d_lowres")
# }
# labels = [1, 2, 3, 4, 5, 6]
# organ_names = {
# 1: "Brainstem",
# 2: "Right_Eye",
# 3: "Left_Eye",
# 4: "Optic_Chiasm",
# 5: "Right_Optic_Nerve",
# 6: "Left_Optic_Nerve"
# }
# # 讓用戶選擇要運行哪個模型
# print("可用的模型:")
# for i, model_name in enumerate(models.keys(), 1):
# print(f"{i}. {model_name}")
# choice = input("\n請選擇要運行的模型 (輸入數字1-3): ")
# try:
# model_index = int(choice) - 1
# model_names = list(models.keys())
# selected_model = model_names[model_index]
# evaluate_model(gt_folder, models[selected_model], selected_model, labels, organ_names, output_dir)
# except (ValueError, IndexError):
# print("無效的選擇請輸入1-3之間的數字。")
import os
import time
import numpy as np
import SimpleITK as sitk
from tqdm import tqdm
import json
def load_nifti(file_path):
"""加載 NIfTI 文件"""
return sitk.GetArrayFromImage(sitk.ReadImage(file_path))
def compute_dice(pred, gt):
"""計算Dice係數"""
intersection = np.sum(pred & gt)
sum_pred = np.sum(pred)
sum_gt = np.sum(gt)
if sum_pred == 0 and sum_gt == 0:
return 1.0
elif sum_pred == 0 or sum_gt == 0:
return 0.0
return 2.0 * intersection / (sum_pred + sum_gt)
def separate_structures(combined_mask, structure_name=""):
"""分離左右結構"""
sitk_image = sitk.GetImageFromArray(combined_mask)
labeled_components = sitk.ConnectedComponent(sitk_image)
labeled_array = sitk.GetArrayFromImage(labeled_components)
unique, counts = np.unique(labeled_array, return_counts=True)
if len(unique) < 3:
print(f"警告:{structure_name} 未找到足夠的連通區域")
return None, None
region_sizes = [(i, count) for i, count in zip(unique[1:], counts[1:])]
sorted_regions = sorted(region_sizes, key=lambda x: x[1], reverse=True)
if len(sorted_regions) < 2:
print(f"警告:{structure_name} 未找到兩個主要區域")
return None, None
region1_mask = (labeled_array == sorted_regions[0][0])
region2_mask = (labeled_array == sorted_regions[1][0])
def get_centroid(mask):
indices = np.where(mask)
return np.mean(indices[2])
centroid1 = get_centroid(region1_mask)
centroid2 = get_centroid(region2_mask)
if centroid1 > centroid2:
right = region1_mask
left = region2_mask
else:
right = region2_mask
left = region1_mask
return right.astype(np.uint8), left.astype(np.uint8)
def evaluate_case(gt_path, pred_path, labels):
"""評估單個案例"""
print(f"\n測試文件: {os.path.basename(pred_path)}")
start_time = time.time()
gt_img = load_nifti(gt_path)
pred_img = load_nifti(pred_path)
load_time = time.time() - start_time
print(f"圖像大小: {gt_img.shape}")
print(f"加載時間: {load_time:.2f}")
results = {}
total_time = 0
# 處理眼睛
start_time = time.time()
combined_gt_eye = ((gt_img == 2) | (gt_img == 3)).astype(np.uint8)
combined_pred_eye = ((pred_img == 2) | (pred_img == 3)).astype(np.uint8)
combined_pred_eye_sitk = sitk.GetImageFromArray(combined_pred_eye)
closing_filter = sitk.BinaryMorphologicalClosingImageFilter()
closing_filter.SetKernelRadius(2)
processed_pred_eye = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_eye_sitk))
eye_dice = compute_dice(processed_pred_eye, combined_gt_eye)
eye_time = time.time() - start_time
total_time += eye_time
results['combined_eye'] = eye_dice
print(f"眼睛合併計算時間: {eye_time:.2f}秒, Dice: {eye_dice:.4f}")
right_pred_eye, left_pred_eye = separate_structures(processed_pred_eye, "眼睛")
right_gt_eye, left_gt_eye = separate_structures(combined_gt_eye, "眼睛")
# 處理視神經
start_time = time.time()
combined_gt_nerve = ((gt_img == 5) | (gt_img == 6)).astype(np.uint8)
combined_pred_nerve = ((pred_img == 5) | (pred_img == 6)).astype(np.uint8)
combined_pred_nerve_sitk = sitk.GetImageFromArray(combined_pred_nerve)
processed_pred_nerve = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_nerve_sitk))
nerve_dice = compute_dice(processed_pred_nerve, combined_gt_nerve)
nerve_time = time.time() - start_time
total_time += nerve_time
results['combined_nerve'] = nerve_dice
print(f"視神經合併計算時間: {nerve_time:.2f}秒, Dice: {nerve_dice:.4f}")
right_pred_nerve, left_pred_nerve = separate_structures(processed_pred_nerve, "視神經")
right_gt_nerve, left_gt_nerve = separate_structures(combined_gt_nerve, "視神經")
for label in labels:
start_time = time.time()
if label == 1: # Brainstem
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 2 and right_pred_eye is not None: # Right Eye
gt_binary = right_gt_eye
pred_binary = right_pred_eye
elif label == 3 and left_pred_eye is not None: # Left Eye
gt_binary = left_gt_eye
pred_binary = left_pred_eye
elif label == 4: # Optic Chiasm
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 5 and right_pred_nerve is not None: # Right Optic Nerve
gt_binary = right_gt_nerve
pred_binary = right_pred_nerve
elif label == 6 and left_pred_nerve is not None: # Left Optic Nerve
gt_binary = left_gt_nerve
pred_binary = left_pred_nerve
else:
continue
try:
dice = compute_dice(pred_binary, gt_binary)
compute_time = time.time() - start_time
total_time += compute_time
results[label] = dice
print(f"標籤 {label} 計算時間: {compute_time:.2f}秒, Dice: {dice:.4f}")
except Exception as e:
print(f"處理標籤 {label} 時出錯:{str(e)}")
results[label] = np.nan
continue
print(f"單個案例總計算時間: {total_time:.2f}")
return results
def evaluate_validation_fold(gt_base_folder, validation_folder, labels):
"""評估單個fold的驗證集"""
fold_results = {label: [] for label in labels}
fold_results['combined_eye'] = []
fold_results['combined_nerve'] = []
pred_files = sorted([f for f in os.listdir(validation_folder) if f.endswith('.nii.gz')])
for pred_file in tqdm(pred_files, desc=f"Processing {os.path.basename(validation_folder)}"):
# 從預測文件名中提取病人ID
patient_id = pred_file.split('.nii.gz')[0]
# 構建對應的ground truth路徑
gt_path = os.path.join(gt_base_folder, f"{patient_id}.nii.gz")
pred_path = os.path.join(validation_folder, pred_file)
if not os.path.exists(gt_path):
print(f"找不到對應的真實標籤文件:{gt_path}")
continue
case_results = evaluate_case(gt_path, pred_path, labels)
for key in case_results:
if not np.isnan(case_results[key]):
if key in fold_results:
fold_results[key].append(case_results[key])
return fold_results
def evaluate_model(gt_folder, model_path, model_name, labels, organ_names, output_dir):
"""評估模型所有fold的驗證集"""
print(f"\n開始評估模型:{model_name}")
all_results = {label: [] for label in labels}
all_results['combined_eye'] = []
all_results['combined_nerve'] = []
fold_results = {}
# 遍歷每個fold
for fold in range(5):
validation_folder = os.path.join(model_path, f"fold_{fold}", "validation")
if not os.path.exists(validation_folder):
print(f"找不到fold_{fold}的驗證資料夾:{validation_folder}")
continue
print(f"\n處理 {model_name} - Fold {fold} 驗證集")
results = evaluate_validation_fold(gt_folder, validation_folder, labels)
fold_results[f"fold_{fold}"] = results
for key in results:
all_results[key].extend(results[key])
# 計算總平均值和標準差
summary = {}
for key in all_results:
if all_results[key]:
mean_dice = np.mean(all_results[key])
std_dice = np.std(all_results[key])
summary[key] = {
'mean_dice': float(mean_dice),
'std_dice': float(std_dice),
'n_samples': len(all_results[key])
}
else:
summary[key] = {
'mean_dice': np.nan,
'std_dice': np.nan,
'n_samples': 0
}
# 保存結果
model_results = {
'summary': summary,
'fold_results': fold_results
}
# 建立完整的輸出路徑
output_path = os.path.join(output_dir, 'CT_CTC_T1_OAR', 'Dice', 'validation')
os.makedirs(output_path, exist_ok=True)
# 使用完整路徑保存結果
filename = os.path.join(output_path, f'dice_results_{model_name}.json')
with open(filename, 'w') as f:
json.dump(model_results, f, indent=4)
print(f"\n{model_name}驗證集結果已保存到 {filename}")
# 打印結果
print(f"\n{model_name} 模型的驗證集 Dice 結果所有fold的平均值:")
# 打印器官結果
for label in labels:
if label in summary:
mean = summary[label]['mean_dice']
std = summary[label]['std_dice']
n = summary[label]['n_samples']
organ_name = organ_names.get(label, f"Label_{label}")
if not np.isnan(mean):
print(f"{organ_name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{organ_name}: 無法計算 (n={n})")
# 打印合併結果
print("\n合併結構的結果:")
for key in ['combined_eye', 'combined_nerve']:
if key in summary:
mean = summary[key]['mean_dice']
std = summary[key]['std_dice']
n = summary[key]['n_samples']
name = "合併眼睛" if key == 'combined_eye' else "合併視神經"
if not np.isnan(mean):
print(f"{name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{name}: 無法計算 (n={n})")
return summary
if __name__ == "__main__":
# 設置路徑
base_dir = "/mnt/1248/onlylian"
gt_folder = os.path.join(base_dir, "nnUNet/nnUNet_raw/Dataset221_OAR/labelsTr")
output_dir = base_dir # 設置基礎輸出目錄
models = {
'2D': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset221_OAR/nnUNetTrainer__nnUNetPlans__2d"),
'3D_fullres': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset221_OAR/nnUNetTrainer__nnUNetPlans__3d_fullres"),
'3D_lowres': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset221_OAR/nnUNetTrainer__nnUNetPlans__3d_lowres")
}
labels = [1, 2, 3, 4, 5, 6]
organ_names = {
1: "Brainstem",
2: "Right_Eye",
3: "Left_Eye",
4: "Optic_Chiasm",
5: "Right_Optic_Nerve",
6: "Left_Optic_Nerve"
}
# 讓用戶選擇要運行哪個模型
print("可用的模型:")
for i, model_name in enumerate(models.keys(), 1):
print(f"{i}. {model_name}")
choice = input("\n請選擇要運行的模型 (輸入數字1-3): ")
try:
model_index = int(choice) - 1
model_names = list(models.keys())
selected_model = model_names[model_index]
evaluate_model(gt_folder, models[selected_model], selected_model, labels, organ_names, output_dir)
except (ValueError, IndexError):
print("無效的選擇請輸入1-3之間的數字。")

View file

@ -0,0 +1,161 @@
import nibabel as nib
import matplotlib.pyplot as plt
import numpy as np
import os
def load_nifti(file_path):
"""載入NIfTI檔案"""
return nib.load(file_path).get_fdata()
def create_colored_mask(data):
"""創建彩色遮罩"""
color_dict = {
1: [144/255, 238/255, 144/255], # Brainstem - 綠色
2: [255/255, 218/255, 150/255], # Right_Eye - 淺黃色
3: [205/255, 170/255, 125/255], # Left_Eye - 棕色
4: [135/255, 206/255, 235/255], # Optic_Chiasm - 藍色
5: [255/255, 99/255, 71/255], # Right_Optic_Nerve - 亮珊瑚紅
6: [255/255, 160/255, 122/255] # Left_Optic_Nerve - 淺珊瑚紅
}
mask = np.zeros((*data.shape, 4))
for label, color in color_dict.items():
organ_mask = (data == label)
if np.any(organ_mask):
mask[organ_mask] = [*color, 1.0]
return mask
def get_brain_bounds(image):
"""獲取腦部區域的邊界"""
mask = image > np.percentile(image, 1)
rows = np.any(mask, axis=1)
cols = np.any(mask, axis=0)
rmin, rmax = np.where(rows)[0][[0, -1]]
cmin, cmax = np.where(cols)[0][[0, -1]]
center_r = (rmin + rmax) // 2
center_c = (cmin + cmax) // 2
radius = int(max(rmax - rmin, cmax - cmin) * 0.55)
margin = int(radius * 0.15)
rmin = max(center_r - radius - margin, 0)
rmax = min(center_r + radius + margin, image.shape[0])
cmin = max(center_c - radius - margin, 0)
cmax = min(center_c + radius + margin, image.shape[1])
return rmin, rmax, cmin, cmax
def check_organs_in_slice(slice_data):
"""檢查切片中存在的器官標籤"""
unique_labels = set(np.unique(slice_data))
if 0 in unique_labels:
unique_labels.remove(0)
organ_dict = {
1: "Brainstem",
2: "Right_Eye",
3: "Left_Eye",
4: "Optic_Chiasm",
5: "Right_Optic_Nerve",
6: "Left_Optic_Nerve"
}
return {organ_dict[label] for label in unique_labels if label in organ_dict}
def adjust_contrast(img):
"""調整圖像對比度"""
p2, p98 = np.percentile(img, (2, 98))
return np.clip((img - p2) / (p98 - p2), 0, 1)
def save_slice(image_ct, image_mri, ground_truth, prediction, slice_num, save_path):
"""保存指定切片的比較圖"""
plt.clf()
fig, axes = plt.subplots(1, 3, figsize=(30, 10))
plt.subplots_adjust(wspace=0.01)
# Process MRI image
img_slice_mri = np.rot90(image_mri[:, :, slice_num])
rmin, rmax, cmin, cmax = get_brain_bounds(img_slice_mri)
# Adjust contrast for MRI
mri_norm = adjust_contrast(img_slice_mri)
# Original MRI Image
axes[0].imshow(mri_norm[rmin:rmax, cmin:cmax], cmap='gray')
axes[0].set_title('Original Image', fontsize=32, pad=20, weight='bold')
axes[0].axis('off')
# Ground Truth with MRI background
gt_slice = np.rot90(ground_truth[:, :, slice_num])
gt_mask = create_colored_mask(gt_slice)
axes[1].imshow(mri_norm[rmin:rmax, cmin:cmax], cmap='gray', zorder=1)
axes[1].imshow(gt_mask[rmin:rmax, cmin:cmax], zorder=2)
axes[1].set_title('Ground Truth', fontsize=32, pad=20, weight='bold')
axes[1].axis('off')
# Prediction with MRI background
pred_slice = np.rot90(prediction[:, :, slice_num])
pred_mask = create_colored_mask(pred_slice)
axes[2].imshow(mri_norm[rmin:rmax, cmin:cmax], cmap='gray', zorder=1)
axes[2].imshow(pred_mask[rmin:rmax, cmin:cmax], zorder=2)
axes[2].set_title('Prediction', fontsize=32, pad=20, weight='bold')
axes[2].axis('off')
plt.savefig(save_path, bbox_inches='tight', dpi=300, pad_inches=0.05)
plt.close()
print(f"已保存切片 {slice_num} 至: {save_path}")
if __name__ == "__main__":
# 載入影像檔案
image_path_1 = '/mnt/1248/onlylian/nnUNet/nnUNet_raw/Dataset221_OAR/imagesTs/OAR_051_0000.nii.gz' #CT
image_path_2 = '/mnt/1248/onlylian/nnUNet/nnUNet_raw/Dataset221_OAR/imagesTs/OAR_051_0001.nii.gz' #MRI
gt_path = '/mnt/1248/onlylian/nnUNet/nnUNet_raw/Dataset221_OAR/labelsTs/OAR_051.nii.gz'
pred_path = '/mnt/1248/onlylian/CT_CTC_T1_OAR/output_predictions/3d_fullres/fold_4/OAR_051.nii.gz'
# 設定保存路徑
save_dir = '/mnt/1248/onlylian/CT_CTC_T1_OAR'
os.makedirs(save_dir, exist_ok=True)
# 讀取影像
print("正在載入影像...")
image_ct = load_nifti(image_path_1)
image_mri = load_nifti(image_path_2)
ground_truth = load_nifti(gt_path)
prediction = load_nifti(pred_path)
print("影像載入完成")
print("\n分析切片中的器官...")
total_slices = image_ct.shape[2]
for slice_num in range(total_slices):
gt_slice = ground_truth[:, :, slice_num]
pred_slice = prediction[:, :, slice_num]
gt_organs = check_organs_in_slice(gt_slice)
pred_organs = check_organs_in_slice(pred_slice)
if gt_organs or pred_organs:
print(f"\n切片 {slice_num}:")
print(f"Ground Truth 包含: {', '.join(sorted(gt_organs))}")
print(f"Prediction 包含: {', '.join(sorted(pred_organs))}")
print("\n開始切片選擇和保存過程...")
while True:
try:
slice_num = int(input("\n請輸入要保存的切片編號 (-1 退出): "))
if slice_num == -1:
print("程序結束")
break
if 0 <= slice_num < total_slices:
save_path = os.path.join(save_dir, f'comparison_L_optic_nerve_051_{slice_num:03d}.png')
save_slice(image_ct, image_mri, ground_truth, prediction, slice_num, save_path)
else:
print(f"切片編號必須在 0 到 {total_slices-1} 之間")
except ValueError:
print("請輸入有效的數字")
except Exception as e:
print(f"發生錯誤: {str(e)}")

165
CT_CTC_T1_OAR/chiasm.py Normal file
View file

@ -0,0 +1,165 @@
import nibabel as nib
import matplotlib.pyplot as plt
import numpy as np
import os
def load_nifti(file_path):
"""載入NIfTI檔案"""
return nib.load(file_path).get_fdata()
def create_colored_mask(data):
"""創建彩色遮罩"""
color_dict = {
1: [144/255, 238/255, 144/255], # Brainstem - 綠色
2: [255/255, 218/255, 150/255], # Right_Eye - 淺黃色
3: [205/255, 170/255, 125/255], # Left_Eye - 棕色
4: [135/255, 206/255, 235/255], # Optic_Chiasm - 藍色
5: [255/255, 99/255, 71/255], # Right_Optic_Nerve - 亮珊瑚紅
6: [255/255, 160/255, 122/255] # Left_Optic_Nerve - 淺珊瑚紅
}
# 創建一個具有透明背景的遮罩
mask = np.zeros((*data.shape, 4)) # 使用4通道RGBA
for label, color in color_dict.items():
# 為每個器官設置顏色,包括完全不透明的 alpha 通道
organ_mask = (data == label)
if np.any(organ_mask): # 只處理存在的器官
mask[organ_mask] = [*color, 1.0] # RGB + alpha
return mask
def get_brain_bounds(image):
"""獲取腦部區域的邊界"""
mask = image > np.percentile(image, 1)
rows = np.any(mask, axis=1)
cols = np.any(mask, axis=0)
rmin, rmax = np.where(rows)[0][[0, -1]]
cmin, cmax = np.where(cols)[0][[0, -1]]
center_r = (rmin + rmax) // 2
center_c = (cmin + cmax) // 2
radius = int(max(rmax - rmin, cmax - cmin) * 0.55)
margin = int(radius * 0.15)
rmin = max(center_r - radius - margin, 0)
rmax = min(center_r + radius + margin, image.shape[0])
cmin = max(center_c - radius - margin, 0)
cmax = min(center_c + radius + margin, image.shape[1])
return rmin, rmax, cmin, cmax
def check_organs_in_slice(slice_data):
"""檢查切片中存在的器官標籤"""
unique_labels = set(np.unique(slice_data))
if 0 in unique_labels:
unique_labels.remove(0)
organ_dict = {
1: "Brainstem",
2: "Right_Eye",
3: "Left_Eye",
4: "Optic_Chiasm",
5: "Right_Optic_Nerve",
6: "Left_Optic_Nerve"
}
return {organ_dict[label] for label in unique_labels if label in organ_dict}
def save_slice(image_ct, image_mri, ground_truth, prediction, slice_num, save_path):
"""保存指定切片的比較圖"""
plt.clf()
fig, axes = plt.subplots(1, 2, figsize=(25, 11))
plt.subplots_adjust(wspace=0.01)
# Process CT and MRI images
img_slice_ct = np.rot90(image_ct[:, :, slice_num])
img_slice_mri = np.rot90(image_mri[:, :, slice_num])
# Get bounds from CT image
rmin, rmax, cmin, cmax = get_brain_bounds(img_slice_ct)
# 調整CT和MRI圖像的對比度
def adjust_contrast(img):
p2, p98 = np.percentile(img, (2, 98))
return np.clip((img - p2) / (p98 - p2), 0, 1)
ct_norm = adjust_contrast(img_slice_ct)
mri_norm = adjust_contrast(img_slice_mri)
# Blend images with equal weights
blended_img = 0.5 * ct_norm + 0.5 * mri_norm
# Ground Truth
gt_slice = np.rot90(ground_truth[:, :, slice_num])
gt_mask = create_colored_mask(gt_slice)
# 使用 zorder 參數來控制圖層順序
axes[0].imshow(blended_img[rmin:rmax, cmin:cmax], cmap='gray', zorder=1)
axes[0].imshow(gt_mask[rmin:rmax, cmin:cmax], zorder=2)
axes[0].set_title('Ground Truth', fontsize=32, pad=20, weight='bold')
axes[0].axis('off')
# Prediction
pred_slice = np.rot90(prediction[:, :, slice_num])
pred_mask = create_colored_mask(pred_slice)
axes[1].imshow(blended_img[rmin:rmax, cmin:cmax], cmap='gray', zorder=1)
axes[1].imshow(pred_mask[rmin:rmax, cmin:cmax], zorder=2)
axes[1].set_title('Prediction', fontsize=32, pad=20, weight='bold')
axes[1].axis('off')
plt.savefig(save_path, bbox_inches='tight', dpi=300, pad_inches=0.05)
plt.close()
print(f"已保存切片 {slice_num} 至: {save_path}")
if __name__ == "__main__":
# 載入影像檔案
image_path_1 = '/mnt/1248/onlylian/nnUNet/nnUNet_raw/Dataset221_OAR/imagesTs/OAR_082_0000.nii.gz' #CT
image_path_2 = '/mnt/1248/onlylian/nnUNet/nnUNet_raw/Dataset221_OAR/imagesTs/OAR_082_0001.nii.gz' #MRI
gt_path = '/mnt/1248/onlylian/nnUNet/nnUNet_raw/Dataset221_OAR/labelsTs/OAR_082.nii.gz'
pred_path = '/mnt/1248/onlylian/CT_CTC_T1_OAR/output_predictions/3d_fullres/fold_4/OAR_082.nii.gz'
# 設定保存路徑
save_dir = '/mnt/1248/onlylian/CT_CTC_T1_OAR'
os.makedirs(save_dir, exist_ok=True)
# 讀取影像
print("正在載入影像...")
image_ct = load_nifti(image_path_1)
image_mri = load_nifti(image_path_2)
ground_truth = load_nifti(gt_path)
prediction = load_nifti(pred_path)
print("影像載入完成")
print("\n分析切片中的器官...")
total_slices = image_ct.shape[2]
for slice_num in range(total_slices):
gt_slice = ground_truth[:, :, slice_num]
pred_slice = prediction[:, :, slice_num]
gt_organs = check_organs_in_slice(gt_slice)
pred_organs = check_organs_in_slice(pred_slice)
if gt_organs or pred_organs:
print(f"\n切片 {slice_num}:")
print(f"Ground Truth 包含: {', '.join(sorted(gt_organs))}")
print(f"Prediction 包含: {', '.join(sorted(pred_organs))}")
print("\n開始切片選擇和保存過程...")
while True:
try:
slice_num = int(input("\n請輸入要保存的切片編號 (-1 退出): "))
if slice_num == -1:
print("程序結束")
break
if 0 <= slice_num < total_slices:
save_path = os.path.join(save_dir, f'comparison_optic_chiasm_082_{slice_num:03d}.png')
save_slice(image_ct, image_mri, ground_truth, prediction, slice_num, save_path)
else:
print(f"切片編號必須在 0 到 {total_slices-1} 之間")
except ValueError:
print("請輸入有效的數字")
except Exception as e:
print(f"發生錯誤: {str(e)}")

View file

@ -0,0 +1,144 @@
import json
import os
import numpy as np
import matplotlib.pyplot as plt
from typing import Dict, Tuple
# Set up Chinese font support
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
def load_data(file_path: str) -> Dict:
"""Load JSON data file"""
try:
with open(file_path, 'r') as file:
return json.load(file)
except FileNotFoundError:
print(f"File not found: {file_path}")
return {}
except json.JSONDecodeError:
print(f"JSON decoding error: {file_path}")
return {}
def extract_scores(data: Dict, model: str, organ_id: str) -> Tuple[float, float]:
"""Extract scores for a specific organ from the data"""
if "summary" not in data or organ_id not in data["summary"]:
return (0, 0)
scores = data["summary"][organ_id]
if model == '3D_fullres':
mean_dice = scores.get("mean_dice", 0)
std_dice = scores.get("std_dice", 0)
else:
mean_dice = scores.get("mean_dice", scores.get("mean", 0))
std_dice = scores.get("std_dice", scores.get("std", 0))
return (mean_dice, std_dice)
def create_plot(model_averages: Dict, model_stds: Dict, organs: Dict, output_path: str):
"""Create and save the plot"""
plt.figure(figsize=(16, 5)) # Reduced figure size
plt.title('T1C_OAR_TV', fontsize=12, pad=10) # Changed title
plt.grid(True, axis='y', linestyle='--', alpha=0.7)
x = np.arange(len(organs)) * 1.5 # Reduced spacing between groups
width = 0.35 # Reduced bar width
colors = {
'2D': 'royalblue',
'3D_fullres': 'limegreen',
'3D_lowres': 'orange'
}
positions = {
'2D': x - width,
'3D_fullres': x,
'3D_lowres': x + width
}
for model, averages in model_averages.items():
bars = plt.bar(positions[model],
[averages[organ] * 100 for organ in organs.keys()],
width,
label=model,
color=colors[model])
# Adjust data label position and size
for j, bar in enumerate(bars):
height = bar.get_height()
organ = list(organs.keys())[j]
std = model_stds[model][organ] * 100
plt.text(bar.get_x() + bar.get_width()/2., height,
f'{height:.1f}±{std:.1f}%',
ha='center',
va='bottom',
fontsize=6, # Reduced font size
rotation=0) # Optional: rotate text if still crowded
plt.ylim(0, 100)
plt.ylabel('DC (%)', fontsize=10)
plt.xlabel('OAR_TV', fontsize=10)
plt.xticks(x, [organs[k] for k in organs.keys()], rotation=30, ha='right', fontsize=8)
plt.legend(loc='upper right', fontsize=8)
plt.tight_layout()
os.makedirs(os.path.dirname(output_path), exist_ok=True)
plt.savefig(output_path, dpi=300, bbox_inches='tight')
plt.close()
def print_results(model_averages: Dict, model_stds: Dict, organs: Dict):
"""Print numerical results"""
print("\nMean Dice Scores (percentage):")
for model in model_averages.keys():
print(f"\n{model}:")
for organ in organs.keys():
avg = model_averages[model][organ] * 100
std = model_stds[model][organ] * 100
print(f"{organs[organ]}: {avg:.1f}±{std:.1f}%")
def main():
# Define paths and organ mapping
base_path = '/mnt/1248/onlylian/T1C_OAR_TV/Dice/test'
paths = {
'2D': os.path.join(base_path, 'T1C_OAR_TV_test_dice_results_2D.json'),
'3D_fullres': os.path.join(base_path, 'T1C_OAR_TV_test_dice_results_3D_fullres.json'),
'3D_lowres': os.path.join(base_path, 'T1C_OAR_TV_test_dice_results_3D_lowres.json')
}
organs = {
"1": "Brainstem",
"2": "Right_Eye",
"3": "Left_Eye",
"4": "Optic_Chiasm",
"5": "Right_Optic_Nerve",
"6": "Left_Optic_Nerve",
"7": "TV"
}
# Initialize data storage
model_dice_scores = {
model: {organ: None for organ in organs.keys()}
for model in paths.keys()
}
# Load and process data
for model, path in paths.items():
data = load_data(path)
for organ_id in organs.keys():
model_dice_scores[model][organ_id] = extract_scores(data, model, organ_id)
# Calculate averages and standard deviations
model_averages = {}
model_stds = {}
for model, scores in model_dice_scores.items():
model_averages[model] = {organ: scores[organ][0] if scores[organ] else 0 for organ in organs.keys()}
model_stds[model] = {organ: scores[organ][1] if scores[organ] else 0 for organ in organs.keys()}
# Create and save the plot
output_path = os.path.join(base_path, 'T1C_OAR_TV_test_dice_scores_comparison.png')
create_plot(model_averages, model_stds, organs, output_path)
# Print the results
print_results(model_averages, model_stds, organs)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,286 @@
import json
import os
import numpy as np
import SimpleITK as sitk
from tqdm import tqdm
import time
def load_nifti(file_path):
"""Load a NIfTI file"""
return sitk.GetArrayFromImage(sitk.ReadImage(file_path))
def compute_dice(pred, gt):
"""Compute Dice coefficient"""
intersection = np.sum(pred & gt)
sum_pred = np.sum(pred)
sum_gt = np.sum(gt)
if sum_pred == 0 and sum_gt == 0:
return 1.0
elif sum_pred == 0 or sum_gt == 0:
return 0.0
return 2.0 * intersection / (sum_pred + sum_gt)
def separate_structures(combined_mask, structure_name=""):
"""Separate left and right structures"""
sitk_image = sitk.GetImageFromArray(combined_mask)
labeled_components = sitk.ConnectedComponent(sitk_image)
labeled_array = sitk.GetArrayFromImage(labeled_components)
unique, counts = np.unique(labeled_array, return_counts=True)
if len(unique) < 3:
print(f"Warning: Not enough connected regions found for {structure_name}")
return None, None
region_sizes = [(i, count) for i, count in zip(unique[1:], counts[1:])]
sorted_regions = sorted(region_sizes, key=lambda x: x[1], reverse=True)
if len(sorted_regions) < 2:
print(f"Warning: Only one major region found for {structure_name}")
return None, None
region1_mask = (labeled_array == sorted_regions[0][0])
region2_mask = (labeled_array == sorted_regions[1][0])
def get_centroid(mask):
indices = np.where(mask)
return np.mean(indices[2])
centroid1 = get_centroid(region1_mask)
centroid2 = get_centroid(region2_mask)
if centroid1 > centroid2:
right = region1_mask
left = region2_mask
else:
right = region2_mask
left = region1_mask
return right.astype(np.uint8), left.astype(np.uint8)
def evaluate_case(gt_path, pred_path, labels):
"""Evaluate a single case"""
print(f"\nTest file: {os.path.basename(pred_path)}")
start_time = time.time()
gt_img = load_nifti(gt_path)
pred_img = load_nifti(pred_path)
load_time = time.time() - start_time
print(f"Image size: {gt_img.shape}")
print(f"Loading time: {load_time:.2f}s")
results = {}
total_time = 0
# Process eyes
start_time = time.time()
combined_gt_eye = ((gt_img == 2) | (gt_img == 3)).astype(np.uint8)
combined_pred_eye = ((pred_img == 2) | (pred_img == 3)).astype(np.uint8)
combined_pred_eye_sitk = sitk.GetImageFromArray(combined_pred_eye)
closing_filter = sitk.BinaryMorphologicalClosingImageFilter()
closing_filter.SetKernelRadius(2)
processed_pred_eye = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_eye_sitk))
eye_dice = compute_dice(processed_pred_eye, combined_gt_eye)
eye_time = time.time() - start_time
total_time += eye_time
results['combined_eye'] = eye_dice
print(f"Eyes combined computation time: {eye_time:.2f}s, Dice: {eye_dice:.4f}")
right_pred_eye, left_pred_eye = separate_structures(processed_pred_eye, "Eyes")
right_gt_eye, left_gt_eye = separate_structures(combined_gt_eye, "Eyes")
# Process optic nerves
start_time = time.time()
combined_gt_nerve = ((gt_img == 5) | (gt_img == 6)).astype(np.uint8)
combined_pred_nerve = ((pred_img == 5) | (pred_img == 6)).astype(np.uint8)
combined_pred_nerve_sitk = sitk.GetImageFromArray(combined_pred_nerve)
processed_pred_nerve = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_nerve_sitk))
nerve_dice = compute_dice(processed_pred_nerve, combined_gt_nerve)
nerve_time = time.time() - start_time
total_time += nerve_time
results['combined_nerve'] = nerve_dice
print(f"Optic nerves combined computation time: {nerve_time:.2f}s, Dice: {nerve_dice:.4f}")
right_pred_nerve, left_pred_nerve = separate_structures(processed_pred_nerve, "Optic Nerves")
right_gt_nerve, left_gt_nerve = separate_structures(combined_gt_nerve, "Optic Nerves")
for label in labels:
start_time = time.time()
if label == 1: # Brainstem
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 2 and right_pred_eye is not None: # Right Eye
gt_binary = right_gt_eye
pred_binary = right_pred_eye
elif label == 3 and left_pred_eye is not None: # Left Eye
gt_binary = left_gt_eye
pred_binary = left_pred_eye
elif label == 4: # Optic Chiasm
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 5 and right_pred_nerve is not None: # Right Optic Nerve
gt_binary = right_gt_nerve
pred_binary = right_pred_nerve
elif label == 6 and left_pred_nerve is not None: # Left Optic Nerve
gt_binary = left_gt_nerve
pred_binary = left_pred_nerve
elif label == 7: # TV
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
else:
continue
try:
dice = compute_dice(pred_binary, gt_binary)
compute_time = time.time() - start_time
total_time += compute_time
results[label] = dice
print(f"Label {label} computation time: {compute_time:.2f}s, Dice: {dice:.4f}")
except Exception as e:
print(f"Error processing label {label}: {str(e)}")
results[label] = np.nan
continue
print(f"Total computation time for this case: {total_time:.2f}s")
return results
def evaluate_fold(gt_folder, pred_folder, labels):
"""Evaluate all cases in a single fold"""
fold_results = {label: [] for label in labels}
fold_results['combined_eye'] = []
fold_results['combined_nerve'] = []
pred_files = sorted([f for f in os.listdir(pred_folder) if f.endswith('.nii.gz')])
for pred_file in tqdm(pred_files, desc=f"Processing {os.path.basename(pred_folder)}"):
gt_path = os.path.join(gt_folder, pred_file)
pred_path = os.path.join(pred_folder, pred_file)
if not os.path.exists(gt_path):
print(f"Could not find corresponding ground truth file: {gt_path}")
continue
case_results = evaluate_case(gt_path, pred_path, labels)
for key in case_results:
if not np.isnan(case_results[key]):
if key in fold_results:
fold_results[key].append(case_results[key])
return fold_results
def evaluate_model(gt_folder, base_pred_folder, model_name, labels, organ_names):
"""Evaluate all folds for a single model"""
print(f"\nStarting evaluation of model: {model_name}")
all_results = {label: [] for label in labels}
all_results['combined_eye'] = []
all_results['combined_nerve'] = []
fold_results = {}
for fold in range(5):
print(f"\nProcessing {model_name} - Fold {fold}")
pred_folder = os.path.join(base_pred_folder, f"fold_{fold}")
if not os.path.exists(pred_folder):
print(f"Could not find prediction folder: {pred_folder}")
continue
results = evaluate_fold(gt_folder, pred_folder, labels)
fold_results[f"fold_{fold}"] = results
for key in results:
all_results[key].extend(results[key])
summary = {}
for key in all_results:
if all_results[key]:
mean_dice = np.mean(all_results[key])
std_dice = np.std(all_results[key])
summary[key] = {
'mean_dice': float(mean_dice),
'std_dice': float(std_dice),
'n_samples': len(all_results[key])
}
else:
summary[key] = {
'mean_dice': np.nan,
'std_dice': np.nan,
'n_samples': 0
}
model_results = {
'summary': summary,
'fold_results': fold_results
}
save_dir = "/mnt/1248/onlylian/CT_CTC_T1_OAR_TV/Dice/test"
os.makedirs(save_dir, exist_ok=True)
filename = os.path.join(save_dir, f'CT_CTC_T1_OAR_TV_test_dice_results_{model_name}.json')
with open(filename, 'w') as f:
json.dump(model_results, f, indent=4)
print(f"\nResults for {model_name} saved to {filename}")
print(f"\nDice results for {model_name} (average across all folds):")
for label in labels:
if label in summary:
mean = summary[label]['mean_dice']
std = summary[label]['std_dice']
n = summary[label]['n_samples']
organ_name = organ_names.get(label, f"Label_{label}")
if not np.isnan(mean):
print(f"{organ_name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{organ_name}: Unable to compute (n={n})")
print("\nResults for combined structures:")
for key in ['combined_eye', 'combined_nerve']:
if key in summary:
mean = summary[key]['mean_dice']
std = summary[key]['std_dice']
n = summary[key]['n_samples']
name = "Combined Eyes" if key == 'combined_eye' else "Combined Optic Nerves"
if not np.isnan(mean):
print(f"{name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{name}: Unable to compute (n={n})")
return summary
if __name__ == "__main__":
gt_folder = "/mnt/1248/onlylian/nnUNet/nnUNet_raw/Dataset222_OAR_TV/labelsTs"
models = {
'2D': '/mnt/1248/onlylian/CT_CTC_T1_OAR_TV/output_predictions/2d',
'3D_fullres': '/mnt/1248/onlylian/CT_CTC_T1_OAR_TV/output_predictions/3d_fullres',
'3D_lowres': '/mnt/1248/onlylian/CT_CTC_T1_OAR_TV/output_predictions/3d_lowres'
}
labels = [1, 2, 3, 4, 5, 6, 7]
organ_names = {
1: "Brainstem",
2: "Right_Eye",
3: "Left_Eye",
4: "Optic_Chiasm",
5: "Right_Optic_Nerve",
6: "Left_Optic_Nerve",
7: "TV"
}
print("Available models:")
for i, model_name in enumerate(models.keys(), 1):
print(f"{i}. {model_name}")
choice = input("\nPlease select a model to run (enter 1-3): ")
try:
model_index = int(choice) - 1
model_names = list(models.keys())
selected_model = model_names[model_index]
evaluate_model(gt_folder, models[selected_model], selected_model, labels, organ_names)
except (ValueError, IndexError):
print("Invalid choice! Please enter a number between 1 and 3.")

View file

@ -0,0 +1,111 @@
import json
import os
import numpy as np
import matplotlib.pyplot as plt
from typing import Dict, Tuple
# Set up Chinese font support
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# Functions up to create_plot remain the same
def create_plot(model_averages: Dict, model_stds: Dict, organs: Dict, output_path: str):
"""Create and save the plot"""
plt.figure(figsize=(16, 5)) # Reduced figure size
plt.title('T1C_OAR_TV', fontsize=12, pad=10) # Changed title
plt.grid(True, axis='y', linestyle='--', alpha=0.7)
x = np.arange(len(organs)) * 1.5 # Reduced spacing between groups
width = 0.35 # Reduced bar width
colors = {
'2D': 'royalblue',
'3D_fullres': 'limegreen',
'3D_lowres': 'orange'
}
positions = {
'2D': x - width,
'3D_fullres': x,
'3D_lowres': x + width
}
for model, averages in model_averages.items():
bars = plt.bar(positions[model],
[averages[organ] * 100 for organ in organs.keys()],
width,
label=model,
color=colors[model])
# Adjust data label position and size
for j, bar in enumerate(bars):
height = bar.get_height()
organ = list(organs.keys())[j]
std = model_stds[model][organ] * 100
plt.text(bar.get_x() + bar.get_width()/2., height + 0.5,
f'{height:.1f}±{std:.1f}%',
ha='center',
va='bottom',
fontsize=6)
plt.ylim(0, 100)
plt.ylabel('DC (%)', fontsize=10)
plt.xlabel('OAR_TV', fontsize=10)
plt.xticks(x, [organs[k] for k in organs.keys()], rotation=30, ha='right', fontsize=8)
plt.legend(loc='upper right', fontsize=8)
plt.tight_layout()
os.makedirs(os.path.dirname(output_path), exist_ok=True)
plt.savefig(output_path, dpi=300, bbox_inches='tight')
plt.close()
# print_results function remains the same
def main():
# Define paths and organ mapping
base_path = '/mnt/1248/onlylian/T1C_OAR_TV/Dice/validation'
paths = {
'2D': os.path.join(base_path, 'T1C_OAR_TV_validation_dice_results_2D.json'),
'3D_fullres': os.path.join(base_path, 'T1C_OAR_TV_validation_dice_results_3D_fullres.json'),
'3D_lowres': os.path.join(base_path, 'T1C_OAR_TV_validation_dice_results_3D_lowres.json')
}
organs = {
"1": "Brainstem",
"2": "Right_Eye",
"3": "Left_Eye",
"4": "Optic_Chiasm",
"5": "Right_Optic_Nerve",
"6": "Left_Optic_Nerve",
"7": "TV"
}
# Initialize data storage
model_dice_scores = {
model: {organ: None for organ in organs.keys()}
for model in paths.keys()
}
# Load and process data
for model, path in paths.items():
data = load_data(path)
for organ_id in organs.keys():
model_dice_scores[model][organ_id] = extract_scores(data, model, organ_id)
# Calculate averages and standard deviations
model_averages = {}
model_stds = {}
for model, scores in model_dice_scores.items():
model_averages[model] = {organ: scores[organ][0] if scores[organ] else 0 for organ in organs.keys()}
model_stds[model] = {organ: scores[organ][1] if scores[organ] else 0 for organ in organs.keys()}
# Create and save the plot
output_path = os.path.join(base_path, 'T1C_OAR_TV_validation_dice_scores_comparison.png')
create_plot(model_averages, model_stds, organs, output_path)
# Print the results
print_results(model_averages, model_stds, organs)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,325 @@
import os
import time
import numpy as np
import SimpleITK as sitk
from tqdm import tqdm
import json
def load_nifti(file_path):
"""Load a NIfTI file"""
return sitk.GetArrayFromImage(sitk.ReadImage(file_path))
def compute_dice(pred, gt):
"""Compute Dice coefficient"""
intersection = np.sum(pred & gt)
sum_pred = np.sum(pred)
sum_gt = np.sum(gt)
if sum_pred == 0 and sum_gt == 0:
return 1.0
elif sum_pred == 0 or sum_gt == 0:
return 0.0
return 2.0 * intersection / (sum_pred + sum_gt)
def preprocess_binary_mask(mask, apply_closing=True):
"""Preprocess binary mask with morphological operations"""
mask_sitk = sitk.GetImageFromArray(mask)
if apply_closing:
closing_filter = sitk.BinaryMorphologicalClosingImageFilter()
closing_filter.SetKernelRadius(2)
mask = sitk.GetArrayFromImage(closing_filter.Execute(mask_sitk))
return mask
def separate_structures(combined_mask, structure_name=""):
"""Separate left and right structures"""
sitk_image = sitk.GetImageFromArray(combined_mask)
labeled_components = sitk.ConnectedComponent(sitk_image)
labeled_array = sitk.GetArrayFromImage(labeled_components)
unique, counts = np.unique(labeled_array, return_counts=True)
if len(unique) < 3:
print(f"Warning: Not enough connected regions found for {structure_name}")
return None, None
region_sizes = [(i, count) for i, count in zip(unique[1:], counts[1:])]
sorted_regions = sorted(region_sizes, key=lambda x: x[1], reverse=True)
if len(sorted_regions) < 2:
print(f"Warning: Only one major region found for {structure_name}")
return None, None
region1_mask = (labeled_array == sorted_regions[0][0])
region2_mask = (labeled_array == sorted_regions[1][0])
def get_centroid(mask):
indices = np.where(mask)
return np.mean(indices[2])
centroid1 = get_centroid(region1_mask)
centroid2 = get_centroid(region2_mask)
if centroid1 > centroid2:
right = region1_mask
left = region2_mask
else:
right = region2_mask
left = region1_mask
return right.astype(np.uint8), left.astype(np.uint8)
def evaluate_case(gt_path, pred_path, labels):
"""Evaluate a single case"""
print(f"\nTest file: {os.path.basename(pred_path)}")
try:
start_time = time.time()
gt_img = load_nifti(gt_path)
pred_img = load_nifti(pred_path)
load_time = time.time() - start_time
print(f"Image size: {gt_img.shape}")
print(f"Loading time: {load_time:.2f}s")
results = {}
total_time = 0
# Process eyes
start_time = time.time()
combined_gt_eye = ((gt_img == 2) | (gt_img == 3)).astype(np.uint8)
combined_pred_eye = ((pred_img == 2) | (pred_img == 3)).astype(np.uint8)
processed_pred_eye = preprocess_binary_mask(combined_pred_eye)
processed_gt_eye = preprocess_binary_mask(combined_gt_eye)
eye_dice = compute_dice(processed_pred_eye, processed_gt_eye)
eye_time = time.time() - start_time
total_time += eye_time
results['combined_eye'] = eye_dice
print(f"Eyes combined computation time: {eye_time:.2f}s, Dice: {eye_dice:.4f}")
right_pred_eye, left_pred_eye = separate_structures(processed_pred_eye, "Eyes")
right_gt_eye, left_gt_eye = separate_structures(processed_gt_eye, "Eyes")
# Process optic nerves
start_time = time.time()
combined_gt_nerve = ((gt_img == 5) | (gt_img == 6)).astype(np.uint8)
combined_pred_nerve = ((pred_img == 5) | (pred_img == 6)).astype(np.uint8)
processed_pred_nerve = preprocess_binary_mask(combined_pred_nerve)
processed_gt_nerve = preprocess_binary_mask(combined_gt_nerve)
nerve_dice = compute_dice(processed_pred_nerve, processed_gt_nerve)
nerve_time = time.time() - start_time
total_time += nerve_time
results['combined_nerve'] = nerve_dice
print(f"Optic nerves combined computation time: {nerve_time:.2f}s, Dice: {nerve_dice:.4f}")
right_pred_nerve, left_pred_nerve = separate_structures(processed_pred_nerve, "Optic Nerves")
right_gt_nerve, left_gt_nerve = separate_structures(processed_gt_nerve, "Optic Nerves")
for label in labels:
start_time = time.time()
if label == 1: # Brainstem
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
apply_closing = True
elif label == 2 and right_pred_eye is not None: # Right Eye
gt_binary = right_gt_eye
pred_binary = right_pred_eye
apply_closing = False # Already processed
elif label == 3 and left_pred_eye is not None: # Left Eye
gt_binary = left_gt_eye
pred_binary = left_pred_eye
apply_closing = False # Already processed
elif label == 4: # Optic Chiasm
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
apply_closing = True
elif label == 5 and right_pred_nerve is not None: # Right Optic Nerve
gt_binary = right_gt_nerve
pred_binary = right_pred_nerve
apply_closing = False # Already processed
elif label == 6 and left_pred_nerve is not None: # Left Optic Nerve
gt_binary = left_gt_nerve
pred_binary = left_pred_nerve
apply_closing = False # Already processed
elif label == 7: # TV
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
apply_closing = True
else:
continue
try:
if apply_closing:
pred_binary = preprocess_binary_mask(pred_binary)
gt_binary = preprocess_binary_mask(gt_binary)
dice = compute_dice(pred_binary, gt_binary)
compute_time = time.time() - start_time
total_time += compute_time
results[label] = dice
print(f"Label {label} computation time: {compute_time:.2f}s, Dice: {dice:.4f}")
except Exception as e:
print(f"Error processing label {label}: {str(e)}")
results[label] = np.nan
continue
print(f"Total computation time for this case: {total_time:.2f}s")
return results
except Exception as e:
print(f"Error evaluating case: {str(e)}")
return None
def evaluate_validation_fold(gt_base_folder, validation_folder, labels):
"""Evaluate the validation set for a single fold"""
fold_results = {label: [] for label in labels}
fold_results['combined_eye'] = []
fold_results['combined_nerve'] = []
pred_files = sorted([f for f in os.listdir(validation_folder) if f.endswith('.nii.gz')])
for pred_file in tqdm(pred_files, desc=f"Processing {os.path.basename(validation_folder)}"):
patient_id = pred_file.split('.nii.gz')[0]
gt_path = os.path.join(gt_base_folder, f"{patient_id}.nii.gz")
pred_path = os.path.join(validation_folder, pred_file)
if not os.path.exists(gt_path):
print(f"Could not find corresponding ground truth file: {gt_path}")
continue
case_results = evaluate_case(gt_path, pred_path, labels)
if case_results is not None:
for key in case_results:
if not np.isnan(case_results[key]):
if key in fold_results:
fold_results[key].append(case_results[key])
return fold_results
def evaluate_model(gt_folder, model_path, model_name, labels, organ_names, output_dir):
"""Evaluate the validation set for all folds of a model"""
print(f"\nStarting evaluation of model: {model_name}")
all_results = {label: [] for label in labels}
all_results['combined_eye'] = []
all_results['combined_nerve'] = []
fold_results = {}
for fold in range(5):
validation_folder = os.path.join(model_path, f"fold_{fold}", "validation")
if not os.path.exists(validation_folder):
print(f"Could not find fold_{fold} validation folder: {validation_folder}")
continue
print(f"\nProcessing {model_name} - Fold {fold} validation set")
results = evaluate_validation_fold(gt_folder, validation_folder, labels)
fold_results[f"fold_{fold}"] = results
for key in results:
all_results[key].extend(results[key])
summary = {}
for key in all_results:
if all_results[key]:
mean_dice = np.mean(all_results[key])
std_dice = np.std(all_results[key])
summary[key] = {
'mean_dice': float(mean_dice),
'std_dice': float(std_dice),
'n_samples': len(all_results[key])
}
else:
summary[key] = {
'mean_dice': np.nan,
'std_dice': np.nan,
'n_samples': 0
}
model_results = {
'summary': summary,
'fold_results': fold_results
}
output_path = os.path.join(output_dir, 'CT_CTC_T1_OAR_TV', 'Dice', 'validation')
os.makedirs(output_path, exist_ok=True)
filename = os.path.join(output_path, f'dice_results_{model_name}.json')
with open(filename, 'w') as f:
json.dump(model_results, f, indent=4)
print(f"\nValidation set results for {model_name} saved to {filename}")
print(f"\nValidation set Dice results for {model_name} (average across all folds):")
# Print individual organ results
print("\nOrgan-specific results:")
for label in labels:
if label in summary:
mean = summary[label]['mean_dice']
std = summary[label]['std_dice']
n = summary[label]['n_samples']
organ_name = organ_names.get(label, f"Label_{label}")
if not np.isnan(mean):
print(f"{organ_name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{organ_name}: Unable to compute (n={n})")
# Print combined structure results
print("\nCombined structure results:")
for key in ['combined_eye', 'combined_nerve']:
if key in summary:
mean = summary[key]['mean_dice']
std = summary[key]['std_dice']
n = summary[key]['n_samples']
name = "Combined Eyes" if key == 'combined_eye' else "Combined Optic Nerves"
if not np.isnan(mean):
print(f"{name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{name}: Unable to compute (n={n})")
return summary
if __name__ == "__main__":
base_dir = "/mnt/1248/onlylian"
gt_folder = os.path.join(base_dir, "nnUNet/nnUNet_raw/Dataset222_OAR_TV/labelsTr")
output_dir = base_dir
models = {
'2D': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset222_OAR_TV/nnUNetTrainer__nnUNetPlans__2d"),
'3D_fullres': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset222_OAR_TV/nnUNetTrainer__nnUNetPlans__3d_fullres"),
'3D_lowres': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset222_OAR_TV/nnUNetTrainer__nnUNetPlans__3d_lowres")
}
labels = [1, 2, 3, 4, 5, 6, 7]
organ_names = {
1: "Brainstem",
2: "Right_Eye",
3: "Left_Eye",
4: "Optic_Chiasm",
5: "Right_Optic_Nerve",
6: "Left_Optic_Nerve",
7: "TV"
}
print("Available models:")
for i, model_name in enumerate(models.keys(), 1):
print(f"{i}. {model_name}")
try:
choice = input("\nPlease select a model to run (enter 1-3): ")
model_index = int(choice) - 1
model_names = list(models.keys())
selected_model = model_names[model_index]
evaluate_model(gt_folder, models[selected_model], selected_model, labels, organ_names, output_dir)
except (ValueError, IndexError):
print("Invalid choice! Please enter a number between 1 and 3.")
except KeyboardInterrupt:
print("\nProgram interrupted by user")
print("You can run the program again to continue processing")
except Exception as e:
print(f"\nAn unexpected error occurred: {str(e)}")
print("Please check the error message and try again")

View file

@ -0,0 +1,151 @@
import nibabel as nib
import matplotlib.pyplot as plt
import numpy as np
import os
def load_nifti(file_path):
"""載入NIfTI檔案"""
return nib.load(file_path).get_fdata()
def create_colored_mask(data):
"""創建彩色遮罩"""
color_dict = {
1: [144/255, 238/255, 144/255], # Brainstem - 綠色
2: [255/255, 218/255, 150/255], # Right_Eye - 淺黃色
3: [205/255, 170/255, 125/255], # Left_Eye - 棕色
4: [135/255, 206/255, 235/255], # Optic_Chiasm - 藍色
5: [255/255, 99/255, 71/255], # Right_Optic_Nerve - 亮珊瑚紅
6: [255/255, 160/255, 122/255], # Left_Optic_Nerve - 淺珊瑚紅
7: [255/255, 0/255, 0/255] # TV - 亮紅色
}
mask = np.zeros((*data.shape, 4))
for label, color in color_dict.items():
organ_mask = (data == label)
if np.any(organ_mask):
mask[organ_mask] = [*color, 1.0]
return mask
def get_display_bounds(image):
"""直接返回較大的顯示區域"""
h, w = image.shape
# 使用圖像中心點
center_h = h // 2
center_w = w // 2
# 取圖像大小的 85% 作為顯示範圍
radius_h = int(h * 0.425)
radius_w = int(w * 0.425)
return (
max(center_h - radius_h, 0),
min(center_h + radius_h, h),
max(center_w - radius_w, 0),
min(center_w + radius_w, w)
)
def check_tv_in_slice(slice_data):
"""檢查切片中是否包含TVlabel 7"""
return 7 in np.unique(slice_data)
def find_tv_slices(ground_truth):
"""找出所有包含TV的切片"""
tv_slices = []
tv_pixels = []
total_slices = ground_truth.shape[2]
for slice_num in range(total_slices):
slice_data = ground_truth[:, :, slice_num]
if check_tv_in_slice(slice_data):
tv_pixel_count = np.sum(slice_data == 7)
tv_slices.append(slice_num)
tv_pixels.append(tv_pixel_count)
sorted_indices = np.argsort(tv_pixels)[::-1]
sorted_slices = [tv_slices[i] for i in sorted_indices]
sorted_pixels = [tv_pixels[i] for i in sorted_indices]
return sorted_slices, sorted_pixels
def save_slice(image, ground_truth, prediction, slice_num, save_path):
"""保存指定切片的三列比較圖原始影像、Ground Truth和預測結果"""
plt.clf()
fig, axes = plt.subplots(1, 3, figsize=(36, 11))
plt.subplots_adjust(wspace=0.01)
img_slice = np.rot90(image[:, :, slice_num])
rmin, rmax, cmin, cmax = get_display_bounds(img_slice)
# 調整圖像對比度
p2, p98 = np.percentile(img_slice, (2, 98))
img_slice = np.clip(img_slice, p2, p98)
img_slice = (img_slice - p2) / (p98 - p2)
# 1. Original Image
axes[0].imshow(img_slice[rmin:rmax, cmin:cmax], cmap='gray', zorder=1)
axes[0].set_title('Original Image', fontsize=32, pad=20, weight='bold')
axes[0].axis('off')
# 2. Ground Truth
gt_slice = np.rot90(ground_truth[:, :, slice_num])
gt_mask = create_colored_mask(gt_slice)
axes[1].imshow(img_slice[rmin:rmax, cmin:cmax], cmap='gray', zorder=1)
axes[1].imshow(gt_mask[rmin:rmax, cmin:cmax], zorder=2)
axes[1].set_title('Ground Truth', fontsize=32, pad=20, weight='bold')
axes[1].axis('off')
# 3. Prediction
pred_slice = np.rot90(prediction[:, :, slice_num])
pred_mask = create_colored_mask(pred_slice)
axes[2].imshow(img_slice[rmin:rmax, cmin:cmax], cmap='gray', zorder=1)
axes[2].imshow(pred_mask[rmin:rmax, cmin:cmax], zorder=2)
axes[2].set_title('Prediction', fontsize=32, pad=20, weight='bold')
axes[2].axis('off')
plt.suptitle('')
plt.savefig(save_path, bbox_inches='tight', dpi=300, pad_inches=0.05)
plt.close()
print(f"已保存切片 {slice_num} 至: {save_path}")
if __name__ == "__main__":
# 設定路徑
image_path = '/mnt/1248/onlylian/nnUNet/nnUNet_raw/Dataset222_OAR_TV/imagesTs/OAR_TV_098_0001.nii.gz'
gt_path = '/mnt/1248/onlylian/nnUNet/nnUNet_raw/Dataset222_OAR_TV/labelsTs/OAR_TV_098.nii.gz'
pred_path = '/mnt/1248/onlylian/CT_CTC_T1_OAR_TV/output_predictions/3d_fullres/fold_4/OAR_TV_098.nii.gz'
save_dir = '/mnt/1248/onlylian/CT_CTC_T1_OAR_TV'
os.makedirs(save_dir, exist_ok=True)
print("正在載入影像...")
image = load_nifti(image_path)
ground_truth = load_nifti(gt_path)
prediction = load_nifti(pred_path)
print("影像載入完成")
print("\n尋找包含TV的切片...")
tv_slices, tv_pixels = find_tv_slices(ground_truth)
if tv_slices:
print(f"\n找到 {len(tv_slices)} 個包含TV的切片:")
for slice_num, pixel_count in zip(tv_slices, tv_pixels):
print(f"切片 {slice_num}: {pixel_count} 像素")
else:
print("未找到包含TV的切片")
print("\n開始切片選擇和保存過程...")
while True:
try:
slice_num = int(input("\n請輸入要保存的切片編號 (-1 退出): "))
if slice_num == -1:
print("程序結束")
break
if 0 <= slice_num < ground_truth.shape[2]:
save_path = os.path.join(save_dir, f'comparison_OAR_TV_098_4_{slice_num:03d}.png')
save_slice(image, ground_truth, prediction, slice_num, save_path)
else:
print(f"切片編號必須在 0 到 {ground_truth.shape[2]-1} 之間")
except ValueError:
print("請輸入有效的數字")
except Exception as e:
print(f"發生錯誤: {str(e)}")

View file

@ -0,0 +1,287 @@
# import json
# import os
# import numpy as np
# import matplotlib.pyplot as plt
# from typing import Dict, Tuple
# # 設置中文字體支持
# plt.rcParams['font.sans-serif'] = ['SimHei']
# plt.rcParams['axes.unicode_minus'] = False
# def load_data(file_path: str) -> Dict:
# """讀取JSON數據文件"""
# try:
# with open(file_path, 'r') as file:
# return json.load(file)
# except FileNotFoundError:
# print(f"文件未找到: {file_path}")
# return {}
# except json.JSONDecodeError:
# print(f"JSON解析錯誤: {file_path}")
# return {}
# def extract_scores(data: Dict, model: str, organ_id: str) -> Tuple[float, float]:
# """從數據中提取特定器官的分數"""
# if "summary" not in data or organ_id not in data["summary"]:
# return (0, 0)
# scores = data["summary"][organ_id]
# if model == '3D_fullres':
# mean_dice = scores.get("mean_dice", 0)
# std_dice = scores.get("std_dice", 0)
# else:
# mean_dice = scores.get("mean_dice", scores.get("mean", 0))
# std_dice = scores.get("std_dice", scores.get("std", 0))
# return (mean_dice, std_dice)
# def create_plot(model_averages: Dict, model_stds: Dict, organs: Dict, output_path: str):
# """創建和保存圖表"""
# plt.figure(figsize=(16, 5)) # Reduced figure size
# plt.title('CTC', fontsize=12, pad=10) # Changed title
# plt.grid(True, axis='y', linestyle='--', alpha=0.7)
# x = np.arange(len(organs)) * 1.5 # Reduced spacing between groups
# width = 0.35 # Reduced bar width
# colors = {
# '2D': 'royalblue',
# '3D_fullres': 'limegreen',
# '3D_lowres': 'orange'
# }
# positions = {
# '2D': x - width,
# '3D_fullres': x,
# '3D_lowres': x + width
# }
# for model, averages in model_averages.items():
# bars = plt.bar(positions[model],
# [averages[organ] * 100 for organ in organs.keys()],
# width,
# label=model,
# color=colors[model])
# # 調整數值標籤位置和大小
# for j, bar in enumerate(bars):
# height = bar.get_height()
# organ = list(organs.keys())[j]
# std = model_stds[model][organ] * 100
# plt.text(bar.get_x() + bar.get_width()/2., height,
# f'{height:.1f}±{std:.1f}%',
# ha='center',
# va='bottom',
# fontsize=6, # Reduced font size
# rotation=0) # Optional: rotate text if still crowded
# plt.ylim(0, 100)
# plt.ylabel('DC (%)', fontsize=10)
# plt.xlabel('OAR', fontsize=10)
# plt.xticks(x, [organs[k] for k in organs.keys()], rotation=30, ha='right', fontsize=8)
# plt.legend(loc='upper right', fontsize=8)
# plt.tight_layout()
# os.makedirs(os.path.dirname(output_path), exist_ok=True)
# plt.savefig(output_path, dpi=300, bbox_inches='tight')
# plt.close()
# def print_results(model_averages: Dict, model_stds: Dict, organs: Dict):
# """打印數值結果"""
# print("\n平均 Dice 分數 (百分比):")
# for model in model_averages.keys():
# print(f"\n{model}:")
# for organ in organs.keys():
# avg = model_averages[model][organ] * 100
# std = model_stds[model][organ] * 100
# print(f"{organs[organ]}: {avg:.1f}±{std:.1f}%")
# def main():
# # 定義路徑和器官映射
# base_path = '/mnt/1248/onlylian/CTC/Dice/test'
# paths = {
# '2D': os.path.join(base_path, 'CTC_test_dice_results_2D.json'),
# '3D_fullres': os.path.join(base_path, 'CTC_test_dice_results_3D_fullres.json'),
# '3D_lowres': os.path.join(base_path, 'CTC_test_dice_results_3D_lowres.json')
# }
# organs = {
# "1": "Brainstem",
# "2": "Right_Eye",
# "3": "Left_Eye",
# "4": "Optic_Chiasm",
# "5": "Right_Optic_Nerve",
# "6": "Left_Optic_Nerve"
# }
# # 初始化數據存儲
# model_dice_scores = {
# model: {organ: None for organ in organs.keys()}
# for model in paths.keys()
# }
# # 讀取和處理數據
# for model, path in paths.items():
# data = load_data(path)
# for organ_id in organs.keys():
# model_dice_scores[model][organ_id] = extract_scores(data, model, organ_id)
# # 計算平均值和標準差
# model_averages = {}
# model_stds = {}
# for model, scores in model_dice_scores.items():
# model_averages[model] = {organ: scores[organ][0] if scores[organ] else 0 for organ in organs.keys()}
# model_stds[model] = {organ: scores[organ][1] if scores[organ] else 0 for organ in organs.keys()}
# # 創建圖表並保存
# output_path = os.path.join(base_path, 'CTC_test_dice_scores_comparison.png')
# create_plot(model_averages, model_stds, organs, output_path)
# # 打印結果
# print_results(model_averages, model_stds, organs)
# if __name__ == "__main__":
# main()
import json
import os
import numpy as np
import matplotlib.pyplot as plt
from typing import Dict, Tuple
# 設置中文字體支持
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
def load_data(file_path: str) -> Dict:
"""讀取JSON數據文件"""
try:
with open(file_path, 'r') as file:
return json.load(file)
except FileNotFoundError:
print(f"文件未找到: {file_path}")
return {}
except json.JSONDecodeError:
print(f"JSON解析錯誤: {file_path}")
return {}
def extract_scores(data: Dict, model: str, organ_id: str) -> Tuple[float, float]:
"""從數據中提取特定器官的分數"""
if "summary" not in data or organ_id not in data["summary"]:
return (0, 0)
scores = data["summary"][organ_id]
if model == '3D_fullres':
mean_dice = scores.get("mean_dice", 0)
std_dice = scores.get("std_dice", 0)
else:
mean_dice = scores.get("mean_dice", scores.get("mean", 0))
std_dice = scores.get("std_dice", scores.get("std", 0))
return (mean_dice, std_dice)
def create_plot(model_averages: Dict, model_stds: Dict, organs: Dict, output_path: str):
"""創建和保存圖表"""
plt.figure(figsize=(16, 5)) # Reduced figure size
plt.title('FLAIR', fontsize=12, pad=10) # Changed title
plt.grid(True, axis='y', linestyle='--', alpha=0.7)
x = np.arange(len(organs)) * 1.5 # Reduced spacing between groups
width = 0.35 # Reduced bar width
colors = {
'2D': 'royalblue',
'3D_fullres': 'limegreen',
'3D_lowres': 'orange'
}
positions = {
'2D': x - width,
'3D_fullres': x,
'3D_lowres': x + width
}
for model, averages in model_averages.items():
bars = plt.bar(positions[model],
[averages[organ] * 100 for organ in organs.keys()],
width,
label=model,
color=colors[model])
# 調整數值標籤位置和大小
for j, bar in enumerate(bars):
height = bar.get_height()
organ = list(organs.keys())[j]
std = model_stds[model][organ] * 100
plt.text(bar.get_x() + bar.get_width()/2., height,
f'{height:.1f}±{std:.1f}%',
ha='center',
va='bottom',
fontsize=6, # Reduced font size
rotation=0) # Optional: rotate text if still crowded
plt.ylim(0, 100)
plt.ylabel('DC (%)', fontsize=10)
plt.xlabel('OAR', fontsize=10)
plt.xticks(x, [organs[k] for k in organs.keys()], rotation=30, ha='right', fontsize=8)
plt.legend(loc='upper right', fontsize=8)
plt.tight_layout()
os.makedirs(os.path.dirname(output_path), exist_ok=True)
plt.savefig(output_path, dpi=300, bbox_inches='tight')
plt.close()
def print_results(model_averages: Dict, model_stds: Dict, organs: Dict):
"""打印數值結果"""
print("\n平均 Dice 分數 (百分比):")
for model in model_averages.keys():
print(f"\n{model}:")
for organ in organs.keys():
avg = model_averages[model][organ] * 100
std = model_stds[model][organ] * 100
print(f"{organs[organ]}: {avg:.1f}±{std:.1f}%")
def main():
# 定義路徑和器官映射
base_path = '/mnt/1248/onlylian/FLAIR/Dice/test'
paths = {
'2D': os.path.join(base_path, 'FLAIR_test_dice_results_2D.json'),
'3D_fullres': os.path.join(base_path, 'FLAIR_test_dice_results_3D_fullres.json'),
'3D_lowres': os.path.join(base_path, 'FLAIR_test_dice_results_3D_lowres.json')
}
organs = {
"1": "Brainstem",
"2": "Right_Eye",
"3": "Left_Eye",
"4": "Optic_Chiasm",
"5": "Right_Optic_Nerve",
"6": "Left_Optic_Nerve"
}
# 初始化數據存儲
model_dice_scores = {
model: {organ: None for organ in organs.keys()}
for model in paths.keys()
}
# 讀取和處理數據
for model, path in paths.items():
data = load_data(path)
for organ_id in organs.keys():
model_dice_scores[model][organ_id] = extract_scores(data, model, organ_id)
# 計算平均值和標準差
model_averages = {}
model_stds = {}
for model, scores in model_dice_scores.items():
model_averages[model] = {organ: scores[organ][0] if scores[organ] else 0 for organ in organs.keys()}
model_stds[model] = {organ: scores[organ][1] if scores[organ] else 0 for organ in organs.keys()}
# 創建圖表並保存
output_path = os.path.join(base_path, 'FLAIR_test_dice_scores_comparison.png')
create_plot(model_averages, model_stds, organs, output_path)
# 打印結果
print_results(model_averages, model_stds, organs)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,614 @@
# import json
# import os
# import numpy as np
# import SimpleITK as sitk
# from tqdm import tqdm
# import time
# def load_nifti(file_path):
# """加載 NIfTI 文件"""
# return sitk.GetArrayFromImage(sitk.ReadImage(file_path))
# def compute_dice(pred, gt):
# """計算Dice係數"""
# intersection = np.sum(pred & gt)
# sum_pred = np.sum(pred)
# sum_gt = np.sum(gt)
# if sum_pred == 0 and sum_gt == 0:
# return 1.0
# elif sum_pred == 0 or sum_gt == 0:
# return 0.0
# return 2.0 * intersection / (sum_pred + sum_gt)
# def separate_structures(combined_mask, structure_name=""):
# """分離左右結構"""
# # 使用SimpleITK的ConnectedComponent
# sitk_image = sitk.GetImageFromArray(combined_mask)
# labeled_components = sitk.ConnectedComponent(sitk_image)
# labeled_array = sitk.GetArrayFromImage(labeled_components)
# # 計算連通區域數量和大小
# unique, counts = np.unique(labeled_array, return_counts=True)
# if len(unique) < 3: # 背景算一個所以至少需要3個
# print(f"警告:{structure_name} 未找到足夠的連通區域")
# return None, None
# # 排除背景標籤0並按大小排序
# region_sizes = [(i, count) for i, count in zip(unique[1:], counts[1:])]
# sorted_regions = sorted(region_sizes, key=lambda x: x[1], reverse=True)
# if len(sorted_regions) < 2:
# print(f"警告:{structure_name} 未找到兩個主要區域")
# return None, None
# # 獲取兩個最大區域
# region1_mask = (labeled_array == sorted_regions[0][0])
# region2_mask = (labeled_array == sorted_regions[1][0])
# # 計算質心
# def get_centroid(mask):
# indices = np.where(mask)
# return np.mean(indices[2]) # 使用x軸第三維度的平均值
# centroid1 = get_centroid(region1_mask)
# centroid2 = get_centroid(region2_mask)
# # 根據x軸位置確定左右
# if centroid1 > centroid2:
# right = region1_mask
# left = region2_mask
# else:
# right = region2_mask
# left = region1_mask
# return right.astype(np.uint8), left.astype(np.uint8)
# def evaluate_case(gt_path, pred_path, labels):
# """評估單個案例"""
# print(f"\n測試文件: {os.path.basename(pred_path)}")
# # 加載圖像
# start_time = time.time()
# gt_img = load_nifti(gt_path)
# pred_img = load_nifti(pred_path)
# load_time = time.time() - start_time
# print(f"圖像大小: {gt_img.shape}")
# print(f"加載時間: {load_time:.2f}秒")
# results = {}
# total_time = 0
# # 處理眼睛
# start_time = time.time()
# combined_gt_eye = ((gt_img == 2) | (gt_img == 3)).astype(np.uint8)
# combined_pred_eye = ((pred_img == 2) | (pred_img == 3)).astype(np.uint8)
# # 形態學處理眼睛
# combined_pred_eye_sitk = sitk.GetImageFromArray(combined_pred_eye)
# closing_filter = sitk.BinaryMorphologicalClosingImageFilter()
# closing_filter.SetKernelRadius(2)
# processed_pred_eye = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_eye_sitk))
# # 計算眼睛的Dice
# eye_dice = compute_dice(processed_pred_eye, combined_gt_eye)
# eye_time = time.time() - start_time
# total_time += eye_time
# results['combined_eye'] = eye_dice
# print(f"眼睛合併計算時間: {eye_time:.2f}秒, Dice: {eye_dice:.4f}")
# # 分離眼睛
# right_pred_eye, left_pred_eye = separate_structures(processed_pred_eye, "眼睛")
# right_gt_eye, left_gt_eye = separate_structures(combined_gt_eye, "眼睛")
# # 處理視神經
# start_time = time.time()
# combined_gt_nerve = ((gt_img == 5) | (gt_img == 6)).astype(np.uint8)
# combined_pred_nerve = ((pred_img == 5) | (pred_img == 6)).astype(np.uint8)
# # 形態學處理視神經
# combined_pred_nerve_sitk = sitk.GetImageFromArray(combined_pred_nerve)
# processed_pred_nerve = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_nerve_sitk))
# # 計算視神經的Dice
# nerve_dice = compute_dice(processed_pred_nerve, combined_gt_nerve)
# nerve_time = time.time() - start_time
# total_time += nerve_time
# results['combined_nerve'] = nerve_dice
# print(f"視神經合併計算時間: {nerve_time:.2f}秒, Dice: {nerve_dice:.4f}")
# # 分離視神經
# right_pred_nerve, left_pred_nerve = separate_structures(processed_pred_nerve, "視神經")
# right_gt_nerve, left_gt_nerve = separate_structures(combined_gt_nerve, "視神經")
# # 計算各個標籤的Dice
# for label in labels:
# start_time = time.time()
# if label == 1: # Brainstem
# gt_binary = (gt_img == label).astype(np.uint8)
# pred_binary = (pred_img == label).astype(np.uint8)
# elif label == 2 and right_pred_eye is not None: # Right Eye
# gt_binary = right_gt_eye
# pred_binary = right_pred_eye
# elif label == 3 and left_pred_eye is not None: # Left Eye
# gt_binary = left_gt_eye
# pred_binary = left_pred_eye
# elif label == 4: # Optic Chiasm
# gt_binary = (gt_img == label).astype(np.uint8)
# pred_binary = (pred_img == label).astype(np.uint8)
# elif label == 5 and right_pred_nerve is not None: # Right Optic Nerve
# gt_binary = right_gt_nerve
# pred_binary = right_pred_nerve
# elif label == 6 and left_pred_nerve is not None: # Left Optic Nerve
# gt_binary = left_gt_nerve
# pred_binary = left_pred_nerve
# else:
# continue
# try:
# dice = compute_dice(pred_binary, gt_binary)
# compute_time = time.time() - start_time
# total_time += compute_time
# results[label] = dice
# print(f"標籤 {label} 計算時間: {compute_time:.2f}秒, Dice: {dice:.4f}")
# except Exception as e:
# print(f"處理標籤 {label} 時出錯:{str(e)}")
# results[label] = np.nan
# continue
# print(f"單個案例總計算時間: {total_time:.2f}秒")
# return results
# def evaluate_fold(gt_folder, pred_folder, labels):
# """評估單個fold的所有案例"""
# fold_results = {label: [] for label in labels}
# fold_results['combined_eye'] = []
# fold_results['combined_nerve'] = []
# pred_files = sorted([f for f in os.listdir(pred_folder) if f.endswith('.nii.gz')])
# for pred_file in tqdm(pred_files, desc=f"Processing {os.path.basename(pred_folder)}"):
# gt_path = os.path.join(gt_folder, pred_file)
# pred_path = os.path.join(pred_folder, pred_file)
# if not os.path.exists(gt_path):
# print(f"找不到對應的真實標籤文件:{gt_path}")
# continue
# case_results = evaluate_case(gt_path, pred_path, labels)
# # 收集結果
# for key in case_results:
# if not np.isnan(case_results[key]):
# if key in fold_results:
# fold_results[key].append(case_results[key])
# return fold_results
# def evaluate_model(gt_folder, base_pred_folder, model_name, labels, organ_names):
# """評估單個模型的所有fold"""
# print(f"\n開始評估模型{model_name}")
# all_results = {label: [] for label in labels}
# all_results['combined_eye'] = []
# all_results['combined_nerve'] = []
# fold_results = {}
# # 處理 fold_0 到 fold_4
# for fold in range(5):
# print(f"\n處理 {model_name} - Fold {fold}")
# pred_folder = os.path.join(base_pred_folder, f"fold_{fold}")
# if not os.path.exists(pred_folder):
# print(f"找不到預測文件夾:{pred_folder}")
# continue
# results = evaluate_fold(gt_folder, pred_folder, labels)
# fold_results[f"fold_{fold}"] = results
# # 合併結果
# for key in results:
# all_results[key].extend(results[key])
# # 計算總平均值和標準差
# summary = {}
# for key in all_results:
# if all_results[key]:
# mean_dice = np.mean(all_results[key])
# std_dice = np.std(all_results[key])
# summary[key] = {
# 'mean_dice': float(mean_dice),
# 'std_dice': float(std_dice),
# 'n_samples': len(all_results[key])
# }
# else:
# summary[key] = {
# 'mean_dice': np.nan,
# 'std_dice': np.nan,
# 'n_samples': 0
# }
# # 保存結果到指定路径
# save_dir = "/mnt/1248/onlylian/CTC/Dice/test"
# os.makedirs(save_dir, exist_ok=True)
# filename = os.path.join(save_dir, f'CTC_test_dice_results_{model_name}.json')
# model_results = {
# 'summary': summary,
# 'fold_results': fold_results
# }
# with open(filename, 'w') as f:
# json.dump(model_results, f, indent=4)
# print(f"\n{model_name}結果已保存到 {filename}")
# # 打印結果
# print(f"\n{model_name} 模型的 Dice 結果所有fold的平均值:")
# # 打印器官結果
# for label in labels:
# if label in summary:
# mean = summary[label]['mean_dice']
# std = summary[label]['std_dice']
# n = summary[label]['n_samples']
# organ_name = organ_names.get(label, f"Label_{label}")
# if not np.isnan(mean):
# print(f"{organ_name}: {mean:.4f} ± {std:.4f} (n={n})")
# else:
# print(f"{organ_name}: 無法計算 (n={n})")
# # 打印合併結果
# print("\n合併結構的結果:")
# for key in ['combined_eye', 'combined_nerve']:
# if key in summary:
# mean = summary[key]['mean_dice']
# std = summary[key]['std_dice']
# n = summary[key]['n_samples']
# name = "合併眼睛" if key == 'combined_eye' else "合併視神經"
# if not np.isnan(mean):
# print(f"{name}: {mean:.4f} ± {std:.4f} (n={n})")
# else:
# print(f"{name}: 無法計算 (n={n})")
# return summary
# if __name__ == "__main__":
# # 設置路徑和標籤
# gt_folder = "/mnt/1248/onlylian/nnUNet/nnUNet_raw/Dataset666/labelsTs"
# models = {
# '2D': '/mnt/1248/onlylian/CTC/output_predictions/2d',
# '3D_fullres': '/mnt/1248/onlylian/CTC/output_predictions/3d_fullres',
# '3D_lowres': '/mnt/1248/onlylian/CTC/output_predictions/3d_lowres'
# }
# labels = [1, 2, 3, 4, 5, 6]
# organ_names = {
# 1: "Brainstem",
# 2: "Right_Eye",
# 3: "Left_Eye",
# 4: "Optic_Chiasm",
# 5: "Right_Optic_Nerve",
# 6: "Left_Optic_Nerve"
# }
# # 讓用戶選擇要運行哪個模型
# print("可用的模型:")
# for i, model_name in enumerate(models.keys(), 1):
# print(f"{i}. {model_name}")
# choice = input("\n請選擇要運行的模型 (輸入數字1-3): ")
# try:
# model_index = int(choice) - 1
# model_names = list(models.keys())
# selected_model = model_names[model_index]
# evaluate_model(gt_folder, models[selected_model], selected_model, labels, organ_names)
# except (ValueError, IndexError):
# print("無效的選擇請輸入1-3之間的數字。")
import json
import os
import numpy as np
import SimpleITK as sitk
from tqdm import tqdm
import time
def load_nifti(file_path):
"""加載 NIfTI 文件"""
return sitk.GetArrayFromImage(sitk.ReadImage(file_path))
def compute_dice(pred, gt):
"""計算Dice係數"""
intersection = np.sum(pred & gt)
sum_pred = np.sum(pred)
sum_gt = np.sum(gt)
if sum_pred == 0 and sum_gt == 0:
return 1.0
elif sum_pred == 0 or sum_gt == 0:
return 0.0
return 2.0 * intersection / (sum_pred + sum_gt)
def separate_structures(combined_mask, structure_name=""):
"""分離左右結構"""
# 使用SimpleITK的ConnectedComponent
sitk_image = sitk.GetImageFromArray(combined_mask)
labeled_components = sitk.ConnectedComponent(sitk_image)
labeled_array = sitk.GetArrayFromImage(labeled_components)
# 計算連通區域數量和大小
unique, counts = np.unique(labeled_array, return_counts=True)
if len(unique) < 3: # 背景算一個所以至少需要3個
print(f"警告:{structure_name} 未找到足夠的連通區域")
return None, None
# 排除背景標籤0並按大小排序
region_sizes = [(i, count) for i, count in zip(unique[1:], counts[1:])]
sorted_regions = sorted(region_sizes, key=lambda x: x[1], reverse=True)
if len(sorted_regions) < 2:
print(f"警告:{structure_name} 未找到兩個主要區域")
return None, None
# 獲取兩個最大區域
region1_mask = (labeled_array == sorted_regions[0][0])
region2_mask = (labeled_array == sorted_regions[1][0])
# 計算質心
def get_centroid(mask):
indices = np.where(mask)
return np.mean(indices[2]) # 使用x軸第三維度的平均值
centroid1 = get_centroid(region1_mask)
centroid2 = get_centroid(region2_mask)
# 根據x軸位置確定左右
if centroid1 > centroid2:
right = region1_mask
left = region2_mask
else:
right = region2_mask
left = region1_mask
return right.astype(np.uint8), left.astype(np.uint8)
def evaluate_case(gt_path, pred_path, labels):
"""評估單個案例"""
print(f"\n測試文件: {os.path.basename(pred_path)}")
# 加載圖像
start_time = time.time()
gt_img = load_nifti(gt_path)
pred_img = load_nifti(pred_path)
load_time = time.time() - start_time
print(f"圖像大小: {gt_img.shape}")
print(f"加載時間: {load_time:.2f}")
results = {}
total_time = 0
# 處理眼睛
start_time = time.time()
combined_gt_eye = ((gt_img == 2) | (gt_img == 3)).astype(np.uint8)
combined_pred_eye = ((pred_img == 2) | (pred_img == 3)).astype(np.uint8)
# 形態學處理眼睛
combined_pred_eye_sitk = sitk.GetImageFromArray(combined_pred_eye)
closing_filter = sitk.BinaryMorphologicalClosingImageFilter()
closing_filter.SetKernelRadius(2)
processed_pred_eye = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_eye_sitk))
# 計算眼睛的Dice
eye_dice = compute_dice(processed_pred_eye, combined_gt_eye)
eye_time = time.time() - start_time
total_time += eye_time
results['combined_eye'] = eye_dice
print(f"眼睛合併計算時間: {eye_time:.2f}秒, Dice: {eye_dice:.4f}")
# 分離眼睛
right_pred_eye, left_pred_eye = separate_structures(processed_pred_eye, "眼睛")
right_gt_eye, left_gt_eye = separate_structures(combined_gt_eye, "眼睛")
# 處理視神經
start_time = time.time()
combined_gt_nerve = ((gt_img == 5) | (gt_img == 6)).astype(np.uint8)
combined_pred_nerve = ((pred_img == 5) | (pred_img == 6)).astype(np.uint8)
# 形態學處理視神經
combined_pred_nerve_sitk = sitk.GetImageFromArray(combined_pred_nerve)
processed_pred_nerve = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_nerve_sitk))
# 計算視神經的Dice
nerve_dice = compute_dice(processed_pred_nerve, combined_gt_nerve)
nerve_time = time.time() - start_time
total_time += nerve_time
results['combined_nerve'] = nerve_dice
print(f"視神經合併計算時間: {nerve_time:.2f}秒, Dice: {nerve_dice:.4f}")
# 分離視神經
right_pred_nerve, left_pred_nerve = separate_structures(processed_pred_nerve, "視神經")
right_gt_nerve, left_gt_nerve = separate_structures(combined_gt_nerve, "視神經")
# 計算各個標籤的Dice
for label in labels:
start_time = time.time()
if label == 1: # Brainstem
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 2 and right_pred_eye is not None: # Right Eye
gt_binary = right_gt_eye
pred_binary = right_pred_eye
elif label == 3 and left_pred_eye is not None: # Left Eye
gt_binary = left_gt_eye
pred_binary = left_pred_eye
elif label == 4: # Optic Chiasm
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 5 and right_pred_nerve is not None: # Right Optic Nerve
gt_binary = right_gt_nerve
pred_binary = right_pred_nerve
elif label == 6 and left_pred_nerve is not None: # Left Optic Nerve
gt_binary = left_gt_nerve
pred_binary = left_pred_nerve
else:
continue
try:
dice = compute_dice(pred_binary, gt_binary)
compute_time = time.time() - start_time
total_time += compute_time
results[label] = dice
print(f"標籤 {label} 計算時間: {compute_time:.2f}秒, Dice: {dice:.4f}")
except Exception as e:
print(f"處理標籤 {label} 時出錯:{str(e)}")
results[label] = np.nan
continue
print(f"單個案例總計算時間: {total_time:.2f}")
return results
def evaluate_fold(gt_folder, pred_folder, labels):
"""評估單個fold的所有案例"""
fold_results = {label: [] for label in labels}
fold_results['combined_eye'] = []
fold_results['combined_nerve'] = []
pred_files = sorted([f for f in os.listdir(pred_folder) if f.endswith('.nii.gz')])
for pred_file in tqdm(pred_files, desc=f"Processing {os.path.basename(pred_folder)}"):
gt_path = os.path.join(gt_folder, pred_file)
pred_path = os.path.join(pred_folder, pred_file)
if not os.path.exists(gt_path):
print(f"找不到對應的真實標籤文件:{gt_path}")
continue
case_results = evaluate_case(gt_path, pred_path, labels)
# 收集結果
for key in case_results:
if not np.isnan(case_results[key]):
if key in fold_results:
fold_results[key].append(case_results[key])
return fold_results
def evaluate_model(gt_folder, base_pred_folder, model_name, labels, organ_names):
"""評估單個模型的所有fold"""
print(f"\n開始評估模型:{model_name}")
all_results = {label: [] for label in labels}
all_results['combined_eye'] = []
all_results['combined_nerve'] = []
fold_results = {}
# 處理 fold_0 到 fold_4
for fold in range(5):
print(f"\n處理 {model_name} - Fold {fold}")
pred_folder = os.path.join(base_pred_folder, f"fold_{fold}")
if not os.path.exists(pred_folder):
print(f"找不到預測文件夾:{pred_folder}")
continue
results = evaluate_fold(gt_folder, pred_folder, labels)
fold_results[f"fold_{fold}"] = results
# 合併結果
for key in results:
all_results[key].extend(results[key])
# 計算總平均值和標準差
summary = {}
for key in all_results:
if all_results[key]:
mean_dice = np.mean(all_results[key])
std_dice = np.std(all_results[key])
summary[key] = {
'mean_dice': float(mean_dice),
'std_dice': float(std_dice),
'n_samples': len(all_results[key])
}
else:
summary[key] = {
'mean_dice': np.nan,
'std_dice': np.nan,
'n_samples': 0
}
# 保存結果到指定路径
save_dir = "/mnt/1248/onlylian/FLAIR/Dice/test"
os.makedirs(save_dir, exist_ok=True)
filename = os.path.join(save_dir, f'FLAIR_test_dice_results_{model_name}.json')
model_results = {
'summary': summary,
'fold_results': fold_results
}
with open(filename, 'w') as f:
json.dump(model_results, f, indent=4)
print(f"\n{model_name}結果已保存到 {filename}")
# 打印結果
print(f"\n{model_name} 模型的 Dice 結果所有fold的平均值:")
# 打印器官結果
for label in labels:
if label in summary:
mean = summary[label]['mean_dice']
std = summary[label]['std_dice']
n = summary[label]['n_samples']
organ_name = organ_names.get(label, f"Label_{label}")
if not np.isnan(mean):
print(f"{organ_name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{organ_name}: 無法計算 (n={n})")
# 打印合併結果
print("\n合併結構的結果:")
for key in ['combined_eye', 'combined_nerve']:
if key in summary:
mean = summary[key]['mean_dice']
std = summary[key]['std_dice']
n = summary[key]['n_samples']
name = "合併眼睛" if key == 'combined_eye' else "合併視神經"
if not np.isnan(mean):
print(f"{name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{name}: 無法計算 (n={n})")
return summary
if __name__ == "__main__":
# 設置路徑和標籤
gt_folder = "/mnt/1248/onlylian/nnUNet/nnUNet_raw/Dataset777/labelsTs"
models = {
'2D': '/mnt/1248/onlylian/FLAIR/output_predictions/2d',
'3D_fullres': '/mnt/1248/onlylian/FLAIR/output_predictions/3d_fullres',
'3D_lowres': '/mnt/1248/onlylian/FLAIR/output_predictions/3d_lowres'
}
labels = [1, 2, 3, 4, 5, 6]
organ_names = {
1: "Brainstem",
2: "Right_Eye",
3: "Left_Eye",
4: "Optic_Chiasm",
5: "Right_Optic_Nerve",
6: "Left_Optic_Nerve"
}
# 讓用戶選擇要運行哪個模型
print("可用的模型:")
for i, model_name in enumerate(models.keys(), 1):
print(f"{i}. {model_name}")
choice = input("\n請選擇要運行的模型 (輸入數字1-3): ")
try:
model_index = int(choice) - 1
model_names = list(models.keys())
selected_model = model_names[model_index]
evaluate_model(gt_folder, models[selected_model], selected_model, labels, organ_names)
except (ValueError, IndexError):
print("無效的選擇請輸入1-3之間的數字。")

View file

@ -0,0 +1,142 @@
import json
import os
import numpy as np
import matplotlib.pyplot as plt
from typing import Dict, Tuple
# 設置中文字體支持
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
def load_data(file_path: str) -> Dict:
"""讀取JSON數據文件"""
try:
with open(file_path, 'r') as file:
return json.load(file)
except FileNotFoundError:
print(f"文件未找到: {file_path}")
return {}
except json.JSONDecodeError:
print(f"JSON解析錯誤: {file_path}")
return {}
def extract_scores(data: Dict, model: str, organ_id: str) -> Tuple[float, float]:
"""從數據中提取特定器官的分數"""
if "summary" not in data or organ_id not in data["summary"]:
return (0, 0)
scores = data["summary"][organ_id]
if model == '3D_fullres':
mean_dice = scores.get("mean_dice", 0)
std_dice = scores.get("std_dice", 0)
else:
mean_dice = scores.get("mean_dice", scores.get("mean", 0))
std_dice = scores.get("std_dice", scores.get("std", 0))
return (mean_dice, std_dice)
def create_plot(model_averages: Dict, model_stds: Dict, organs: Dict, output_path: str):
"""創建和保存圖表"""
plt.figure(figsize=(16, 5)) # Reduced figure size
plt.title('FLAIR', fontsize=12, pad=10) # Changed title
plt.grid(True, axis='y', linestyle='--', alpha=0.7)
x = np.arange(len(organs)) * 1.5 # Reduced spacing between groups
width = 0.35 # Reduced bar width
colors = {
'2D': 'royalblue',
'3D_fullres': 'limegreen',
'3D_lowres': 'orange'
}
positions = {
'2D': x - width,
'3D_fullres': x,
'3D_lowres': x + width
}
for model, averages in model_averages.items():
bars = plt.bar(positions[model],
[averages[organ] * 100 for organ in organs.keys()],
width,
label=model,
color=colors[model])
# 添加數值標籤
for j, bar in enumerate(bars):
height = bar.get_height()
organ = list(organs.keys())[j]
std = model_stds[model][organ] * 100
plt.text(bar.get_x() + bar.get_width()/2., height + 0.5,
f'{height:.1f}±{std:.1f}%',
ha='center',
va='bottom',
fontsize=6) # Reduced font size
plt.ylim(0, 100)
plt.ylabel('DC (%)', fontsize=10)
plt.xlabel('OAR', fontsize=10)
plt.xticks(x, [organs[k] for k in organs.keys()], rotation=30, ha='right', fontsize=8)
plt.legend(loc='upper right', fontsize=8)
plt.tight_layout()
os.makedirs(os.path.dirname(output_path), exist_ok=True)
plt.savefig(output_path, dpi=300, bbox_inches='tight')
plt.close()
def print_results(model_averages: Dict, model_stds: Dict, organs: Dict):
"""打印數值結果"""
print("\n平均 Dice 分數 (百分比):")
for model in model_averages.keys():
print(f"\n{model}:")
for organ in organs.keys():
avg = model_averages[model][organ] * 100
std = model_stds[model][organ] * 100
print(f"{organs[organ]}: {avg:.1f}±{std:.1f}%")
def main():
# 定義路徑和器官映射
base_path = '/mnt/1248/onlylian/FLAIR/Dice/validation'
paths = {
'2D': os.path.join(base_path, 'FLAIR_validation_dice_results_2D.json'),
'3D_fullres': os.path.join(base_path, 'FLAIR_validation_dice_results_3D_fullres.json'),
'3D_lowres': os.path.join(base_path, 'FLAIR_validation_dice_results_3D_lowres.json')
}
organs = {
"1": "Brainstem",
"2": "Right_Eye",
"3": "Left_Eye",
"4": "Optic_Chiasm",
"5": "Right_Optic_Nerve",
"6": "Left_Optic_Nerve"
}
# 初始化數據存儲
model_dice_scores = {
model: {organ: None for organ in organs.keys()}
for model in paths.keys()
}
# 讀取和處理數據
for model, path in paths.items():
data = load_data(path)
for organ_id in organs.keys():
model_dice_scores[model][organ_id] = extract_scores(data, model, organ_id)
# 計算平均值和標準差
model_averages = {}
model_stds = {}
for model, scores in model_dice_scores.items():
model_averages[model] = {organ: scores[organ][0] if scores[organ] else 0 for organ in organs.keys()}
model_stds[model] = {organ: scores[organ][1] if scores[organ] else 0 for organ in organs.keys()}
# 創建圖表並保存
output_path = os.path.join(base_path, 'FLAIR_validation_dice_scores_comparison.png')
create_plot(model_averages, model_stds, organs, output_path)
# 打印結果
print_results(model_averages, model_stds, organs)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,288 @@
import os
import time
import numpy as np
import SimpleITK as sitk
from tqdm import tqdm
import json
def load_nifti(file_path):
"""加載 NIfTI 文件"""
return sitk.GetArrayFromImage(sitk.ReadImage(file_path))
def compute_dice(pred, gt):
"""計算Dice係數"""
intersection = np.sum(pred & gt)
sum_pred = np.sum(pred)
sum_gt = np.sum(gt)
if sum_pred == 0 and sum_gt == 0:
return 1.0
elif sum_pred == 0 or sum_gt == 0:
return 0.0
return 2.0 * intersection / (sum_pred + sum_gt)
def separate_structures(combined_mask, structure_name=""):
"""分離左右結構"""
sitk_image = sitk.GetImageFromArray(combined_mask)
labeled_components = sitk.ConnectedComponent(sitk_image)
labeled_array = sitk.GetArrayFromImage(labeled_components)
unique, counts = np.unique(labeled_array, return_counts=True)
if len(unique) < 3:
print(f"警告:{structure_name} 未找到足夠的連通區域")
return None, None
region_sizes = [(i, count) for i, count in zip(unique[1:], counts[1:])]
sorted_regions = sorted(region_sizes, key=lambda x: x[1], reverse=True)
if len(sorted_regions) < 2:
print(f"警告:{structure_name} 未找到兩個主要區域")
return None, None
region1_mask = (labeled_array == sorted_regions[0][0])
region2_mask = (labeled_array == sorted_regions[1][0])
def get_centroid(mask):
indices = np.where(mask)
return np.mean(indices[2])
centroid1 = get_centroid(region1_mask)
centroid2 = get_centroid(region2_mask)
if centroid1 > centroid2:
right = region1_mask
left = region2_mask
else:
right = region2_mask
left = region1_mask
return right.astype(np.uint8), left.astype(np.uint8)
def evaluate_case(gt_path, pred_path, labels):
"""評估單個案例"""
print(f"\n測試文件: {os.path.basename(pred_path)}")
start_time = time.time()
gt_img = load_nifti(gt_path)
pred_img = load_nifti(pred_path)
load_time = time.time() - start_time
print(f"圖像大小: {gt_img.shape}")
print(f"加載時間: {load_time:.2f}")
results = {}
total_time = 0
# 處理眼睛
start_time = time.time()
combined_gt_eye = ((gt_img == 2) | (gt_img == 3)).astype(np.uint8)
combined_pred_eye = ((pred_img == 2) | (pred_img == 3)).astype(np.uint8)
combined_pred_eye_sitk = sitk.GetImageFromArray(combined_pred_eye)
closing_filter = sitk.BinaryMorphologicalClosingImageFilter()
closing_filter.SetKernelRadius(2)
processed_pred_eye = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_eye_sitk))
eye_dice = compute_dice(processed_pred_eye, combined_gt_eye)
eye_time = time.time() - start_time
total_time += eye_time
results['combined_eye'] = eye_dice
print(f"眼睛合併計算時間: {eye_time:.2f}秒, Dice: {eye_dice:.4f}")
right_pred_eye, left_pred_eye = separate_structures(processed_pred_eye, "眼睛")
right_gt_eye, left_gt_eye = separate_structures(combined_gt_eye, "眼睛")
# 處理視神經
start_time = time.time()
combined_gt_nerve = ((gt_img == 5) | (gt_img == 6)).astype(np.uint8)
combined_pred_nerve = ((pred_img == 5) | (pred_img == 6)).astype(np.uint8)
combined_pred_nerve_sitk = sitk.GetImageFromArray(combined_pred_nerve)
processed_pred_nerve = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_nerve_sitk))
nerve_dice = compute_dice(processed_pred_nerve, combined_gt_nerve)
nerve_time = time.time() - start_time
total_time += nerve_time
results['combined_nerve'] = nerve_dice
print(f"視神經合併計算時間: {nerve_time:.2f}秒, Dice: {nerve_dice:.4f}")
right_pred_nerve, left_pred_nerve = separate_structures(processed_pred_nerve, "視神經")
right_gt_nerve, left_gt_nerve = separate_structures(combined_gt_nerve, "視神經")
for label in labels:
start_time = time.time()
if label == 1: # Brainstem
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 2 and right_pred_eye is not None: # Right Eye
gt_binary = right_gt_eye
pred_binary = right_pred_eye
elif label == 3 and left_pred_eye is not None: # Left Eye
gt_binary = left_gt_eye
pred_binary = left_pred_eye
elif label == 4: # Optic Chiasm
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 5 and right_pred_nerve is not None: # Right Optic Nerve
gt_binary = right_gt_nerve
pred_binary = right_pred_nerve
elif label == 6 and left_pred_nerve is not None: # Left Optic Nerve
gt_binary = left_gt_nerve
pred_binary = left_pred_nerve
else:
continue
try:
dice = compute_dice(pred_binary, gt_binary)
compute_time = time.time() - start_time
total_time += compute_time
results[label] = dice
print(f"標籤 {label} 計算時間: {compute_time:.2f}秒, Dice: {dice:.4f}")
except Exception as e:
print(f"處理標籤 {label} 時出錯:{str(e)}")
results[label] = np.nan
continue
print(f"單個案例總計算時間: {total_time:.2f}")
return results
def evaluate_validation_fold(gt_base_folder, validation_folder, labels):
"""評估單個fold的驗證集"""
fold_results = {label: [] for label in labels}
fold_results['combined_eye'] = []
fold_results['combined_nerve'] = []
pred_files = sorted([f for f in os.listdir(validation_folder) if f.endswith('.nii.gz')])
for pred_file in tqdm(pred_files, desc=f"Processing {os.path.basename(validation_folder)}"):
patient_id = pred_file.split('.nii.gz')[0]
gt_path = os.path.join(gt_base_folder, f"{patient_id}.nii.gz")
pred_path = os.path.join(validation_folder, pred_file)
if not os.path.exists(gt_path):
print(f"找不到對應的真實標籤文件:{gt_path}")
continue
case_results = evaluate_case(gt_path, pred_path, labels)
for key in case_results:
if not np.isnan(case_results[key]):
if key in fold_results:
fold_results[key].append(case_results[key])
return fold_results
def evaluate_model(gt_folder, model_path, model_name, labels, organ_names, output_dir):
"""評估模型所有fold的驗證集"""
print(f"\n開始評估模型:{model_name}")
all_results = {label: [] for label in labels}
all_results['combined_eye'] = []
all_results['combined_nerve'] = []
fold_results = {}
for fold in range(5):
validation_folder = os.path.join(model_path, f"fold_{fold}", "validation")
if not os.path.exists(validation_folder):
print(f"找不到fold_{fold}的驗證資料夾:{validation_folder}")
continue
print(f"\n處理 {model_name} - Fold {fold} 驗證集")
results = evaluate_validation_fold(gt_folder, validation_folder, labels)
fold_results[f"fold_{fold}"] = results
for key in results:
all_results[key].extend(results[key])
summary = {}
for key in all_results:
if all_results[key]:
mean_dice = np.mean(all_results[key])
std_dice = np.std(all_results[key])
summary[key] = {
'mean_dice': float(mean_dice),
'std_dice': float(std_dice),
'n_samples': len(all_results[key])
}
else:
summary[key] = {
'mean_dice': np.nan,
'std_dice': np.nan,
'n_samples': 0
}
model_results = {
'summary': summary,
'fold_results': fold_results
}
output_path = os.path.join(output_dir, 'FLAIR', 'Dice', 'validation')
os.makedirs(output_path, exist_ok=True)
filename = os.path.join(output_path, f'dice_results_{model_name}.json')
with open(filename, 'w') as f:
json.dump(model_results, f, indent=4)
print(f"\n{model_name}驗證集結果已保存到 {filename}")
print(f"\n{model_name} 模型的驗證集 Dice 結果所有fold的平均值:")
for label in labels:
if label in summary:
mean = summary[label]['mean_dice']
std = summary[label]['std_dice']
n = summary[label]['n_samples']
organ_name = organ_names.get(label, f"Label_{label}")
if not np.isnan(mean):
print(f"{organ_name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{organ_name}: 無法計算 (n={n})")
print("\n合併結構的結果:")
for key in ['combined_eye', 'combined_nerve']:
if key in summary:
mean = summary[key]['mean_dice']
std = summary[key]['std_dice']
n = summary[key]['n_samples']
name = "合併眼睛" if key == 'combined_eye' else "合併視神經"
if not np.isnan(mean):
print(f"{name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{name}: 無法計算 (n={n})")
return summary
if __name__ == "__main__":
base_dir = "/mnt/1248/onlylian"
gt_folder = os.path.join(base_dir, "nnUNet/nnUNet_raw/Dataset777/labelsTr")
output_dir = base_dir
models = {
'2D': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset777/nnUNetTrainer__nnUNetPlans__2d"),
'3D_fullres': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset777/nnUNetTrainer__nnUNetPlans__3d_fullres"),
'3D_lowres': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset777/nnUNetTrainer__nnUNetPlans__3d_lowres")
}
labels = [1, 2, 3, 4, 5, 6]
organ_names = {
1: "Brainstem",
2: "Right_Eye",
3: "Left_Eye",
4: "Optic_Chiasm",
5: "Right_Optic_Nerve",
6: "Left_Optic_Nerve"
}
print("可用的模型:")
for i, model_name in enumerate(models.keys(), 1):
print(f"{i}. {model_name}")
choice = input("\n請選擇要運行的模型 (輸入數字1-3): ")
try:
model_index = int(choice) - 1
model_names = list(models.keys())
selected_model = model_names[model_index]
evaluate_model(gt_folder, models[selected_model], selected_model, labels, organ_names, output_dir)
except (ValueError, IndexError):
print("無效的選擇請輸入1-3之間的數字。")

View file

@ -0,0 +1,143 @@
import json
import os
import numpy as np
import matplotlib.pyplot as plt
from typing import Dict, Tuple
# 設置中文字體支持
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
def load_data(file_path: str) -> Dict:
"""讀取JSON數據文件"""
try:
with open(file_path, 'r') as file:
return json.load(file)
except FileNotFoundError:
print(f"文件未找到: {file_path}")
return {}
except json.JSONDecodeError:
print(f"JSON解析錯誤: {file_path}")
return {}
def extract_scores(data: Dict, model: str, organ_id: str) -> Tuple[float, float]:
"""從數據中提取特定器官的分數"""
if "summary" not in data or organ_id not in data["summary"]:
return (0, 0)
scores = data["summary"][organ_id]
if model == '3D_fullres':
mean_dice = scores.get("mean_dice", 0)
std_dice = scores.get("std_dice", 0)
else:
mean_dice = scores.get("mean_dice", scores.get("mean", 0))
std_dice = scores.get("std_dice", scores.get("std", 0))
return (mean_dice, std_dice)
def create_plot(model_averages: Dict, model_stds: Dict, organs: Dict, output_path: str):
"""創建和保存圖表"""
plt.figure(figsize=(16, 5)) # Reduced figure size
plt.title('T1', fontsize=12, pad=10) # Changed title
plt.grid(True, axis='y', linestyle='--', alpha=0.7)
x = np.arange(len(organs)) * 1.5 # Reduced spacing between groups
width = 0.35 # Reduced bar width
colors = {
'2D': 'royalblue',
'3D_fullres': 'limegreen',
'3D_lowres': 'orange'
}
positions = {
'2D': x - width,
'3D_fullres': x,
'3D_lowres': x + width
}
for model, averages in model_averages.items():
bars = plt.bar(positions[model],
[averages[organ] * 100 for organ in organs.keys()],
width,
label=model,
color=colors[model])
# 調整數值標籤位置和大小
for j, bar in enumerate(bars):
height = bar.get_height()
organ = list(organs.keys())[j]
std = model_stds[model][organ] * 100
plt.text(bar.get_x() + bar.get_width()/2., height,
f'{height:.1f}±{std:.1f}%',
ha='center',
va='bottom',
fontsize=6, # Reduced font size
rotation=0) # Optional: rotate text if still crowded
plt.ylim(0, 100)
plt.ylabel('DC (%)', fontsize=10)
plt.xlabel('OAR', fontsize=10)
plt.xticks(x, [organs[k] for k in organs.keys()], rotation=30, ha='right', fontsize=8)
plt.legend(loc='upper right', fontsize=8)
plt.tight_layout()
os.makedirs(os.path.dirname(output_path), exist_ok=True)
plt.savefig(output_path, dpi=300, bbox_inches='tight')
plt.close()
def print_results(model_averages: Dict, model_stds: Dict, organs: Dict):
"""打印數值結果"""
print("\n平均 Dice 分數 (百分比):")
for model in model_averages.keys():
print(f"\n{model}:")
for organ in organs.keys():
avg = model_averages[model][organ] * 100
std = model_stds[model][organ] * 100
print(f"{organs[organ]}: {avg:.1f}±{std:.1f}%")
def main():
# 定義路徑和器官映射
base_path = '/mnt/1248/onlylian/T1/Dice/test'
paths = {
'2D': os.path.join(base_path, 'T1_test_dice_results_2D.json'),
'3D_fullres': os.path.join(base_path, 'T1_test_dice_results_3D_fullres.json'),
'3D_lowres': os.path.join(base_path, 'T1_test_dice_results_3D_lowres.json')
}
organs = {
"1": "Brainstem",
"2": "Right_Eye",
"3": "Left_Eye",
"4": "Optic_Chiasm",
"5": "Right_Optic_Nerve",
"6": "Left_Optic_Nerve"
}
# 初始化數據存儲
model_dice_scores = {
model: {organ: None for organ in organs.keys()}
for model in paths.keys()
}
# 讀取和處理數據
for model, path in paths.items():
data = load_data(path)
for organ_id in organs.keys():
model_dice_scores[model][organ_id] = extract_scores(data, model, organ_id)
# 計算平均值和標準差
model_averages = {}
model_stds = {}
for model, scores in model_dice_scores.items():
model_averages[model] = {organ: scores[organ][0] if scores[organ] else 0 for organ in organs.keys()}
model_stds[model] = {organ: scores[organ][1] if scores[organ] else 0 for organ in organs.keys()}
# 創建圖表並保存
output_path = os.path.join(base_path, 'T1_test_dice_scores_comparison.png')
create_plot(model_averages, model_stds, organs, output_path)
# 打印結果
print_results(model_averages, model_stds, organs)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,282 @@
import json
import os
import numpy as np
import SimpleITK as sitk
from tqdm import tqdm
import time
def load_nifti(file_path):
"""加載 NIfTI 文件"""
return sitk.GetArrayFromImage(sitk.ReadImage(file_path))
def compute_dice(pred, gt):
"""計算Dice係數"""
intersection = np.sum(pred & gt)
sum_pred = np.sum(pred)
sum_gt = np.sum(gt)
if sum_pred == 0 and sum_gt == 0:
return 1.0
elif sum_pred == 0 or sum_gt == 0:
return 0.0
return 2.0 * intersection / (sum_pred + sum_gt)
def separate_structures(combined_mask, structure_name=""):
"""分離左右結構"""
sitk_image = sitk.GetImageFromArray(combined_mask)
labeled_components = sitk.ConnectedComponent(sitk_image)
labeled_array = sitk.GetArrayFromImage(labeled_components)
unique, counts = np.unique(labeled_array, return_counts=True)
if len(unique) < 3:
print(f"警告:{structure_name} 未找到足夠的連通區域")
return None, None
region_sizes = [(i, count) for i, count in zip(unique[1:], counts[1:])]
sorted_regions = sorted(region_sizes, key=lambda x: x[1], reverse=True)
if len(sorted_regions) < 2:
print(f"警告:{structure_name} 未找到兩個主要區域")
return None, None
region1_mask = (labeled_array == sorted_regions[0][0])
region2_mask = (labeled_array == sorted_regions[1][0])
def get_centroid(mask):
indices = np.where(mask)
return np.mean(indices[2])
centroid1 = get_centroid(region1_mask)
centroid2 = get_centroid(region2_mask)
if centroid1 > centroid2:
right = region1_mask
left = region2_mask
else:
right = region2_mask
left = region1_mask
return right.astype(np.uint8), left.astype(np.uint8)
def evaluate_case(gt_path, pred_path, labels):
"""評估單個案例"""
print(f"\n測試文件: {os.path.basename(pred_path)}")
start_time = time.time()
gt_img = load_nifti(gt_path)
pred_img = load_nifti(pred_path)
load_time = time.time() - start_time
print(f"圖像大小: {gt_img.shape}")
print(f"加載時間: {load_time:.2f}")
results = {}
total_time = 0
# 處理眼睛
start_time = time.time()
combined_gt_eye = ((gt_img == 2) | (gt_img == 3)).astype(np.uint8)
combined_pred_eye = ((pred_img == 2) | (pred_img == 3)).astype(np.uint8)
combined_pred_eye_sitk = sitk.GetImageFromArray(combined_pred_eye)
closing_filter = sitk.BinaryMorphologicalClosingImageFilter()
closing_filter.SetKernelRadius(2)
processed_pred_eye = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_eye_sitk))
eye_dice = compute_dice(processed_pred_eye, combined_gt_eye)
eye_time = time.time() - start_time
total_time += eye_time
results['combined_eye'] = eye_dice
print(f"眼睛合併計算時間: {eye_time:.2f}秒, Dice: {eye_dice:.4f}")
right_pred_eye, left_pred_eye = separate_structures(processed_pred_eye, "眼睛")
right_gt_eye, left_gt_eye = separate_structures(combined_gt_eye, "眼睛")
# 處理視神經
start_time = time.time()
combined_gt_nerve = ((gt_img == 5) | (gt_img == 6)).astype(np.uint8)
combined_pred_nerve = ((pred_img == 5) | (pred_img == 6)).astype(np.uint8)
combined_pred_nerve_sitk = sitk.GetImageFromArray(combined_pred_nerve)
processed_pred_nerve = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_nerve_sitk))
nerve_dice = compute_dice(processed_pred_nerve, combined_gt_nerve)
nerve_time = time.time() - start_time
total_time += nerve_time
results['combined_nerve'] = nerve_dice
print(f"視神經合併計算時間: {nerve_time:.2f}秒, Dice: {nerve_dice:.4f}")
right_pred_nerve, left_pred_nerve = separate_structures(processed_pred_nerve, "視神經")
right_gt_nerve, left_gt_nerve = separate_structures(combined_gt_nerve, "視神經")
for label in labels:
start_time = time.time()
if label == 1: # Brainstem
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 2 and right_pred_eye is not None: # Right Eye
gt_binary = right_gt_eye
pred_binary = right_pred_eye
elif label == 3 and left_pred_eye is not None: # Left Eye
gt_binary = left_gt_eye
pred_binary = left_pred_eye
elif label == 4: # Optic Chiasm
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 5 and right_pred_nerve is not None: # Right Optic Nerve
gt_binary = right_gt_nerve
pred_binary = right_pred_nerve
elif label == 6 and left_pred_nerve is not None: # Left Optic Nerve
gt_binary = left_gt_nerve
pred_binary = left_pred_nerve
else:
continue
try:
dice = compute_dice(pred_binary, gt_binary)
compute_time = time.time() - start_time
total_time += compute_time
results[label] = dice
print(f"標籤 {label} 計算時間: {compute_time:.2f}秒, Dice: {dice:.4f}")
except Exception as e:
print(f"處理標籤 {label} 時出錯:{str(e)}")
results[label] = np.nan
continue
print(f"單個案例總計算時間: {total_time:.2f}")
return results
def evaluate_fold(gt_folder, pred_folder, labels):
"""評估單個fold的所有案例"""
fold_results = {label: [] for label in labels}
fold_results['combined_eye'] = []
fold_results['combined_nerve'] = []
pred_files = sorted([f for f in os.listdir(pred_folder) if f.endswith('.nii.gz')])
for pred_file in tqdm(pred_files, desc=f"Processing {os.path.basename(pred_folder)}"):
gt_path = os.path.join(gt_folder, pred_file)
pred_path = os.path.join(pred_folder, pred_file)
if not os.path.exists(gt_path):
print(f"找不到對應的真實標籤文件:{gt_path}")
continue
case_results = evaluate_case(gt_path, pred_path, labels)
for key in case_results:
if not np.isnan(case_results[key]):
if key in fold_results:
fold_results[key].append(case_results[key])
return fold_results
def evaluate_model(gt_folder, base_pred_folder, model_name, labels, organ_names):
"""評估單個模型的所有fold"""
print(f"\n開始評估模型:{model_name}")
all_results = {label: [] for label in labels}
all_results['combined_eye'] = []
all_results['combined_nerve'] = []
fold_results = {}
for fold in range(5):
print(f"\n處理 {model_name} - Fold {fold}")
pred_folder = os.path.join(base_pred_folder, f"fold_{fold}")
if not os.path.exists(pred_folder):
print(f"找不到預測文件夾:{pred_folder}")
continue
results = evaluate_fold(gt_folder, pred_folder, labels)
fold_results[f"fold_{fold}"] = results
for key in results:
all_results[key].extend(results[key])
summary = {}
for key in all_results:
if all_results[key]:
mean_dice = np.mean(all_results[key])
std_dice = np.std(all_results[key])
summary[key] = {
'mean_dice': float(mean_dice),
'std_dice': float(std_dice),
'n_samples': len(all_results[key])
}
else:
summary[key] = {
'mean_dice': np.nan,
'std_dice': np.nan,
'n_samples': 0
}
model_results = {
'summary': summary,
'fold_results': fold_results
}
save_dir = "/mnt/1248/onlylian/T1/Dice/test"
os.makedirs(save_dir, exist_ok=True)
filename = os.path.join(save_dir, f'T1_test_dice_results_{model_name}.json')
with open(filename, 'w') as f:
json.dump(model_results, f, indent=4)
print(f"\n{model_name}結果已保存到 {filename}")
print(f"\n{model_name} 模型的 Dice 結果所有fold的平均值:")
for label in labels:
if label in summary:
mean = summary[label]['mean_dice']
std = summary[label]['std_dice']
n = summary[label]['n_samples']
organ_name = organ_names.get(label, f"Label_{label}")
if not np.isnan(mean):
print(f"{organ_name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{organ_name}: 無法計算 (n={n})")
print("\n合併結構的結果:")
for key in ['combined_eye', 'combined_nerve']:
if key in summary:
mean = summary[key]['mean_dice']
std = summary[key]['std_dice']
n = summary[key]['n_samples']
name = "合併眼睛" if key == 'combined_eye' else "合併視神經"
if not np.isnan(mean):
print(f"{name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{name}: 無法計算 (n={n})")
return summary
if __name__ == "__main__":
gt_folder = "/mnt/1248/onlylian/nnUNet/nnUNet_raw/Dataset888/labelsTs"
models = {
'2D': '/mnt/1248/onlylian/T1/output_predictions/2d',
'3D_fullres': '/mnt/1248/onlylian/T1/output_predictions/3d_fullres',
'3D_lowres': '/mnt/1248/onlylian/T1/output_predictions/3d_lowres'
}
labels = [1, 2, 3, 4, 5, 6]
organ_names = {
1: "Brainstem",
2: "Right_Eye",
3: "Left_Eye",
4: "Optic_Chiasm",
5: "Right_Optic_Nerve",
6: "Left_Optic_Nerve"
}
print("可用的模型:")
for i, model_name in enumerate(models.keys(), 1):
print(f"{i}. {model_name}")
choice = input("\n請選擇要運行的模型 (輸入數字1-3): ")
try:
model_index = int(choice) - 1
model_names = list(models.keys())
selected_model = model_names[model_index]
evaluate_model(gt_folder, models[selected_model], selected_model, labels, organ_names)
except (ValueError, IndexError):
print("無效的選擇請輸入1-3之間的數字。")

View file

@ -0,0 +1,142 @@
import json
import os
import numpy as np
import matplotlib.pyplot as plt
from typing import Dict, Tuple
# 設置中文字體支持
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
def load_data(file_path: str) -> Dict:
"""讀取JSON數據文件"""
try:
with open(file_path, 'r') as file:
return json.load(file)
except FileNotFoundError:
print(f"文件未找到: {file_path}")
return {}
except json.JSONDecodeError:
print(f"JSON解析錯誤: {file_path}")
return {}
def extract_scores(data: Dict, model: str, organ_id: str) -> Tuple[float, float]:
"""從數據中提取特定器官的分數"""
if "summary" not in data or organ_id not in data["summary"]:
return (0, 0)
scores = data["summary"][organ_id]
if model == '3D_fullres':
mean_dice = scores.get("mean_dice", 0)
std_dice = scores.get("std_dice", 0)
else:
mean_dice = scores.get("mean_dice", scores.get("mean", 0))
std_dice = scores.get("std_dice", scores.get("std", 0))
return (mean_dice, std_dice)
def create_plot(model_averages: Dict, model_stds: Dict, organs: Dict, output_path: str):
"""創建和保存圖表"""
plt.figure(figsize=(16, 5)) # Reduced figure size
plt.title('T1', fontsize=12, pad=10) # Changed title from FLAIR to T1
plt.grid(True, axis='y', linestyle='--', alpha=0.7)
x = np.arange(len(organs)) * 1.5 # Reduced spacing between groups
width = 0.35 # Reduced bar width
colors = {
'2D': 'royalblue',
'3D_fullres': 'limegreen',
'3D_lowres': 'orange'
}
positions = {
'2D': x - width,
'3D_fullres': x,
'3D_lowres': x + width
}
for model, averages in model_averages.items():
bars = plt.bar(positions[model],
[averages[organ] * 100 for organ in organs.keys()],
width,
label=model,
color=colors[model])
# 添加數值標籤
for j, bar in enumerate(bars):
height = bar.get_height()
organ = list(organs.keys())[j]
std = model_stds[model][organ] * 100
plt.text(bar.get_x() + bar.get_width()/2., height + 0.5,
f'{height:.1f}±{std:.1f}%',
ha='center',
va='bottom',
fontsize=6) # Reduced font size
plt.ylim(0, 100)
plt.ylabel('DC (%)', fontsize=10)
plt.xlabel('OAR', fontsize=10)
plt.xticks(x, [organs[k] for k in organs.keys()], rotation=30, ha='right', fontsize=8)
plt.legend(loc='upper right', fontsize=8)
plt.tight_layout()
os.makedirs(os.path.dirname(output_path), exist_ok=True)
plt.savefig(output_path, dpi=300, bbox_inches='tight')
plt.close()
def print_results(model_averages: Dict, model_stds: Dict, organs: Dict):
"""打印數值結果"""
print("\n平均 Dice 分數 (百分比):")
for model in model_averages.keys():
print(f"\n{model}:")
for organ in organs.keys():
avg = model_averages[model][organ] * 100
std = model_stds[model][organ] * 100
print(f"{organs[organ]}: {avg:.1f}±{std:.1f}%")
def main():
# 定義路徑和器官映射
base_path = '/mnt/1248/onlylian/T1/Dice/validation' # Changed from FLAIR to T1
paths = {
'2D': os.path.join(base_path, 'T1_validation_dice_results_2D.json'), # Changed FLAIR to T1
'3D_fullres': os.path.join(base_path, 'T1_validation_dice_results_3D_fullres.json'), # Changed FLAIR to T1
'3D_lowres': os.path.join(base_path, 'T1_validation_dice_results_3D_lowres.json') # Changed FLAIR to T1
}
organs = {
"1": "Brainstem",
"2": "Right_Eye",
"3": "Left_Eye",
"4": "Optic_Chiasm",
"5": "Right_Optic_Nerve",
"6": "Left_Optic_Nerve"
}
# 初始化數據存儲
model_dice_scores = {
model: {organ: None for organ in organs.keys()}
for model in paths.keys()
}
# 讀取和處理數據
for model, path in paths.items():
data = load_data(path)
for organ_id in organs.keys():
model_dice_scores[model][organ_id] = extract_scores(data, model, organ_id)
# 計算平均值和標準差
model_averages = {}
model_stds = {}
for model, scores in model_dice_scores.items():
model_averages[model] = {organ: scores[organ][0] if scores[organ] else 0 for organ in organs.keys()}
model_stds[model] = {organ: scores[organ][1] if scores[organ] else 0 for organ in organs.keys()}
# 創建圖表並保存
output_path = os.path.join(base_path, 'T1_validation_dice_scores_comparison.png') # Changed FLAIR to T1
create_plot(model_averages, model_stds, organs, output_path)
# 打印結果
print_results(model_averages, model_stds, organs)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,288 @@
import os
import time
import numpy as np
import SimpleITK as sitk
from tqdm import tqdm
import json
def load_nifti(file_path):
"""Load a NIfTI file"""
return sitk.GetArrayFromImage(sitk.ReadImage(file_path))
def compute_dice(pred, gt):
"""Compute Dice coefficient"""
intersection = np.sum(pred & gt)
sum_pred = np.sum(pred)
sum_gt = np.sum(gt)
if sum_pred == 0 and sum_gt == 0:
return 1.0
elif sum_pred == 0 or sum_gt == 0:
return 0.0
return 2.0 * intersection / (sum_pred + sum_gt)
def separate_structures(combined_mask, structure_name=""):
"""Separate left and right structures"""
sitk_image = sitk.GetImageFromArray(combined_mask)
labeled_components = sitk.ConnectedComponent(sitk_image)
labeled_array = sitk.GetArrayFromImage(labeled_components)
unique, counts = np.unique(labeled_array, return_counts=True)
if len(unique) < 3:
print(f"Warning: Not enough connected regions found for {structure_name}")
return None, None
region_sizes = [(i, count) for i, count in zip(unique[1:], counts[1:])]
sorted_regions = sorted(region_sizes, key=lambda x: x[1], reverse=True)
if len(sorted_regions) < 2:
print(f"Warning: Only one major region found for {structure_name}")
return None, None
region1_mask = (labeled_array == sorted_regions[0][0])
region2_mask = (labeled_array == sorted_regions[1][0])
def get_centroid(mask):
indices = np.where(mask)
return np.mean(indices[2])
centroid1 = get_centroid(region1_mask)
centroid2 = get_centroid(region2_mask)
if centroid1 > centroid2:
right = region1_mask
left = region2_mask
else:
right = region2_mask
left = region1_mask
return right.astype(np.uint8), left.astype(np.uint8)
def evaluate_case(gt_path, pred_path, labels):
"""Evaluate a single case"""
print(f"\nTest file: {os.path.basename(pred_path)}")
start_time = time.time()
gt_img = load_nifti(gt_path)
pred_img = load_nifti(pred_path)
load_time = time.time() - start_time
print(f"Image size: {gt_img.shape}")
print(f"Loading time: {load_time:.2f}s")
results = {}
total_time = 0
# Process eyes
start_time = time.time()
combined_gt_eye = ((gt_img == 2) | (gt_img == 3)).astype(np.uint8)
combined_pred_eye = ((pred_img == 2) | (pred_img == 3)).astype(np.uint8)
combined_pred_eye_sitk = sitk.GetImageFromArray(combined_pred_eye)
closing_filter = sitk.BinaryMorphologicalClosingImageFilter()
closing_filter.SetKernelRadius(2)
processed_pred_eye = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_eye_sitk))
eye_dice = compute_dice(processed_pred_eye, combined_gt_eye)
eye_time = time.time() - start_time
total_time += eye_time
results['combined_eye'] = eye_dice
print(f"Eyes combined computation time: {eye_time:.2f}s, Dice: {eye_dice:.4f}")
right_pred_eye, left_pred_eye = separate_structures(processed_pred_eye, "Eyes")
right_gt_eye, left_gt_eye = separate_structures(combined_gt_eye, "Eyes")
# Process optic nerves
start_time = time.time()
combined_gt_nerve = ((gt_img == 5) | (gt_img == 6)).astype(np.uint8)
combined_pred_nerve = ((pred_img == 5) | (pred_img == 6)).astype(np.uint8)
combined_pred_nerve_sitk = sitk.GetImageFromArray(combined_pred_nerve)
processed_pred_nerve = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_nerve_sitk))
nerve_dice = compute_dice(processed_pred_nerve, combined_gt_nerve)
nerve_time = time.time() - start_time
total_time += nerve_time
results['combined_nerve'] = nerve_dice
print(f"Optic nerves combined computation time: {nerve_time:.2f}s, Dice: {nerve_dice:.4f}")
right_pred_nerve, left_pred_nerve = separate_structures(processed_pred_nerve, "Optic Nerves")
right_gt_nerve, left_gt_nerve = separate_structures(combined_gt_nerve, "Optic Nerves")
for label in labels:
start_time = time.time()
if label == 1: # Brainstem
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 2 and right_pred_eye is not None: # Right Eye
gt_binary = right_gt_eye
pred_binary = right_pred_eye
elif label == 3 and left_pred_eye is not None: # Left Eye
gt_binary = left_gt_eye
pred_binary = left_pred_eye
elif label == 4: # Optic Chiasm
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 5 and right_pred_nerve is not None: # Right Optic Nerve
gt_binary = right_gt_nerve
pred_binary = right_pred_nerve
elif label == 6 and left_pred_nerve is not None: # Left Optic Nerve
gt_binary = left_gt_nerve
pred_binary = left_pred_nerve
else:
continue
try:
dice = compute_dice(pred_binary, gt_binary)
compute_time = time.time() - start_time
total_time += compute_time
results[label] = dice
print(f"Label {label} computation time: {compute_time:.2f}s, Dice: {dice:.4f}")
except Exception as e:
print(f"Error processing label {label}: {str(e)}")
results[label] = np.nan
continue
print(f"Total computation time for this case: {total_time:.2f}s")
return results
def evaluate_validation_fold(gt_base_folder, validation_folder, labels):
"""Evaluate the validation set for a single fold"""
fold_results = {label: [] for label in labels}
fold_results['combined_eye'] = []
fold_results['combined_nerve'] = []
pred_files = sorted([f for f in os.listdir(validation_folder) if f.endswith('.nii.gz')])
for pred_file in tqdm(pred_files, desc=f"Processing {os.path.basename(validation_folder)}"):
patient_id = pred_file.split('.nii.gz')[0]
gt_path = os.path.join(gt_base_folder, f"{patient_id}.nii.gz")
pred_path = os.path.join(validation_folder, pred_file)
if not os.path.exists(gt_path):
print(f"Could not find corresponding ground truth file: {gt_path}")
continue
case_results = evaluate_case(gt_path, pred_path, labels)
for key in case_results:
if not np.isnan(case_results[key]):
if key in fold_results:
fold_results[key].append(case_results[key])
return fold_results
def evaluate_model(gt_folder, model_path, model_name, labels, organ_names, output_dir):
"""Evaluate the validation set for all folds of a model"""
print(f"\nStarting evaluation of model: {model_name}")
all_results = {label: [] for label in labels}
all_results['combined_eye'] = []
all_results['combined_nerve'] = []
fold_results = {}
for fold in range(5):
validation_folder = os.path.join(model_path, f"fold_{fold}", "validation")
if not os.path.exists(validation_folder):
print(f"Could not find fold_{fold} validation folder: {validation_folder}")
continue
print(f"\nProcessing {model_name} - Fold {fold} validation set")
results = evaluate_validation_fold(gt_folder, validation_folder, labels)
fold_results[f"fold_{fold}"] = results
for key in results:
all_results[key].extend(results[key])
summary = {}
for key in all_results:
if all_results[key]:
mean_dice = np.mean(all_results[key])
std_dice = np.std(all_results[key])
summary[key] = {
'mean_dice': float(mean_dice),
'std_dice': float(std_dice),
'n_samples': len(all_results[key])
}
else:
summary[key] = {
'mean_dice': np.nan,
'std_dice': np.nan,
'n_samples': 0
}
model_results = {
'summary': summary,
'fold_results': fold_results
}
output_path = os.path.join(output_dir, 'T1', 'Dice', 'validation')
os.makedirs(output_path, exist_ok=True)
filename = os.path.join(output_path, f'dice_results_{model_name}.json')
with open(filename, 'w') as f:
json.dump(model_results, f, indent=4)
print(f"\nValidation set results for {model_name} saved to {filename}")
print(f"\nValidation set Dice results for {model_name} (average across all folds):")
for label in labels:
if label in summary:
mean = summary[label]['mean_dice']
std = summary[label]['std_dice']
n = summary[label]['n_samples']
organ_name = organ_names.get(label, f"Label_{label}")
if not np.isnan(mean):
print(f"{organ_name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{organ_name}: Unable to compute (n={n})")
print("\nResults for combined structures:")
for key in ['combined_eye', 'combined_nerve']:
if key in summary:
mean = summary[key]['mean_dice']
std = summary[key]['std_dice']
n = summary[key]['n_samples']
name = "Combined Eyes" if key == 'combined_eye' else "Combined Optic Nerves"
if not np.isnan(mean):
print(f"{name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{name}: Unable to compute (n={n})")
return summary
if __name__ == "__main__":
base_dir = "/mnt/1248/onlylian"
gt_folder = os.path.join(base_dir, "nnUNet/nnUNet_raw/Dataset888/labelsTr")
output_dir = base_dir
models = {
'2D': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset888/nnUNetTrainer__nnUNetPlans__2d"),
'3D_fullres': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset888/nnUNetTrainer__nnUNetPlans__3d_fullres"),
'3D_lowres': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset888/nnUNetTrainer__nnUNetPlans__3d_lowres")
}
labels = [1, 2, 3, 4, 5, 6]
organ_names = {
1: "Brainstem",
2: "Right_Eye",
3: "Left_Eye",
4: "Optic_Chiasm",
5: "Right_Optic_Nerve",
6: "Left_Optic_Nerve"
}
print("Available models:")
for i, model_name in enumerate(models.keys(), 1):
print(f"{i}. {model_name}")
choice = input("\nPlease select a model to run (enter 1-3): ")
try:
model_index = int(choice) - 1
model_names = list(models.keys())
selected_model = model_names[model_index]
evaluate_model(gt_folder, models[selected_model], selected_model, labels, organ_names, output_dir)
except (ValueError, IndexError):
print("Invalid choice! Please enter a number between 1 and 3.")

View file

@ -0,0 +1,331 @@
# import nibabel as nib
# import matplotlib.pyplot as plt
# import numpy as np
# import os
# def load_nifti(file_path):
# """載入NIfTI檔案"""
# return nib.load(file_path).get_fdata()
# def create_colored_mask(data):
# """創建彩色遮罩"""
# color_dict = {
# 1: [144/255, 238/255, 144/255], # Brainstem - 綠色
# 2: [255/255, 218/255, 150/255], # Right_Eye - 淺黃色
# 3: [205/255, 170/255, 125/255], # Left_Eye - 棕色
# 4: [135/255, 206/255, 235/255], # Optic_Chiasm - 藍色
# 5: [255/255, 99/255, 71/255], # Right_Optic_Nerve - 亮珊瑚紅
# 6: [255/255, 160/255, 122/255] # Left_Optic_Nerve - 淺珊瑚紅
# }
# mask = np.zeros((*data.shape, 4))
# for label, color in color_dict.items():
# organ_mask = (data == label)
# if np.any(organ_mask):
# mask[organ_mask] = [*color, 1.0]
# return mask
# def get_brain_bounds(image):
# """獲取腦部區域的邊界"""
# mask = image > np.percentile(image, 1)
# rows = np.any(mask, axis=1)
# cols = np.any(mask, axis=0)
# rmin, rmax = np.where(rows)[0][[0, -1]]
# cmin, cmax = np.where(cols)[0][[0, -1]]
# center_r = (rmin + rmax) // 2
# center_c = (cmin + cmax) // 2
# radius = int(max(rmax - rmin, cmax - cmin) * 0.55)
# margin = int(radius * 0.15)
# rmin = max(center_r - radius - margin, 0)
# rmax = min(center_r + radius + margin, image.shape[0])
# cmin = max(center_c - radius - margin, 0)
# cmax = min(center_c + radius + margin, image.shape[1])
# return rmin, rmax, cmin, cmax
# def check_organs_in_slice(slice_data):
# """檢查切片中存在的器官標籤"""
# unique_labels = set(np.unique(slice_data))
# if 0 in unique_labels:
# unique_labels.remove(0)
# organ_dict = {
# 1: "Brainstem",
# 2: "Right_Eye",
# 3: "Left_Eye",
# 4: "Optic_Chiasm",
# 5: "Right_Optic_Nerve",
# 6: "Left_Optic_Nerve"
# }
# return {organ_dict[label] for label in unique_labels if label in organ_dict}
# def save_slice(image, ground_truth, prediction, slice_num, save_path):
# """保存指定切片的比較圖"""
# plt.clf()
# fig, axes = plt.subplots(1, 3, figsize=(30, 10))
# plt.subplots_adjust(wspace=0.01)
# for ax in axes:
# ax.axis('off')
# img_slice = np.rot90(image[:, :, slice_num])
# rmin, rmax, cmin, cmax = get_brain_bounds(img_slice)
# # 調整圖像對比度
# p2, p98 = np.percentile(img_slice, (2, 98))
# img_slice = np.clip(img_slice, p2, p98)
# img_slice = (img_slice - p2) / (p98 - p2)
# # 原始圖像
# axes[0].imshow(img_slice[rmin:rmax, cmin:cmax], cmap='gray')
# axes[0].set_title('Original Image', fontsize=32, pad=20, weight='bold')
# # Ground Truth
# gt_slice = np.rot90(ground_truth[:, :, slice_num])
# gt_mask = create_colored_mask(gt_slice)
# axes[1].imshow(img_slice[rmin:rmax, cmin:cmax], cmap='gray', zorder=1)
# axes[1].imshow(gt_mask[rmin:rmax, cmin:cmax], zorder=2)
# axes[1].set_title('Ground Truth', fontsize=32, pad=20, weight='bold')
# # Prediction
# pred_slice = np.rot90(prediction[:, :, slice_num])
# pred_mask = create_colored_mask(pred_slice)
# axes[2].imshow(img_slice[rmin:rmax, cmin:cmax], cmap='gray', zorder=1)
# axes[2].imshow(pred_mask[rmin:rmax, cmin:cmax], zorder=2)
# axes[2].set_title('Prediction', fontsize=32, pad=20, weight='bold')
# plt.savefig(save_path, bbox_inches='tight', dpi=300, pad_inches=0.05)
# plt.close()
# print(f"已保存切片 {slice_num} 至: {save_path}")
# if __name__ == "__main__":
# base_dir = "/mnt/1248/onlylian"
# image_path = os.path.join(base_dir, "nnUNet/nnUNet_raw/Dataset888/imagesTs/T1_069_0000.nii.gz")
# gt_path = os.path.join(base_dir, "nnUNet/nnUNet_raw/Dataset888/labelsTs/T1_069.nii.gz")
# pred_path = os.path.join(base_dir, "T1/output_predictions/T1_069.nii.gz")
# save_dir = os.path.join(base_dir, "T1")
# os.makedirs(save_dir, exist_ok=True)
# print("正在載入影像...")
# image = load_nifti(image_path)
# ground_truth = load_nifti(gt_path)
# prediction = load_nifti(pred_path)
# print("影像載入完成")
# print("\n分析切片中的器官...")
# total_slices = image.shape[2]
# for slice_num in range(total_slices):
# gt_slice = ground_truth[:, :, slice_num]
# pred_slice = prediction[:, :, slice_num]
# gt_organs = check_organs_in_slice(gt_slice)
# pred_organs = check_organs_in_slice(pred_slice)
# if gt_organs or pred_organs:
# print(f"\n切片 {slice_num}:")
# print(f"Ground Truth 包含: {', '.join(sorted(gt_organs))}")
# print(f"Prediction 包含: {', '.join(sorted(pred_organs))}")
# print("\n開始切片選擇和保存過程...")
# while True:
# try:
# slice_num = int(input("\n請輸入要保存的切片編號 (-1 退出): "))
# if slice_num == -1:
# print("程序結束")
# break
# if 0 <= slice_num < total_slices:
# save_path = os.path.join(save_dir, f'comparison_T1_069_{slice_num:03d}.png')
# save_slice(image, ground_truth, prediction, slice_num, save_path)
# else:
# print(f"切片編號必須在 0 到 {total_slices-1} 之間")
# except ValueError:
# print("請輸入有效的數字")
# except Exception as e:
# print(f"發生錯誤: {str(e)}")
import nibabel as nib
import matplotlib.pyplot as plt
import numpy as np
import os
def load_nifti(file_path):
"""載入NIfTI檔案"""
return nib.load(file_path).get_fdata()
def create_colored_mask(data):
"""創建彩色遮罩"""
color_dict = {
1: [144/255, 238/255, 144/255], # Brainstem - 綠色
2: [255/255, 218/255, 150/255], # Right_Eye - 淺黃色
3: [205/255, 170/255, 125/255], # Left_Eye - 棕色
4: [135/255, 206/255, 235/255], # Optic_Chiasm - 藍色
5: [255/255, 99/255, 71/255], # Right_Optic_Nerve - 亮珊瑚紅
6: [255/255, 160/255, 122/255] # Left_Optic_Nerve - 淺珊瑚紅
}
mask = np.zeros((*data.shape, 4))
for label, color in color_dict.items():
organ_mask = (data == label)
if np.any(organ_mask):
mask[organ_mask] = [*color, 1.0]
return mask
def get_brain_bounds(image):
"""獲取腦部區域的邊界,並調整顯示位置以避開底部基架"""
# 基本閾值處理
mask = image > np.percentile(image, 1)
# 獲取非零區域的邊界
rows = np.any(mask, axis=1)
cols = np.any(mask, axis=0)
# 確保有足夠的點
if not np.any(rows) or not np.any(cols):
return 0, image.shape[0], 0, image.shape[1]
row_indices = np.where(rows)[0]
col_indices = np.where(cols)[0]
# 獲取初始邊界
rmin, rmax = row_indices[[0, -1]]
cmin, cmax = col_indices[[0, -1]]
# 計算中心點,但略微上移以避開基架
center_r = (rmin + rmax) // 2 - int(image.shape[0] * 0.15) # 上移15%
center_c = (cmin + cmax) // 2
# 計算顯示範圍
height = image.shape[0]
width = image.shape[1]
display_size = int(min(height, width) * 0.7) # 使用70%的圖像大小
# 設置新的邊界,確保不會超出圖像範圍
rmin = max(center_r - display_size//2, 0)
rmax = min(center_r + display_size//2, height - int(height * 0.2)) # 留出底部空間
cmin = max(center_c - display_size//2, 0)
cmax = min(center_c + display_size//2, width)
# 如果上邊界太小,適當下移整個顯示區域
if rmin < height * 0.1:
shift = int(height * 0.1) - rmin
rmin += shift
rmax += shift
return rmin, rmax, cmin, cmax
def apply_brain_window(image, window_width=80, window_level=40):
"""應用brain window設置"""
window_min = window_level - window_width/2
window_max = window_level + window_width/2
windowed = np.clip(image, window_min, window_max)
windowed = (windowed - window_min) / (window_max - window_min)
return windowed
def check_organs_in_slice(slice_data):
"""檢查切片中存在的器官標籤"""
unique_labels = set(np.unique(slice_data))
if 0 in unique_labels:
unique_labels.remove(0)
organ_dict = {
1: "Brainstem",
2: "Right_Eye",
3: "Left_Eye",
4: "Optic_Chiasm",
5: "Right_Optic_Nerve",
6: "Left_Optic_Nerve"
}
return {organ_dict[label] for label in unique_labels if label in organ_dict}
def save_slice(image, ground_truth, prediction, slice_num, save_path):
"""保存指定切片的比較圖"""
plt.clf()
fig, axes = plt.subplots(1, 3, figsize=(24, 8))
plt.subplots_adjust(wspace=0.01)
for ax in axes:
ax.axis('off')
img_slice = np.rot90(image[:, :, slice_num])
rmin, rmax, cmin, cmax = get_brain_bounds(img_slice)
# 使用brain window設置
img_slice = apply_brain_window(img_slice)
# 原始圖像
axes[0].imshow(img_slice[rmin:rmax, cmin:cmax], cmap='gray')
axes[0].set_title('Original Image', fontsize=32, pad=20, weight='bold')
# Ground Truth
gt_slice = np.rot90(ground_truth[:, :, slice_num])
gt_mask = create_colored_mask(gt_slice)
axes[1].imshow(img_slice[rmin:rmax, cmin:cmax], cmap='gray', zorder=1)
axes[1].imshow(gt_mask[rmin:rmax, cmin:cmax], zorder=2)
axes[1].set_title('Ground Truth', fontsize=32, pad=20, weight='bold')
# Prediction
pred_slice = np.rot90(prediction[:, :, slice_num])
pred_mask = create_colored_mask(pred_slice)
axes[2].imshow(img_slice[rmin:rmax, cmin:cmax], cmap='gray', zorder=1)
axes[2].imshow(pred_mask[rmin:rmax, cmin:cmax], zorder=2)
axes[2].set_title('Prediction', fontsize=32, pad=20, weight='bold')
plt.savefig(save_path, bbox_inches='tight', dpi=300, pad_inches=0.05)
plt.close()
print(f"已保存切片 {slice_num} 至: {save_path}")
if __name__ == "__main__":
base_dir = "/mnt/1248/onlylian"
image_path = os.path.join(base_dir, "nnUNet/nnUNet_raw/Dataset888/imagesTs/T1_069_0000.nii.gz")
gt_path = os.path.join(base_dir, "nnUNet/nnUNet_raw/Dataset888/labelsTs/T1_069.nii.gz")
pred_path = os.path.join(base_dir, "T1/output_predictions/T1_069.nii.gz")
save_dir = os.path.join(base_dir, "T1")
os.makedirs(save_dir, exist_ok=True)
print("正在載入影像...")
image = load_nifti(image_path)
ground_truth = load_nifti(gt_path)
prediction = load_nifti(pred_path)
print("影像載入完成")
print("\n分析切片中的器官...")
total_slices = image.shape[2]
for slice_num in range(total_slices):
gt_slice = ground_truth[:, :, slice_num]
pred_slice = prediction[:, :, slice_num]
gt_organs = check_organs_in_slice(gt_slice)
pred_organs = check_organs_in_slice(pred_slice)
if gt_organs or pred_organs:
print(f"\n切片 {slice_num}:")
print(f"Ground Truth 包含: {', '.join(sorted(gt_organs))}")
print(f"Prediction 包含: {', '.join(sorted(pred_organs))}")
print("\n開始切片選擇和保存過程...")
while True:
try:
slice_num = int(input("\n請輸入要保存的切片編號 (-1 退出): "))
if slice_num == -1:
print("程序結束")
break
if 0 <= slice_num < total_slices:
save_path = os.path.join(save_dir, f'comparison_T1_069_{slice_num:03d}.png')
save_slice(image, ground_truth, prediction, slice_num, save_path)
else:
print(f"切片編號必須在 0 到 {total_slices-1} 之間")
except ValueError:
print("請輸入有效的數字")
except Exception as e:
print(f"發生錯誤: {str(e)}")

View file

@ -0,0 +1,143 @@
import json
import os
import numpy as np
import matplotlib.pyplot as plt
from typing import Dict, Tuple
# 設置中文字體支持
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
def load_data(file_path: str) -> Dict:
"""讀取JSON數據文件"""
try:
with open(file_path, 'r') as file:
return json.load(file)
except FileNotFoundError:
print(f"文件未找到: {file_path}")
return {}
except json.JSONDecodeError:
print(f"JSON解析錯誤: {file_path}")
return {}
def extract_scores(data: Dict, model: str, organ_id: str) -> Tuple[float, float]:
"""從數據中提取特定器官的分數"""
if "summary" not in data or organ_id not in data["summary"]:
return (0, 0)
scores = data["summary"][organ_id]
if model == '3D_fullres':
mean_dice = scores.get("mean_dice", 0)
std_dice = scores.get("std_dice", 0)
else:
mean_dice = scores.get("mean_dice", scores.get("mean", 0))
std_dice = scores.get("std_dice", scores.get("std", 0))
return (mean_dice, std_dice)
def create_plot(model_averages: Dict, model_stds: Dict, organs: Dict, output_path: str):
"""創建和保存圖表"""
plt.figure(figsize=(16, 5)) # Reduced figure size
plt.title('T1C', fontsize=12, pad=10) # Changed title
plt.grid(True, axis='y', linestyle='--', alpha=0.7)
x = np.arange(len(organs)) * 1.5 # Reduced spacing between groups
width = 0.35 # Reduced bar width
colors = {
'2D': 'royalblue',
'3D_fullres': 'limegreen',
'3D_lowres': 'orange'
}
positions = {
'2D': x - width,
'3D_fullres': x,
'3D_lowres': x + width
}
for model, averages in model_averages.items():
bars = plt.bar(positions[model],
[averages[organ] * 100 for organ in organs.keys()],
width,
label=model,
color=colors[model])
# 調整數值標籤位置和大小
for j, bar in enumerate(bars):
height = bar.get_height()
organ = list(organs.keys())[j]
std = model_stds[model][organ] * 100
plt.text(bar.get_x() + bar.get_width()/2., height,
f'{height:.1f}±{std:.1f}%',
ha='center',
va='bottom',
fontsize=6, # Reduced font size
rotation=0) # Optional: rotate text if still crowded
plt.ylim(0, 100)
plt.ylabel('DC (%)', fontsize=10)
plt.xlabel('OAR', fontsize=10)
plt.xticks(x, [organs[k] for k in organs.keys()], rotation=30, ha='right', fontsize=8)
plt.legend(loc='upper right', fontsize=8)
plt.tight_layout()
os.makedirs(os.path.dirname(output_path), exist_ok=True)
plt.savefig(output_path, dpi=300, bbox_inches='tight')
plt.close()
def print_results(model_averages: Dict, model_stds: Dict, organs: Dict):
"""打印數值結果"""
print("\n平均 Dice 分數 (百分比):")
for model in model_averages.keys():
print(f"\n{model}:")
for organ in organs.keys():
avg = model_averages[model][organ] * 100
std = model_stds[model][organ] * 100
print(f"{organs[organ]}: {avg:.1f}±{std:.1f}%")
def main():
# 定義路徑和器官映射
base_path = '/mnt/1248/onlylian/T1C/Dice/test'
paths = {
'2D': os.path.join(base_path, 'T1C_test_dice_results_2D.json'),
'3D_fullres': os.path.join(base_path, 'T1C_test_dice_results_3D_fullres.json'),
'3D_lowres': os.path.join(base_path, 'T1C_test_dice_results_3D_lowres.json')
}
organs = {
"1": "Brainstem",
"2": "Right_Eye",
"3": "Left_Eye",
"4": "Optic_Chiasm",
"5": "Right_Optic_Nerve",
"6": "Left_Optic_Nerve"
}
# 初始化數據存儲
model_dice_scores = {
model: {organ: None for organ in organs.keys()}
for model in paths.keys()
}
# 讀取和處理數據
for model, path in paths.items():
data = load_data(path)
for organ_id in organs.keys():
model_dice_scores[model][organ_id] = extract_scores(data, model, organ_id)
# 計算平均值和標準差
model_averages = {}
model_stds = {}
for model, scores in model_dice_scores.items():
model_averages[model] = {organ: scores[organ][0] if scores[organ] else 0 for organ in organs.keys()}
model_stds[model] = {organ: scores[organ][1] if scores[organ] else 0 for organ in organs.keys()}
# 創建圖表並保存
output_path = os.path.join(base_path, 'T1C_test_dice_scores_comparison.png')
create_plot(model_averages, model_stds, organs, output_path)
# 打印結果
print_results(model_averages, model_stds, organs)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,282 @@
import json
import os
import numpy as np
import SimpleITK as sitk
from tqdm import tqdm
import time
def load_nifti(file_path):
"""加載 NIfTI 文件"""
return sitk.GetArrayFromImage(sitk.ReadImage(file_path))
def compute_dice(pred, gt):
"""計算Dice係數"""
intersection = np.sum(pred & gt)
sum_pred = np.sum(pred)
sum_gt = np.sum(gt)
if sum_pred == 0 and sum_gt == 0:
return 1.0
elif sum_pred == 0 or sum_gt == 0:
return 0.0
return 2.0 * intersection / (sum_pred + sum_gt)
def separate_structures(combined_mask, structure_name=""):
"""分離左右結構"""
sitk_image = sitk.GetImageFromArray(combined_mask)
labeled_components = sitk.ConnectedComponent(sitk_image)
labeled_array = sitk.GetArrayFromImage(labeled_components)
unique, counts = np.unique(labeled_array, return_counts=True)
if len(unique) < 3:
print(f"警告:{structure_name} 未找到足夠的連通區域")
return None, None
region_sizes = [(i, count) for i, count in zip(unique[1:], counts[1:])]
sorted_regions = sorted(region_sizes, key=lambda x: x[1], reverse=True)
if len(sorted_regions) < 2:
print(f"警告:{structure_name} 未找到兩個主要區域")
return None, None
region1_mask = (labeled_array == sorted_regions[0][0])
region2_mask = (labeled_array == sorted_regions[1][0])
def get_centroid(mask):
indices = np.where(mask)
return np.mean(indices[2])
centroid1 = get_centroid(region1_mask)
centroid2 = get_centroid(region2_mask)
if centroid1 > centroid2:
right = region1_mask
left = region2_mask
else:
right = region2_mask
left = region1_mask
return right.astype(np.uint8), left.astype(np.uint8)
def evaluate_case(gt_path, pred_path, labels):
"""評估單個案例"""
print(f"\n測試文件: {os.path.basename(pred_path)}")
start_time = time.time()
gt_img = load_nifti(gt_path)
pred_img = load_nifti(pred_path)
load_time = time.time() - start_time
print(f"圖像大小: {gt_img.shape}")
print(f"加載時間: {load_time:.2f}")
results = {}
total_time = 0
# 處理眼睛
start_time = time.time()
combined_gt_eye = ((gt_img == 2) | (gt_img == 3)).astype(np.uint8)
combined_pred_eye = ((pred_img == 2) | (pred_img == 3)).astype(np.uint8)
combined_pred_eye_sitk = sitk.GetImageFromArray(combined_pred_eye)
closing_filter = sitk.BinaryMorphologicalClosingImageFilter()
closing_filter.SetKernelRadius(2)
processed_pred_eye = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_eye_sitk))
eye_dice = compute_dice(processed_pred_eye, combined_gt_eye)
eye_time = time.time() - start_time
total_time += eye_time
results['combined_eye'] = eye_dice
print(f"眼睛合併計算時間: {eye_time:.2f}秒, Dice: {eye_dice:.4f}")
right_pred_eye, left_pred_eye = separate_structures(processed_pred_eye, "眼睛")
right_gt_eye, left_gt_eye = separate_structures(combined_gt_eye, "眼睛")
# 處理視神經
start_time = time.time()
combined_gt_nerve = ((gt_img == 5) | (gt_img == 6)).astype(np.uint8)
combined_pred_nerve = ((pred_img == 5) | (pred_img == 6)).astype(np.uint8)
combined_pred_nerve_sitk = sitk.GetImageFromArray(combined_pred_nerve)
processed_pred_nerve = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_nerve_sitk))
nerve_dice = compute_dice(processed_pred_nerve, combined_gt_nerve)
nerve_time = time.time() - start_time
total_time += nerve_time
results['combined_nerve'] = nerve_dice
print(f"視神經合併計算時間: {nerve_time:.2f}秒, Dice: {nerve_dice:.4f}")
right_pred_nerve, left_pred_nerve = separate_structures(processed_pred_nerve, "視神經")
right_gt_nerve, left_gt_nerve = separate_structures(combined_gt_nerve, "視神經")
for label in labels:
start_time = time.time()
if label == 1: # Brainstem
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 2 and right_pred_eye is not None: # Right Eye
gt_binary = right_gt_eye
pred_binary = right_pred_eye
elif label == 3 and left_pred_eye is not None: # Left Eye
gt_binary = left_gt_eye
pred_binary = left_pred_eye
elif label == 4: # Optic Chiasm
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 5 and right_pred_nerve is not None: # Right Optic Nerve
gt_binary = right_gt_nerve
pred_binary = right_pred_nerve
elif label == 6 and left_pred_nerve is not None: # Left Optic Nerve
gt_binary = left_gt_nerve
pred_binary = left_pred_nerve
else:
continue
try:
dice = compute_dice(pred_binary, gt_binary)
compute_time = time.time() - start_time
total_time += compute_time
results[label] = dice
print(f"標籤 {label} 計算時間: {compute_time:.2f}秒, Dice: {dice:.4f}")
except Exception as e:
print(f"處理標籤 {label} 時出錯:{str(e)}")
results[label] = np.nan
continue
print(f"單個案例總計算時間: {total_time:.2f}")
return results
def evaluate_fold(gt_folder, pred_folder, labels):
"""評估單個fold的所有案例"""
fold_results = {label: [] for label in labels}
fold_results['combined_eye'] = []
fold_results['combined_nerve'] = []
pred_files = sorted([f for f in os.listdir(pred_folder) if f.endswith('.nii.gz')])
for pred_file in tqdm(pred_files, desc=f"Processing {os.path.basename(pred_folder)}"):
gt_path = os.path.join(gt_folder, pred_file)
pred_path = os.path.join(pred_folder, pred_file)
if not os.path.exists(gt_path):
print(f"找不到對應的真實標籤文件:{gt_path}")
continue
case_results = evaluate_case(gt_path, pred_path, labels)
for key in case_results:
if not np.isnan(case_results[key]):
if key in fold_results:
fold_results[key].append(case_results[key])
return fold_results
def evaluate_model(gt_folder, base_pred_folder, model_name, labels, organ_names):
"""評估單個模型的所有fold"""
print(f"\n開始評估模型:{model_name}")
all_results = {label: [] for label in labels}
all_results['combined_eye'] = []
all_results['combined_nerve'] = []
fold_results = {}
for fold in range(5):
print(f"\n處理 {model_name} - Fold {fold}")
pred_folder = os.path.join(base_pred_folder, f"fold_{fold}")
if not os.path.exists(pred_folder):
print(f"找不到預測文件夾:{pred_folder}")
continue
results = evaluate_fold(gt_folder, pred_folder, labels)
fold_results[f"fold_{fold}"] = results
for key in results:
all_results[key].extend(results[key])
summary = {}
for key in all_results:
if all_results[key]:
mean_dice = np.mean(all_results[key])
std_dice = np.std(all_results[key])
summary[key] = {
'mean_dice': float(mean_dice),
'std_dice': float(std_dice),
'n_samples': len(all_results[key])
}
else:
summary[key] = {
'mean_dice': np.nan,
'std_dice': np.nan,
'n_samples': 0
}
model_results = {
'summary': summary,
'fold_results': fold_results
}
save_dir = "/mnt/1248/onlylian/T1C/Dice/test"
os.makedirs(save_dir, exist_ok=True)
filename = os.path.join(save_dir, f'T1C_test_dice_results_{model_name}.json')
with open(filename, 'w') as f:
json.dump(model_results, f, indent=4)
print(f"\n{model_name}結果已保存到 {filename}")
print(f"\n{model_name} 模型的 Dice 結果所有fold的平均值:")
for label in labels:
if label in summary:
mean = summary[label]['mean_dice']
std = summary[label]['std_dice']
n = summary[label]['n_samples']
organ_name = organ_names.get(label, f"Label_{label}")
if not np.isnan(mean):
print(f"{organ_name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{organ_name}: 無法計算 (n={n})")
print("\n合併結構的結果:")
for key in ['combined_eye', 'combined_nerve']:
if key in summary:
mean = summary[key]['mean_dice']
std = summary[key]['std_dice']
n = summary[key]['n_samples']
name = "合併眼睛" if key == 'combined_eye' else "合併視神經"
if not np.isnan(mean):
print(f"{name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{name}: 無法計算 (n={n})")
return summary
if __name__ == "__main__":
gt_folder = "/mnt/1248/onlylian/nnUNet/nnUNet_raw/Dataset999/labelsTs"
models = {
'2D': '/mnt/1248/onlylian/T1C/output_predictions/2d',
'3D_fullres': '/mnt/1248/onlylian/T1C/output_predictions/3d_fullres',
'3D_lowres': '/mnt/1248/onlylian/T1C/output_predictions/3d_lowres'
}
labels = [1, 2, 3, 4, 5, 6]
organ_names = {
1: "Brainstem",
2: "Right_Eye",
3: "Left_Eye",
4: "Optic_Chiasm",
5: "Right_Optic_Nerve",
6: "Left_Optic_Nerve"
}
print("可用的模型:")
for i, model_name in enumerate(models.keys(), 1):
print(f"{i}. {model_name}")
choice = input("\n請選擇要運行的模型 (輸入數字1-3): ")
try:
model_index = int(choice) - 1
model_names = list(models.keys())
selected_model = model_names[model_index]
evaluate_model(gt_folder, models[selected_model], selected_model, labels, organ_names)
except (ValueError, IndexError):
print("無效的選擇請輸入1-3之間的數字。")

View file

@ -0,0 +1,142 @@
import json
import os
import numpy as np
import matplotlib.pyplot as plt
from typing import Dict, Tuple
# 設置中文字體支持
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
def load_data(file_path: str) -> Dict:
"""讀取JSON數據文件"""
try:
with open(file_path, 'r') as file:
return json.load(file)
except FileNotFoundError:
print(f"文件未找到: {file_path}")
return {}
except json.JSONDecodeError:
print(f"JSON解析錯誤: {file_path}")
return {}
def extract_scores(data: Dict, model: str, organ_id: str) -> Tuple[float, float]:
"""從數據中提取特定器官的分數"""
if "summary" not in data or organ_id not in data["summary"]:
return (0, 0)
scores = data["summary"][organ_id]
if model == '3D_fullres':
mean_dice = scores.get("mean_dice", 0)
std_dice = scores.get("std_dice", 0)
else:
mean_dice = scores.get("mean_dice", scores.get("mean", 0))
std_dice = scores.get("std_dice", scores.get("std", 0))
return (mean_dice, std_dice)
def create_plot(model_averages: Dict, model_stds: Dict, organs: Dict, output_path: str):
"""創建和保存圖表"""
plt.figure(figsize=(16, 5)) # Reduced figure size
plt.title('T1C', fontsize=12, pad=10) # Changed title from T1 to T1C
plt.grid(True, axis='y', linestyle='--', alpha=0.7)
x = np.arange(len(organs)) * 1.5 # Reduced spacing between groups
width = 0.35 # Reduced bar width
colors = {
'2D': 'royalblue',
'3D_fullres': 'limegreen',
'3D_lowres': 'orange'
}
positions = {
'2D': x - width,
'3D_fullres': x,
'3D_lowres': x + width
}
for model, averages in model_averages.items():
bars = plt.bar(positions[model],
[averages[organ] * 100 for organ in organs.keys()],
width,
label=model,
color=colors[model])
# 添加數值標籤
for j, bar in enumerate(bars):
height = bar.get_height()
organ = list(organs.keys())[j]
std = model_stds[model][organ] * 100
plt.text(bar.get_x() + bar.get_width()/2., height + 0.5,
f'{height:.1f}±{std:.1f}%',
ha='center',
va='bottom',
fontsize=6) # Reduced font size
plt.ylim(0, 100)
plt.ylabel('DC (%)', fontsize=10)
plt.xlabel('OAR', fontsize=10)
plt.xticks(x, [organs[k] for k in organs.keys()], rotation=30, ha='right', fontsize=8)
plt.legend(loc='upper right', fontsize=8)
plt.tight_layout()
os.makedirs(os.path.dirname(output_path), exist_ok=True)
plt.savefig(output_path, dpi=300, bbox_inches='tight')
plt.close()
def print_results(model_averages: Dict, model_stds: Dict, organs: Dict):
"""打印數值結果"""
print("\n平均 Dice 分數 (百分比):")
for model in model_averages.keys():
print(f"\n{model}:")
for organ in organs.keys():
avg = model_averages[model][organ] * 100
std = model_stds[model][organ] * 100
print(f"{organs[organ]}: {avg:.1f}±{std:.1f}%")
def main():
# 定義路徑和器官映射
base_path = '/mnt/1248/onlylian/T1C/Dice/validation' # Changed from T1 to T1C
paths = {
'2D': os.path.join(base_path, 'T1C_validation_dice_results_2D.json'), # Changed T1 to T1C
'3D_fullres': os.path.join(base_path, 'T1C_validation_dice_results_3D_fullres.json'), # Changed T1 to T1C
'3D_lowres': os.path.join(base_path, 'T1C_validation_dice_results_3D_lowres.json') # Changed T1 to T1C
}
organs = {
"1": "Brainstem",
"2": "Right_Eye",
"3": "Left_Eye",
"4": "Optic_Chiasm",
"5": "Right_Optic_Nerve",
"6": "Left_Optic_Nerve"
}
# 初始化數據存儲
model_dice_scores = {
model: {organ: None for organ in organs.keys()}
for model in paths.keys()
}
# 讀取和處理數據
for model, path in paths.items():
data = load_data(path)
for organ_id in organs.keys():
model_dice_scores[model][organ_id] = extract_scores(data, model, organ_id)
# 計算平均值和標準差
model_averages = {}
model_stds = {}
for model, scores in model_dice_scores.items():
model_averages[model] = {organ: scores[organ][0] if scores[organ] else 0 for organ in organs.keys()}
model_stds[model] = {organ: scores[organ][1] if scores[organ] else 0 for organ in organs.keys()}
# 創建圖表並保存
output_path = os.path.join(base_path, 'T1C_validation_dice_scores_comparison.png') # Changed T1 to T1C
create_plot(model_averages, model_stds, organs, output_path)
# 打印結果
print_results(model_averages, model_stds, organs)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,288 @@
import os
import time
import numpy as np
import SimpleITK as sitk
from tqdm import tqdm
import json
def load_nifti(file_path):
"""加載 NIfTI 文件"""
return sitk.GetArrayFromImage(sitk.ReadImage(file_path))
def compute_dice(pred, gt):
"""計算Dice係數"""
intersection = np.sum(pred & gt)
sum_pred = np.sum(pred)
sum_gt = np.sum(gt)
if sum_pred == 0 and sum_gt == 0:
return 1.0
elif sum_pred == 0 or sum_gt == 0:
return 0.0
return 2.0 * intersection / (sum_pred + sum_gt)
def separate_structures(combined_mask, structure_name=""):
"""分離左右結構"""
sitk_image = sitk.GetImageFromArray(combined_mask)
labeled_components = sitk.ConnectedComponent(sitk_image)
labeled_array = sitk.GetArrayFromImage(labeled_components)
unique, counts = np.unique(labeled_array, return_counts=True)
if len(unique) < 3:
print(f"警告:{structure_name} 未找到足夠的連通區域")
return None, None
region_sizes = [(i, count) for i, count in zip(unique[1:], counts[1:])]
sorted_regions = sorted(region_sizes, key=lambda x: x[1], reverse=True)
if len(sorted_regions) < 2:
print(f"警告:{structure_name} 未找到兩個主要區域")
return None, None
region1_mask = (labeled_array == sorted_regions[0][0])
region2_mask = (labeled_array == sorted_regions[1][0])
def get_centroid(mask):
indices = np.where(mask)
return np.mean(indices[2])
centroid1 = get_centroid(region1_mask)
centroid2 = get_centroid(region2_mask)
if centroid1 > centroid2:
right = region1_mask
left = region2_mask
else:
right = region2_mask
left = region1_mask
return right.astype(np.uint8), left.astype(np.uint8)
def evaluate_case(gt_path, pred_path, labels):
"""評估單個案例"""
print(f"\n測試文件: {os.path.basename(pred_path)}")
start_time = time.time()
gt_img = load_nifti(gt_path)
pred_img = load_nifti(pred_path)
load_time = time.time() - start_time
print(f"圖像大小: {gt_img.shape}")
print(f"加載時間: {load_time:.2f}")
results = {}
total_time = 0
# 處理眼睛
start_time = time.time()
combined_gt_eye = ((gt_img == 2) | (gt_img == 3)).astype(np.uint8)
combined_pred_eye = ((pred_img == 2) | (pred_img == 3)).astype(np.uint8)
combined_pred_eye_sitk = sitk.GetImageFromArray(combined_pred_eye)
closing_filter = sitk.BinaryMorphologicalClosingImageFilter()
closing_filter.SetKernelRadius(2)
processed_pred_eye = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_eye_sitk))
eye_dice = compute_dice(processed_pred_eye, combined_gt_eye)
eye_time = time.time() - start_time
total_time += eye_time
results['combined_eye'] = eye_dice
print(f"眼睛合併計算時間: {eye_time:.2f}秒, Dice: {eye_dice:.4f}")
right_pred_eye, left_pred_eye = separate_structures(processed_pred_eye, "眼睛")
right_gt_eye, left_gt_eye = separate_structures(combined_gt_eye, "眼睛")
# 處理視神經
start_time = time.time()
combined_gt_nerve = ((gt_img == 5) | (gt_img == 6)).astype(np.uint8)
combined_pred_nerve = ((pred_img == 5) | (pred_img == 6)).astype(np.uint8)
combined_pred_nerve_sitk = sitk.GetImageFromArray(combined_pred_nerve)
processed_pred_nerve = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_nerve_sitk))
nerve_dice = compute_dice(processed_pred_nerve, combined_gt_nerve)
nerve_time = time.time() - start_time
total_time += nerve_time
results['combined_nerve'] = nerve_dice
print(f"視神經合併計算時間: {nerve_time:.2f}秒, Dice: {nerve_dice:.4f}")
right_pred_nerve, left_pred_nerve = separate_structures(processed_pred_nerve, "視神經")
right_gt_nerve, left_gt_nerve = separate_structures(combined_gt_nerve, "視神經")
for label in labels:
start_time = time.time()
if label == 1: # Brainstem
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 2 and right_pred_eye is not None: # Right Eye
gt_binary = right_gt_eye
pred_binary = right_pred_eye
elif label == 3 and left_pred_eye is not None: # Left Eye
gt_binary = left_gt_eye
pred_binary = left_pred_eye
elif label == 4: # Optic Chiasm
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 5 and right_pred_nerve is not None: # Right Optic Nerve
gt_binary = right_gt_nerve
pred_binary = right_pred_nerve
elif label == 6 and left_pred_nerve is not None: # Left Optic Nerve
gt_binary = left_gt_nerve
pred_binary = left_pred_nerve
else:
continue
try:
dice = compute_dice(pred_binary, gt_binary)
compute_time = time.time() - start_time
total_time += compute_time
results[label] = dice
print(f"標籤 {label} 計算時間: {compute_time:.2f}秒, Dice: {dice:.4f}")
except Exception as e:
print(f"處理標籤 {label} 時出錯:{str(e)}")
results[label] = np.nan
continue
print(f"單個案例總計算時間: {total_time:.2f}")
return results
def evaluate_validation_fold(gt_base_folder, validation_folder, labels):
"""評估單個fold的驗證集"""
fold_results = {label: [] for label in labels}
fold_results['combined_eye'] = []
fold_results['combined_nerve'] = []
pred_files = sorted([f for f in os.listdir(validation_folder) if f.endswith('.nii.gz')])
for pred_file in tqdm(pred_files, desc=f"Processing {os.path.basename(validation_folder)}"):
patient_id = pred_file.split('.nii.gz')[0]
gt_path = os.path.join(gt_base_folder, f"{patient_id}.nii.gz")
pred_path = os.path.join(validation_folder, pred_file)
if not os.path.exists(gt_path):
print(f"找不到對應的真實標籤文件:{gt_path}")
continue
case_results = evaluate_case(gt_path, pred_path, labels)
for key in case_results:
if not np.isnan(case_results[key]):
if key in fold_results:
fold_results[key].append(case_results[key])
return fold_results
def evaluate_model(gt_folder, model_path, model_name, labels, organ_names, output_dir):
"""評估模型所有fold的驗證集"""
print(f"\n開始評估模型:{model_name}")
all_results = {label: [] for label in labels}
all_results['combined_eye'] = []
all_results['combined_nerve'] = []
fold_results = {}
for fold in range(5):
validation_folder = os.path.join(model_path, f"fold_{fold}", "validation")
if not os.path.exists(validation_folder):
print(f"找不到fold_{fold}的驗證資料夾:{validation_folder}")
continue
print(f"\n處理 {model_name} - Fold {fold} 驗證集")
results = evaluate_validation_fold(gt_folder, validation_folder, labels)
fold_results[f"fold_{fold}"] = results
for key in results:
all_results[key].extend(results[key])
summary = {}
for key in all_results:
if all_results[key]:
mean_dice = np.mean(all_results[key])
std_dice = np.std(all_results[key])
summary[key] = {
'mean_dice': float(mean_dice),
'std_dice': float(std_dice),
'n_samples': len(all_results[key])
}
else:
summary[key] = {
'mean_dice': np.nan,
'std_dice': np.nan,
'n_samples': 0
}
model_results = {
'summary': summary,
'fold_results': fold_results
}
output_path = os.path.join(output_dir, 'T1C', 'Dice', 'validation')
os.makedirs(output_path, exist_ok=True)
filename = os.path.join(output_path, f'dice_results_{model_name}.json')
with open(filename, 'w') as f:
json.dump(model_results, f, indent=4)
print(f"\n{model_name}驗證集結果已保存到 {filename}")
print(f"\n{model_name} 模型的驗證集 Dice 結果所有fold的平均值:")
for label in labels:
if label in summary:
mean = summary[label]['mean_dice']
std = summary[label]['std_dice']
n = summary[label]['n_samples']
organ_name = organ_names.get(label, f"Label_{label}")
if not np.isnan(mean):
print(f"{organ_name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{organ_name}: 無法計算 (n={n})")
print("\n合併結構的結果:")
for key in ['combined_eye', 'combined_nerve']:
if key in summary:
mean = summary[key]['mean_dice']
std = summary[key]['std_dice']
n = summary[key]['n_samples']
name = "合併眼睛" if key == 'combined_eye' else "合併視神經"
if not np.isnan(mean):
print(f"{name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{name}: 無法計算 (n={n})")
return summary
if __name__ == "__main__":
base_dir = "/mnt/1248/onlylian"
gt_folder = os.path.join(base_dir, "nnUNet/nnUNet_raw/Dataset999/labelsTr")
output_dir = base_dir
models = {
'2D': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset999/nnUNetTrainer__nnUNetPlans__2d"),
'3D_fullres': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset999/nnUNetTrainer__nnUNetPlans__3d_fullres"),
'3D_lowres': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset999/nnUNetTrainer__nnUNetPlans__3d_lowres")
}
labels = [1, 2, 3, 4, 5, 6]
organ_names = {
1: "Brainstem",
2: "Right_Eye",
3: "Left_Eye",
4: "Optic_Chiasm",
5: "Right_Optic_Nerve",
6: "Left_Optic_Nerve"
}
print("可用的模型:")
for i, model_name in enumerate(models.keys(), 1):
print(f"{i}. {model_name}")
choice = input("\n請選擇要運行的模型 (輸入數字1-3): ")
try:
model_index = int(choice) - 1
model_names = list(models.keys())
selected_model = model_names[model_index]
evaluate_model(gt_folder, models[selected_model], selected_model, labels, organ_names, output_dir)
except (ValueError, IndexError):
print("無效的選擇請輸入1-3之間的數字。")

149
T1C/low_chiasm.py Normal file
View file

@ -0,0 +1,149 @@
import nibabel as nib
import matplotlib.pyplot as plt
import numpy as np
import os
def load_nifti(file_path):
"""載入NIfTI檔案"""
return nib.load(file_path).get_fdata()
def create_colored_mask(data):
"""創建彩色遮罩"""
color_dict = {
1: [144/255, 238/255, 144/255], # Brainstem - 綠色
2: [255/255, 218/255, 150/255], # Right_Eye - 淺黃色
3: [205/255, 170/255, 125/255], # Left_Eye - 棕色
4: [135/255, 206/255, 235/255], # Optic_Chiasm - 藍色
5: [255/255, 99/255, 71/255], # Right_Optic_Nerve - 亮珊瑚紅
6: [255/255, 160/255, 122/255] # Left_Optic_Nerve - 淺珊瑚紅
}
# 創建一個具有透明背景的遮罩
mask = np.zeros((*data.shape, 4)) # 使用4通道RGBA
for label, color in color_dict.items():
# 為每個器官設置顏色,包括完全不透明的 alpha 通道
organ_mask = (data == label)
if np.any(organ_mask): # 只處理存在的器官
mask[organ_mask] = [*color, 1.0] # RGB + alpha
return mask
def get_brain_bounds(image):
"""獲取腦部區域的邊界"""
mask = image > np.percentile(image, 1)
rows = np.any(mask, axis=1)
cols = np.any(mask, axis=0)
rmin, rmax = np.where(rows)[0][[0, -1]]
cmin, cmax = np.where(cols)[0][[0, -1]]
center_r = (rmin + rmax) // 2
center_c = (cmin + cmax) // 2
radius = int(max(rmax - rmin, cmax - cmin) * 0.55)
margin = int(radius * 0.15)
rmin = max(center_r - radius - margin, 0)
rmax = min(center_r + radius + margin, image.shape[0])
cmin = max(center_c - radius - margin, 0)
cmax = min(center_c + radius + margin, image.shape[1])
return rmin, rmax, cmin, cmax
def check_organs_in_slice(slice_data):
"""檢查切片中存在的器官標籤"""
unique_labels = set(np.unique(slice_data))
if 0 in unique_labels:
unique_labels.remove(0)
organ_dict = {
1: "Brainstem",
2: "Right_Eye",
3: "Left_Eye",
4: "Optic_Chiasm",
5: "Right_Optic_Nerve",
6: "Left_Optic_Nerve"
}
return {organ_dict[label] for label in unique_labels if label in organ_dict}
def save_slice(image, ground_truth, prediction, slice_num, save_path):
"""保存指定切片的比較圖"""
plt.clf()
fig, axes = plt.subplots(1, 2, figsize=(25, 11))
plt.subplots_adjust(wspace=0.01)
img_slice = np.rot90(image[:, :, slice_num])
rmin, rmax, cmin, cmax = get_brain_bounds(img_slice)
# 調整圖像對比度以提高可見度
p2, p98 = np.percentile(img_slice, (2, 98))
img_slice = np.clip(img_slice, p2, p98)
img_slice = (img_slice - p2) / (p98 - p2)
# Ground Truth
gt_slice = np.rot90(ground_truth[:, :, slice_num])
gt_mask = create_colored_mask(gt_slice)
# 使用 imshow 的 zorder 參數來控制圖層順序
axes[0].imshow(img_slice[rmin:rmax, cmin:cmax], cmap='gray', zorder=1)
axes[0].imshow(gt_mask[rmin:rmax, cmin:cmax], zorder=2)
axes[0].set_title('Ground Truth', fontsize=32, pad=20, weight='bold')
axes[0].axis('off')
# Prediction
pred_slice = np.rot90(prediction[:, :, slice_num])
pred_mask = create_colored_mask(pred_slice)
axes[1].imshow(img_slice[rmin:rmax, cmin:cmax], cmap='gray', zorder=1)
axes[1].imshow(pred_mask[rmin:rmax, cmin:cmax], zorder=2)
axes[1].set_title('Prediction', fontsize=32, pad=20, weight='bold')
axes[1].axis('off')
plt.savefig(save_path, bbox_inches='tight', dpi=300, pad_inches=0.05)
plt.close()
print(f"已保存切片 {slice_num} 至: {save_path}")
if __name__ == "__main__":
base_dir = "/mnt/1248/onlylian"
image_path = os.path.join(base_dir, "nnUNet/nnUNet_raw/Dataset999/imagesTs/T1C_043_0000.nii.gz")
gt_path = os.path.join(base_dir, "nnUNet/nnUNet_raw/Dataset999/labelsTs/T1C_043.nii.gz")
pred_path = os.path.join(base_dir, "T1C/output_predictions/2d/fold_2/T1C_043.nii.gz") # Updated path
save_dir = os.path.join(base_dir, "T1C")
os.makedirs(save_dir, exist_ok=True)
print("正在載入影像...")
image = load_nifti(image_path)
ground_truth = load_nifti(gt_path)
prediction = load_nifti(pred_path)
print("影像載入完成")
print("\n分析切片中的器官...")
total_slices = image.shape[2]
for slice_num in range(total_slices):
gt_slice = ground_truth[:, :, slice_num]
pred_slice = prediction[:, :, slice_num]
gt_organs = check_organs_in_slice(gt_slice)
pred_organs = check_organs_in_slice(pred_slice)
if gt_organs or pred_organs:
print(f"\n切片 {slice_num}:")
print(f"Ground Truth 包含: {', '.join(sorted(gt_organs))}")
print(f"Prediction 包含: {', '.join(sorted(pred_organs))}")
print("\n開始切片選擇和保存過程...")
while True:
try:
slice_num = int(input("\n請輸入要保存的切片編號 (-1 退出): "))
if slice_num == -1:
print("程序結束")
break
if 0 <= slice_num < total_slices:
save_path = os.path.join(save_dir, f'comparison_T1C_043_{slice_num:03d}.png')
save_slice(image, ground_truth, prediction, slice_num, save_path)
else:
print(f"切片編號必須在 0 到 {total_slices-1} 之間")
except ValueError:
print("請輸入有效的數字")
except Exception as e:
print(f"發生錯誤: {str(e)}")

View file

@ -0,0 +1,303 @@
# import nibabel as nib
# import matplotlib.pyplot as plt
# import numpy as np
# import os
# from typing import Tuple, Set, Dict, Optional
# def load_nifti(file_path: str) -> np.ndarray:
# """Load a NIfTI file and return its data array."""
# return nib.load(file_path).get_fdata()
# def create_colored_mask(data: np.ndarray) -> np.ndarray:
# """Create a colored mask for segmentation visualization.
# Args:
# data: Input segmentation array
# Returns:
# RGB mask array
# """
# color_dict = {
# 1: [144/255, 238/255, 144/255], # Brainstem - Light green
# 2: [255/255, 218/255, 150/255], # Right_Eye - Light yellow
# 3: [205/255, 170/255, 125/255], # Left_Eye - Brown
# 4: [135/255, 206/255, 235/255], # Optic_Chiasm - Sky blue
# 5: [255/255, 99/255, 71/255], # Right_Optic_Nerve - Bright coral
# 6: [255/255, 160/255, 122/255], # Left_Optic_Nerve - Light coral
# 7: [147/255, 112/255, 219/255] # TV - Medium purple
# }
# mask = np.zeros((*data.shape, 3))
# for label, color in color_dict.items():
# mask_3d = np.dstack([data == label] * 3)
# mask = np.where(mask_3d, color, mask)
# return mask
# def get_brain_bounds(image: np.ndarray, padding_factor: float = 0.15) -> Tuple[int, int, int, int]:
# """Get the boundaries of the brain region with adjustable padding.
# Args:
# image: Input image array
# padding_factor: Factor to determine padding around the region of interest
# Returns:
# Tuple of (rmin, rmax, cmin, cmax) coordinates
# """
# mask = image > np.percentile(image, 1)
# rows = np.any(mask, axis=1)
# cols = np.any(mask, axis=0)
# rmin, rmax = np.where(rows)[0][[0, -1]]
# cmin, cmax = np.where(cols)[0][[0, -1]]
# center_r = (rmin + rmax) // 2
# center_c = (cmin + cmax) // 2
# radius = int(max(rmax - rmin, cmax - cmin) * 0.55)
# margin = int(radius * padding_factor)
# return (
# max(center_r - radius - margin, 0),
# min(center_r + radius + margin, image.shape[0]),
# max(center_c - radius - margin, 0),
# min(center_c + radius + margin, image.shape[1])
# )
# def check_organs_in_slice(slice_data: np.ndarray) -> Set[str]:
# """Check which organs are present in a given slice.
# Args:
# slice_data: 2D array of the slice
# Returns:
# Set of organ names present in the slice
# """
# organ_dict = {
# 1: "Brainstem",
# 2: "Right_Eye",
# 3: "Left_Eye",
# 4: "Optic_Chiasm",
# 5: "Right_Optic_Nerve",
# 6: "Left_Optic_Nerve",
# 7: "TV"
# }
# unique_labels = set(np.unique(slice_data)) - {0}
# return {organ_dict[label] for label in unique_labels if label in organ_dict}
# def save_slice(image: np.ndarray, ground_truth: np.ndarray,
# prediction: np.ndarray, slice_num: int,
# save_path: str) -> None:
# """Save comparison visualization of a specific slice.
# Args:
# image: MRI volume
# ground_truth: Ground truth segmentation volume
# prediction: Predicted segmentation volume
# slice_num: Slice number to visualize
# save_path: Path to save the visualization
# """
# plt.clf()
# fig, axes = plt.subplots(1, 2, figsize=(25, 11))
# plt.subplots_adjust(wspace=0.01)
# # Process image
# img_slice = np.rot90(image[:, :, slice_num])
# # Get bounds
# rmin, rmax, cmin, cmax = get_brain_bounds(img_slice)
# # Normalize image for display
# img_norm = (img_slice - np.min(img_slice)) / (np.max(img_slice) - np.min(img_slice))
# # Ground Truth
# gt_slice = np.rot90(ground_truth[:, :, slice_num])
# gt_mask = create_colored_mask(gt_slice)
# axes[0].imshow(img_norm[rmin:rmax, cmin:cmax], cmap='gray')
# axes[0].imshow(gt_mask[rmin:rmax, cmin:cmax], alpha=0.5)
# axes[0].set_title('Ground Truth', fontsize=32, pad=20, weight='bold')
# axes[0].axis('off')
# # Prediction
# pred_slice = np.rot90(prediction[:, :, slice_num])
# pred_mask = create_colored_mask(pred_slice)
# axes[1].imshow(img_norm[rmin:rmax, cmin:cmax], cmap='gray')
# axes[1].imshow(pred_mask[rmin:rmax, cmin:cmax], alpha=0.5)
# axes[1].set_title('Prediction', fontsize=32, pad=20, weight='bold')
# axes[1].axis('off')
# plt.savefig(save_path, bbox_inches='tight', dpi=300, pad_inches=0.05)
# plt.close()
# print(f"Saved slice {slice_num} to: {save_path}")
# def main():
# """Main function to run the visualization pipeline."""
# # Load images
# base_path = '/mnt/1248/onlylian/nnUNet/nnUNet_raw/Dataset999'
# image_mri = load_nifti(os.path.join(base_path, 'imagesTs/T1C_043_0000.nii.gz'))
# ground_truth = load_nifti(os.path.join(base_path, 'labelsTs/T1C_043.nii.gz'))
# prediction = load_nifti('/mnt/1248/onlylian/T1C/output_predictions/2d/fold_2/T1C_043.nii.gz')
# save_dir = '/mnt/1248/onlylian/T1C'
# # Analyze slices
# total_slices = image_mri.shape[2]
# for slice_num in range(total_slices):
# gt_organs = check_organs_in_slice(ground_truth[:, :, slice_num])
# pred_organs = check_organs_in_slice(prediction[:, :, slice_num])
# if gt_organs or pred_organs:
# print(f"\nSlice {slice_num}:")
# print(f"Ground Truth contains: {', '.join(sorted(gt_organs))}")
# print(f"Prediction contains: {', '.join(sorted(pred_organs))}")
# # Interactive slice selection
# while True:
# try:
# slice_num = int(input("\nEnter slice number to save (-1 to exit): "))
# if slice_num == -1:
# break
# if 0 <= slice_num < total_slices:
# save_path = os.path.join(save_dir, f'comparison_{slice_num:03d}.png')
# save_slice(image_mri, ground_truth, prediction,
# slice_num, save_path)
# else:
# print(f"Slice number must be between 0 and {total_slices-1}")
# except ValueError:
# print("Please enter a valid number")
# except Exception as e:
# print(f"Error occurred: {str(e)}")
# if __name__ == "__main__":
# main()
import nibabel as nib
import matplotlib.pyplot as plt
import numpy as np
import os
from PIL import Image
from typing import Tuple, Set, Dict, Optional
import io
def load_nifti(file_path: str) -> np.ndarray:
"""Load a NIfTI file and return its data array."""
return nib.load(file_path).get_fdata()
def create_colored_mask(data: np.ndarray) -> np.ndarray:
"""Create a colored mask for segmentation visualization."""
color_dict = {
1: [144/255, 238/255, 144/255], # Brainstem - Light green
2: [255/255, 218/255, 150/255], # Right_Eye - Light yellow
3: [205/255, 170/255, 125/255], # Left_Eye - Brown
4: [135/255, 206/255, 235/255], # Optic_Chiasm - Sky blue
5: [255/255, 99/255, 71/255], # Right_Optic_Nerve - Bright coral
6: [255/255, 160/255, 122/255], # Left_Optic_Nerve - Light coral
7: [147/255, 112/255, 219/255] # TV - Medium purple
}
mask = np.zeros((*data.shape, 3))
for label, color in color_dict.items():
mask_3d = np.dstack([data == label] * 3)
mask = np.where(mask_3d, color, mask)
return mask
def get_brain_bounds(image: np.ndarray, padding_factor: float = 0.15) -> Tuple[int, int, int, int]:
"""Get the boundaries of the brain region with adjustable padding."""
mask = image > np.percentile(image, 1)
rows = np.any(mask, axis=1)
cols = np.any(mask, axis=0)
rmin, rmax = np.where(rows)[0][[0, -1]]
cmin, cmax = np.where(cols)[0][[0, -1]]
center_r = (rmin + rmax) // 2
center_c = (cmin + cmax) // 2
radius = int(max(rmax - rmin, cmax - cmin) * 0.55)
margin = int(radius * padding_factor)
return (
max(center_r - radius - margin, 0),
min(center_r + radius + margin, image.shape[0]),
max(center_c - radius - margin, 0),
min(center_c + radius + margin, image.shape[1])
)
def create_slice_image(image: np.ndarray, ground_truth: np.ndarray,
prediction: np.ndarray, slice_num: int) -> Image.Image:
"""Create image for a specific slice."""
plt.clf()
fig, axes = plt.subplots(1, 2, figsize=(25, 11))
plt.subplots_adjust(wspace=0.01)
img_slice = np.rot90(image[:, :, slice_num])
rmin, rmax, cmin, cmax = get_brain_bounds(img_slice)
img_norm = (img_slice - np.min(img_slice)) / (np.max(img_slice) - np.min(img_slice))
# Ground Truth
gt_slice = np.rot90(ground_truth[:, :, slice_num])
gt_mask = create_colored_mask(gt_slice)
axes[0].imshow(img_norm[rmin:rmax, cmin:cmax], cmap='gray')
axes[0].imshow(gt_mask[rmin:rmax, cmin:cmax], alpha=0.5)
axes[0].set_title('Ground Truth', fontsize=32, pad=20, weight='bold')
axes[0].axis('off')
# Prediction
pred_slice = np.rot90(prediction[:, :, slice_num])
pred_mask = create_colored_mask(pred_slice)
axes[1].imshow(img_norm[rmin:rmax, cmin:cmax], cmap='gray')
axes[1].imshow(pred_mask[rmin:rmax, cmin:cmax], alpha=0.5)
axes[1].set_title('Prediction', fontsize=32, pad=20, weight='bold')
axes[1].axis('off')
# Convert plot to PIL Image
buf = io.BytesIO()
plt.savefig(buf, format='png', bbox_inches='tight', dpi=300, pad_inches=0.05)
plt.close()
buf.seek(0)
return Image.open(buf)
def create_animated_gif(image: np.ndarray, ground_truth: np.ndarray,
prediction: np.ndarray, start_slice: int,
end_slice: int, output_path: str,
duration: int = 500) -> None:
"""Create an animated GIF from a range of slices."""
frames = []
for slice_num in range(start_slice, end_slice + 1):
print(f"Processing slice {slice_num}...")
frame = create_slice_image(image, ground_truth, prediction, slice_num)
frames.append(frame)
# Save the animated GIF
frames[0].save(
output_path,
save_all=True,
append_images=frames[1:],
duration=duration,
loop=0
)
print(f"Saved animated GIF to: {output_path}")
def main():
"""Main function to create the animated visualization."""
# Load images with new paths
base_path = '/mnt/1248/onlylian/nnUNet/nnUNet_raw/Dataset999'
image_mri = load_nifti(os.path.join(base_path, 'imagesTs/T1C_043_0000.nii.gz'))
ground_truth = load_nifti(os.path.join(base_path, 'labelsTs/T1C_043.nii.gz'))
prediction = load_nifti('/mnt/1248/onlylian/T1C/output_predictions/2d/fold_2/T1C_043.nii.gz')
# Create animated GIF for slices 111-121
output_path = '/mnt/1248/onlylian/T1C/slice_animation.gif'
create_animated_gif(
image_mri,
ground_truth,
prediction,
start_slice=111,
end_slice=121,
output_path=output_path,
duration=500 # 500ms per frame
)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,144 @@
import json
import os
import numpy as np
import matplotlib.pyplot as plt
from typing import Dict, Tuple
# Set up Chinese font support
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
def load_data(file_path: str) -> Dict:
"""Load JSON data file"""
try:
with open(file_path, 'r') as file:
return json.load(file)
except FileNotFoundError:
print(f"File not found: {file_path}")
return {}
except json.JSONDecodeError:
print(f"JSON decoding error: {file_path}")
return {}
def extract_scores(data: Dict, model: str, organ_id: str) -> Tuple[float, float]:
"""Extract scores for a specific organ from the data"""
if "summary" not in data or organ_id not in data["summary"]:
return (0, 0)
scores = data["summary"][organ_id]
if model == '3D_fullres':
mean_dice = scores.get("mean_dice", 0)
std_dice = scores.get("std_dice", 0)
else:
mean_dice = scores.get("mean_dice", scores.get("mean", 0))
std_dice = scores.get("std_dice", scores.get("std", 0))
return (mean_dice, std_dice)
def create_plot(model_averages: Dict, model_stds: Dict, organs: Dict, output_path: str):
"""Create and save the plot"""
plt.figure(figsize=(16, 5)) # Reduced figure size
plt.title('T1C_OAR_TV', fontsize=12, pad=10) # Changed title
plt.grid(True, axis='y', linestyle='--', alpha=0.7)
x = np.arange(len(organs)) * 1.5 # Reduced spacing between groups
width = 0.35 # Reduced bar width
colors = {
'2D': 'royalblue',
'3D_fullres': 'limegreen',
'3D_lowres': 'orange'
}
positions = {
'2D': x - width,
'3D_fullres': x,
'3D_lowres': x + width
}
for model, averages in model_averages.items():
bars = plt.bar(positions[model],
[averages[organ] * 100 for organ in organs.keys()],
width,
label=model,
color=colors[model])
# Adjust data label position and size
for j, bar in enumerate(bars):
height = bar.get_height()
organ = list(organs.keys())[j]
std = model_stds[model][organ] * 100
plt.text(bar.get_x() + bar.get_width()/2., height,
f'{height:.1f}±{std:.1f}%',
ha='center',
va='bottom',
fontsize=6, # Reduced font size
rotation=0) # Optional: rotate text if still crowded
plt.ylim(0, 100)
plt.ylabel('DC (%)', fontsize=10)
plt.xlabel('OAR_TV', fontsize=10)
plt.xticks(x, [organs[k] for k in organs.keys()], rotation=30, ha='right', fontsize=8)
plt.legend(loc='upper right', fontsize=8)
plt.tight_layout()
os.makedirs(os.path.dirname(output_path), exist_ok=True)
plt.savefig(output_path, dpi=300, bbox_inches='tight')
plt.close()
def print_results(model_averages: Dict, model_stds: Dict, organs: Dict):
"""Print numerical results"""
print("\nMean Dice Scores (percentage):")
for model in model_averages.keys():
print(f"\n{model}:")
for organ in organs.keys():
avg = model_averages[model][organ] * 100
std = model_stds[model][organ] * 100
print(f"{organs[organ]}: {avg:.1f}±{std:.1f}%")
def main():
# Define paths and organ mapping
base_path = '/mnt/1248/onlylian/T1C_OAR_TV/Dice/test'
paths = {
'2D': os.path.join(base_path, 'T1C_OAR_TV_test_dice_results_2D.json'),
'3D_fullres': os.path.join(base_path, 'T1C_OAR_TV_test_dice_results_3D_fullres.json'),
'3D_lowres': os.path.join(base_path, 'T1C_OAR_TV_test_dice_results_3D_lowres.json')
}
organs = {
"1": "Brainstem",
"2": "Right_Eye",
"3": "Left_Eye",
"4": "Optic_Chiasm",
"5": "Right_Optic_Nerve",
"6": "Left_Optic_Nerve",
"7": "TV"
}
# Initialize data storage
model_dice_scores = {
model: {organ: None for organ in organs.keys()}
for model in paths.keys()
}
# Load and process data
for model, path in paths.items():
data = load_data(path)
for organ_id in organs.keys():
model_dice_scores[model][organ_id] = extract_scores(data, model, organ_id)
# Calculate averages and standard deviations
model_averages = {}
model_stds = {}
for model, scores in model_dice_scores.items():
model_averages[model] = {organ: scores[organ][0] if scores[organ] else 0 for organ in organs.keys()}
model_stds[model] = {organ: scores[organ][1] if scores[organ] else 0 for organ in organs.keys()}
# Create and save the plot
output_path = os.path.join(base_path, 'T1C_OAR_TV_test_dice_scores_comparison.png')
create_plot(model_averages, model_stds, organs, output_path)
# Print the results
print_results(model_averages, model_stds, organs)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,318 @@
import json
import os
import numpy as np
import SimpleITK as sitk
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from tqdm import tqdm
import time
from datetime import datetime
from scipy.ndimage import distance_transform_edt
import multiprocessing
from joblib import Parallel, delayed
class IntegratedAnalyzer:
def __init__(self, base_path):
"""初始化分析器"""
self.base_path = base_path
self.organ_names = {
1: "Brainstem",
2: "Right_Eye",
3: "Left_Eye",
4: "Optic_Chiasm",
5: "Right_Optic_Nerve",
6: "Left_Optic_Nerve",
7: "TV"
}
self.configurations = {
'Dataset012_OAR_TV': {
'2d': {
'model': 'nnUNetTrainer__nnUNetPlans__2d',
'path': '/mnt/1248/onlylian/T1C_OAR_TV/output_predictions/2d',
'folds': range(5)
},
'3d_fullres': {
'model': 'nnUNetTrainer__nnUNetPlans__3d_fullres',
'path': '/mnt/1248/onlylian/T1C_OAR_TV/output_predictions/3d_fullres',
'folds': range(5)
},
'3d_lowres': {
'model': 'nnUNetTrainer__nnUNetPlans__3d_lowres',
'path': '/mnt/1248/onlylian/T1C_OAR_TV/output_predictions/3d_lowres',
'folds': range(5)
}
}
}
# 設定並行處理的核心數
self.n_jobs = max(1, multiprocessing.cpu_count() - 1)
self.setup_paths()
self.create_output_folders()
def setup_paths(self):
"""設置所有需要的路徑"""
self.dataset_paths = {}
for dataset, model_configs in self.configurations.items():
self.dataset_paths[dataset] = {
'ground_truth': os.path.join(
"/mnt/1248/onlylian/nnUNet/nnUNet_raw",
dataset,
"labelsTs"
),
'predictions': {}
}
for model_type, config in model_configs.items():
self.dataset_paths[dataset]['predictions'][model_type] = {
fold: os.path.join(config['path'], f"fold_{fold}")
for fold in config['folds']
}
self.output_path = os.path.join(
self.base_path,
"analysis_results",
datetime.now().strftime("%Y%m%d_%H%M%S")
)
def create_output_folders(self):
"""建立輸出資料夾"""
os.makedirs(self.output_path, exist_ok=True)
def setup_font_configuration(self):
"""設置基本字型配置"""
plt.rcParams.update(plt.rcParamsDefault)
plt.rcParams['font.family'] = 'DejaVu Sans'
plt.rcParams['axes.unicode_minus'] = False
plt.rcParams['font.size'] = 12
plt.rcParams['axes.titlesize'] = 14
plt.rcParams['axes.labelsize'] = 12
plt.rcParams['xtick.labelsize'] = 10
plt.rcParams['ytick.labelsize'] = 10
return True
def load_nifti(self, file_path):
"""載入 NIfTI 檔案"""
return sitk.GetArrayFromImage(sitk.ReadImage(file_path))
def separate_structures(self, combined_mask, structure_name=""):
"""分離左右結構,改進的版本"""
sitk_image = sitk.GetImageFromArray(combined_mask)
labeled_components = sitk.ConnectedComponent(sitk_image)
labeled_array = sitk.GetArrayFromImage(labeled_components)
unique, counts = np.unique(labeled_array, return_counts=True)
if len(unique) < 3: # 背景+至少兩個區域
# 只在實際需要時輸出警告
if np.sum(combined_mask) > 0: # 只有當mask中確實有內容時才輸出警告
print(f"Warning: Not enough connected regions found for {structure_name}")
return None, None
region_sizes = [(i, count) for i, count in zip(unique[1:], counts[1:])]
sorted_regions = sorted(region_sizes, key=lambda x: x[1], reverse=True)
if len(sorted_regions) < 2:
if np.sum(combined_mask) > 0: # 只有當mask中確實有內容時才輸出警告
print(f"Warning: Only one major region found for {structure_name}")
return None, None
region1_mask = (labeled_array == sorted_regions[0][0])
region2_mask = (labeled_array == sorted_regions[1][0])
def get_centroid(mask):
indices = np.where(mask)
return np.mean(indices[2])
centroid1 = get_centroid(region1_mask)
centroid2 = get_centroid(region2_mask)
if centroid1 > centroid2:
right = region1_mask
left = region2_mask
else:
right = region2_mask
left = region1_mask
return right.astype(np.uint8), left.astype(np.uint8)
def compute_dice(self, pred, gt):
"""計算 Dice 係數"""
intersection = np.sum(pred & gt)
sum_pred = np.sum(pred)
sum_gt = np.sum(gt)
if sum_pred == 0 and sum_gt == 0:
return 1.0
elif sum_pred == 0 or sum_gt == 0:
return 0.0
return 2.0 * intersection / (sum_pred + sum_gt)
def evaluate_case(self, gt_path, pred_path):
"""評估單一案例,包含腫瘤距離分析"""
gt_img = self.load_nifti(gt_path)
pred_img = self.load_nifti(pred_path)
# 計算腫瘤距離圖
tumor_mask = (gt_img == 7) # 7 是腫瘤的標籤
tumor_distance = distance_transform_edt(~tumor_mask)
results = {}
distance_results = {}
# Process eyes
combined_gt_eye = ((gt_img == 2) | (gt_img == 3)).astype(np.uint8)
combined_pred_eye = ((pred_img == 2) | (pred_img == 3)).astype(np.uint8)
combined_pred_eye_sitk = sitk.GetImageFromArray(combined_pred_eye)
closing_filter = sitk.BinaryMorphologicalClosingImageFilter()
closing_filter.SetKernelRadius(2)
processed_pred_eye = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_eye_sitk))
results['combined_eye'] = self.compute_dice(processed_pred_eye, combined_gt_eye)
eye_distances = tumor_distance[combined_gt_eye > 0]
if len(eye_distances) > 0:
distance_results['combined_eye'] = np.mean(eye_distances)
right_pred_eye, left_pred_eye = self.separate_structures(processed_pred_eye, "Eyes")
right_gt_eye, left_gt_eye = self.separate_structures(combined_gt_eye, "Eyes")
# Process optic nerves
combined_gt_nerve = ((gt_img == 5) | (gt_img == 6)).astype(np.uint8)
combined_pred_nerve = ((pred_img == 5) | (pred_img == 6)).astype(np.uint8)
combined_pred_nerve_sitk = sitk.GetImageFromArray(combined_pred_nerve)
processed_pred_nerve = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_nerve_sitk))
results['combined_nerve'] = self.compute_dice(processed_pred_nerve, combined_gt_nerve)
nerve_distances = tumor_distance[combined_gt_nerve > 0]
if len(nerve_distances) > 0:
distance_results['combined_nerve'] = np.mean(nerve_distances)
right_pred_nerve, left_pred_nerve = self.separate_structures(processed_pred_nerve, "Optic Nerves")
right_gt_nerve, left_gt_nerve = self.separate_structures(combined_gt_nerve, "Optic Nerves")
# Process individual structures
for label in self.organ_names.keys():
if label == 1: # Brainstem
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 2 and right_pred_eye is not None: # Right Eye
gt_binary = right_gt_eye
pred_binary = right_pred_eye
elif label == 3 and left_pred_eye is not None: # Left Eye
gt_binary = left_gt_eye
pred_binary = left_pred_eye
elif label == 4: # Optic Chiasm
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 5 and right_pred_nerve is not None: # Right Optic Nerve
gt_binary = right_gt_nerve
pred_binary = right_pred_nerve
elif label == 6 and left_pred_nerve is not None: # Left Optic Nerve
gt_binary = left_gt_nerve
pred_binary = left_pred_nerve
elif label == 7: # TV (tumor)
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
else:
continue
try:
dice = self.compute_dice(pred_binary, gt_binary)
results[label] = dice
# 計算每個器官到腫瘤的距離
if label != 7: # 不計算腫瘤自身的距離
organ_distances = tumor_distance[gt_binary > 0]
if len(organ_distances) > 0:
distance_results[label] = np.mean(organ_distances)
except Exception as e:
print(f"Error processing label {label}: {str(e)}")
results[label] = np.nan
distance_results[label] = np.nan
return results, distance_results
def process_single_case(self, args):
"""處理單一案例的包裝函數"""
gt_path, pred_path = args
return self.evaluate_case(gt_path, pred_path)
def process_fold(self, model_type, fold, model_config, dataset):
"""處理單一fold的函數"""
results = []
pred_folder = os.path.join(model_config['path'], f"fold_{fold}")
gt_folder = self.dataset_paths[dataset]['ground_truth']
if not os.path.exists(pred_folder):
print(f"Warning: Prediction folder does not exist: {pred_folder}")
return results
pred_files = [f for f in os.listdir(pred_folder) if f.endswith('.nii.gz')]
# 準備參數
case_args = [(os.path.join(gt_folder, pred_file),
os.path.join(pred_folder, pred_file))
for pred_file in pred_files
if os.path.exists(os.path.join(gt_folder, pred_file))]
# 使用 joblib 進行並行處理
processed_results = Parallel(n_jobs=self.n_jobs)(
delayed(self.process_single_case)(args) for args in tqdm(case_args,
desc=f"Processing fold {fold}")
)
# 整理結果
for (dice_results, distance_results), (gt_path, pred_path) in zip(processed_results, case_args):
pred_file = os.path.basename(pred_path)
for key, value in dice_results.items():
if isinstance(key, int):
result_entry = {
'model_type': model_type,
'fold': fold,
'case_id': pred_file,
'structure': self.organ_names[key],
'dice_score': value,
'distance_to_tumor': distance_results.get(key, np.nan)
}
results.append(result_entry)
return results
def analyze_and_visualize(self):
"""分析並視覺化結果,包含距離分析"""
self.setup_font_configuration()
plots_path = os.path.join(self.output_path, "plots")
os.makedirs(plots_path, exist_ok=True)
for dataset in self.configurations.keys():
all_results = []
for model_type, model_config in self.configurations[dataset].items():
print(f"\nProcessing {dataset} - {model_type}")
# 並行處理每個 fold
fold_results = Parallel(n_jobs=self.n_jobs)(
delayed(self.process_fold)(model_type, fold, model_config, dataset)
for fold in model_config['folds']
)
# 整合所有結果
for results in fold_results:
all_results.extend(results)
# 創建DataFrame並視覺化
if all_results:
df = pd.DataFrame(all_results)
# 基本的模型比較圖
plt.figure(figsize=(12, 6))
sns.boxplot(data=df, x='model_type', y='dice_score')
plt.title(f'{dataset} - Model Comparison')
plt.xlabel('Model Type')
plt.ylabel('Dice Score')
plt.tight_layout()
plt.savefig(os.path.join(plots_path, f'{dataset}_model_comparison.png'))
plt.close()
# 結構wise比較圖
plt.figure(figsize=(15, 8))

View file

@ -0,0 +1,111 @@
import json
import os
import numpy as np
import matplotlib.pyplot as plt
from typing import Dict, Tuple
# Set up Chinese font support
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# Functions up to create_plot remain the same
def create_plot(model_averages: Dict, model_stds: Dict, organs: Dict, output_path: str):
"""Create and save the plot"""
plt.figure(figsize=(16, 5)) # Reduced figure size
plt.title('T1C_OAR_TV', fontsize=12, pad=10) # Changed title
plt.grid(True, axis='y', linestyle='--', alpha=0.7)
x = np.arange(len(organs)) * 1.5 # Reduced spacing between groups
width = 0.35 # Reduced bar width
colors = {
'2D': 'royalblue',
'3D_fullres': 'limegreen',
'3D_lowres': 'orange'
}
positions = {
'2D': x - width,
'3D_fullres': x,
'3D_lowres': x + width
}
for model, averages in model_averages.items():
bars = plt.bar(positions[model],
[averages[organ] * 100 for organ in organs.keys()],
width,
label=model,
color=colors[model])
# Adjust data label position and size
for j, bar in enumerate(bars):
height = bar.get_height()
organ = list(organs.keys())[j]
std = model_stds[model][organ] * 100
plt.text(bar.get_x() + bar.get_width()/2., height + 0.5,
f'{height:.1f}±{std:.1f}%',
ha='center',
va='bottom',
fontsize=6)
plt.ylim(0, 100)
plt.ylabel('DC (%)', fontsize=10)
plt.xlabel('OAR_TV', fontsize=10)
plt.xticks(x, [organs[k] for k in organs.keys()], rotation=30, ha='right', fontsize=8)
plt.legend(loc='upper right', fontsize=8)
plt.tight_layout()
os.makedirs(os.path.dirname(output_path), exist_ok=True)
plt.savefig(output_path, dpi=300, bbox_inches='tight')
plt.close()
# print_results function remains the same
def main():
# Define paths and organ mapping
base_path = '/mnt/1248/onlylian/T1C_OAR_TV/Dice/validation'
paths = {
'2D': os.path.join(base_path, 'T1C_OAR_TV_validation_dice_results_2D.json'),
'3D_fullres': os.path.join(base_path, 'T1C_OAR_TV_validation_dice_results_3D_fullres.json'),
'3D_lowres': os.path.join(base_path, 'T1C_OAR_TV_validation_dice_results_3D_lowres.json')
}
organs = {
"1": "Brainstem",
"2": "Right_Eye",
"3": "Left_Eye",
"4": "Optic_Chiasm",
"5": "Right_Optic_Nerve",
"6": "Left_Optic_Nerve",
"7": "TV"
}
# Initialize data storage
model_dice_scores = {
model: {organ: None for organ in organs.keys()}
for model in paths.keys()
}
# Load and process data
for model, path in paths.items():
data = load_data(path)
for organ_id in organs.keys():
model_dice_scores[model][organ_id] = extract_scores(data, model, organ_id)
# Calculate averages and standard deviations
model_averages = {}
model_stds = {}
for model, scores in model_dice_scores.items():
model_averages[model] = {organ: scores[organ][0] if scores[organ] else 0 for organ in organs.keys()}
model_stds[model] = {organ: scores[organ][1] if scores[organ] else 0 for organ in organs.keys()}
# Create and save the plot
output_path = os.path.join(base_path, 'T1C_OAR_TV_validation_dice_scores_comparison.png')
create_plot(model_averages, model_stds, organs, output_path)
# Print the results
print_results(model_averages, model_stds, organs)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,325 @@
import os
import time
import numpy as np
import SimpleITK as sitk
from tqdm import tqdm
import json
def load_nifti(file_path):
"""Load a NIfTI file"""
return sitk.GetArrayFromImage(sitk.ReadImage(file_path))
def compute_dice(pred, gt):
"""Compute Dice coefficient"""
intersection = np.sum(pred & gt)
sum_pred = np.sum(pred)
sum_gt = np.sum(gt)
if sum_pred == 0 and sum_gt == 0:
return 1.0
elif sum_pred == 0 or sum_gt == 0:
return 0.0
return 2.0 * intersection / (sum_pred + sum_gt)
def preprocess_binary_mask(mask, apply_closing=True):
"""Preprocess binary mask with morphological operations"""
mask_sitk = sitk.GetImageFromArray(mask)
if apply_closing:
closing_filter = sitk.BinaryMorphologicalClosingImageFilter()
closing_filter.SetKernelRadius(2)
mask = sitk.GetArrayFromImage(closing_filter.Execute(mask_sitk))
return mask
def separate_structures(combined_mask, structure_name=""):
"""Separate left and right structures"""
sitk_image = sitk.GetImageFromArray(combined_mask)
labeled_components = sitk.ConnectedComponent(sitk_image)
labeled_array = sitk.GetArrayFromImage(labeled_components)
unique, counts = np.unique(labeled_array, return_counts=True)
if len(unique) < 3:
print(f"Warning: Not enough connected regions found for {structure_name}")
return None, None
region_sizes = [(i, count) for i, count in zip(unique[1:], counts[1:])]
sorted_regions = sorted(region_sizes, key=lambda x: x[1], reverse=True)
if len(sorted_regions) < 2:
print(f"Warning: Only one major region found for {structure_name}")
return None, None
region1_mask = (labeled_array == sorted_regions[0][0])
region2_mask = (labeled_array == sorted_regions[1][0])
def get_centroid(mask):
indices = np.where(mask)
return np.mean(indices[2])
centroid1 = get_centroid(region1_mask)
centroid2 = get_centroid(region2_mask)
if centroid1 > centroid2:
right = region1_mask
left = region2_mask
else:
right = region2_mask
left = region1_mask
return right.astype(np.uint8), left.astype(np.uint8)
def evaluate_case(gt_path, pred_path, labels):
"""Evaluate a single case"""
print(f"\nTest file: {os.path.basename(pred_path)}")
try:
start_time = time.time()
gt_img = load_nifti(gt_path)
pred_img = load_nifti(pred_path)
load_time = time.time() - start_time
print(f"Image size: {gt_img.shape}")
print(f"Loading time: {load_time:.2f}s")
results = {}
total_time = 0
# Process eyes
start_time = time.time()
combined_gt_eye = ((gt_img == 2) | (gt_img == 3)).astype(np.uint8)
combined_pred_eye = ((pred_img == 2) | (pred_img == 3)).astype(np.uint8)
processed_pred_eye = preprocess_binary_mask(combined_pred_eye)
processed_gt_eye = preprocess_binary_mask(combined_gt_eye)
eye_dice = compute_dice(processed_pred_eye, processed_gt_eye)
eye_time = time.time() - start_time
total_time += eye_time
results['combined_eye'] = eye_dice
print(f"Eyes combined computation time: {eye_time:.2f}s, Dice: {eye_dice:.4f}")
right_pred_eye, left_pred_eye = separate_structures(processed_pred_eye, "Eyes")
right_gt_eye, left_gt_eye = separate_structures(processed_gt_eye, "Eyes")
# Process optic nerves
start_time = time.time()
combined_gt_nerve = ((gt_img == 5) | (gt_img == 6)).astype(np.uint8)
combined_pred_nerve = ((pred_img == 5) | (pred_img == 6)).astype(np.uint8)
processed_pred_nerve = preprocess_binary_mask(combined_pred_nerve)
processed_gt_nerve = preprocess_binary_mask(combined_gt_nerve)
nerve_dice = compute_dice(processed_pred_nerve, processed_gt_nerve)
nerve_time = time.time() - start_time
total_time += nerve_time
results['combined_nerve'] = nerve_dice
print(f"Optic nerves combined computation time: {nerve_time:.2f}s, Dice: {nerve_dice:.4f}")
right_pred_nerve, left_pred_nerve = separate_structures(processed_pred_nerve, "Optic Nerves")
right_gt_nerve, left_gt_nerve = separate_structures(processed_gt_nerve, "Optic Nerves")
for label in labels:
start_time = time.time()
if label == 1: # Brainstem
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
apply_closing = True
elif label == 2 and right_pred_eye is not None: # Right Eye
gt_binary = right_gt_eye
pred_binary = right_pred_eye
apply_closing = False # Already processed
elif label == 3 and left_pred_eye is not None: # Left Eye
gt_binary = left_gt_eye
pred_binary = left_pred_eye
apply_closing = False # Already processed
elif label == 4: # Optic Chiasm
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
apply_closing = True
elif label == 5 and right_pred_nerve is not None: # Right Optic Nerve
gt_binary = right_gt_nerve
pred_binary = right_pred_nerve
apply_closing = False # Already processed
elif label == 6 and left_pred_nerve is not None: # Left Optic Nerve
gt_binary = left_gt_nerve
pred_binary = left_pred_nerve
apply_closing = False # Already processed
elif label == 7: # TV
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
apply_closing = True
else:
continue
try:
if apply_closing:
pred_binary = preprocess_binary_mask(pred_binary)
gt_binary = preprocess_binary_mask(gt_binary)
dice = compute_dice(pred_binary, gt_binary)
compute_time = time.time() - start_time
total_time += compute_time
results[label] = dice
print(f"Label {label} computation time: {compute_time:.2f}s, Dice: {dice:.4f}")
except Exception as e:
print(f"Error processing label {label}: {str(e)}")
results[label] = np.nan
continue
print(f"Total computation time for this case: {total_time:.2f}s")
return results
except Exception as e:
print(f"Error evaluating case: {str(e)}")
return None
def evaluate_validation_fold(gt_base_folder, validation_folder, labels):
"""Evaluate the validation set for a single fold"""
fold_results = {label: [] for label in labels}
fold_results['combined_eye'] = []
fold_results['combined_nerve'] = []
pred_files = sorted([f for f in os.listdir(validation_folder) if f.endswith('.nii.gz')])
for pred_file in tqdm(pred_files, desc=f"Processing {os.path.basename(validation_folder)}"):
patient_id = pred_file.split('.nii.gz')[0]
gt_path = os.path.join(gt_base_folder, f"{patient_id}.nii.gz")
pred_path = os.path.join(validation_folder, pred_file)
if not os.path.exists(gt_path):
print(f"Could not find corresponding ground truth file: {gt_path}")
continue
case_results = evaluate_case(gt_path, pred_path, labels)
if case_results is not None:
for key in case_results:
if not np.isnan(case_results[key]):
if key in fold_results:
fold_results[key].append(case_results[key])
return fold_results
def evaluate_model(gt_folder, model_path, model_name, labels, organ_names, output_dir):
"""Evaluate the validation set for all folds of a model"""
print(f"\nStarting evaluation of model: {model_name}")
all_results = {label: [] for label in labels}
all_results['combined_eye'] = []
all_results['combined_nerve'] = []
fold_results = {}
for fold in range(5):
validation_folder = os.path.join(model_path, f"fold_{fold}", "validation")
if not os.path.exists(validation_folder):
print(f"Could not find fold_{fold} validation folder: {validation_folder}")
continue
print(f"\nProcessing {model_name} - Fold {fold} validation set")
results = evaluate_validation_fold(gt_folder, validation_folder, labels)
fold_results[f"fold_{fold}"] = results
for key in results:
all_results[key].extend(results[key])
summary = {}
for key in all_results:
if all_results[key]:
mean_dice = np.mean(all_results[key])
std_dice = np.std(all_results[key])
summary[key] = {
'mean_dice': float(mean_dice),
'std_dice': float(std_dice),
'n_samples': len(all_results[key])
}
else:
summary[key] = {
'mean_dice': np.nan,
'std_dice': np.nan,
'n_samples': 0
}
model_results = {
'summary': summary,
'fold_results': fold_results
}
output_path = os.path.join(output_dir, 'T1C_OAR_TV', 'Dice', 'validation')
os.makedirs(output_path, exist_ok=True)
filename = os.path.join(output_path, f'dice_results_{model_name}.json')
with open(filename, 'w') as f:
json.dump(model_results, f, indent=4)
print(f"\nValidation set results for {model_name} saved to {filename}")
print(f"\nValidation set Dice results for {model_name} (average across all folds):")
# Print individual organ results
print("\nOrgan-specific results:")
for label in labels:
if label in summary:
mean = summary[label]['mean_dice']
std = summary[label]['std_dice']
n = summary[label]['n_samples']
organ_name = organ_names.get(label, f"Label_{label}")
if not np.isnan(mean):
print(f"{organ_name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{organ_name}: Unable to compute (n={n})")
# Print combined structure results
print("\nCombined structure results:")
for key in ['combined_eye', 'combined_nerve']:
if key in summary:
mean = summary[key]['mean_dice']
std = summary[key]['std_dice']
n = summary[key]['n_samples']
name = "Combined Eyes" if key == 'combined_eye' else "Combined Optic Nerves"
if not np.isnan(mean):
print(f"{name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{name}: Unable to compute (n={n})")
return summary
if __name__ == "__main__":
base_dir = "/mnt/1248/onlylian"
gt_folder = os.path.join(base_dir, "nnUNet/nnUNet_raw/Dataset012_OAR_TV/labelsTr")
output_dir = base_dir
models = {
'2D': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset012_OAR_TV/nnUNetTrainer__nnUNetPlans__2d"),
'3D_fullres': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset012_OAR_TV/nnUNetTrainer__nnUNetPlans__3d_fullres"),
'3D_lowres': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset012_OAR_TV/nnUNetTrainer__nnUNetPlans__3d_lowres")
}
labels = [1, 2, 3, 4, 5, 6, 7]
organ_names = {
1: "Brainstem",
2: "Right_Eye",
3: "Left_Eye",
4: "Optic_Chiasm",
5: "Right_Optic_Nerve",
6: "Left_Optic_Nerve",
7: "TV"
}
print("Available models:")
for i, model_name in enumerate(models.keys(), 1):
print(f"{i}. {model_name}")
try:
choice = input("\nPlease select a model to run (enter 1-3): ")
model_index = int(choice) - 1
model_names = list(models.keys())
selected_model = model_names[model_index]
evaluate_model(gt_folder, models[selected_model], selected_model, labels, organ_names, output_dir)
except (ValueError, IndexError):
print("Invalid choice! Please enter a number between 1 and 3.")
except KeyboardInterrupt:
print("\nProgram interrupted by user")
print("You can run the program again to continue processing")
except Exception as e:
print(f"\nAn unexpected error occurred: {str(e)}")
print("Please check the error message and try again")

View file

@ -0,0 +1,151 @@
import nibabel as nib
import matplotlib.pyplot as plt
import numpy as np
import os
def load_nifti(file_path):
"""載入NIfTI檔案"""
return nib.load(file_path).get_fdata()
def create_colored_mask(data):
"""創建彩色遮罩"""
color_dict = {
1: [144/255, 238/255, 144/255], # Brainstem - 綠色
2: [255/255, 218/255, 150/255], # Right_Eye - 淺黃色
3: [205/255, 170/255, 125/255], # Left_Eye - 棕色
4: [135/255, 206/255, 235/255], # Optic_Chiasm - 藍色
5: [255/255, 99/255, 71/255], # Right_Optic_Nerve - 亮珊瑚紅
6: [255/255, 160/255, 122/255], # Left_Optic_Nerve - 淺珊瑚紅
7: [255/255, 0/255, 0/255] # TV - 亮紅色
}
mask = np.zeros((*data.shape, 4))
for label, color in color_dict.items():
organ_mask = (data == label)
if np.any(organ_mask):
mask[organ_mask] = [*color, 1.0]
return mask
def get_display_bounds(image):
"""直接返回較大的顯示區域"""
h, w = image.shape
# 使用圖像中心點
center_h = h // 2
center_w = w // 2
# 取圖像大小的 85% 作為顯示範圍
radius_h = int(h * 0.425)
radius_w = int(w * 0.425)
return (
max(center_h - radius_h, 0),
min(center_h + radius_h, h),
max(center_w - radius_w, 0),
min(center_w + radius_w, w)
)
def check_tv_in_slice(slice_data):
"""檢查切片中是否包含TVlabel 7"""
return 7 in np.unique(slice_data)
def find_tv_slices(ground_truth):
"""找出所有包含TV的切片"""
tv_slices = []
tv_pixels = []
total_slices = ground_truth.shape[2]
for slice_num in range(total_slices):
slice_data = ground_truth[:, :, slice_num]
if check_tv_in_slice(slice_data):
tv_pixel_count = np.sum(slice_data == 7)
tv_slices.append(slice_num)
tv_pixels.append(tv_pixel_count)
sorted_indices = np.argsort(tv_pixels)[::-1]
sorted_slices = [tv_slices[i] for i in sorted_indices]
sorted_pixels = [tv_pixels[i] for i in sorted_indices]
return sorted_slices, sorted_pixels
def save_slice(image, ground_truth, prediction, slice_num, save_path):
"""保存指定切片的三列比較圖原始影像、Ground Truth和預測結果"""
plt.clf()
fig, axes = plt.subplots(1, 3, figsize=(36, 11))
plt.subplots_adjust(wspace=0.01)
img_slice = np.rot90(image[:, :, slice_num])
rmin, rmax, cmin, cmax = get_display_bounds(img_slice)
# 調整圖像對比度
p2, p98 = np.percentile(img_slice, (2, 98))
img_slice = np.clip(img_slice, p2, p98)
img_slice = (img_slice - p2) / (p98 - p2)
# 1. Original Image
axes[0].imshow(img_slice[rmin:rmax, cmin:cmax], cmap='gray', zorder=1)
axes[0].set_title('Original Image', fontsize=32, pad=20, weight='bold')
axes[0].axis('off')
# 2. Ground Truth
gt_slice = np.rot90(ground_truth[:, :, slice_num])
gt_mask = create_colored_mask(gt_slice)
axes[1].imshow(img_slice[rmin:rmax, cmin:cmax], cmap='gray', zorder=1)
axes[1].imshow(gt_mask[rmin:rmax, cmin:cmax], zorder=2)
axes[1].set_title('Ground Truth', fontsize=32, pad=20, weight='bold')
axes[1].axis('off')
# 3. Prediction
pred_slice = np.rot90(prediction[:, :, slice_num])
pred_mask = create_colored_mask(pred_slice)
axes[2].imshow(img_slice[rmin:rmax, cmin:cmax], cmap='gray', zorder=1)
axes[2].imshow(pred_mask[rmin:rmax, cmin:cmax], zorder=2)
axes[2].set_title('Prediction', fontsize=32, pad=20, weight='bold')
axes[2].axis('off')
plt.suptitle('')
plt.savefig(save_path, bbox_inches='tight', dpi=300, pad_inches=0.05)
plt.close()
print(f"已保存切片 {slice_num} 至: {save_path}")
if __name__ == "__main__":
base_dir = "/mnt/1248/onlylian"
image_path = os.path.join(base_dir, "nnUNet/nnUNet_raw/Dataset012_OAR_TV/imagesTs/OAR_TV_092_0000.nii.gz")
gt_path = os.path.join(base_dir, "nnUNet/nnUNet_raw/Dataset012_OAR_TV/labelsTs/OAR_TV_092.nii.gz")
pred_path = os.path.join(base_dir, "T1C_OAR_TV/output_predictions/2d/fold_2/OAR_TV_092.nii.gz") # 更新預測結果路徑
save_dir = os.path.join(base_dir, "T1C_OAR_TV")
os.makedirs(save_dir, exist_ok=True)
print("正在載入影像...")
image = load_nifti(image_path)
ground_truth = load_nifti(gt_path)
prediction = load_nifti(pred_path) # 載入預測結果
print("影像載入完成")
print("\n尋找包含TV的切片...")
tv_slices, tv_pixels = find_tv_slices(ground_truth)
if tv_slices:
print(f"\n找到 {len(tv_slices)} 個包含TV的切片:")
for slice_num, pixel_count in zip(tv_slices, tv_pixels):
print(f"切片 {slice_num}: {pixel_count} 像素")
else:
print("未找到包含TV的切片")
print("\n開始切片選擇和保存過程...")
while True:
try:
slice_num = int(input("\n請輸入要保存的切片編號 (-1 退出): "))
if slice_num == -1:
print("程序結束")
break
if 0 <= slice_num < ground_truth.shape[2]:
save_path = os.path.join(save_dir, f'comparison_OAR_TV_092_3_{slice_num:03d}.png')
save_slice(image, ground_truth, prediction, slice_num, save_path)
else:
print(f"切片編號必須在 0 到 {ground_truth.shape[2]-1} 之間")
except ValueError:
print("請輸入有效的數字")
except Exception as e:
print(f"發生錯誤: {str(e)}")

View file

@ -0,0 +1,496 @@
# import nibabel as nib
# import matplotlib.pyplot as plt
# import numpy as np
# import os
# import SimpleITK as sitk
# def load_nifti(file_path):
# """載入NIfTI檔案"""
# return nib.load(file_path).get_fdata()
# def process_eyes_segmentation(pred_array):
# """
# 後處理眼睛的分割結果
# Parameters:
# pred_array: numpy array, 預測的分割結果
# Returns:
# result: numpy array, 處理後的分割結果
# """
# # 轉換為SimpleITK影像以使用形態學操作
# pred_img = sitk.GetImageFromArray(pred_array)
# # 提取左右眼的mask (標籤2和3)
# right_eye = (pred_array == 2).astype(np.uint8)
# left_eye = (pred_array == 3).astype(np.uint8)
# # 合併左右眼
# combined_eyes = right_eye | left_eye
# combined_eyes_sitk = sitk.GetImageFromArray(combined_eyes)
# # 應用形態學操作
# # 1. 閉運算填充小洞
# closing_filter = sitk.BinaryMorphologicalClosingImageFilter()
# closing_filter.SetKernelRadius(2)
# closed_eyes = closing_filter.Execute(combined_eyes_sitk)
# # 2. 移除小的孤立區域
# cc_filter = sitk.ConnectedComponentImageFilter()
# cc_filter.FullyConnectedOn()
# components = cc_filter.Execute(closed_eyes)
# # 獲取標籤統計信息
# label_stats = sitk.LabelShapeStatisticsImageFilter()
# label_stats.Execute(components)
# # 保留最大的兩個連通區域(左右眼)
# n_objects = cc_filter.GetObjectCount()
# if n_objects > 2:
# sizes = [(i, label_stats.GetPhysicalSize(i)) for i in range(1, n_objects + 1)]
# sizes.sort(key=lambda x: x[1], reverse=True)
# # 創建mask只保留最大的兩個區域
# binary_mask = sitk.Image(components.GetSize(), sitk.sitkUInt8)
# binary_mask.CopyInformation(components)
# binary_mask = sitk.Mask(components, components)
# for i in range(n_objects):
# if i >= 2: # 移除小於第二大的區域
# binary_mask = sitk.BinaryThreshold(binary_mask,
# sizes[i][0], sizes[i][0],
# 0, 1)
# else:
# binary_mask = closed_eyes
# # 將處理後的mask轉回numpy array
# processed_mask = sitk.GetArrayFromImage(binary_mask)
# # 基於質心分離左右眼
# label_img = sitk.GetArrayFromImage(components)
# regions = []
# for i in range(1, n_objects + 1):
# if label_stats.GetPhysicalSize(i) > 0:
# region_mask = (label_img == i)
# centroid = np.mean(np.where(region_mask), axis=1)
# regions.append((i, centroid[2])) # 使用x座標來判斷左右
# # 根據質心x座標排序
# regions.sort(key=lambda x: x[1])
# # 重建左右眼標籤
# result = np.zeros_like(pred_array)
# if len(regions) >= 2:
# # 將最右邊的區域標記為右眼(標籤2)
# result[label_img == regions[-1][0]] = 2
# # 將最左邊的區域標記為左眼(標籤3)
# result[label_img == regions[0][0]] = 3
# return result
# def process_optic_nerves_segmentation(pred_array):
# """
# 後處理視神經的分割結果
# Parameters:
# pred_array: numpy array, 預測的分割結果
# Returns:
# result: numpy array, 處理後的分割結果
# """
# # 提取視神經的mask (標籤5和6)
# right_nerve = (pred_array == 5).astype(np.uint8)
# left_nerve = (pred_array == 6).astype(np.uint8)
# # 合併視神經
# combined_nerves = right_nerve | left_nerve
# combined_nerves_sitk = sitk.GetImageFromArray(combined_nerves)
# # 應用形態學操作
# closing_filter = sitk.BinaryMorphologicalClosingImageFilter()
# closing_filter.SetKernelRadius(2)
# closed_nerves = closing_filter.Execute(combined_nerves_sitk)
# # 連通區域分析
# cc_filter = sitk.ConnectedComponentImageFilter()
# cc_filter.FullyConnectedOn()
# components = cc_filter.Execute(closed_nerves)
# # 獲取標籤統計信息
# label_stats = sitk.LabelShapeStatisticsImageFilter()
# label_stats.Execute(components)
# # 處理連通區域
# n_objects = cc_filter.GetObjectCount()
# label_img = sitk.GetArrayFromImage(components)
# regions = []
# for i in range(1, n_objects + 1):
# if label_stats.GetPhysicalSize(i) > 0:
# region_mask = (label_img == i)
# centroid = np.mean(np.where(region_mask), axis=1)
# regions.append((i, centroid[2]))
# # 根據質心x座標排序
# regions.sort(key=lambda x: x[1])
# # 重建左右視神經標籤
# result = np.zeros_like(pred_array)
# if len(regions) >= 2:
# # 將最右邊的區域標記為右視神經(標籤5)
# result[label_img == regions[-1][0]] = 5
# # 將最左邊的區域標記為左視神經(標籤6)
# result[label_img == regions[0][0]] = 6
# return result
# def post_process_symmetrical_organs(prediction):
# """對稱器官後處理"""
# processed_prediction = prediction.copy()
# # 處理眼睛
# eyes_result = process_eyes_segmentation(processed_prediction)
# # 更新眼睛的標籤
# processed_prediction[eyes_result > 0] = eyes_result[eyes_result > 0]
# # 處理視神經
# nerves_result = process_optic_nerves_segmentation(processed_prediction)
# # 更新視神經的標籤
# processed_prediction[nerves_result > 0] = nerves_result[nerves_result > 0]
# return processed_prediction
# def create_colored_mask(data):
# """創建彩色遮罩"""
# color_dict = {
# 1: [144/255, 238/255, 144/255], # Brainstem - 綠色
# 2: [255/255, 218/255, 150/255], # Right_Eye - 淺黃色
# 3: [205/255, 170/255, 125/255], # Left_Eye - 棕色
# 4: [135/255, 206/255, 235/255], # Optic_Chiasm - 藍色
# 5: [255/255, 99/255, 71/255], # Right_Optic_Nerve - 亮珊瑚紅
# 6: [255/255, 160/255, 122/255], # Left_Optic_Nerve - 淺珊瑚紅
# 7: [255/255, 0/255, 0/255] # TV - 亮紅色
# }
# # 創建一個具有透明背景的遮罩
# mask = np.zeros((*data.shape, 4)) # 使用4通道RGBA
# for label, color in color_dict.items():
# # 為每個器官設置顏色,包括完全不透明的 alpha 通道
# organ_mask = (data == label)
# if np.any(organ_mask): # 只處理存在的器官
# mask[organ_mask] = [*color, 1.0] # RGB + alpha
# return mask
# def get_brain_bounds(image):
# """獲取腦部區域的邊界"""
# mask = image > np.percentile(image, 1)
# rows = np.any(mask, axis=1)
# cols = np.any(mask, axis=0)
# rmin, rmax = np.where(rows)[0][[0, -1]]
# cmin, cmax = np.where(cols)[0][[0, -1]]
# center_r = (rmin + rmax) // 2
# center_c = (cmin + cmax) // 2
# radius = int(max(rmax - rmin, cmax - cmin) * 0.55)
# margin = int(radius * 0.15)
# rmin = max(center_r - radius - margin, 0)
# rmax = min(center_r + radius + margin, image.shape[0])
# cmin = max(center_c - radius - margin, 0)
# cmax = min(center_c + radius + margin, image.shape[1])
# return rmin, rmax, cmin, cmax
# def check_organs_in_slice(slice_data):
# """檢查切片中存在的器官標籤"""
# unique_labels = set(np.unique(slice_data))
# if 0 in unique_labels:
# unique_labels.remove(0)
# organ_dict = {
# 1: "Brainstem",
# 2: "Right_Eye",
# 3: "Left_Eye",
# 4: "Optic_Chiasm",
# 5: "Right_Optic_Nerve",
# 6: "Left_Optic_Nerve",
# 7: "TV"
# }
# return {organ_dict[label] for label in unique_labels if label in organ_dict}
# def save_slice(image, ground_truth, prediction, slice_num, save_path):
# """保存指定切片的比較圖"""
# plt.clf()
# fig, axes = plt.subplots(1, 2, figsize=(25, 11))
# plt.subplots_adjust(wspace=0.01)
# img_slice = np.rot90(image[:, :, slice_num])
# rmin, rmax, cmin, cmax = get_brain_bounds(img_slice)
# # 調整圖像對比度以提高可見度
# p2, p98 = np.percentile(img_slice, (2, 98))
# img_slice = np.clip(img_slice, p2, p98)
# img_slice = (img_slice - p2) / (p98 - p2)
# # Ground Truth
# gt_slice = np.rot90(ground_truth[:, :, slice_num])
# gt_mask = create_colored_mask(gt_slice)
# # 使用 imshow 的 zorder 參數來控制圖層順序
# axes[0].imshow(img_slice[rmin:rmax, cmin:cmax], cmap='gray', zorder=1)
# axes[0].imshow(gt_mask[rmin:rmax, cmin:cmax], zorder=2)
# axes[0].set_title('Ground Truth', fontsize=32, pad=20, weight='bold')
# axes[0].axis('off')
# # 對整個prediction進行後處理
# processed_prediction = post_process_symmetrical_organs(prediction)
# # Prediction
# pred_slice = np.rot90(processed_prediction[:, :, slice_num])
# pred_mask = create_colored_mask(pred_slice)
# axes[1].imshow(img_slice[rmin:rmax, cmin:cmax], cmap='gray', zorder=1)
# axes[1].imshow(pred_mask[rmin:rmax, cmin:cmax], zorder=2)
# axes[1].set_title('Prediction', fontsize=32, pad=20, weight='bold')
# axes[1].axis('off')
# plt.savefig(save_path, bbox_inches='tight', dpi=300, pad_inches=0.05)
# plt.close()
# print(f"已保存切片 {slice_num} 至: {save_path}")
# if __name__ == "__main__":
# base_dir = "/mnt/1248/onlylian"
# image_path = os.path.join(base_dir, "nnUNet/nnUNet_raw/Dataset012_OAR_TV/imagesTs/OAR_TV_092_0000.nii.gz")
# gt_path = os.path.join(base_dir, "nnUNet/nnUNet_raw/Dataset012_OAR_TV/labelsTs/OAR_TV_092.nii.gz")
# pred_path = os.path.join(base_dir, "T1C_OAR_TV/output_predictions/2d/fold_2/OAR_TV_092.nii.gz")
# save_dir = os.path.join(base_dir, "T1C_OAR_TV")
# os.makedirs(save_dir, exist_ok=True)
# print("正在載入影像...")
# image = load_nifti(image_path)
# ground_truth = load_nifti(gt_path)
# prediction = load_nifti(pred_path)
# print("影像載入完成")
# print("\n分析切片中的器官...")
# total_slices = image.shape[2]
# for slice_num in range(total_slices):
# gt_slice = ground_truth[:, :, slice_num]
# pred_slice = prediction[:, :, slice_num]
# gt_organs = check_organs_in_slice(gt_slice)
# pred_organs = check_organs_in_slice(pred_slice)
# if gt_organs or pred_organs:
# print(f"\n切片 {slice_num}:")
# print(f"Ground Truth 包含: {', '.join(sorted(gt_organs))}")
# print(f"Prediction 包含: {', '.join(sorted(pred_organs))}")
# print("\n開始切片選擇和保存過程...")
# while True:
# try:
# slice_num = int(input("\n請輸入要保存的切片編號 (-1 退出): "))
# if slice_num == -1:
# print("程序結束")
# break
# if 0 <= slice_num < total_slices:
# save_path = os.path.join(save_dir, f'comparison_OAR_TV_092_{slice_num:03d}.png')
# save_slice(image, ground_truth, prediction, slice_num, save_path)
# else:
# print(f"切片編號必須在 0 到 {total_slices-1} 之間")
# except ValueError:
# print("請輸入有效的數字")
# except Exception as e:
# print(f"發生錯誤: {str(e)}")
import nibabel as nib
import matplotlib.pyplot as plt
import numpy as np
import os
import matplotlib.patches as patches
def load_nifti(file_path):
"""載入NIfTI檔案"""
return nib.load(file_path).get_fdata()
def create_colored_mask(data):
"""創建彩色遮罩"""
color_dict = {
1: [144/255, 238/255, 144/255], # Brainstem - 綠色
2: [255/255, 218/255, 150/255], # Right_Eye - 淺黃色
3: [205/255, 170/255, 125/255], # Left_Eye - 棕色
4: [135/255, 206/255, 235/255], # Optic_Chiasm - 藍色
5: [255/255, 99/255, 71/255], # Right_Optic_Nerve - 亮珊瑚紅
6: [255/255, 160/255, 122/255], # Left_Optic_Nerve - 淺珊瑚紅
7: [255/255, 0/255, 0/255] # TV - 亮紅色
}
mask = np.zeros((*data.shape, 4))
for label, color in color_dict.items():
organ_mask = (data == label)
if np.any(organ_mask):
mask[organ_mask] = [*color, 1.0]
return mask
def get_brain_bounds(image):
"""獲取腦部區域的邊界"""
mask = image > np.percentile(image, 1)
rows = np.any(mask, axis=1)
cols = np.any(mask, axis=0)
rmin, rmax = np.where(rows)[0][[0, -1]]
cmin, cmax = np.where(cols)[0][[0, -1]]
center_r = (rmin + rmax) // 2
center_c = (cmin + cmax) // 2
radius = int(max(rmax - rmin, cmax - cmin) * 0.55)
margin = int(radius * 0.15)
return (
max(center_r - radius - margin, 0),
min(center_r + radius + margin, image.shape[0]),
max(center_c - radius - margin, 0),
min(center_c + radius + margin, image.shape[1])
)
def check_organs_in_slice(slice_data):
"""檢查切片中存在的器官標籤"""
unique_labels = set(np.unique(slice_data))
if 0 in unique_labels:
unique_labels.remove(0)
organ_dict = {
1: "Brainstem",
2: "Right_Eye",
3: "Left_Eye",
4: "Optic_Chiasm",
5: "Right_Optic_Nerve",
6: "Left_Optic_Nerve",
7: "TV"
}
return {organ_dict[label] for label in unique_labels if label in organ_dict}
def create_circular_mask(shape, center, radius):
"""創建圓形遮罩"""
Y, X = np.ogrid[:shape[0], :shape[1]]
dist_from_center = np.sqrt((X - center[0])**2 + (Y - center[1])**2)
mask = dist_from_center <= radius
return mask
def save_slice(image, ground_truth, prediction, slice_num, save_path):
"""保存指定切片的比較圖"""
plt.clf()
fig, axes = plt.subplots(1, 3, figsize=(36, 11))
plt.subplots_adjust(wspace=0.01)
img_slice = np.rot90(image[:, :, slice_num])
rmin, rmax, cmin, cmax = get_brain_bounds(img_slice)
# 計算圓形遮罩的參數
height, width = rmax-rmin, cmax-cmin
center = (width//2, height//2)
radius = min(width, height)//2
mask = create_circular_mask((height, width), center, radius)
# 調整圖像對比度
p2, p98 = np.percentile(img_slice, (2, 98))
img_slice = np.clip(img_slice, p2, p98)
img_slice = (img_slice - p2) / (p98 - p2)
cropped_slice = img_slice[rmin:rmax, cmin:cmax]
gt_slice = np.rot90(ground_truth[:, :, slice_num])[rmin:rmax, cmin:cmax]
pred_slice = np.rot90(prediction[:, :, slice_num])[rmin:rmax, cmin:cmax]
# 創建遮罩
gt_mask = create_colored_mask(gt_slice)
pred_mask = create_colored_mask(pred_slice)
# 設置背景顏色為黑色
fig.patch.set_facecolor('black')
titles = ['Original Image', 'Ground Truth', 'Prediction']
images = [
(cropped_slice, None),
(cropped_slice, gt_mask),
(cropped_slice, pred_mask)
]
for ax, title, (img, overlay) in zip(axes, titles, images):
ax.set_facecolor('black')
# 應用圓形遮罩
masked_img = np.copy(img)
masked_img[~mask] = 0
# 顯示基礎圖像
ax.imshow(masked_img, cmap='gray', zorder=1)
# 如果有overlay顯示它
if overlay is not None:
masked_overlay = overlay.copy()
masked_overlay[~mask] = [0, 0, 0, 0]
ax.imshow(masked_overlay, zorder=2)
# 設置標題
ax.set_title(title, fontsize=32, pad=20, weight='bold', color='white')
ax.axis('off')
plt.savefig(save_path, bbox_inches='tight', dpi=300, pad_inches=0.05,
facecolor='black', edgecolor='none')
plt.close()
print(f"已保存切片 {slice_num} 至: {save_path}")
if __name__ == "__main__":
base_dir = "/mnt/1248/onlylian"
image_path = os.path.join(base_dir, "nnUNet/nnUNet_raw/Dataset012_OAR_TV/imagesTs/OAR_TV_092_0000.nii.gz")
gt_path = os.path.join(base_dir, "nnUNet/nnUNet_raw/Dataset012_OAR_TV/labelsTs/OAR_TV_092.nii.gz")
pred_path = os.path.join(base_dir, "T1C_OAR_TV/output_predictions/2d/fold_2/OAR_TV_092.nii.gz")
save_dir = os.path.join(base_dir, "T1C_OAR_TV")
os.makedirs(save_dir, exist_ok=True)
print("正在載入影像...")
image = load_nifti(image_path)
ground_truth = load_nifti(gt_path)
prediction = load_nifti(pred_path)
print("影像載入完成")
print("\n分析切片中的器官...")
total_slices = image.shape[2]
for slice_num in range(total_slices):
gt_slice = ground_truth[:, :, slice_num]
pred_slice = prediction[:, :, slice_num]
gt_organs = check_organs_in_slice(gt_slice)
pred_organs = check_organs_in_slice(pred_slice)
if gt_organs or pred_organs:
print(f"\n切片 {slice_num}:")
print(f"Ground Truth 包含: {', '.join(sorted(gt_organs))}")
print(f"Prediction 包含: {', '.join(sorted(pred_organs))}")
print("\n開始切片選擇和保存過程...")
while True:
try:
slice_num = int(input("\n請輸入要保存的切片編號 (-1 退出): "))
if slice_num == -1:
print("程序結束")
break
if 0 <= slice_num < total_slices:
save_path = os.path.join(save_dir, f'comparison_OAR_TV_092_{slice_num:03d}.png')
save_slice(image, ground_truth, prediction, slice_num, save_path)
else:
print(f"切片編號必須在 0 到 {total_slices-1} 之間")
except ValueError:
print("請輸入有效的數字")
except Exception as e:
print(f"發生錯誤: {str(e)}")

View file

@ -0,0 +1,143 @@
import json
import os
import numpy as np
import matplotlib.pyplot as plt
from typing import Dict, Tuple
# Set up Chinese font support
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
def load_data(file_path: str) -> Dict:
"""Load JSON data file"""
try:
with open(file_path, 'r') as file:
return json.load(file)
except FileNotFoundError:
print(f"File not found: {file_path}")
return {}
except json.JSONDecodeError:
print(f"JSON decoding error: {file_path}")
return {}
def extract_scores(data: Dict, model: str, organ_id: str) -> Tuple[float, float]:
"""Extract scores for a specific organ from the data"""
if "summary" not in data or organ_id not in data["summary"]:
return (0, 0)
scores = data["summary"][organ_id]
if model == '3D_fullres':
mean_dice = scores.get("mean_dice", 0)
std_dice = scores.get("std_dice", 0)
else:
mean_dice = scores.get("mean_dice", scores.get("mean", 0))
std_dice = scores.get("std_dice", scores.get("std", 0))
return (mean_dice, std_dice)
def create_plot(model_averages: Dict, model_stds: Dict, organs: Dict, output_path: str):
"""Create and save the plot"""
plt.figure(figsize=(16, 5)) # Reduced figure size
plt.title('T2', fontsize=12, pad=10) # Changed title
plt.grid(True, axis='y', linestyle='--', alpha=0.7)
x = np.arange(len(organs)) * 1.5 # Reduced spacing between groups
width = 0.35 # Reduced bar width
colors = {
'2D': 'royalblue',
'3D_fullres': 'limegreen',
'3D_lowres': 'orange'
}
positions = {
'2D': x - width,
'3D_fullres': x,
'3D_lowres': x + width
}
for model, averages in model_averages.items():
bars = plt.bar(positions[model],
[averages[organ] * 100 for organ in organs.keys()],
width,
label=model,
color=colors[model])
# Adjust data label position and size
for j, bar in enumerate(bars):
height = bar.get_height()
organ = list(organs.keys())[j]
std = model_stds[model][organ] * 100
plt.text(bar.get_x() + bar.get_width()/2., height,
f'{height:.1f}±{std:.1f}%',
ha='center',
va='bottom',
fontsize=6, # Reduced font size
rotation=0) # Optional: rotate text if still crowded
plt.ylim(0, 100)
plt.ylabel('DC (%)', fontsize=10)
plt.xlabel('OAR', fontsize=10)
plt.xticks(x, [organs[k] for k in organs.keys()], rotation=30, ha='right', fontsize=8)
plt.legend(loc='upper right', fontsize=8)
plt.tight_layout()
os.makedirs(os.path.dirname(output_path), exist_ok=True)
plt.savefig(output_path, dpi=300, bbox_inches='tight')
plt.close()
def print_results(model_averages: Dict, model_stds: Dict, organs: Dict):
"""Print numerical results"""
print("\nMean Dice Scores (percentage):")
for model in model_averages.keys():
print(f"\n{model}:")
for organ in organs.keys():
avg = model_averages[model][organ] * 100
std = model_stds[model][organ] * 100
print(f"{organs[organ]}: {avg:.1f}±{std:.1f}%")
def main():
# Define paths and organ mapping
base_path = '/mnt/1248/onlylian/T2/Dice/test'
paths = {
'2D': os.path.join(base_path, 'T2_test_dice_results_2D.json'),
'3D_fullres': os.path.join(base_path, 'T2_test_dice_results_3D_fullres.json'),
'3D_lowres': os.path.join(base_path, 'T2_test_dice_results_3D_lowres.json')
}
organs = {
"1": "Brainstem",
"2": "Right_Eye",
"3": "Left_Eye",
"4": "Optic_Chiasm",
"5": "Right_Optic_Nerve",
"6": "Left_Optic_Nerve"
}
# Initialize data storage
model_dice_scores = {
model: {organ: None for organ in organs.keys()}
for model in paths.keys()
}
# Load and process data
for model, path in paths.items():
data = load_data(path)
for organ_id in organs.keys():
model_dice_scores[model][organ_id] = extract_scores(data, model, organ_id)
# Calculate averages and standard deviations
model_averages = {}
model_stds = {}
for model, scores in model_dice_scores.items():
model_averages[model] = {organ: scores[organ][0] if scores[organ] else 0 for organ in organs.keys()}
model_stds[model] = {organ: scores[organ][1] if scores[organ] else 0 for organ in organs.keys()}
# Create and save the plot
output_path = os.path.join(base_path, 'T2_test_dice_scores_comparison.png')
create_plot(model_averages, model_stds, organs, output_path)
# Print the results
print_results(model_averages, model_stds, organs)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,306 @@
import os
import time
import numpy as np
import SimpleITK as sitk
from tqdm import tqdm
import json
def load_nifti(file_path):
"""加載 NIfTI 文件"""
return sitk.GetArrayFromImage(sitk.ReadImage(file_path))
def compute_dice(pred, gt):
"""計算Dice係數"""
intersection = np.sum(pred & gt)
sum_pred = np.sum(pred)
sum_gt = np.sum(gt)
if sum_pred == 0 and sum_gt == 0:
return 1.0
elif sum_pred == 0 or sum_gt == 0:
return 0.0
return 2.0 * intersection / (sum_pred + sum_gt)
def separate_structures(combined_mask, structure_name=""):
"""分離左右結構"""
# 使用SimpleITK的ConnectedComponent
sitk_image = sitk.GetImageFromArray(combined_mask)
labeled_components = sitk.ConnectedComponent(sitk_image)
labeled_array = sitk.GetArrayFromImage(labeled_components)
# 計算連通區域數量和大小
unique, counts = np.unique(labeled_array, return_counts=True)
if len(unique) < 3: # 背景算一個所以至少需要3個
print(f"警告:{structure_name} 未找到足夠的連通區域")
return None, None
# 排除背景標籤0並按大小排序
region_sizes = [(i, count) for i, count in zip(unique[1:], counts[1:])]
sorted_regions = sorted(region_sizes, key=lambda x: x[1], reverse=True)
if len(sorted_regions) < 2:
print(f"警告:{structure_name} 未找到兩個主要區域")
return None, None
# 獲取兩個最大區域
region1_mask = (labeled_array == sorted_regions[0][0])
region2_mask = (labeled_array == sorted_regions[1][0])
# 計算質心
def get_centroid(mask):
indices = np.where(mask)
return np.mean(indices[2]) # 使用x軸第三維度的平均值
centroid1 = get_centroid(region1_mask)
centroid2 = get_centroid(region2_mask)
# 根據x軸位置確定左右
if centroid1 > centroid2:
right = region1_mask
left = region2_mask
else:
right = region2_mask
left = region1_mask
return right.astype(np.uint8), left.astype(np.uint8)
def evaluate_case(gt_path, pred_path, labels):
"""評估單個案例"""
print(f"\n測試文件: {os.path.basename(pred_path)}")
# 加載圖像
start_time = time.time()
gt_img = load_nifti(gt_path)
pred_img = load_nifti(pred_path)
load_time = time.time() - start_time
print(f"圖像大小: {gt_img.shape}")
print(f"加載時間: {load_time:.2f}")
results = {}
total_time = 0
# 處理眼睛
start_time = time.time()
combined_gt_eye = ((gt_img == 2) | (gt_img == 3)).astype(np.uint8)
combined_pred_eye = ((pred_img == 2) | (pred_img == 3)).astype(np.uint8)
# 形態學處理眼睛
combined_pred_eye_sitk = sitk.GetImageFromArray(combined_pred_eye)
closing_filter = sitk.BinaryMorphologicalClosingImageFilter()
closing_filter.SetKernelRadius(2)
processed_pred_eye = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_eye_sitk))
# 計算眼睛的Dice
eye_dice = compute_dice(processed_pred_eye, combined_gt_eye)
eye_time = time.time() - start_time
total_time += eye_time
results['combined_eye'] = eye_dice
print(f"眼睛合併計算時間: {eye_time:.2f}秒, Dice: {eye_dice:.4f}")
# 分離眼睛
right_pred_eye, left_pred_eye = separate_structures(processed_pred_eye, "眼睛")
right_gt_eye, left_gt_eye = separate_structures(combined_gt_eye, "眼睛")
# 處理視神經
start_time = time.time()
combined_gt_nerve = ((gt_img == 5) | (gt_img == 6)).astype(np.uint8)
combined_pred_nerve = ((pred_img == 5) | (pred_img == 6)).astype(np.uint8)
# 形態學處理視神經
combined_pred_nerve_sitk = sitk.GetImageFromArray(combined_pred_nerve)
processed_pred_nerve = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_nerve_sitk))
# 計算視神經的Dice
nerve_dice = compute_dice(processed_pred_nerve, combined_gt_nerve)
nerve_time = time.time() - start_time
total_time += nerve_time
results['combined_nerve'] = nerve_dice
print(f"視神經合併計算時間: {nerve_time:.2f}秒, Dice: {nerve_dice:.4f}")
# 分離視神經
right_pred_nerve, left_pred_nerve = separate_structures(processed_pred_nerve, "視神經")
right_gt_nerve, left_gt_nerve = separate_structures(combined_gt_nerve, "視神經")
# 計算各個標籤的Dice
for label in labels:
start_time = time.time()
if label == 1: # Brainstem
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 2 and right_pred_eye is not None: # Right Eye
gt_binary = right_gt_eye
pred_binary = right_pred_eye
elif label == 3 and left_pred_eye is not None: # Left Eye
gt_binary = left_gt_eye
pred_binary = left_pred_eye
elif label == 4: # Optic Chiasm
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 5 and right_pred_nerve is not None: # Right Optic Nerve
gt_binary = right_gt_nerve
pred_binary = right_pred_nerve
elif label == 6 and left_pred_nerve is not None: # Left Optic Nerve
gt_binary = left_gt_nerve
pred_binary = left_pred_nerve
else:
continue
try:
dice = compute_dice(pred_binary, gt_binary)
compute_time = time.time() - start_time
total_time += compute_time
results[label] = dice
print(f"標籤 {label} 計算時間: {compute_time:.2f}秒, Dice: {dice:.4f}")
except Exception as e:
print(f"處理標籤 {label} 時出錯:{str(e)}")
results[label] = np.nan
continue
print(f"單個案例總計算時間: {total_time:.2f}")
return results
def evaluate_fold(gt_folder, pred_folder, labels):
"""評估單個fold的所有案例"""
fold_results = {label: [] for label in labels}
fold_results['combined_eye'] = []
fold_results['combined_nerve'] = []
pred_files = sorted([f for f in os.listdir(pred_folder) if f.endswith('.nii.gz')])
for pred_file in tqdm(pred_files, desc=f"Processing {os.path.basename(pred_folder)}"):
gt_path = os.path.join(gt_folder, pred_file)
pred_path = os.path.join(pred_folder, pred_file)
if not os.path.exists(gt_path):
print(f"找不到對應的真實標籤文件:{gt_path}")
continue
case_results = evaluate_case(gt_path, pred_path, labels)
# 收集結果
for key in case_results:
if not np.isnan(case_results[key]):
if key in fold_results:
fold_results[key].append(case_results[key])
return fold_results
def evaluate_model(gt_folder, base_pred_folder, model_name, labels, organ_names):
"""評估單個模型的所有fold"""
print(f"\n開始評估模型:{model_name}")
all_results = {label: [] for label in labels}
all_results['combined_eye'] = []
all_results['combined_nerve'] = []
fold_results = {}
# 處理 fold_0 到 fold_4
for fold in range(5):
print(f"\n處理 {model_name} - Fold {fold}")
pred_folder = os.path.join(base_pred_folder, f"fold_{fold}")
if not os.path.exists(pred_folder):
print(f"找不到預測文件夾:{pred_folder}")
continue
results = evaluate_fold(gt_folder, pred_folder, labels)
fold_results[f"fold_{fold}"] = results
# 合併結果
for key in results:
all_results[key].extend(results[key])
# 計算總平均值和標準差
summary = {}
for key in all_results:
if all_results[key]:
mean_dice = np.mean(all_results[key])
std_dice = np.std(all_results[key])
summary[key] = {
'mean_dice': float(mean_dice),
'std_dice': float(std_dice),
'n_samples': len(all_results[key])
}
else:
summary[key] = {
'mean_dice': np.nan,
'std_dice': np.nan,
'n_samples': 0
}
# 保存結果到指定路径
save_dir = "/mnt/1248/onlylian"
os.makedirs(save_dir, exist_ok=True)
filename = os.path.join(save_dir, f'T2_test_dice_results_{model_name}.json')
model_results = {
'summary': summary,
'fold_results': fold_results
}
with open(filename, 'w') as f:
json.dump(model_results, f, indent=4)
print(f"\n{model_name}結果已保存到 {filename}")
# 打印結果
print(f"\n{model_name} 模型的 Dice 結果所有fold的平均值:")
# 打印器官結果
for label in labels:
if label in summary:
mean = summary[label]['mean_dice']
std = summary[label]['std_dice']
n = summary[label]['n_samples']
organ_name = organ_names.get(label, f"Label_{label}")
if not np.isnan(mean):
print(f"{organ_name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{organ_name}: 無法計算 (n={n})")
# 打印合併結果
print("\n合併結構的結果:")
for key in ['combined_eye', 'combined_nerve']:
if key in summary:
mean = summary[key]['mean_dice']
std = summary[key]['std_dice']
n = summary[key]['n_samples']
name = "合併眼睛" if key == 'combined_eye' else "合併視神經"
if not np.isnan(mean):
print(f"{name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{name}: 無法計算 (n={n})")
return summary
if __name__ == "__main__":
# 設置路徑和標籤
gt_folder = "/mnt/1248/onlylian/nnUNet/nnUNet_raw/Dataset111/labelsTs"
models = {
'2D': '/mnt/1248/onlylian/T2/output_predictions/2d',
'3D_fullres': '/mnt/1248/onlylian/T2/output_predictions/3d_fullres',
'3D_lowres': '/mnt/1248/onlylian/T2/output_predictions/3d_lowres'
}
labels = [1, 2, 3, 4, 5, 6]
organ_names = {
1: "Brainstem",
2: "Right_Eye",
3: "Left_Eye",
4: "Optic_Chiasm",
5: "Right_Optic_Nerve",
6: "Left_Optic_Nerve"
}
# 讓用戶選擇要運行哪個模型
print("可用的模型:")
for i, model_name in enumerate(models.keys(), 1):
print(f"{i}. {model_name}")
choice = input("\n請選擇要運行的模型 (輸入數字1-3): ")
try:
model_index = int(choice) - 1
model_names = list(models.keys())
selected_model = model_names[model_index]
evaluate_model(gt_folder, models[selected_model], selected_model, labels, organ_names)
except (ValueError, IndexError):
print("無效的選擇請輸入1-3之間的數字。")

View file

@ -0,0 +1,142 @@
import json
import os
import numpy as np
import matplotlib.pyplot as plt
from typing import Dict, Tuple
# Set up Chinese font support
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
def load_data(file_path: str) -> Dict:
"""Load JSON data file"""
try:
with open(file_path, 'r') as file:
return json.load(file)
except FileNotFoundError:
print(f"File not found: {file_path}")
return {}
except json.JSONDecodeError:
print(f"JSON decoding error: {file_path}")
return {}
def extract_scores(data: Dict, model: str, organ_id: str) -> Tuple[float, float]:
"""Extract scores for a specific organ from the data"""
if "summary" not in data or organ_id not in data["summary"]:
return (0, 0)
scores = data["summary"][organ_id]
if model == '3D_fullres':
mean_dice = scores.get("mean_dice", 0)
std_dice = scores.get("std_dice", 0)
else:
mean_dice = scores.get("mean_dice", scores.get("mean", 0))
std_dice = scores.get("std_dice", scores.get("std", 0))
return (mean_dice, std_dice)
def create_plot(model_averages: Dict, model_stds: Dict, organs: Dict, output_path: str):
"""Create and save the plot"""
plt.figure(figsize=(16, 5)) # Reduced figure size
plt.title('T2', fontsize=12, pad=10) # Reduced title size and padding
plt.grid(True, axis='y', linestyle='--', alpha=0.7)
x = np.arange(len(organs)) * 1.5 # Reduced spacing between groups
width = 0.35 # Reduced bar width
colors = {
'2D': 'royalblue',
'3D_fullres': 'limegreen',
'3D_lowres': 'orange'
}
positions = {
'2D': x - width,
'3D_fullres': x,
'3D_lowres': x + width
}
for model, averages in model_averages.items():
bars = plt.bar(positions[model],
[averages[organ] * 100 for organ in organs.keys()],
width,
label=model,
color=colors[model])
# Add data labels
for j, bar in enumerate(bars):
height = bar.get_height()
organ = list(organs.keys())[j]
std = model_stds[model][organ] * 100
plt.text(bar.get_x() + bar.get_width()/2., height + 0.5,
f'{height:.1f}±{std:.1f}%',
ha='center',
va='bottom',
fontsize=6) # Reduced font size
plt.ylim(0, 100)
plt.ylabel('DC (%)', fontsize=10)
plt.xlabel('OAR', fontsize=10)
plt.xticks(x, [organs[k] for k in organs.keys()], rotation=30, ha='right', fontsize=8)
plt.legend(loc='upper right', fontsize=8)
plt.tight_layout()
os.makedirs(os.path.dirname(output_path), exist_ok=True)
plt.savefig(output_path, dpi=300, bbox_inches='tight')
plt.close()
def print_results(model_averages: Dict, model_stds: Dict, organs: Dict):
"""Print numerical results"""
print("\nMean Dice Scores (percentage):")
for model in model_averages.keys():
print(f"\n{model}:")
for organ in organs.keys():
avg = model_averages[model][organ] * 100
std = model_stds[model][organ] * 100
print(f"{organs[organ]}: {avg:.1f}±{std:.1f}%")
def main():
# Define paths and organ mapping
base_path = '/mnt/1248/onlylian/T2/Dice/validation'
paths = {
'2D': os.path.join(base_path, 'T2_validation_dice_results_2D.json'),
'3D_fullres': os.path.join(base_path, 'T2_validation_dice_results_3D_fullres.json'),
'3D_lowres': os.path.join(base_path, 'T2_validation_dice_results_3D_lowres.json')
}
organs = {
"1": "Brainstem",
"2": "Right_Eye",
"3": "Left_Eye",
"4": "Optic_Chiasm",
"5": "Right_Optic_Nerve",
"6": "Left_Optic_Nerve"
}
# Initialize data storage
model_dice_scores = {
model: {organ: None for organ in organs.keys()}
for model in paths.keys()
}
# Load and process data
for model, path in paths.items():
data = load_data(path)
for organ_id in organs.keys():
model_dice_scores[model][organ_id] = extract_scores(data, model, organ_id)
# Calculate averages and standard deviations
model_averages = {}
model_stds = {}
for model, scores in model_dice_scores.items():
model_averages[model] = {organ: scores[organ][0] if scores[organ] else 0 for organ in organs.keys()}
model_stds[model] = {organ: scores[organ][1] if scores[organ] else 0 for organ in organs.keys()}
# Create and save the plot
output_path = os.path.join(base_path, 'T2_validation_dice_scores_comparison.png')
create_plot(model_averages, model_stds, organs, output_path)
# Print the results
print_results(model_averages, model_stds, organs)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,300 @@
import os
import time
import numpy as np
import SimpleITK as sitk
from tqdm import tqdm
import json
def load_nifti(file_path):
"""加載 NIfTI 文件"""
return sitk.GetArrayFromImage(sitk.ReadImage(file_path))
def compute_dice(pred, gt):
"""計算Dice係數"""
intersection = np.sum(pred & gt)
sum_pred = np.sum(pred)
sum_gt = np.sum(gt)
if sum_pred == 0 and sum_gt == 0:
return 1.0
elif sum_pred == 0 or sum_gt == 0:
return 0.0
return 2.0 * intersection / (sum_pred + sum_gt)
def separate_structures(combined_mask, structure_name=""):
"""分離左右結構"""
sitk_image = sitk.GetImageFromArray(combined_mask)
labeled_components = sitk.ConnectedComponent(sitk_image)
labeled_array = sitk.GetArrayFromImage(labeled_components)
unique, counts = np.unique(labeled_array, return_counts=True)
if len(unique) < 3:
print(f"警告:{structure_name} 未找到足夠的連通區域")
return None, None
region_sizes = [(i, count) for i, count in zip(unique[1:], counts[1:])]
sorted_regions = sorted(region_sizes, key=lambda x: x[1], reverse=True)
if len(sorted_regions) < 2:
print(f"警告:{structure_name} 未找到兩個主要區域")
return None, None
region1_mask = (labeled_array == sorted_regions[0][0])
region2_mask = (labeled_array == sorted_regions[1][0])
def get_centroid(mask):
indices = np.where(mask)
return np.mean(indices[2])
centroid1 = get_centroid(region1_mask)
centroid2 = get_centroid(region2_mask)
if centroid1 > centroid2:
right = region1_mask
left = region2_mask
else:
right = region2_mask
left = region1_mask
return right.astype(np.uint8), left.astype(np.uint8)
def evaluate_case(gt_path, pred_path, labels):
"""評估單個案例"""
print(f"\n測試文件: {os.path.basename(pred_path)}")
start_time = time.time()
gt_img = load_nifti(gt_path)
pred_img = load_nifti(pred_path)
load_time = time.time() - start_time
print(f"圖像大小: {gt_img.shape}")
print(f"加載時間: {load_time:.2f}")
results = {}
total_time = 0
# 處理眼睛
start_time = time.time()
combined_gt_eye = ((gt_img == 2) | (gt_img == 3)).astype(np.uint8)
combined_pred_eye = ((pred_img == 2) | (pred_img == 3)).astype(np.uint8)
combined_pred_eye_sitk = sitk.GetImageFromArray(combined_pred_eye)
closing_filter = sitk.BinaryMorphologicalClosingImageFilter()
closing_filter.SetKernelRadius(2)
processed_pred_eye = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_eye_sitk))
eye_dice = compute_dice(processed_pred_eye, combined_gt_eye)
eye_time = time.time() - start_time
total_time += eye_time
results['combined_eye'] = eye_dice
print(f"眼睛合併計算時間: {eye_time:.2f}秒, Dice: {eye_dice:.4f}")
right_pred_eye, left_pred_eye = separate_structures(processed_pred_eye, "眼睛")
right_gt_eye, left_gt_eye = separate_structures(combined_gt_eye, "眼睛")
# 處理視神經
start_time = time.time()
combined_gt_nerve = ((gt_img == 5) | (gt_img == 6)).astype(np.uint8)
combined_pred_nerve = ((pred_img == 5) | (pred_img == 6)).astype(np.uint8)
combined_pred_nerve_sitk = sitk.GetImageFromArray(combined_pred_nerve)
processed_pred_nerve = sitk.GetArrayFromImage(closing_filter.Execute(combined_pred_nerve_sitk))
nerve_dice = compute_dice(processed_pred_nerve, combined_gt_nerve)
nerve_time = time.time() - start_time
total_time += nerve_time
results['combined_nerve'] = nerve_dice
print(f"視神經合併計算時間: {nerve_time:.2f}秒, Dice: {nerve_dice:.4f}")
right_pred_nerve, left_pred_nerve = separate_structures(processed_pred_nerve, "視神經")
right_gt_nerve, left_gt_nerve = separate_structures(combined_gt_nerve, "視神經")
for label in labels:
start_time = time.time()
if label == 1: # Brainstem
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 2 and right_pred_eye is not None: # Right Eye
gt_binary = right_gt_eye
pred_binary = right_pred_eye
elif label == 3 and left_pred_eye is not None: # Left Eye
gt_binary = left_gt_eye
pred_binary = left_pred_eye
elif label == 4: # Optic Chiasm
gt_binary = (gt_img == label).astype(np.uint8)
pred_binary = (pred_img == label).astype(np.uint8)
elif label == 5 and right_pred_nerve is not None: # Right Optic Nerve
gt_binary = right_gt_nerve
pred_binary = right_pred_nerve
elif label == 6 and left_pred_nerve is not None: # Left Optic Nerve
gt_binary = left_gt_nerve
pred_binary = left_pred_nerve
else:
continue
try:
dice = compute_dice(pred_binary, gt_binary)
compute_time = time.time() - start_time
total_time += compute_time
results[label] = dice
print(f"標籤 {label} 計算時間: {compute_time:.2f}秒, Dice: {dice:.4f}")
except Exception as e:
print(f"處理標籤 {label} 時出錯:{str(e)}")
results[label] = np.nan
continue
print(f"單個案例總計算時間: {total_time:.2f}")
return results
def evaluate_validation_fold(gt_base_folder, validation_folder, labels):
"""評估單個fold的驗證集"""
fold_results = {label: [] for label in labels}
fold_results['combined_eye'] = []
fold_results['combined_nerve'] = []
pred_files = sorted([f for f in os.listdir(validation_folder) if f.endswith('.nii.gz')])
for pred_file in tqdm(pred_files, desc=f"Processing {os.path.basename(validation_folder)}"):
# 從預測文件名中提取病人ID
patient_id = pred_file.split('.nii.gz')[0]
# 構建對應的ground truth路徑
gt_path = os.path.join(gt_base_folder, f"{patient_id}.nii.gz")
pred_path = os.path.join(validation_folder, pred_file)
if not os.path.exists(gt_path):
print(f"找不到對應的真實標籤文件:{gt_path}")
continue
case_results = evaluate_case(gt_path, pred_path, labels)
for key in case_results:
if not np.isnan(case_results[key]):
if key in fold_results:
fold_results[key].append(case_results[key])
return fold_results
def evaluate_model(gt_folder, model_path, model_name, labels, organ_names, output_dir):
"""評估模型所有fold的驗證集"""
print(f"\n開始評估模型:{model_name}")
all_results = {label: [] for label in labels}
all_results['combined_eye'] = []
all_results['combined_nerve'] = []
fold_results = {}
# 遍歷每個fold
for fold in range(5):
validation_folder = os.path.join(model_path, f"fold_{fold}", "validation")
if not os.path.exists(validation_folder):
print(f"找不到fold_{fold}的驗證資料夾:{validation_folder}")
continue
print(f"\n處理 {model_name} - Fold {fold} 驗證集")
results = evaluate_validation_fold(gt_folder, validation_folder, labels)
fold_results[f"fold_{fold}"] = results
for key in results:
all_results[key].extend(results[key])
# 計算總平均值和標準差
summary = {}
for key in all_results:
if all_results[key]:
mean_dice = np.mean(all_results[key])
std_dice = np.std(all_results[key])
summary[key] = {
'mean_dice': float(mean_dice),
'std_dice': float(std_dice),
'n_samples': len(all_results[key])
}
else:
summary[key] = {
'mean_dice': np.nan,
'std_dice': np.nan,
'n_samples': 0
}
# 保存結果
model_results = {
'summary': summary,
'fold_results': fold_results
}
# 確保輸出目錄存在
os.makedirs(output_dir, exist_ok=True)
# 使用完整路徑保存結果
filename = os.path.join(output_dir, f'T2_validation_dice_results_{model_name}.json')
with open(filename, 'w') as f:
json.dump(model_results, f, indent=4)
print(f"\n{model_name}驗證集結果已保存到 {filename}")
# 打印結果
print(f"\n{model_name} 模型的驗證集 Dice 結果所有fold的平均值:")
# 打印器官結果
for label in labels:
if label in summary:
mean = summary[label]['mean_dice']
std = summary[label]['std_dice']
n = summary[label]['n_samples']
organ_name = organ_names.get(label, f"Label_{label}")
if not np.isnan(mean):
print(f"{organ_name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{organ_name}: 無法計算 (n={n})")
# 打印合併結果
print("\n合併結構的結果:")
for key in ['combined_eye', 'combined_nerve']:
if key in summary:
mean = summary[key]['mean_dice']
std = summary[key]['std_dice']
n = summary[key]['n_samples']
name = "合併眼睛" if key == 'combined_eye' else "合併視神經"
if not np.isnan(mean):
print(f"{name}: {mean:.4f} ± {std:.4f} (n={n})")
else:
print(f"{name}: 無法計算 (n={n})")
return summary
if __name__ == "__main__":
# 設置路徑
base_dir = "/mnt/1248/onlylian"
gt_folder = os.path.join(base_dir, "nnUNet/nnUNet_raw/Dataset111/labelsTr")
output_dir = base_dir # 設置輸出目錄為 /mnt/1248/onlylian
models = {
'2D': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset111/nnUNetTrainer__nnUNetPlans__2d"),
'3D_fullres': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset111/nnUNetTrainer__nnUNetPlans__3d_fullres"),
'3D_lowres': os.path.join(base_dir, "nnUNet/nnUNet_results/Dataset111/nnUNetTrainer__nnUNetPlans__3d_lowres")
}
labels = [1, 2, 3, 4, 5, 6]
organ_names = {
1: "Brainstem",
2: "Right_Eye",
3: "Left_Eye",
4: "Optic_Chiasm",
5: "Right_Optic_Nerve",
6: "Left_Optic_Nerve"
}
# 讓用戶選擇要運行哪個模型
print("可用的模型:")
for i, model_name in enumerate(models.keys(), 1):
print(f"{i}. {model_name}")
choice = input("\n請選擇要運行的模型 (輸入數字1-3): ")
try:
model_index = int(choice) - 1
model_names = list(models.keys())
selected_model = model_names[model_index]
evaluate_model(gt_folder, models[selected_model], selected_model, labels, organ_names, output_dir)
except (ValueError, IndexError):
print("無效的選擇請輸入1-3之間的數字。")

View file

@ -0,0 +1,151 @@
import nibabel as nib
import matplotlib.pyplot as plt
import numpy as np
import os
def load_nifti(file_path):
"""載入NIfTI檔案"""
return nib.load(file_path).get_fdata()
def create_colored_mask(data):
"""創建彩色遮罩"""
color_dict = {
1: [144/255, 238/255, 144/255], # Brainstem - 綠色
2: [255/255, 218/255, 150/255], # Right_Eye - 淺黃色
3: [205/255, 170/255, 125/255], # Left_Eye - 棕色
4: [135/255, 206/255, 235/255], # Optic_Chiasm - 藍色
5: [255/255, 99/255, 71/255], # Right_Optic_Nerve - 亮珊瑚紅
6: [255/255, 160/255, 122/255] # Left_Optic_Nerve - 淺珊瑚紅
}
mask = np.zeros((*data.shape, 3))
for label, color in color_dict.items():
mask_3d = np.dstack([data == label] * 3)
mask = np.where(mask_3d, color, mask)
return mask
def get_brain_bounds(image):
"""獲取腦部區域的邊界"""
mask = image > np.percentile(image, 1)
rows = np.any(mask, axis=1)
cols = np.any(mask, axis=0)
rmin, rmax = np.where(rows)[0][[0, -1]]
cmin, cmax = np.where(cols)[0][[0, -1]]
center_r = (rmin + rmax) // 2
center_c = (cmin + cmax) // 2
# 減小裁剪半徑以放大顯示區域
radius = int(max(rmax - rmin, cmax - cmin) * 0.55)
# 調整邊距
margin = int(radius * 0.15)
# 根據中心點和半徑設定新的邊界
rmin = max(center_r - radius - margin, 0)
rmax = min(center_r + radius + margin, image.shape[0])
cmin = max(center_c - radius - margin, 0)
cmax = min(center_c + radius + margin, image.shape[1])
return rmin, rmax, cmin, cmax
def check_organs_in_slice(slice_data):
"""檢查切片中存在的器官標籤"""
unique_labels = set(np.unique(slice_data))
if 0 in unique_labels:
unique_labels.remove(0)
organ_dict = {
1: "Brainstem",
2: "Right_Eye",
3: "Left_Eye",
4: "Optic_Chiasm",
5: "Right_Optic_Nerve",
6: "Left_Optic_Nerve"
}
return {organ_dict[label] for label in unique_labels if label in organ_dict}
def save_slice(image, ground_truth, prediction, slice_num, save_path):
"""保存指定切片的比較圖"""
plt.clf()
fig, axes = plt.subplots(1, 2, figsize=(25, 11))
plt.subplots_adjust(wspace=0.01)
img_slice = np.rot90(image[:, :, slice_num])
rmin, rmax, cmin, cmax = get_brain_bounds(img_slice)
# Ground Truth
gt_slice = np.rot90(ground_truth[:, :, slice_num])
gt_mask = create_colored_mask(gt_slice)
axes[0].imshow(img_slice[rmin:rmax, cmin:cmax], cmap='gray')
axes[0].imshow(gt_mask[rmin:rmax, cmin:cmax], alpha=0.5)
axes[0].set_title('Ground Truth', fontsize=32, pad=20, weight='bold')
axes[0].axis('off')
# Prediction
pred_slice = np.rot90(prediction[:, :, slice_num])
pred_mask = create_colored_mask(pred_slice)
axes[1].imshow(img_slice[rmin:rmax, cmin:cmax], cmap='gray')
axes[1].imshow(pred_mask[rmin:rmax, cmin:cmax], alpha=0.5)
axes[1].set_title('Prediction', fontsize=32, pad=20, weight='bold')
axes[1].axis('off')
plt.savefig(save_path, bbox_inches='tight', dpi=300, pad_inches=0.05)
plt.close()
print(f"已保存切片 {slice_num} 至: {save_path}")
if __name__ == "__main__":
# 設定路徑
base_dir = "/mnt/1248/onlylian"
image_path = os.path.join(base_dir, "nnUNet/nnUNet_raw/Dataset111/imagesTs/T2_039_0000.nii.gz")
gt_path = os.path.join(base_dir, "nnUNet/nnUNet_raw/Dataset111/labelsTs/T2_039.nii.gz")
pred_path = os.path.join(base_dir, "T2/output_predictions/2d/fold_4/T2_039.nii.gz")
save_dir = os.path.join(base_dir, "T2")
# 確保保存目錄存在
os.makedirs(save_dir, exist_ok=True)
# 載入影像
print("正在載入影像...")
image = load_nifti(image_path)
ground_truth = load_nifti(gt_path)
prediction = load_nifti(pred_path)
print("影像載入完成")
# 檢查每個切片包含的器官
print("\n分析切片中的器官...")
total_slices = image.shape[2]
for slice_num in range(total_slices):
gt_slice = ground_truth[:, :, slice_num]
pred_slice = prediction[:, :, slice_num]
# 獲取器官信息
gt_organs = check_organs_in_slice(gt_slice)
pred_organs = check_organs_in_slice(pred_slice)
if gt_organs or pred_organs:
print(f"\n切片 {slice_num}:")
print(f"Ground Truth 包含: {', '.join(sorted(gt_organs))}")
print(f"Prediction 包含: {', '.join(sorted(pred_organs))}")
# 讓用戶選擇要保存的切片
print("\n開始切片選擇和保存過程...")
while True:
try:
slice_num = int(input("\n請輸入要保存的切片編號 (-1 退出): "))
if slice_num == -1:
print("程序結束")
break
if 0 <= slice_num < total_slices:
save_path = os.path.join(save_dir, f'comparison_T2_069_{slice_num:03d}.png')
save_slice(image, ground_truth, prediction, slice_num, save_path)
else:
print(f"切片編號必須在 0 到 {total_slices-1} 之間")
except ValueError:
print("請輸入有效的數字")
except Exception as e:
print(f"發生錯誤: {str(e)}")

View file

@ -0,0 +1,112 @@
import os
import shutil
import pandas as pd
def load_excel(file_path):
"""載入 Excel 文件"""
return pd.read_excel(file_path, sheet_name='Sheet1')
def ensure_directories_exist(target_dir, modalities):
"""確保目標目錄存在"""
for modality in modalities:
os.makedirs(os.path.join(target_dir, modality), exist_ok=True)
def find_file_recursive(filename, search_path):
"""在給定目錄中遞迴搜索文件。"""
for root, dirs, files in os.walk(search_path):
if filename in files:
return os.path.join(root, filename)
return None
def extract_patient_and_date(file_path):
"""從文件路徑中提取病人檔名和日期。"""
parts = file_path.split(os.sep)
if len(parts) >= 4:
return parts[-4], parts[-3] # 假設病人檔名在倒數第四層,日期在倒數第三層
return None, None
def is_swi_file(file_name):
"""檢查檔案名稱是否包含 'swi'"""
return 'swi' in file_name.lower()
def copy_files(df, source_dir, target_dir):
"""遍歷數據框並將對應的 .warp.nii.gz 文件複製到相應的模態資料夾,若無 .warp.nii.gz 則複製原始的 .nii.gz 文件,保留病人檔名和日期"""
processed_patients = set()
for index, row in df.iterrows():
file_name = row['name']
modality = row['modality_nn']
# 檢查是否為 swi 檔案
if is_swi_file(file_name):
print(f'跳過 swi 檔案: {file_name}')
continue
# 修改來源檔名:將 .nii.gz 替換為 .warp.nii.gz
warp_file_name = file_name.replace('.nii.gz', '.warp.nii.gz')
# 在源目錄中遞迴搜索 .warp.nii.gz 文件
warp_file_path = find_file_recursive(warp_file_name, source_dir)
if warp_file_path:
# 如果找到 .warp.nii.gz 文件,複製該文件
patient_id, date = extract_patient_and_date(warp_file_path)
if patient_id and date:
if patient_id not in processed_patients:
processed_patients.add(patient_id)
# 複製到個別的模態資料夾
target_dir_path = os.path.join(target_dir, modality, patient_id, date)
os.makedirs(target_dir_path, exist_ok=True)
target_file_path = os.path.join(target_dir_path, warp_file_name)
shutil.copy2(warp_file_path, target_file_path)
print(f'已複製 {warp_file_name}{target_dir_path} 資料夾。')
# 如果是 CT 或 CTC也複製到 CT+CTC 資料夾
if modality in ['CT', 'CTC']:
combined_target_dir_path = os.path.join(target_dir, 'CT+CTC', patient_id, date)
os.makedirs(combined_target_dir_path, exist_ok=True)
combined_target_file_path = os.path.join(combined_target_dir_path, warp_file_name)
shutil.copy2(warp_file_path, combined_target_file_path)
print(f'已複製 {warp_file_name}{combined_target_dir_path} 資料夾。')
else:
# 如果找不到 .warp.nii.gz 文件,則複製原始的 .nii.gz 文件
original_file_path = find_file_recursive(file_name, source_dir)
if original_file_path:
patient_id, date = extract_patient_and_date(original_file_path)
if patient_id and date:
if patient_id not in processed_patients:
processed_patients.add(patient_id)
# 複製到個別的模態資料夾
target_dir_path = os.path.join(target_dir, modality, patient_id, date)
os.makedirs(target_dir_path, exist_ok=True)
target_file_path = os.path.join(target_dir_path, file_name)
shutil.copy2(original_file_path, target_file_path)
print(f'已複製 {file_name}{target_dir_path} 資料夾。')
# 如果是 CT 或 CTC也複製到 CT+CTC 資料夾
if modality in ['CT', 'CTC']:
combined_target_dir_path = os.path.join(target_dir, 'CT+CTC', patient_id, date)
os.makedirs(combined_target_dir_path, exist_ok=True)
combined_target_file_path = os.path.join(combined_target_dir_path, file_name)
shutil.copy2(original_file_path, combined_target_file_path)
print(f'已複製 {file_name}{combined_target_dir_path} 資料夾。')
else:
print(f'{file_name}{warp_file_name} 均在源目錄中不存在。')
def main():
file_path = '/home/onlylian/modality_by_nn.xlsx'
source_dir = '/home/onlylian/M6_24Q3'
target_dir = '/home/onlylian/sorted_modality_files_inference'
modalities = ['CT', 'CTC', 'T1', 'T1C', 'T2', 'FLAIR', 'CT+CTC'] # 加入 CT+CTC
df = load_excel(file_path)
ensure_directories_exist(target_dir, modalities)
copy_files(df, source_dir, target_dir)
print('文件分類和複製完成。')
if __name__ == "__main__":
main()

View file

@ -0,0 +1,47 @@
import os
import shutil
def merge_directories(src1, src2, dest):
if not os.path.exists(dest):
os.makedirs(dest)
def copy_or_merge(src, dest):
for patient in os.listdir(src):
patient_src_path = os.path.join(src, patient)
patient_dest_path = os.path.join(dest, patient)
if os.path.isdir(patient_src_path):
if os.path.exists(patient_dest_path):
# If destination folder exists, merge the contents
for item in os.listdir(patient_src_path):
s = os.path.join(patient_src_path, item)
d = os.path.join(patient_dest_path, item)
if os.path.isdir(s):
shutil.copytree(s, d, dirs_exist_ok=True)
else:
shutil.copy2(s, d)
else:
# If destination folder does not exist, copy the whole folder
shutil.copytree(patient_src_path, patient_dest_path)
# Copy or merge from both source directories to destination directory
copy_or_merge(src1, dest)
copy_or_merge(src2, dest)
print("Directories merged successfully.")
# Define source and destination paths
src1 = '/mnt/1220/Public/dataset2/H4'
src2 = '/mnt/1220/Public/dataset2/N6'
dest = "/home/onlylian/dataset2_H4_N6"
# Call the function to merge the directories
merge_directories(src1, src2, dest)

View file

@ -0,0 +1,52 @@
import os
import shutil
def copy_last_file(source_base_dir, target_base_dir, modality):
# 確保目標目錄存在
os.makedirs(target_base_dir, exist_ok=True)
# 按照病患資料夾名稱進行排序
for patient_dir in sorted(os.listdir(source_base_dir)):
patient_path = os.path.join(source_base_dir, patient_dir)
if os.path.isdir(patient_path):
# 按照日期資料夾名稱進行排序
date_dirs = sorted(os.listdir(patient_path))
for date_dir in date_dirs:
date_path = os.path.join(patient_path, date_dir)
if os.path.isdir(date_path):
files = os.listdir(date_path)
# 篩選出 .nii.gz 和 .warp.nii.gz 檔案
nii_files = [f for f in files if f.endswith('.nii.gz')]
warp_files = [f for f in files if f.endswith('.warp.nii.gz')]
# 先選擇最後一個 .warp.nii.gz 檔案,如果沒有則選擇 .nii.gz 檔案
if warp_files:
warp_files.sort()
last_file = warp_files[-1] # 選擇最後一個 .warp.nii.gz 檔案
elif nii_files:
nii_files.sort()
last_file = nii_files[-1] # 選擇最後一個 .nii.gz 檔案
else:
print(f"No valid .nii.gz or .warp.nii.gz files found in {date_path}")
continue
last_file_path = os.path.join(date_path, last_file)
# 根據模態重新命名文件
new_file_name = f"{modality}_{patient_dir}.nii.gz"
target_file_path = os.path.join(target_base_dir, new_file_name)
# 複製文件到目標目錄
shutil.copy2(last_file_path, target_file_path)
print(f"Copied: {last_file_path} to {target_file_path}")
# T1C 模態的處理
source_base_dir_t1c = '/home/onlylian/sorted_modality_files/T1C'
target_base_dir_t1c = '/home/onlylian/T1C_image_folder'
copy_last_file(source_base_dir_t1c, target_base_dir_t1c, 'T1C')
# CT+CTC 模態的處理
source_base_dir_ctctc = '/home/onlylian/sorted_modality_files/CT+CTC'
target_base_dir_ctctc = '/home/onlylian/CT+CTC_image_folder'
copy_last_file(source_base_dir_ctctc, target_base_dir_ctctc, 'CT+CTC')

View file

@ -0,0 +1,51 @@
import os
import pandas as pd
# 要扫描的根目录
root_dir = '/home/onlylian/sorted_modality_files'
# 为 DataFrame 准备数据
data = []
# 遍历目录
for subdir, _, files in os.walk(root_dir):
for file in files:
if file.endswith('.nii.gz'):
parts = subdir.split('/')
try:
if len(parts) >= 8: # 标准结构
modalities = parts[4] # 提取模态类型
patient_id = parts[5] # 提取患者信息
date = parts[6] # 提取日期
name = file # 提取文件名
elif len(parts) == 7: # 当模态类型与具体模态合并时
modalities = parts[4] # 提取模态类型
patient_id = parts[5] # 提取患者信息
date = parts[6] # 提取日期
name = file # 提取文件名
elif len(parts) == 6: # 当只有日期和模态类型时
modalities = parts[4] # 提取模态类型
patient_id = parts[5] # 提取患者信息
date = '' # 没有日期信息
name = file # 提取文件名
else:
print(f"Path {subdir} does not have enough parts to extract data")
continue
data.append([modalities, patient_id, date, name])
print(f"Added: {modalities}/{patient_id}/{date}/{name}") # 调试信息
except IndexError as e:
print(f"Error processing path {subdir}: {e}")
# 检查数据是否已经添加
if not data:
print("No data found. Please check the directory structure and files.")
# 创建 DataFrame
df = pd.DataFrame(data, columns=['Modalities', 'patient_id', 'date', 'name'])
# 输出到 Excel
output_path = '/home/onlylian/patient_modalities_files.xlsx'
df.to_excel(output_path, index=False)
print(f"Excel 文件已生成在: {output_path}")

View file

@ -0,0 +1,46 @@
import os
import shutil
import pandas as pd
# 讀取 Excel 文件
file_path = '/home/onlylian/patient_modalities_files.xlsx'
df = pd.read_excel(file_path)
# 定義來源和目標資料夾
source_dir = '/home/onlylian/Complete_NEW_OAR_Files'
target_dir = '/home/onlylian/sorted_OAR_files'
modalities = ['CT', 'CTC', 'T1', 'T1C', 'T2', 'FLAIR', 'CT+CTC']
# 創建目標資料夾結構
for modality in modalities:
modality_path = os.path.join(target_dir, modality)
os.makedirs(modality_path, exist_ok=True)
# 遍歷每個病人ID和日期
for _, row in df.iterrows():
modality = row['Modalities']
patient_id = row['patient_id']
date = str(row['date'])
if modality in modalities:
# 構建來源資料夾路徑
source_path = os.path.join(source_dir, patient_id, date)
print(f"Checking source path: {source_path}") # 檢查路徑
if os.path.exists(source_path):
# 構建目標資料夾路徑
modality_path = os.path.join(target_dir, modality, patient_id, date)
os.makedirs(modality_path, exist_ok=True)
# 遍歷來源資料夾中的所有檔案
for file_name in os.listdir(source_path):
# 更新條件檢查
if file_name.endswith('.nii.gz'): # 確保是正確的檔案類型
source_file = os.path.join(source_path, file_name)
target_file = os.path.join(modality_path, file_name)
print(f"Copying {source_file} to {target_file}") # 跟蹤檔案複製
# 複製檔案到目標資料夾
shutil.copy2(source_file, target_file)
else:
print(f"Source path not found: {source_path}")
else:
print(f"Unknown modality {modality} for patient {patient_id} on {date}")

View file

@ -0,0 +1,47 @@
import os
import shutil
def merge_directories(src1, src2, dest):
if not os.path.exists(dest):
os.makedirs(dest)
def copy_or_merge(src, dest):
for patient in os.listdir(src):
patient_src_path = os.path.join(src, patient)
patient_dest_path = os.path.join(dest, patient)
if os.path.isdir(patient_src_path):
if os.path.exists(patient_dest_path):
# If destination folder exists, merge the contents
for item in os.listdir(patient_src_path):
s = os.path.join(patient_src_path, item)
d = os.path.join(patient_dest_path, item)
if os.path.isdir(s):
shutil.copytree(s, d, dirs_exist_ok=True)
else:
shutil.copy2(s, d)
else:
# If destination folder does not exist, copy the whole folder
shutil.copytree(patient_src_path, patient_dest_path)
# Copy or merge from both source directories to destination directory
copy_or_merge(src1, dest)
copy_or_merge(src2, dest)
print("Directories merged successfully.")
# Define source and destination paths
src1 = '/mnt/1220/Public/dataset2/G4'
src2 = '/mnt/1220/Public/dataset2/M6'
dest = "/home/onlylian/dataset2_G4_M6"
# Call the function to merge the directories
merge_directories(src1, src2, dest)

View file

@ -0,0 +1,46 @@
import os
import shutil
import re
def extract_patient_id(filename):
# 假設文件名格式為 "something_PATIENTID.nii.gz"
match = re.search(r'_([A-Z0-9]+)\.nii\.gz$', filename)
if match:
return match.group(1)
return None
def merge_folders(folder1, folder2, output_folder):
# 確保輸出資料夾存在
if not os.path.exists(output_folder):
os.makedirs(output_folder)
# 獲取兩個資料夾中的所有文件
files1 = set(os.listdir(folder1))
files2 = set(os.listdir(folder2))
# 合併所有唯一的病人ID
all_files = files1.union(files2)
for file in all_files:
patient_id = extract_patient_id(file)
if patient_id:
new_filename = f"label_{patient_id}.nii.gz"
source_folder = folder1 if file in files1 else folder2
source_path = os.path.join(source_folder, file)
dest_path = os.path.join(output_folder, new_filename)
# 複製文件到輸出資料夾並重命名
shutil.copy2(source_path, dest_path)
print(f"Copied and renamed {file} to {new_filename}")
else:
print(f"Skipped {file} - could not extract patient ID")
# 設定資料夾路徑
folder1 = '/home/onlylian/resampled_T1C_label'
folder2 = '/home/onlylian/resampled_CT+CTC_label'
output_folder = '/home/onlylian/merged_label_folder'
# 執行合併
merge_folders(folder1, folder2, output_folder)
print("Merging complete!")

View file

@ -0,0 +1,89 @@
import os
import SimpleITK as sitk
import difflib
# 定義資料夾路徑
T1C_folder_path = '/home/onlylian/sorted_OAR_files/T1C'
CTCTC_folder_path = '/home/onlylian/sorted_OAR_files/CT+CTC'
T1C_output_folder_path = '/home/onlylian/T1C_label_folder'
CTCTC_output_folder_path = '/home/onlylian/CT+CTC_label_folder'
# 定義組織標籤對應關係
labels = {
"background": "0",
"Brainstem": "1",
"Right_Eye": "2",
"Left_Eye": "3",
"Optic_Chiasm": "4",
"Right_Optic_Nerve": "5",
"Left_Optic_Nerve": "6"
}
# 確保輸出資料夾存在
os.makedirs(T1C_output_folder_path, exist_ok=True)
os.makedirs(CTCTC_output_folder_path, exist_ok=True)
def resample_image(image, reference_image):
resample = sitk.ResampleImageFilter()
resample.SetReferenceImage(reference_image)
resample.SetInterpolator(sitk.sitkNearestNeighbor)
resample.SetDefaultPixelValue(0)
return resample.Execute(image)
def process_images(input_folder, output_folder, image_type):
# 獲取所有病人的資料夾路徑
patient_folders = sorted([os.path.join(dp, d) for dp, dn, filenames in os.walk(input_folder) for d in dn])
file_counter = 1
for patient_folder in patient_folders:
# 創建空的影像,用來合成所有器官
combined_image = None
# 獲取病人文件夾中的所有文件名,並轉換為小寫形式
patient_files = {f.lower(): f for f in os.listdir(patient_folder)}
filename = patient_folder.split('/')[-2]
# 遍歷每個病人的所有影像文件
for organ_name, label in labels.items():
organ_filename = f'struct_{organ_name}.nii.gz'.lower()
# 使用 difflib.get_close_matches 查找相似文件名
possible_matches = difflib.get_close_matches(organ_filename, patient_files.keys(), n=1, cutoff=0.8)
if possible_matches:
matched_filename = patient_files[possible_matches[0]]
organ_path = os.path.join(patient_folder, matched_filename)
organ_image = sitk.ReadImage(organ_path)
if combined_image is None:
# 初始化合成影像
combined_image = sitk.Image(organ_image.GetSize(), sitk.sitkUInt8)
combined_image.CopyInformation(organ_image)
# 重新取樣器官影像以匹配合成影像的大小
resampled_organ_image = resample_image(organ_image, combined_image)
# 將器官影像添加到合成影像中
organ_array = sitk.GetArrayFromImage(resampled_organ_image)
combined_array = sitk.GetArrayFromImage(combined_image)
combined_array[organ_array > 0] = int(label)
combined_image = sitk.GetImageFromArray(combined_array)
combined_image.CopyInformation(resampled_organ_image)
# 保存合成影像
if combined_image:
output_filename = f'{image_type}_label_{filename}.nii.gz'
output_path = os.path.join(output_folder, output_filename)
sitk.WriteImage(combined_image, output_path)
print(f'Saved combined image to {output_path}')
file_counter += 1
# 處理 T1C 影像
print("Processing T1C images...")
process_images(T1C_folder_path, T1C_output_folder_path, 'T1C')
# 處理 CT+CTC 影像
print("Processing CT+CTC images...")
process_images(CTCTC_folder_path, CTCTC_output_folder_path, 'CT+CTC')
print("All processing completed.")

View file

@ -0,0 +1,138 @@
import os
import shutil
import difflib
def copy_organ_files(source_root, target_root):
# 確保目標資料夾存在
if not os.path.exists(target_root):
os.makedirs(target_root)
print(f"Created directory {target_root}")
# 遍歷來源的根資料夾和所有子資料夾,尋找名為 ORGAN 的資料夾
for root, dirs, files in os.walk(source_root):
for dir in dirs:
if dir == 'ORGAN':
source_folder_path = os.path.join(root, dir)
# 提取病例資料夾和日期資料夾
case_folder = os.path.basename(os.path.dirname(os.path.dirname(os.path.dirname(source_folder_path))))
date_folder = os.path.basename(os.path.dirname(os.path.dirname(source_folder_path)))
# 組合目標資料夾路徑
target_folder_path = os.path.join(target_root, case_folder, date_folder)
# 確保目標資料夾存在
if not os.path.exists(target_folder_path):
os.makedirs(target_folder_path)
print(f"Created directory {target_folder_path}")
# 複製資料夾中的所有檔案到目標資料夾
for file_name in os.listdir(source_folder_path):
source_file_path = os.path.join(source_folder_path, file_name)
target_file_path = os.path.join(target_folder_path, file_name)
# 如果目標檔案已經存在,重新命名以避免覆蓋
if os.path.exists(target_file_path):
base, extension = os.path.splitext(file_name)
counter = 1
new_file_name = f"{base}_{counter}{extension}"
target_file_path = os.path.join(target_folder_path, new_file_name)
while os.path.exists(target_file_path):
counter += 1
new_file_name = f"{base}_{counter}{extension}"
target_file_path = os.path.join(target_folder_path, new_file_name)
shutil.copy(source_file_path, target_file_path)
print(f"Copied {source_file_path} to {target_file_path}")
print("All ORGAN files have been copied.")
def check_and_copy_files(root_dir, required_file_groups, target_dir, match_cutoff=0.7):
# 確保目標目錄存在
os.makedirs(target_dir, exist_ok=True)
# 計算缺少文件的病人數量
missing_files_patients = 0
copied_files_patients = 0
# 遍歷根目錄下的每個病人文件夾
for patient_id in os.listdir(root_dir):
patient_dir = os.path.join(root_dir, patient_id)
# 確保是目錄
if os.path.isdir(patient_dir):
# 遍歷病人目錄下的日期文件夾
for date_folder in os.listdir(patient_dir):
date_dir = os.path.join(patient_dir, date_folder)
existing_files = os.listdir(date_dir)
# 檢查指定的OAR文件是否存在
missing_files = []
for file_group in required_file_groups:
# 對於左右眼的文件,進行更精確的匹配
if 'Left_Eye' in file_group[0]:
matches = [f for f in existing_files if 'Left_Eye' in f]
elif 'Right_Eye' in file_group[0]:
matches = [f for f in existing_files if 'Right_Eye' in f]
else:
# 對其他文件使用模糊匹配來查找接近的文件名稱
matches = difflib.get_close_matches(file_group[0], existing_files, n=1, cutoff=match_cutoff)
if not matches:
missing_files.append(file_group[0]) # 使用文件組中的第一個文件名作為缺失文件的表示
else:
print(f"匹配到的文件: {matches[0]} 對應到 {file_group[0]}")
if missing_files:
missing_files_patients += 1
print(f"病人: {patient_id}, 日期: {date_folder}, 缺失的文件: {missing_files}")
else:
# 沒有缺失文件,複製文件到新目錄
new_patient_dir = os.path.join(target_dir, patient_id)
new_date_dir = os.path.join(new_patient_dir, date_folder)
os.makedirs(new_date_dir, exist_ok=True)
for file_name in existing_files:
src_file = os.path.join(date_dir, file_name)
dst_file = os.path.join(new_date_dir, file_name)
shutil.copy(src_file, dst_file)
copied_files_patients += 1
print(f"病人: {patient_id}, 日期: {date_folder}, 文件已成功複製")
print(f"缺少所需OAR文件的資料總數: {missing_files_patients}")
print(f"文件完整且已複製的資料總數: {copied_files_patients}")
def count_patients_and_records(target_dir):
patient_count = 0
record_count = 0
for patient_id in os.listdir(target_dir):
patient_dir = os.path.join(target_dir, patient_id)
if os.path.isdir(patient_dir):
patient_count += 1
record_count += len(os.listdir(patient_dir))
print(f"完整OAR文件的病人總數: {patient_count}")
print(f"完整OAR文件的資料總數: {record_count}")
if __name__ == "__main__":
# 第一部分:複製 ORGAN 文件
source_root = '/home/onlylian/dataset2_G4_M6'
intermediate_target = '/home/onlylian/NEW_ORGAN_Files'
copy_organ_files(source_root, intermediate_target)
# 第二部分:檢查並複製完整的 OAR 文件
required_file_groups = [
['Struct_Brain_Stem.nii.gz'],
['Struct_Left_Optic_Nerve.nii.gz'],
['Struct_Right_Optic_Nerve.nii.gz'],
['Struct_Left_Eye.nii.gz'],
['Struct_Right_Eye.nii.gz'],
['Struct_Optic_Chiasm.nii.gz']
]
final_target = '/home/onlylian/Complete_NEW_OAR_Files'
check_and_copy_files(intermediate_target, required_file_groups, final_target)
# 計算總共的病人和記錄數量
count_patients_and_records(final_target)

View file

@ -0,0 +1,62 @@
import os
import shutil
import pandas as pd
# 讀取 Excel 文件
file_path = '/home/onlylian/patient_modalities_files.xlsx'
df = pd.read_excel(file_path)
# 定義來源和目標資料夾
source_dir = '/home/onlylian/Complete_NEW_ORGAN_Target_Files'
target_dir = '/home/onlylian/sorted_OAR_Target_files'
modalities = ['CT', 'CTC', 'T1', 'T1C', 'T2', 'FLAIR', 'CT+CTC']
# 創建目標資料夾結構
for modality in modalities:
modality_path = os.path.join(target_dir, modality)
os.makedirs(modality_path, exist_ok=True)
# 遍歷每個病人ID和日期
for _, row in df.iterrows():
modality = row['Modalities']
patient_id = row['patient_id']
date = str(row['date'])
if modality in modalities:
# 構建來源資料夾路徑
source_path = os.path.join(source_dir, patient_id, date)
print(f"Checking source path: {source_path}") # 檢查路徑
if os.path.exists(source_path):
# 分別處理 ORGAN 和 RT 目錄
organ_path = os.path.join(source_path, 'ORGAN')
rt_path = os.path.join(source_path, 'RT')
# 構建目標資料夾路徑
modality_path = os.path.join(target_dir, modality, patient_id, date)
os.makedirs(modality_path, exist_ok=True)
# 遍歷 ORGAN 目錄中的所有檔案
if os.path.exists(organ_path):
for file_name in os.listdir(organ_path):
if file_name.endswith('.nii.gz'): # 檢查檔案格式
source_file = os.path.join(organ_path, file_name)
target_file = os.path.join(modality_path, file_name)
print(f"Copying {source_file} to {target_file}")
shutil.copy2(source_file, target_file)
else:
print(f"ORGAN folder not found: {organ_path}")
# 遍歷 RT 目錄中的所有檔案
if os.path.exists(rt_path):
for file_name in os.listdir(rt_path):
if file_name.endswith('.nii.gz'): # 檢查檔案格式
source_file = os.path.join(rt_path, file_name)
target_file = os.path.join(modality_path, file_name)
print(f"Copying {source_file} to {target_file}")
shutil.copy2(source_file, target_file)
else:
print(f"RT folder not found: {rt_path}")
else:
print(f"Source path not found: {source_path}")
else:
print(f"Unknown modality {modality} for patient {patient_id} on {date}")

View file

@ -0,0 +1,47 @@
import os
import shutil
def merge_directories(src1, src2, dest):
if not os.path.exists(dest):
os.makedirs(dest)
def copy_or_merge(src, dest):
for patient in os.listdir(src):
patient_src_path = os.path.join(src, patient)
patient_dest_path = os.path.join(dest, patient)
if os.path.isdir(patient_src_path):
if os.path.exists(patient_dest_path):
# If destination folder exists, merge the contents
for item in os.listdir(patient_src_path):
s = os.path.join(patient_src_path, item)
d = os.path.join(patient_dest_path, item)
if os.path.isdir(s):
shutil.copytree(s, d, dirs_exist_ok=True)
else:
shutil.copy2(s, d)
else:
# If destination folder does not exist, copy the whole folder
shutil.copytree(patient_src_path, patient_dest_path)
# Copy or merge from both source directories to destination directory
copy_or_merge(src1, dest)
copy_or_merge(src2, dest)
print("Directories merged successfully.")
# Define source and destination paths
src1 = '/mnt/1220/Public/dataset2/G4'
src2 = '/mnt/1220/Public/dataset2/M6'
dest = "/home/onlylian/dataset2_G4_M6"
# Call the function to merge the directories
merge_directories(src1, src2, dest)

View file

@ -0,0 +1,46 @@
import os
import shutil
import re
def extract_patient_id(filename):
# 假設文件名格式為 "something_PATIENTID.nii.gz"
match = re.search(r'_([A-Z0-9]+)\.nii\.gz$', filename)
if match:
return match.group(1)
return None
def merge_folders(folder1, folder2, output_folder):
# 確保輸出資料夾存在
if not os.path.exists(output_folder):
os.makedirs(output_folder)
# 獲取兩個資料夾中的所有文件
files1 = set(os.listdir(folder1))
files2 = set(os.listdir(folder2))
# 合併所有唯一的病人ID
all_files = files1.union(files2)
for file in all_files:
patient_id = extract_patient_id(file)
if patient_id:
new_filename = f"label_{patient_id}.nii.gz"
source_folder = folder1 if file in files1 else folder2
source_path = os.path.join(source_folder, file)
dest_path = os.path.join(output_folder, new_filename)
# 複製文件到輸出資料夾並重命名
shutil.copy2(source_path, dest_path)
print(f"Copied and renamed {file} to {new_filename}")
else:
print(f"Skipped {file} - could not extract patient ID")
# 設定資料夾路徑
folder1 = '/home/onlylian/resampled_T1C_TV_label'
folder2 = '/home/onlylian/resampled_CT+CTC_TV_label'
output_folder = '/home/onlylian/merged_TV_label_folder'
# 執行合併
merge_folders(folder1, folder2, output_folder)
print("Merging complete!")

View file

@ -0,0 +1,156 @@
import os
import shutil
import difflib
import time
def copy_organ_and_tv_files(source_root, target_root):
# 確保目標資料夾存在
if not os.path.exists(target_root):
os.makedirs(target_root)
print(f"Created directory {target_root}")
# 複製 'ORGAN' 資料夾的檔案
for root, dirs, files in os.walk(source_root):
for dir in dirs:
if dir == 'ORGAN':
source_folder_path = os.path.join(root, dir)
case_folder = os.path.basename(os.path.dirname(os.path.dirname(os.path.dirname(source_folder_path))))
date_folder = os.path.basename(os.path.dirname(os.path.dirname(source_folder_path)))
target_folder_path = os.path.join(target_root, case_folder, date_folder, 'ORGAN')
if not os.path.exists(target_folder_path):
os.makedirs(target_folder_path)
print(f"Created directory {target_folder_path}")
for file_name in os.listdir(source_folder_path):
source_file_path = os.path.join(source_folder_path, file_name)
target_file_path = os.path.join(target_folder_path, file_name)
copy_with_retry(source_file_path, target_file_path)
# 複製 TV 檔案
for root, dirs, files in os.walk(source_root):
for file_name in files:
if file_name.startswith('TV') and file_name.endswith('.nii.gz'):
source_file_path = os.path.join(root, file_name)
case_folder = os.path.basename(os.path.dirname(os.path.dirname(os.path.dirname(source_file_path))))
date_folder = os.path.basename(os.path.dirname(os.path.dirname(source_file_path)))
target_folder_path = os.path.join(target_root, case_folder, date_folder, 'RT')
if not os.path.exists(target_folder_path):
os.makedirs(target_folder_path)
print(f"Created directory {target_folder_path}")
target_file_path = os.path.join(target_folder_path, file_name)
copy_with_retry(source_file_path, target_file_path)
print("All ORGAN and TV files have been copied.")
def check_and_copy_files(root_dir, required_oar_files, target_dir):
os.makedirs(target_dir, exist_ok=True)
missing_files_patients = 0
copied_files_patients = 0
for patient_id in os.listdir(root_dir):
patient_dir = os.path.join(root_dir, patient_id)
if os.path.isdir(patient_dir):
for date_folder in os.listdir(patient_dir):
date_dir = os.path.join(patient_dir, date_folder)
organ_dir = os.path.join(date_dir, 'ORGAN')
rt_dir = os.path.join(date_dir, 'RT')
if os.path.isdir(organ_dir) and os.path.isdir(rt_dir):
organ_files = os.listdir(organ_dir)
rt_files = os.listdir(rt_dir)
missing_oar_files = []
for oar_file in required_oar_files:
if 'Left_Eye' in oar_file:
matches = [f for f in organ_files if 'Left_Eye' in f]
elif 'Right_Eye' in oar_file:
matches = [f for f in organ_files if 'Right_Eye' in f]
else:
matches = difflib.get_close_matches(oar_file, organ_files, n=1, cutoff=0.8)
if not matches:
missing_oar_files.append(oar_file)
target_files = [f for f in rt_files if f.endswith('.nii.gz')]
if not target_files:
missing_oar_files.append('Target File')
if missing_oar_files:
missing_files_patients += 1
print(f"病人: {patient_id}, 日期: {date_folder}, 缺失的文件: {missing_oar_files}")
else:
new_patient_dir = os.path.join(target_dir, patient_id, date_folder)
os.makedirs(new_patient_dir, exist_ok=True)
for file_name in organ_files:
src_file = os.path.join(organ_dir, file_name)
dst_file = os.path.join(new_patient_dir, 'ORGAN', file_name)
os.makedirs(os.path.join(new_patient_dir, 'ORGAN'), exist_ok=True)
copy_with_retry(src_file, dst_file)
for file_name in rt_files:
src_file = os.path.join(rt_dir, file_name)
dst_file = os.path.join(new_patient_dir, 'RT', file_name)
os.makedirs(os.path.join(new_patient_dir, 'RT'), exist_ok=True)
copy_with_retry(src_file, dst_file)
copied_files_patients += 1
print(f"病人: {patient_id}, 日期: {date_folder}, 文件已成功複製")
else:
missing_files_patients += 1
print(f"病人: {patient_id}, 日期: {date_folder}, 缺少 ORGAN 或 RT 目錄")
print(f"缺少所需文件的資料總數: {missing_files_patients}")
print(f"文件完整且已複製的資料總數: {copied_files_patients}")
def copy_with_retry(src, dst, retries=3, delay=5):
for attempt in range(retries):
try:
shutil.copy(src, dst)
print(f"Copied {src} to {dst}")
return
except BlockingIOError as e:
if attempt < retries - 1:
print(f"資源暫時不可用,等待 {delay} 秒後重試... (第 {attempt + 1} 次)")
time.sleep(delay)
else:
raise e
def count_patients_and_records(target_dir):
patient_count = 0
record_count = 0
for patient_id in os.listdir(target_dir):
patient_dir = os.path.join(target_dir, patient_id)
if os.path.isdir(patient_dir):
patient_count += 1
record_count += len(os.listdir(patient_dir))
print(f"完整OAR和Target文件的病人總數: {patient_count}")
print(f"完整OAR和Target文件的資料總數: {record_count}")
if __name__ == "__main__":
# 設定路徑
source_root = '/home/onlylian/dataset2_G4_M6'
intermediate_target = '/home/onlylian/NEW_ORGAN_Target_Files'
final_target = '/home/onlylian/Complete_NEW_ORGAN_Target_Files'
# 指定要檢查的OAR文件
required_oar_files = [
'Struct_Brain_Stem.nii.gz',
'Struct_Left_Optic_Nerve.nii.gz',
'Struct_Right_Optic_Nerve.nii.gz',
'Struct_Left_Eye.nii.gz',
'Struct_Right_Eye.nii.gz',
'Struct_Optic_Chiasm.nii.gz'
]
# 執行複製和檢查過程
copy_organ_and_tv_files(source_root, intermediate_target)
check_and_copy_files(intermediate_target, required_oar_files, final_target)
count_patients_and_records(final_target)

View file

@ -0,0 +1,107 @@
import os
import glob
import SimpleITK as sitk
from difflib import get_close_matches
def process_images(input_base_path, output_base_path, output_prefix):
# 定義組織標籤對應關係
labels = {
"background": "0",
"Struct_Brain_Stem": "1",
"Struct_Right_Eye": "2",
"Struct_Left_Eye": "3",
"Struct_Optic_Chiasm": "4",
"Struct_Right_Optic_Nerve": "5",
"Struct_Left_Optic_Nerve": "6",
"TV": "7" # Tumor
}
# 確保輸出資料夾存在
os.makedirs(output_base_path, exist_ok=True)
# 定義重新取樣函數
def resample_image(image, reference_image):
resample = sitk.ResampleImageFilter()
resample.SetReferenceImage(reference_image)
resample.SetInterpolator(sitk.sitkNearestNeighbor) # 使用最近鄰插值避免標籤變化
resample.SetDefaultPixelValue(0)
resampled_image = resample.Execute(image)
return resampled_image
# 遍歷所有病人的資料夾
for patient_id in os.listdir(input_base_path):
patient_folder_path = os.path.join(input_base_path, patient_id)
if not os.path.isdir(patient_folder_path):
continue # 跳過非資料夾的項目
# 確定病人資料夾下的日期資料夾
date_folder = os.listdir(patient_folder_path)[0]
ORGAN_folder_path = os.path.join(patient_folder_path, date_folder)
# 創建一個空的影像來合併所有標籤
combined_image = None
# 列出病人資料夾內的所有檔案
all_files = os.listdir(ORGAN_folder_path)
# 遍歷每個標籤並合併
for organ_name, label in labels.items():
if organ_name == "TV":
# 動態搜尋 TV 檔案
tv_files = glob.glob(os.path.join(ORGAN_folder_path, "TV-*.nii.gz"))
if len(tv_files) == 0:
print(f"TV file not found for patient {patient_id}, skipping TV...")
continue
organ_path = tv_files[0] # 取第一個找到的檔案
else:
# 使用模糊比對尋找最接近的檔案名
matches = get_close_matches(organ_name, all_files, n=1, cutoff=0.6)
if len(matches) == 0:
print(f"No close match found for {organ_name} for patient {patient_id}, skipping...")
continue
organ_filename = matches[0]
organ_path = os.path.join(ORGAN_folder_path, organ_filename)
if not os.path.exists(organ_path):
print(f"File {organ_path} not found for patient {patient_id}, skipping...")
continue
organ_image = sitk.ReadImage(organ_path)
if combined_image is None:
# 初始化合併影像的大小和參考
combined_image = sitk.Image(organ_image.GetSize(), sitk.sitkUInt8)
combined_image.CopyInformation(organ_image)
else:
# 如果器官影像的尺寸和合併影像不同,進行重新取樣
if organ_image.GetSize() != combined_image.GetSize():
print(f"Resampling organ {organ_name} for patient {patient_id} due to size mismatch...")
organ_image = resample_image(organ_image, combined_image)
# 將器官影像添加到合併影像中
organ_array = sitk.GetArrayFromImage(organ_image)
combined_array = sitk.GetArrayFromImage(combined_image)
# 更新合併影像的陣列,根據標籤值來設定非背景區域
combined_array[organ_array > 0] = int(label)
# 將更新的陣列轉回 SimpleITK 影像
combined_image = sitk.GetImageFromArray(combined_array)
combined_image.CopyInformation(organ_image)
# 保存合併後的影像
output_filename = f'{output_prefix}_label_{patient_id}.nii.gz'
output_path = os.path.join(output_base_path, output_filename)
sitk.WriteImage(combined_image, output_path)
print(f'Saved combined image for patient {patient_id} to {output_path}')
if __name__ == "__main__":
# 處理 T1C 影像
T1C_input_path = '/home/onlylian/sorted_OAR_Target_files/T1C'
T1C_output_path = '/home/onlylian/T1C_OAR_Target_label_folder'
process_images(T1C_input_path, T1C_output_path, "T1C")
# 處理 CT+CTC 影像
CT_CTC_input_path = '/home/onlylian/sorted_OAR_Target_files/T1C' # 注意:這裡使用相同的輸入路徑,如果需要不同的路徑請修改
CT_CTC_output_path = '/home/onlylian/CT+CTC_OAR_Target_label_folder'
process_images(CT_CTC_input_path, CT_CTC_output_path, "CT+CTC")

View file

@ -0,0 +1,108 @@
import SimpleITK as sitk
import os
import numpy as np
def standardize_origin(image, target_origin):
"""將影像的原點標準化為目標原點"""
return sitk.Resample(
image,
image.GetSize(),
sitk.Transform(),
sitk.sitkLinear,
target_origin,
image.GetSpacing(),
image.GetDirection(),
0,
image.GetPixelID()
)
def resample_image(image, reference_image, is_label=False):
resampler = sitk.ResampleImageFilter()
resampler.SetReferenceImage(reference_image)
resampler.SetTransform(sitk.Transform())
resampler.SetOutputSpacing(reference_image.GetSpacing())
resampler.SetSize(reference_image.GetSize())
resampler.SetOutputDirection(reference_image.GetDirection())
resampler.SetOutputOrigin(reference_image.GetOrigin())
if is_label:
resampler.SetInterpolator(sitk.sitkNearestNeighbor)
resampler.SetDefaultPixelValue(0)
else:
resampler.SetInterpolator(sitk.sitkLinear)
resampler.SetDefaultPixelValue(image.GetPixelIDValue())
return resampler.Execute(image)
def process_patient(t1c_image_path, ctctc_image_path, label_path, output_t1c_dir, output_ctctc_dir, output_label_dir, patient_id):
print(f"\nProcessing patient: {patient_id}")
# 讀取影像
t1c_image = sitk.ReadImage(t1c_image_path)
ctctc_image = sitk.ReadImage(ctctc_image_path)
label = sitk.ReadImage(label_path)
print(f" Original T1C image size: {t1c_image.GetSize()}, origin: {t1c_image.GetOrigin()}")
print(f" Original CT+CTC image size: {ctctc_image.GetSize()}, origin: {ctctc_image.GetOrigin()}")
print(f" Original label size: {label.GetSize()}, origin: {label.GetOrigin()}")
# 使用 T1C 影像作為參考
reference_image = t1c_image
# 重採樣 CT+CTC 影像以匹配 T1C 影像
resampled_ctctc = resample_image(ctctc_image, reference_image)
# 重採樣 label 以匹配 T1C 影像
resampled_label = resample_image(label, reference_image, is_label=True)
print(f" Resampled CT+CTC image size: {resampled_ctctc.GetSize()}, origin: {resampled_ctctc.GetOrigin()}")
print(f" Resampled label size: {resampled_label.GetSize()}, origin: {resampled_label.GetOrigin()}")
# 保存重採樣後的影像和標籤
t1c_output_path = os.path.join(output_t1c_dir, f"T1C_{patient_id}.nii.gz")
ctctc_output_path = os.path.join(output_ctctc_dir, f"CT+CTC_{patient_id}.nii.gz")
label_output_path = os.path.join(output_label_dir, f"label_{patient_id}.nii.gz")
sitk.WriteImage(t1c_image, t1c_output_path)
sitk.WriteImage(resampled_ctctc, ctctc_output_path)
sitk.WriteImage(resampled_label, label_output_path)
print(f" T1C image saved to: {t1c_output_path}")
print(f" Resampled CT+CTC image saved to: {ctctc_output_path}")
print(f" Resampled label saved to: {label_output_path}")
def process_directory(t1c_dir, ctctc_dir, label_dir, output_t1c_dir, output_ctctc_dir, output_label_dir):
print("Starting directory processing")
os.makedirs(output_t1c_dir, exist_ok=True)
os.makedirs(output_ctctc_dir, exist_ok=True)
os.makedirs(output_label_dir, exist_ok=True)
# 獲取所有病人 ID
patient_ids = set()
for filename in os.listdir(t1c_dir):
if filename.startswith("T1C_") and filename.endswith(".nii.gz"):
patient_id = filename[4:-7] # 去掉 "T1C_" 和 ".nii.gz"
patient_ids.add(patient_id)
for patient_id in patient_ids:
t1c_path = os.path.join(t1c_dir, f"T1C_{patient_id}.nii.gz")
ctctc_path = os.path.join(ctctc_dir, f"CT+CTC_{patient_id}.nii.gz")
label_path = os.path.join(label_dir, f"T1C_label_{patient_id}.nii.gz") # 假設 label 文件名與 T1C 相同
if os.path.exists(t1c_path) and os.path.exists(ctctc_path) and os.path.exists(label_path):
process_patient(t1c_path, ctctc_path, label_path, output_t1c_dir, output_ctctc_dir, output_label_dir, patient_id)
else:
print(f" WARNING: Missing files for patient {patient_id}")
# 主程序
t1c_dir = '/home/onlylian/T1C_image_folder'
ctctc_dir = '/home/onlylian/CT+CTC_image_folder'
label_dir = '/home/onlylian/T1C_label_folder' # 使用 T1C 的 label
output_t1c_dir = '/home/onlylian/resampled_T1C_image'
output_ctctc_dir = '/home/onlylian/resampled_CT+CTC_image'
output_label_dir = '/home/onlylian/resampled_label'
print("Starting image preprocessing")
process_directory(t1c_dir, ctctc_dir, label_dir, output_t1c_dir, output_ctctc_dir, output_label_dir)
print("All processing completed.")

View file

@ -0,0 +1,119 @@
import json
import os
import shutil
import glob
import re
import pandas as pd
# 創建 dataset.json 內容
dataset_json = {
"channel_names": {
"0": "T1C"
},
"labels": {
"background": "0",
"Brainstem": "1",
"Right_Eye": "2",
"Left_Eye": "3",
"Optic_Chiasm": "4",
"Right_Optic_Nerve": "5",
"Left_Optic_Nerve": "6",
"TV": "7"
},
"numTraining": 0,
"file_ending": ".nii.gz"
}
# 定義目錄
source_image_dir = "/home/onlylian/resampled_T1C_TV_image"
source_label_dir = "/home/onlylian/resampled_tv_label"
target_base_dir = "/home/onlylian/nnUNet/nnUNet_raw/Dataset012_OAR_TV"
imagesTr_dir = os.path.join(target_base_dir, "imagesTr")
labelsTr_dir = os.path.join(target_base_dir, "labelsTr")
# 創建目錄
os.makedirs(imagesTr_dir, exist_ok=True)
os.makedirs(labelsTr_dir, exist_ok=True)
def extract_patient_id(filename):
match = re.search(r'(T1C|label)_([^.]+)\.nii\.gz', os.path.basename(filename))
return match.group(2) if match else None
# 獲取並排序原始圖像文件
source_image_files = sorted(glob.glob(os.path.join(source_image_dir, "*.nii.gz")))
processed_files = 0
patient_mapping = []
# 創建病歷號到文件名的映射
label_files = glob.glob(os.path.join(source_label_dir, "*.nii.gz"))
label_map = {extract_patient_id(f): f for f in label_files}
print(f"找到 {len(label_map)} 個標籤文件")
print(f"找到 {len(source_image_files)} 個圖像文件")
# 處理所有資料
for index, source_image_file in enumerate(source_image_files, start=1):
if index % 10 == 0: # 每處理10個文件顯示一次進度
print(f"\n正在處理檔案 {index}/{len(source_image_files)}:")
patient_id = extract_patient_id(source_image_file)
if patient_id is None:
print(f"警告: 無法提取病歷號,跳過文件: {source_image_file}")
continue
# 使用新的文件名格式
new_filename = f"OAR_TV_{index:03d}"
# 複製並重命名圖像文件
target_image_file = os.path.join(imagesTr_dir, f"{new_filename}_0000.nii.gz")
shutil.copyfile(source_image_file, target_image_file)
# 處理標籤文件
if patient_id in label_map:
source_label_file = label_map[patient_id]
target_label_file = os.path.join(labelsTr_dir, f"{new_filename}.nii.gz")
shutil.copyfile(source_label_file, target_label_file)
processed_files += 1
# 添加到映射列表
patient_mapping.append({
"編號": f"{index:03d}",
"病歷號": patient_id
})
# 輸出處理細節
print(f"T1C: {source_image_file}")
print(f"複製圖像檔案: {source_image_file}{target_image_file}")
print(f"複製標籤檔案: {source_label_file}{target_label_file}")
print() # 空行分隔每筆記錄
else:
print(f"警告: 未找到 {patient_id} 的標籤檔案。")
print("\n所有資料處理完畢。")
# 更新 dataset.json
dataset_json["numTraining"] = processed_files
# 寫入 dataset.json 檔案
dataset_json_path = os.path.join(target_base_dir, "dataset.json")
with open(dataset_json_path, 'w') as f:
json.dump(dataset_json, f, indent=4)
print(f"dataset.json 已寫入到 {dataset_json_path}")
print(f"總共處理了 {processed_files} 個完整的資料集(圖像加標籤)")
# 創建 Excel 文件
df = pd.DataFrame(patient_mapping)
excel_path = os.path.join(target_base_dir, "patient_mapping.xlsx")
df.to_excel(excel_path, index=False)
print(f"病人映射表已保存到 {excel_path}")
# 輸出處理摘要
print("\n處理摘要:")
print(f"總共找到的圖像: {len(source_image_files)}")
print(f"總共找到的標籤文件: {len(label_map)}")
print(f"成功處理的完整資料集: {processed_files}")
print(f"跳過的文件數量: {len(source_image_files) - processed_files}")

View file

@ -0,0 +1,128 @@
import json
import os
import shutil
import glob
import re
import pandas as pd
# 創建 dataset.json 內容
dataset_json = {
"channel_names": {
"0": "CT+CTC",
"1": "T1C"
},
"labels": {
"background": "0",
"Brainstem": "1",
"Right_Eye": "2",
"Left_Eye": "3",
"Optic_Chiasm": "4",
"Right_Optic_Nerve": "5",
"Left_Optic_Nerve": "6"
},
"numTraining": 0,
"file_ending": ".nii.gz"
}
# 定義目錄
source_image1_dir = "/home/onlylian/resampled_CT+CTC_image"
source_image2_dir = "/home/onlylian/resampled_T1C_image"
source_label_dir = "/home/onlylian/resampled_label"
target_base_dir = "/home/onlylian/nnUNet/nnUNet_raw/Dataset021_OAR"
imagesTr_dir = os.path.join(target_base_dir, "imagesTr")
labelsTr_dir = os.path.join(target_base_dir, "labelsTr")
# 創建目錄
os.makedirs(imagesTr_dir, exist_ok=True)
os.makedirs(labelsTr_dir, exist_ok=True)
def extract_patient_id(filename):
match = re.search(r'(CT\+CTC|T1C|label)_([^.]+)\.nii\.gz', os.path.basename(filename))
return match.group(2) if match else None
# 獲取並排序原始圖像文件
source_image1_files = sorted(glob.glob(os.path.join(source_image1_dir, "*.nii.gz")))
source_image2_files = sorted(glob.glob(os.path.join(source_image2_dir, "*.nii.gz")))
processed_files = 0
patient_mapping = []
# 創建病歷號到文件名的映射
label_files = glob.glob(os.path.join(source_label_dir, "*.nii.gz"))
label_map = {extract_patient_id(f): f for f in label_files}
print(f"找到 {len(label_map)} 個標籤文件")
print(f"找到 {len(source_image1_files)} 個 CT+CTC 圖像文件")
print(f"找到 {len(source_image2_files)} 個 T1C 圖像文件")
# 處理所有資料
for index, (source_image1_file, source_image2_file) in enumerate(zip(source_image1_files, source_image2_files), start=1):
if index % 10 == 0: # 每處理10個文件顯示一次進度
print(f"\n正在處理檔案 {index}/{len(source_image1_files)}:")
patient_id1 = extract_patient_id(source_image1_file)
patient_id2 = extract_patient_id(source_image2_file)
if patient_id1 != patient_id2 or patient_id1 is None or patient_id2 is None:
print(f"警告: 病歷號不匹配或無法提取,跳過這對文件: {source_image1_file}, {source_image2_file}")
continue
# 使用新的文件名格式
new_filename = f"OAR_{index:03d}"
# 複製並重命名圖像文件
target_image1_file = os.path.join(imagesTr_dir, f"{new_filename}_0000.nii.gz")
target_image2_file = os.path.join(imagesTr_dir, f"{new_filename}_0001.nii.gz")
shutil.copyfile(source_image1_file, target_image1_file)
shutil.copyfile(source_image2_file, target_image2_file)
# 處理標籤文件
if patient_id1 in label_map:
source_label_file = label_map[patient_id1]
target_label_file = os.path.join(labelsTr_dir, f"{new_filename}.nii.gz")
shutil.copyfile(source_label_file, target_label_file)
processed_files += 1
# 添加到映射列表
patient_mapping.append({
"編號": f"{index:03d}",
"病歷號": patient_id1
})
# 輸出處理細節
print(f"CT+CTC: {source_image1_file}")
print(f"T1C: {source_image2_file}")
print(f"複製圖像檔案: {source_image1_file}{target_image1_file}")
print(f"複製圖像檔案: {source_image2_file}{target_image2_file}")
print(f"複製標籤檔案: {source_label_file}{target_label_file}")
print() # 空行分隔每筆記錄
else:
print(f"警告: 未找到 {patient_id1} 的標籤檔案。")
print("\n所有資料處理完畢。")
# 更新 dataset.json
dataset_json["numTraining"] = processed_files
# 寫入 dataset.json 檔案
dataset_json_path = os.path.join(target_base_dir, "dataset.json")
with open(dataset_json_path, 'w') as f:
json.dump(dataset_json, f, indent=4)
print(f"dataset.json 已寫入到 {dataset_json_path}")
print(f"總共處理了 {processed_files} 個完整的資料集(圖像對加標籤)")
# 創建 Excel 文件
df = pd.DataFrame(patient_mapping)
excel_path = os.path.join(target_base_dir, "patient_mapping.xlsx")
df.to_excel(excel_path, index=False)
print(f"病人映射表已保存到 {excel_path}")
# 輸出處理摘要
print("\n處理摘要:")
print(f"總共找到的 CT+CTC 圖像: {len(source_image1_files)}")
print(f"總共找到的 T1C 圖像: {len(source_image2_files)}")
print(f"總共找到的標籤文件: {len(label_map)}")
print(f"成功處理的完整資料集: {processed_files}")
print(f"跳過的文件對數量: {len(source_image1_files) - processed_files}")

View file

@ -0,0 +1,130 @@
import json
import os
import shutil
import glob
import re
import pandas as pd
# 創建 dataset.json 內容
dataset_json = {
"channel_names": {
"0": "CT+CTC",
"1": "T1C"
},
"labels": {
"background": "0",
"Brainstem": "1",
"Right_Eye": "2",
"Left_Eye": "3",
"Optic_Chiasm": "4",
"Right_Optic_Nerve": "5",
"Left_Optic_Nerve": "6",
"TV": "7"
},
"numTraining": 0,
"file_ending": ".nii.gz"
}
# 定義目錄
source_image1_dir = "/home/onlylian/resampled_CT+CTC_TV_image"
source_image2_dir = "/home/onlylian/resampled_T1C_TV_image"
source_label_dir = "/home/onlylian/resampled_tv_label"
target_base_dir = "/home/onlylian/nnUNet/nnUNet_raw/Dataset022_OAR_TV"
imagesTr_dir = os.path.join(target_base_dir, "imagesTr")
labelsTr_dir = os.path.join(target_base_dir, "labelsTr")
# 創建目錄
os.makedirs(imagesTr_dir, exist_ok=True)
os.makedirs(labelsTr_dir, exist_ok=True)
def extract_patient_id(filename):
match = re.search(r'(CT\+CTC|T1C|label)_([^.]+)\.nii\.gz', os.path.basename(filename))
return match.group(2) if match else None
# 獲取並排序原始圖像文件
source_image1_files = sorted(glob.glob(os.path.join(source_image1_dir, "*.nii.gz")))
source_image2_files = sorted(glob.glob(os.path.join(source_image2_dir, "*.nii.gz")))
processed_files = 0
patient_mapping = []
# 創建病歷號到文件名的映射
label_files = glob.glob(os.path.join(source_label_dir, "*.nii.gz"))
label_map = {extract_patient_id(f): f for f in label_files}
print(f"找到 {len(label_map)} 個標籤文件")
print(f"找到 {len(source_image1_files)} 個 CT+CTC 圖像文件")
print(f"找到 {len(source_image2_files)} 個 T1C 圖像文件")
# 處理所有資料
for index, (source_image1_file, source_image2_file) in enumerate(zip(source_image1_files, source_image2_files), start=1):
if index % 10 == 0: # 每處理10個文件顯示一次進度
print(f"\n正在處理檔案 {index}/{len(source_image1_files)}:")
patient_id1 = extract_patient_id(source_image1_file)
patient_id2 = extract_patient_id(source_image2_file)
if patient_id1 != patient_id2 or patient_id1 is None or patient_id2 is None:
print(f"警告: 病歷號不匹配或無法提取,跳過這對文件: {source_image1_file}, {source_image2_file}")
continue
# 使用新的文件名格式
new_filename = f"OAR_TV_{index:03d}"
# 複製並重命名圖像文件
target_image1_file = os.path.join(imagesTr_dir, f"{new_filename}_0000.nii.gz")
target_image2_file = os.path.join(imagesTr_dir, f"{new_filename}_0001.nii.gz")
shutil.copyfile(source_image1_file, target_image1_file)
shutil.copyfile(source_image2_file, target_image2_file)
# 處理標籤文件
if patient_id1 in label_map:
source_label_file = label_map[patient_id1]
target_label_file = os.path.join(labelsTr_dir, f"{new_filename}.nii.gz")
shutil.copyfile(source_label_file, target_label_file)
processed_files += 1
# 添加到映射列表
patient_mapping.append({
"編號": f"{index:03d}",
"病歷號": patient_id1
})
# 輸出處理細節
print(f"CT+CTC: {source_image1_file}")
print(f"T1C: {source_image2_file}")
print(f"複製圖像檔案: {source_image1_file}{target_image1_file}")
print(f"複製圖像檔案: {source_image2_file}{target_image2_file}")
print(f"複製標籤檔案: {source_label_file}{target_label_file}")
print() # 空行分隔每筆記錄
else:
print(f"警告: 未找到 {patient_id1} 的標籤檔案。")
print("\n所有資料處理完畢。")
# 更新 dataset.json
dataset_json["numTraining"] = processed_files
# 寫入 dataset.json 檔案
dataset_json_path = os.path.join(target_base_dir, "dataset.json")
with open(dataset_json_path, 'w') as f:
json.dump(dataset_json, f, indent=4)
print(f"dataset.json 已寫入到 {dataset_json_path}")
print(f"總共處理了 {processed_files} 個完整的資料集(圖像對加標籤)")
# 創建 Excel 文件
df = pd.DataFrame(patient_mapping)
excel_path = os.path.join(target_base_dir, "patient_mapping.xlsx")
df.to_excel(excel_path, index=False)
print(f"病人映射表已保存到 {excel_path}")
# 輸出處理摘要
print("\n處理摘要:")
print(f"總共找到的 CT+CTC 圖像: {len(source_image1_files)}")
print(f"總共找到的 T1C 圖像: {len(source_image2_files)}")
print(f"總共找到的標籤文件: {len(label_map)}")
print(f"成功處理的完整資料集: {processed_files}")
print(f"跳過的文件對數量: {len(source_image1_files) - processed_files}")

View file

@ -0,0 +1,110 @@
import SimpleITK as sitk
import os
import numpy as np
def standardize_origin(image, target_origin):
"""將影像的原點標準化為目標原點"""
return sitk.Resample(
image,
image.GetSize(),
sitk.Transform(),
sitk.sitkLinear,
target_origin,
image.GetSpacing(),
image.GetDirection(),
0,
image.GetPixelID()
)
def resample_image(image, reference_image, is_label=False):
resampler = sitk.ResampleImageFilter()
resampler.SetReferenceImage(reference_image)
resampler.SetTransform(sitk.Transform())
resampler.SetOutputSpacing(reference_image.GetSpacing())
resampler.SetSize(reference_image.GetSize())
resampler.SetOutputDirection(reference_image.GetDirection())
resampler.SetOutputOrigin(reference_image.GetOrigin())
if is_label:
resampler.SetInterpolator(sitk.sitkNearestNeighbor)
resampler.SetDefaultPixelValue(0)
else:
resampler.SetInterpolator(sitk.sitkLinear)
resampler.SetDefaultPixelValue(image.GetPixelIDValue())
return resampler.Execute(image)
def process_patient(t1c_image_path, ctctc_image_path, label_path, output_t1c_dir, output_ctctc_dir, output_label_dir, patient_id):
print(f"\nProcessing patient: {patient_id}")
# 讀取影像
t1c_image = sitk.ReadImage(t1c_image_path)
ctctc_image = sitk.ReadImage(ctctc_image_path)
label = sitk.ReadImage(label_path)
print(f" Original T1C image size: {t1c_image.GetSize()}, origin: {t1c_image.GetOrigin()}")
print(f" Original CT+CTC image size: {ctctc_image.GetSize()}, origin: {ctctc_image.GetOrigin()}")
print(f" Original label size: {label.GetSize()}, origin: {label.GetOrigin()}")
# 使用 T1C 影像作為參考
reference_image = t1c_image
# 重採樣 CT+CTC 影像以匹配 T1C 影像
resampled_ctctc = resample_image(ctctc_image, reference_image)
# 重採樣 label 以匹配 T1C 影像
resampled_label = resample_image(label, reference_image, is_label=True)
print(f" Resampled CT+CTC image size: {resampled_ctctc.GetSize()}, origin: {resampled_ctctc.GetOrigin()}")
print(f" Resampled label size: {resampled_label.GetSize()}, origin: {resampled_label.GetOrigin()}")
# 保存重採樣後的影像和標籤
t1c_output_path = os.path.join(output_t1c_dir, f"T1C_{patient_id}.nii.gz")
ctctc_output_path = os.path.join(output_ctctc_dir, f"CT+CTC_{patient_id}.nii.gz")
label_output_path = os.path.join(output_label_dir, f"label_{patient_id}.nii.gz")
sitk.WriteImage(t1c_image, t1c_output_path)
sitk.WriteImage(resampled_ctctc, ctctc_output_path)
sitk.WriteImage(resampled_label, label_output_path)
print(f" T1C image saved to: {t1c_output_path}")
print(f" Resampled CT+CTC image saved to: {ctctc_output_path}")
print(f" Resampled label saved to: {label_output_path}")
def process_directory(t1c_dir, ctctc_dir, label_dir, output_t1c_dir, output_ctctc_dir, output_label_dir):
print("Starting directory processing")
os.makedirs(output_t1c_dir, exist_ok=True)
os.makedirs(output_ctctc_dir, exist_ok=True)
os.makedirs(output_label_dir, exist_ok=True)
# 獲取所有病人 ID
patient_ids = set()
for filename in os.listdir(t1c_dir):
if filename.startswith("T1C_") and filename.endswith(".nii.gz"):
patient_id = filename[4:-7] # 去掉 "T1C_" 和 ".nii.gz"
patient_ids.add(patient_id)
for patient_id in patient_ids:
t1c_path = os.path.join(t1c_dir, f"T1C_{patient_id}.nii.gz")
ctctc_path = os.path.join(ctctc_dir, f"CT+CTC_{patient_id}.nii.gz")
label_path = os.path.join(label_dir, f"T1C_label_{patient_id}.nii.gz") # 假設 label 文件名與 T1C 相同
if os.path.exists(t1c_path) and os.path.exists(ctctc_path) and os.path.exists(label_path):
process_patient(t1c_path, ctctc_path, label_path, output_t1c_dir, output_ctctc_dir, output_label_dir, patient_id)
else:
print(f" WARNING: Missing files for patient {patient_id}")
# 主程序
t1c_dir = '/home/onlylian/T1C_image_folder'
ctctc_dir = '/home/onlylian/CT+CTC_image_folder'
label_dir = '/home/onlylian/T1C_OAR_Target_label_folder' # 使用 T1C 的 label
output_t1c_dir = '/home/onlylian/resampled_T1C_TV_image_2'
output_ctctc_dir = '/home/onlylian/resampled_CT+CTC_TV_image_2'
output_label_dir = '/home/onlylian/resampled_tv_label'
print("Starting image preprocessing")
process_directory(t1c_dir, ctctc_dir, label_dir, output_t1c_dir, output_ctctc_dir, output_label_dir)
print("All processing completed.")

View file

@ -0,0 +1,147 @@
import os
import shutil
# 定义所有需要的路径
final_image_folder = '/mnt/1218/onlylian/resampled_T1_image_test'
final_label_folder = '/mnt/1218/onlylian/resampled_label_test'
mapping_file = '/mnt/1218/onlylian/patient_mapping.txt'
# nnUNet路径
base_nnunet_path = '/home/onlylian/nnUNet/nnUNet_raw/Dataset888'
nnunet_image_folder = os.path.join(base_nnunet_path, 'imagesTs')
nnunet_label_folder = os.path.join(base_nnunet_path, 'labelsTs')
# 打印初始状态
print("\nChecking directories:")
print(f"Source image folder exists: {os.path.exists(final_image_folder)}")
print(f"Source label folder exists: {os.path.exists(final_label_folder)}")
# 确保目录存在
try:
os.makedirs(base_nnunet_path, exist_ok=True)
os.makedirs(nnunet_image_folder, exist_ok=True)
os.makedirs(nnunet_label_folder, exist_ok=True)
print(f"Successfully created/verified nnUNet directories")
except Exception as e:
print(f"Error creating directories: {e}")
def get_patient_ids(folder_path, prefix='T1_'):
"""從檔案名稱中提取病人ID"""
if not os.path.exists(folder_path):
print(f"WARNING: Folder does not exist: {folder_path}")
return set()
patient_ids = set()
try:
for filename in os.listdir(folder_path):
if filename.endswith('.nii.gz'):
if filename.startswith(prefix):
# 對於T1影像: T1_XXXXX.nii.gz -> XXXXX
patient_id = filename[len(prefix):-7]
patient_ids.add(patient_id)
elif prefix == 'T1_' and filename.startswith('label_'):
# 對於標籤: label_XXXXX.nii.gz -> XXXXX
patient_id = filename[6:-7]
patient_ids.add(patient_id)
print(f"Found {len(patient_ids)} patients in {folder_path}")
except Exception as e:
print(f"Error reading directory {folder_path}: {e}")
return patient_ids
def create_filtered_folder(image_folder, label_folder, output_image_folder, output_label_folder):
"""过滤并重命名文件,同时创建映射文件"""
print("\nStarting file processing...")
# 获取病人ID
print("\nGetting patient IDs...")
image_patients = get_patient_ids(image_folder, 'T1_')
label_patients = get_patient_ids(label_folder, 'label_') # 修改為正確的前綴
# 打印一些檔案範例以供驗證
print("\nSample files in directories:")
print("Images directory:")
for f in sorted(os.listdir(image_folder))[:3]:
print(f" - {f}")
print("Labels directory:")
for f in sorted(os.listdir(label_folder))[:3]:
print(f" - {f}")
common_patients = image_patients.intersection(label_patients)
print(f"\nFound {len(common_patients)} common patients")
mapping_list = []
processed_files = {"images": 0, "labels": 0}
for index, patient_id in enumerate(sorted(common_patients), 1):
new_filename = f"T1_{index:03d}"
mapping_list.append(f"Original ID: {patient_id} -> New ID: {new_filename}")
# 处理图像文件
image_file = f'T1_{patient_id}.nii.gz'
src_image_path = os.path.join(image_folder, image_file)
dst_image_path = os.path.join(output_image_folder, f"{new_filename}_0000.nii.gz")
# 处理标签文件
label_file = f'label_{patient_id}.nii.gz' # 修改為正確的命名格式
src_label_path = os.path.join(label_folder, label_file)
dst_label_path = os.path.join(output_label_folder, f"{new_filename}.nii.gz")
# 複製檔案並記錄結果
if os.path.exists(src_image_path):
try:
shutil.copy2(src_image_path, dst_image_path)
processed_files["images"] += 1
print(f"Copied image: {os.path.basename(dst_image_path)}")
except Exception as e:
print(f"Error copying image {src_image_path}: {e}")
else:
print(f"Source image not found: {src_image_path}")
if os.path.exists(src_label_path):
try:
shutil.copy2(src_label_path, dst_label_path)
processed_files["labels"] += 1
print(f"Copied label: {os.path.basename(dst_label_path)}")
except Exception as e:
print(f"Error copying label {src_label_path}: {e}")
else:
print(f"Source label not found: {src_label_path}")
# 写入映射文件
try:
with open(mapping_file, 'w') as f:
f.write("Patient ID Mapping:\n")
f.write("=================\n\n")
for mapping in mapping_list:
f.write(f"{mapping}\n")
f.write(f"\nTotal number of patients: {len(mapping_list)}")
print(f"\nMapping file created: {mapping_file}")
except Exception as e:
print(f"Error writing mapping file: {e}")
return len(common_patients), processed_files
print("\nStarting file processing...")
common_count, processed_files = create_filtered_folder(
final_image_folder, final_label_folder,
nnunet_image_folder, nnunet_label_folder
)
print(f"\nProcessing completed:")
print(f"- Total number of common patients: {common_count}")
print(f"- Files processed: {processed_files['images']} images, {processed_files['labels']} labels")
print(f"- Patient ID mapping has been saved to: {mapping_file}")
# 验证最终结果
print("\nFinal verification:")
if os.path.exists(nnunet_label_folder):
label_files = os.listdir(nnunet_label_folder)
print(f"Files in labels directory: {len(label_files)}")
if len(label_files) > 0:
print("Sample files:")
for f in sorted(label_files)[:5]:
print(f" - {f}")
else:
print("WARNING: Labels directory is empty!")
else:
print("WARNING: Labels directory does not exist!")

View file

@ -0,0 +1,112 @@
import os
import shutil
import pandas as pd
def load_excel(file_path):
"""載入 Excel 文件"""
return pd.read_excel(file_path, sheet_name='Sheet1')
def ensure_directories_exist(target_dir, modalities):
"""確保目標目錄存在"""
for modality in modalities:
os.makedirs(os.path.join(target_dir, modality), exist_ok=True)
def find_file_recursive(filename, search_path):
"""在給定目錄中遞迴搜索文件。"""
for root, dirs, files in os.walk(search_path):
if filename in files:
return os.path.join(root, filename)
return None
def extract_patient_and_date(file_path):
"""從文件路徑中提取病人檔名和日期。"""
parts = file_path.split(os.sep)
if len(parts) >= 4:
return parts[-4], parts[-3] # 假設病人檔名在倒數第四層,日期在倒數第三層
return None, None
def is_swi_file(file_name):
"""檢查檔案名稱是否包含 'swi'"""
return 'swi' in file_name.lower()
def copy_files(df, source_dir, target_dir):
"""遍歷數據框並將對應的 .warp.nii.gz 文件複製到相應的模態資料夾,若無 .warp.nii.gz 則複製原始的 .nii.gz 文件,保留病人檔名和日期"""
processed_patients = set()
for index, row in df.iterrows():
file_name = row['name']
modality = row['modality_nn']
# 檢查是否為 swi 檔案
if is_swi_file(file_name):
print(f'跳過 swi 檔案: {file_name}')
continue
# 修改來源檔名:將 .nii.gz 替換為 .warp.nii.gz
warp_file_name = file_name.replace('.nii.gz', '.warp.nii.gz')
# 在源目錄中遞迴搜索 .warp.nii.gz 文件
warp_file_path = find_file_recursive(warp_file_name, source_dir)
if warp_file_path:
# 如果找到 .warp.nii.gz 文件,複製該文件
patient_id, date = extract_patient_and_date(warp_file_path)
if patient_id and date:
if patient_id not in processed_patients:
processed_patients.add(patient_id)
# 複製到個別的模態資料夾
target_dir_path = os.path.join(target_dir, modality, patient_id, date)
os.makedirs(target_dir_path, exist_ok=True)
target_file_path = os.path.join(target_dir_path, warp_file_name)
shutil.copy2(warp_file_path, target_file_path)
print(f'已複製 {warp_file_name}{target_dir_path} 資料夾。')
# 如果是 CT 或 CTC也複製到 CT+CTC 資料夾
if modality in ['CT', 'CTC']:
combined_target_dir_path = os.path.join(target_dir, 'CT+CTC', patient_id, date)
os.makedirs(combined_target_dir_path, exist_ok=True)
combined_target_file_path = os.path.join(combined_target_dir_path, warp_file_name)
shutil.copy2(warp_file_path, combined_target_file_path)
print(f'已複製 {warp_file_name}{combined_target_dir_path} 資料夾。')
else:
# 如果找不到 .warp.nii.gz 文件,則複製原始的 .nii.gz 文件
original_file_path = find_file_recursive(file_name, source_dir)
if original_file_path:
patient_id, date = extract_patient_and_date(original_file_path)
if patient_id and date:
if patient_id not in processed_patients:
processed_patients.add(patient_id)
# 複製到個別的模態資料夾
target_dir_path = os.path.join(target_dir, modality, patient_id, date)
os.makedirs(target_dir_path, exist_ok=True)
target_file_path = os.path.join(target_dir_path, file_name)
shutil.copy2(original_file_path, target_file_path)
print(f'已複製 {file_name}{target_dir_path} 資料夾。')
# 如果是 CT 或 CTC也複製到 CT+CTC 資料夾
if modality in ['CT', 'CTC']:
combined_target_dir_path = os.path.join(target_dir, 'CT+CTC', patient_id, date)
os.makedirs(combined_target_dir_path, exist_ok=True)
combined_target_file_path = os.path.join(combined_target_dir_path, file_name)
shutil.copy2(original_file_path, combined_target_file_path)
print(f'已複製 {file_name}{combined_target_dir_path} 資料夾。')
else:
print(f'{file_name}{warp_file_name} 均在源目錄中不存在。')
def main():
file_path = '/mnt/1218/onlylian/modality_by_nn.xlsx'
source_dir = '/mnt/1218/onlylian/N6_24Q3'
target_dir = '/mnt/1218/onlylian/sorted_modality_files'
modalities = ['CT', 'CTC', 'T1', 'T1C', 'T2', 'FLAIR', 'CT+CTC'] # 加入 CT+CTC
df = load_excel(file_path)
ensure_directories_exist(target_dir, modalities)
copy_files(df, source_dir, target_dir)
print('文件分類和複製完成。')
if __name__ == "__main__":
main()

View file

@ -0,0 +1,47 @@
import os
import shutil
def merge_directories(src1, src2, dest):
if not os.path.exists(dest):
os.makedirs(dest)
def copy_or_merge(src, dest):
for patient in os.listdir(src):
patient_src_path = os.path.join(src, patient)
patient_dest_path = os.path.join(dest, patient)
if os.path.isdir(patient_src_path):
if os.path.exists(patient_dest_path):
# If destination folder exists, merge the contents
for item in os.listdir(patient_src_path):
s = os.path.join(patient_src_path, item)
d = os.path.join(patient_dest_path, item)
if os.path.isdir(s):
shutil.copytree(s, d, dirs_exist_ok=True)
else:
shutil.copy2(s, d)
else:
# If destination folder does not exist, copy the whole folder
shutil.copytree(patient_src_path, patient_dest_path)
# Copy or merge from both source directories to destination directory
copy_or_merge(src1, dest)
copy_or_merge(src2, dest)
print("Directories merged successfully.")
# Define source and destination paths
src1 = '/mnt/1220/Public/dataset2/H4'
src2 = '/mnt/1220/Public/dataset2/N6'
dest = "/home/onlylian/dataset2_H4_N6"
# Call the function to merge the directories
merge_directories(src1, src2, dest)

View file

@ -0,0 +1,96 @@
import os
import shutil
def copy_last_file(source_base_dir, target_base_dir, modality):
# 確保目標目錄存在
os.makedirs(target_base_dir, exist_ok=True)
# 按照病患資料夾名稱進行排序
for patient_dir in sorted(os.listdir(source_base_dir)):
patient_path = os.path.join(source_base_dir, patient_dir)
if os.path.isdir(patient_path):
# 按照日期資料夾名稱進行排序
date_dirs = sorted(os.listdir(patient_path))
for date_dir in date_dirs:
date_path = os.path.join(patient_path, date_dir)
if os.path.isdir(date_path):
files = os.listdir(date_path)
# 篩選出 .nii.gz 和 .warp.nii.gz 檔案
nii_files = [f for f in files if f.endswith('.nii.gz')]
warp_files = [f for f in files if f.endswith('.warp.nii.gz')]
# 先選擇最後一個 .warp.nii.gz 檔案,如果沒有則選擇 .nii.gz 檔案
if warp_files:
warp_files.sort()
last_file = warp_files[-1] # 選擇最後一個 .warp.nii.gz 檔案
elif nii_files:
nii_files.sort()
last_file = nii_files[-1] # 選擇最後一個 .nii.gz 檔案
else:
print(f"No valid .nii.gz or .warp.nii.gz files found in {date_path}")
continue
last_file_path = os.path.join(date_path, last_file)
# 根據模態重新命名文件
new_file_name = f"{modality}_{patient_dir}.nii.gz"
target_file_path = os.path.join(target_base_dir, new_file_name)
# 複製文件到目標目錄
shutil.copy2(last_file_path, target_file_path)
print(f"Copied: {last_file_path} to {target_file_path}")
# T1C 模態的處理
source_base_dir_t1c = '/mnt/1218/onlylian/sorted_modality_files_test/T1'
target_base_dir_t1c = '/mnt/1218/onlylian/T1_image_folder_test'
copy_last_file(source_base_dir_t1c, target_base_dir_t1c, 'T1')
# import os
# import shutil
# def copy_last_file(source_base_dir, target_base_dir, modality):
# # 確保目標目錄存在
# os.makedirs(target_base_dir, exist_ok=True)
# # 按照病患資料夾名稱進行排序
# for patient_dir in sorted(os.listdir(source_base_dir)):
# patient_path = os.path.join(source_base_dir, patient_dir)
# if os.path.isdir(patient_path):
# # 按照日期資料夾名稱進行排序
# date_dirs = sorted(os.listdir(patient_path))
# if date_dirs: # 確保有日期資料夾
# # 取最新的日期資料夾
# latest_date_dir = date_dirs[-1]
# date_path = os.path.join(patient_path, latest_date_dir)
# if os.path.isdir(date_path):
# # 獲取該資料夾中的所有檔案
# files = [f for f in os.listdir(date_path) if f.endswith('.nii.gz')]
# if files: # 如果有符合條件的檔案
# # 按名稱排序並取最後一個檔案
# files.sort()
# last_file = files[-1]
# # 構建完整的檔案路徑
# last_file_path = os.path.join(date_path, last_file)
# # 根據模態重新命名檔案
# new_file_name = f"{modality}_{patient_dir}.nii.gz"
# target_file_path = os.path.join(target_base_dir, new_file_name)
# # 複製檔案到目標目錄
# shutil.copy2(last_file_path, target_file_path)
# print(f"Copied: {last_file_path} to {target_file_path}")
# else:
# print(f"No .nii.gz files found in {date_path}")
# # 設定實際的路徑
# source_base_dir = '/mnt/1218/onlylian/sorted_modality_files_test/T1C' # 更新為您的實際T1C檔案路徑
# target_base_dir = '/mnt/1218/onlylian/T1C_image_folder_test' # 更新為您想要的輸出路徑
# # 執行檔案複製
# copy_last_file(source_base_dir, target_base_dir, 'T1C')

View file

@ -0,0 +1,165 @@
# import os
# import pandas as pd
# # 要扫描的根目录
# root_dir = '/mnt/1218/onlylian/sorted_modality_files'
# # 为 DataFrame 准备数据
# data = []
# # 遍历目录
# for subdir, _, files in os.walk(root_dir):
# for file in files:
# if file.endswith('.nii.gz'):
# parts = subdir.split('/')
# try:
# if len(parts) >= 8: # 标准结构
# modalities = parts[4] # 提取模态类型
# patient_id = parts[5] # 提取患者信息
# date = parts[6] # 提取日期
# name = file # 提取文件名
# elif len(parts) == 7: # 当模态类型与具体模态合并时
# modalities = parts[4] # 提取模态类型
# patient_id = parts[5] # 提取患者信息
# date = parts[6] # 提取日期
# name = file # 提取文件名
# elif len(parts) == 6: # 当只有日期和模态类型时
# modalities = parts[4] # 提取模态类型
# patient_id = parts[5] # 提取患者信息
# date = '' # 没有日期信息
# name = file # 提取文件名
# else:
# print(f"Path {subdir} does not have enough parts to extract data")
# continue
# data.append([modalities, patient_id, date, name])
# print(f"Added: {modalities}/{patient_id}/{date}/{name}") # 调试信息
# except IndexError as e:
# print(f"Error processing path {subdir}: {e}")
# # 检查数据是否已经添加
# if not data:
# print("No data found. Please check the directory structure and files.")
# # 创建 DataFrame
# df = pd.DataFrame(data, columns=['Modalities', 'patient_id', 'date', 'name'])
# # 输出到 Excel
# output_path = '/mnt/1218/onlylian/patient_modalities_files.xlsx'
# df.to_excel(output_path, index=False)
# print(f"Excel 文件已生成在: {output_path}")
import os
import pandas as pd
from datetime import datetime
# 设置根目录
root_dir = '/mnt/1218/onlylian/sorted_modality_files'
# 为 DataFrame 准备数据
data = []
# 添加计数器和集合来跟踪统计信息
total_files = 0
unique_patients = set()
unique_modalities = set()
processed_dates = set()
def process_file_path(file_path):
"""
处理文件路径提取相关信息
示例路径: /mnt/1218/onlylian/sorted_modality_files/CT/2U5BG75W/20240802/1.1_CyberKnife_head(MAR)_20240802155850_6.nii.gz
"""
try:
# 分割路径
parts = file_path.split('/')
# 确保文件是 .nii.gz 格式
if not parts[-1].endswith('.nii.gz'):
return None
# 从路径中提取所需信息
modalities = parts[-4] # CT
patient_id = parts[-3] # 2U5BG75W
date = parts[-2] # 20240802
name = parts[-1] # filename.nii.gz
return [modalities, patient_id, date, name]
except IndexError as e:
print(f"Error processing path {file_path}: {e}")
return None
# 获取当前时间作为运行标记
current_time = datetime.now().strftime("%Y%m%d_%H%M%S")
print("开始扫描文件...")
# 遍历目录
for subdir, _, files in os.walk(root_dir):
for file in files:
if file.endswith('.nii.gz'):
full_path = os.path.join(subdir, file)
file_info = process_file_path(full_path)
if file_info:
data.append(file_info)
total_files += 1
unique_patients.add(file_info[1]) # 添加病人ID到集合
unique_modalities.add(file_info[0]) # 添加模态类型到集合
processed_dates.add(file_info[2]) # 添加日期到集合
# 每处理100个文件显示一次进度
if total_files % 100 == 0:
print(f"已处理 {total_files} 个文件...")
# 检查数据是否已经添加
if not data:
print("未找到数据。请检查目录结构和文件。")
else:
# 创建 DataFrame
df = pd.DataFrame(data, columns=['Modalities', 'patient_id', 'date', 'name'])
# 生成带时间戳的输出文件名,以避免覆盖现有文件
output_path = f'/mnt/1218/onlylian/patient_modalities_files_{current_time}.xlsx'
# 输出到 Excel
df.to_excel(output_path, index=False)
# 打印统计信息
print("\n处理完成!统计信息:")
print(f"总文件数: {total_files}")
print(f"病人数量: {len(unique_patients)}")
print(f"模态类型数: {len(unique_modalities)}")
print(f"检查日期数: {len(processed_dates)}")
print("\n各模态类型及其文件数量:")
modality_counts = df['Modalities'].value_counts()
for modality, count in modality_counts.items():
print(f"{modality}: {count} 个文件")
print(f"\nExcel 文件已生成在: {output_path}")
# 输出病人ID分布统计
patient_file_counts = df['patient_id'].value_counts()
print("\n病人文件数量分布:")
print(f"最少文件数: {patient_file_counts.min()}")
print(f"最多文件数: {patient_file_counts.max()}")
print(f"平均每个病人文件数: {patient_file_counts.mean():.2f}")
# 错误处理和日志
try:
# 同时保存一份日志文件
log_path = f'/mnt/1218/onlylian/processing_log_{current_time}.txt'
with open(log_path, 'w') as log_file:
log_file.write(f"处理时间: {current_time}\n")
log_file.write(f"总文件数: {total_files}\n")
log_file.write(f"病人数量: {len(unique_patients)}\n")
log_file.write(f"模态类型数: {len(unique_modalities)}\n")
log_file.write(f"检查日期数: {len(processed_dates)}\n")
log_file.write("\n各模态类型及其文件数量:\n")
for modality, count in modality_counts.items():
log_file.write(f"{modality}: {count} 个文件\n")
print(f"\n处理日志已保存在: {log_path}")
except Exception as e:
print(f"保存日志文件时出错: {e}")

View file

@ -0,0 +1,46 @@
import os
import shutil
import pandas as pd
# 讀取 Excel 文件
file_path = '/mnt/1218/onlylian/patient_modalities_files_20241031_181028.xlsx'
df = pd.read_excel(file_path)
# 定義來源和目標資料夾
source_dir = '/mnt/1218/onlylian/Complete_NEW_OAR_Files'
target_dir = '/mnt/1218/onlylian/sorted_OAR_files_test'
modalities = ['CT', 'CTC', 'T1', 'T1C', 'T2', 'FLAIR', 'CT+CTC']
# 創建目標資料夾結構
for modality in modalities:
modality_path = os.path.join(target_dir, modality)
os.makedirs(modality_path, exist_ok=True)
# 遍歷每個病人ID和日期
for _, row in df.iterrows():
modality = row['Modalities']
patient_id = row['patient_id']
date = str(row['date'])
if modality in modalities:
# 構建來源資料夾路徑
source_path = os.path.join(source_dir, patient_id, date)
print(f"Checking source path: {source_path}") # 檢查路徑
if os.path.exists(source_path):
# 構建目標資料夾路徑
modality_path = os.path.join(target_dir, modality, patient_id, date)
os.makedirs(modality_path, exist_ok=True)
# 遍歷來源資料夾中的所有檔案
for file_name in os.listdir(source_path):
# 更新條件檢查
if file_name.endswith('.nii.gz'): # 確保是正確的檔案類型
source_file = os.path.join(source_path, file_name)
target_file = os.path.join(modality_path, file_name)
print(f"Copying {source_file} to {target_file}") # 跟蹤檔案複製
# 複製檔案到目標資料夾
shutil.copy2(source_file, target_file)
else:
print(f"Source path not found: {source_path}")
else:
print(f"Unknown modality {modality} for patient {patient_id} on {date}")

View file

@ -0,0 +1,47 @@
import os
import shutil
def merge_directories(src1, src2, dest):
if not os.path.exists(dest):
os.makedirs(dest)
def copy_or_merge(src, dest):
for patient in os.listdir(src):
patient_src_path = os.path.join(src, patient)
patient_dest_path = os.path.join(dest, patient)
if os.path.isdir(patient_src_path):
if os.path.exists(patient_dest_path):
# If destination folder exists, merge the contents
for item in os.listdir(patient_src_path):
s = os.path.join(patient_src_path, item)
d = os.path.join(patient_dest_path, item)
if os.path.isdir(s):
shutil.copytree(s, d, dirs_exist_ok=True)
else:
shutil.copy2(s, d)
else:
# If destination folder does not exist, copy the whole folder
shutil.copytree(patient_src_path, patient_dest_path)
# Copy or merge from both source directories to destination directory
copy_or_merge(src1, dest)
copy_or_merge(src2, dest)
print("Directories merged successfully.")
# Define source and destination paths
src1 = '/mnt/1220/Public/dataset2/G4'
src2 = '/mnt/1220/Public/dataset2/M6'
dest = "/home/onlylian/dataset2_G4_M6"
# Call the function to merge the directories
merge_directories(src1, src2, dest)

View file

@ -0,0 +1,46 @@
import os
import shutil
import re
def extract_patient_id(filename):
# 假設文件名格式為 "something_PATIENTID.nii.gz"
match = re.search(r'_([A-Z0-9]+)\.nii\.gz$', filename)
if match:
return match.group(1)
return None
def merge_folders(folder1, folder2, output_folder):
# 確保輸出資料夾存在
if not os.path.exists(output_folder):
os.makedirs(output_folder)
# 獲取兩個資料夾中的所有文件
files1 = set(os.listdir(folder1))
files2 = set(os.listdir(folder2))
# 合併所有唯一的病人ID
all_files = files1.union(files2)
for file in all_files:
patient_id = extract_patient_id(file)
if patient_id:
new_filename = f"label_{patient_id}.nii.gz"
source_folder = folder1 if file in files1 else folder2
source_path = os.path.join(source_folder, file)
dest_path = os.path.join(output_folder, new_filename)
# 複製文件到輸出資料夾並重命名
shutil.copy2(source_path, dest_path)
print(f"Copied and renamed {file} to {new_filename}")
else:
print(f"Skipped {file} - could not extract patient ID")
# 設定資料夾路徑
folder1 = '/home/onlylian/resampled_T1C_label'
folder2 = '/home/onlylian/resampled_CT+CTC_label'
output_folder = '/home/onlylian/merged_label_folder'
# 執行合併
merge_folders(folder1, folder2, output_folder)
print("Merging complete!")

View file

@ -0,0 +1,172 @@
# import os
# import SimpleITK as sitk
# import difflib
# # 定義資料夾路徑
# T1C_folder_path = '/home/onlylian/sorted_OAR_files/T1C'
# CTCTC_folder_path = '/home/onlylian/sorted_OAR_files/CT+CTC'
# T1C_output_folder_path = '/home/onlylian/T1C_label_folder'
# CTCTC_output_folder_path = '/home/onlylian/CT+CTC_label_folder'
# # 定義組織標籤對應關係
# labels = {
# "background": "0",
# "Brainstem": "1",
# "Right_Eye": "2",
# "Left_Eye": "3",
# "Optic_Chiasm": "4",
# "Right_Optic_Nerve": "5",
# "Left_Optic_Nerve": "6"
# }
# # 確保輸出資料夾存在
# os.makedirs(T1C_output_folder_path, exist_ok=True)
# os.makedirs(CTCTC_output_folder_path, exist_ok=True)
# def resample_image(image, reference_image):
# resample = sitk.ResampleImageFilter()
# resample.SetReferenceImage(reference_image)
# resample.SetInterpolator(sitk.sitkNearestNeighbor)
# resample.SetDefaultPixelValue(0)
# return resample.Execute(image)
# def process_images(input_folder, output_folder, image_type):
# # 獲取所有病人的資料夾路徑
# patient_folders = sorted([os.path.join(dp, d) for dp, dn, filenames in os.walk(input_folder) for d in dn])
# file_counter = 1
# for patient_folder in patient_folders:
# # 創建空的影像,用來合成所有器官
# combined_image = None
# # 獲取病人文件夾中的所有文件名,並轉換為小寫形式
# patient_files = {f.lower(): f for f in os.listdir(patient_folder)}
# filename = patient_folder.split('/')[-2]
# # 遍歷每個病人的所有影像文件
# for organ_name, label in labels.items():
# organ_filename = f'struct_{organ_name}.nii.gz'.lower()
# # 使用 difflib.get_close_matches 查找相似文件名
# possible_matches = difflib.get_close_matches(organ_filename, patient_files.keys(), n=1, cutoff=0.8)
# if possible_matches:
# matched_filename = patient_files[possible_matches[0]]
# organ_path = os.path.join(patient_folder, matched_filename)
# organ_image = sitk.ReadImage(organ_path)
# if combined_image is None:
# # 初始化合成影像
# combined_image = sitk.Image(organ_image.GetSize(), sitk.sitkUInt8)
# combined_image.CopyInformation(organ_image)
# # 重新取樣器官影像以匹配合成影像的大小
# resampled_organ_image = resample_image(organ_image, combined_image)
# # 將器官影像添加到合成影像中
# organ_array = sitk.GetArrayFromImage(resampled_organ_image)
# combined_array = sitk.GetArrayFromImage(combined_image)
# combined_array[organ_array > 0] = int(label)
# combined_image = sitk.GetImageFromArray(combined_array)
# combined_image.CopyInformation(resampled_organ_image)
# # 保存合成影像
# if combined_image:
# output_filename = f'{image_type}_label_{filename}.nii.gz'
# output_path = os.path.join(output_folder, output_filename)
# sitk.WriteImage(combined_image, output_path)
# print(f'Saved combined image to {output_path}')
# file_counter += 1
# # 處理 T1C 影像
# print("Processing T1C images...")
# process_images(T1C_folder_path, T1C_output_folder_path, 'T1C')
# # 處理 CT+CTC 影像
# print("Processing CT+CTC images...")
# process_images(CTCTC_folder_path, CTCTC_output_folder_path, 'CT+CTC')
# print("All processing completed.")
import os
import SimpleITK as sitk
import difflib
# 定义文件夹路径
T1_folder_path = '/mnt/1218/onlylian/sorted_OAR_files_test/T1C'
T1_output_folder_path = '/mnt/1218/onlylian/T1C_label_folder_test_2'
# 定义组织标签对应关系
labels = {
"background": "0",
"Brainstem": "1",
"Right_Eye": "2",
"Left_Eye": "3",
"Optic_Chiasm": "4",
"Right_Optic_Nerve": "5",
"Left_Optic_Nerve": "6"
}
# 确保输出文件夹存在
os.makedirs(T1_output_folder_path, exist_ok=True)
def resample_image(image, reference_image):
resample = sitk.ResampleImageFilter()
resample.SetReferenceImage(reference_image)
resample.SetInterpolator(sitk.sitkNearestNeighbor)
resample.SetDefaultPixelValue(0)
return resample.Execute(image)
def process_images(input_folder, output_folder, image_type):
# 获取所有病人的文件夹路径
patient_folders = sorted([os.path.join(dp, d) for dp, dn, filenames in os.walk(input_folder) for d in dn])
file_counter = 1
for patient_folder in patient_folders:
# 创建空的图像,用来合成所有器官
combined_image = None
# 获取病人文件夹中的所有文件名,并转换为小写形式
patient_files = {f.lower(): f for f in os.listdir(patient_folder)}
filename = patient_folder.split('/')[-2]
# 遍历每个病人的所有图像文件
for organ_name, label in labels.items():
organ_filename = f'struct_{organ_name}.nii.gz'.lower()
# 使用 difflib.get_close_matches 查找相似文件名
possible_matches = difflib.get_close_matches(organ_filename, patient_files.keys(), n=1, cutoff=0.8)
if possible_matches:
matched_filename = patient_files[possible_matches[0]]
organ_path = os.path.join(patient_folder, matched_filename)
organ_image = sitk.ReadImage(organ_path)
if combined_image is None:
# 初始化合成图像
combined_image = sitk.Image(organ_image.GetSize(), sitk.sitkUInt8)
combined_image.CopyInformation(organ_image)
# 重新采样器官图像以匹配合成图像的大小
resampled_organ_image = resample_image(organ_image, combined_image)
# 将器官图像添加到合成图像中
organ_array = sitk.GetArrayFromImage(resampled_organ_image)
combined_array = sitk.GetArrayFromImage(combined_image)
combined_array[organ_array > 0] = int(label)
combined_image = sitk.GetImageFromArray(combined_array)
combined_image.CopyInformation(resampled_organ_image)
# 保存合成图像
if combined_image:
output_filename = f'{image_type}_label_{filename}.nii.gz'
output_path = os.path.join(output_folder, output_filename)
sitk.WriteImage(combined_image, output_path)
print(f'Saved combined image to {output_path}')
file_counter += 1
# 处理 T1C 图像
print("Processing T1C images...")
process_images(T1_folder_path, T1_output_folder_path, 'T1C')
print("Processing completed.")

View file

@ -0,0 +1,138 @@
import os
import shutil
import difflib
def copy_organ_files(source_root, target_root):
# 確保目標資料夾存在
if not os.path.exists(target_root):
os.makedirs(target_root)
print(f"Created directory {target_root}")
# 遍歷來源的根資料夾和所有子資料夾,尋找名為 ORGAN 的資料夾
for root, dirs, files in os.walk(source_root):
for dir in dirs:
if dir == 'ORGAN':
source_folder_path = os.path.join(root, dir)
# 提取病例資料夾和日期資料夾
case_folder = os.path.basename(os.path.dirname(os.path.dirname(os.path.dirname(source_folder_path))))
date_folder = os.path.basename(os.path.dirname(os.path.dirname(source_folder_path)))
# 組合目標資料夾路徑
target_folder_path = os.path.join(target_root, case_folder, date_folder)
# 確保目標資料夾存在
if not os.path.exists(target_folder_path):
os.makedirs(target_folder_path)
print(f"Created directory {target_folder_path}")
# 複製資料夾中的所有檔案到目標資料夾
for file_name in os.listdir(source_folder_path):
source_file_path = os.path.join(source_folder_path, file_name)
target_file_path = os.path.join(target_folder_path, file_name)
# 如果目標檔案已經存在,重新命名以避免覆蓋
if os.path.exists(target_file_path):
base, extension = os.path.splitext(file_name)
counter = 1
new_file_name = f"{base}_{counter}{extension}"
target_file_path = os.path.join(target_folder_path, new_file_name)
while os.path.exists(target_file_path):
counter += 1
new_file_name = f"{base}_{counter}{extension}"
target_file_path = os.path.join(target_folder_path, new_file_name)
shutil.copy(source_file_path, target_file_path)
print(f"Copied {source_file_path} to {target_file_path}")
print("All ORGAN files have been copied.")
def check_and_copy_files(root_dir, required_file_groups, target_dir, match_cutoff=0.7):
# 確保目標目錄存在
os.makedirs(target_dir, exist_ok=True)
# 計算缺少文件的病人數量
missing_files_patients = 0
copied_files_patients = 0
# 遍歷根目錄下的每個病人文件夾
for patient_id in os.listdir(root_dir):
patient_dir = os.path.join(root_dir, patient_id)
# 確保是目錄
if os.path.isdir(patient_dir):
# 遍歷病人目錄下的日期文件夾
for date_folder in os.listdir(patient_dir):
date_dir = os.path.join(patient_dir, date_folder)
existing_files = os.listdir(date_dir)
# 檢查指定的OAR文件是否存在
missing_files = []
for file_group in required_file_groups:
# 對於左右眼的文件,進行更精確的匹配
if 'Left_Eye' in file_group[0]:
matches = [f for f in existing_files if 'Left_Eye' in f]
elif 'Right_Eye' in file_group[0]:
matches = [f for f in existing_files if 'Right_Eye' in f]
else:
# 對其他文件使用模糊匹配來查找接近的文件名稱
matches = difflib.get_close_matches(file_group[0], existing_files, n=1, cutoff=match_cutoff)
if not matches:
missing_files.append(file_group[0]) # 使用文件組中的第一個文件名作為缺失文件的表示
else:
print(f"匹配到的文件: {matches[0]} 對應到 {file_group[0]}")
if missing_files:
missing_files_patients += 1
print(f"病人: {patient_id}, 日期: {date_folder}, 缺失的文件: {missing_files}")
else:
# 沒有缺失文件,複製文件到新目錄
new_patient_dir = os.path.join(target_dir, patient_id)
new_date_dir = os.path.join(new_patient_dir, date_folder)
os.makedirs(new_date_dir, exist_ok=True)
for file_name in existing_files:
src_file = os.path.join(date_dir, file_name)
dst_file = os.path.join(new_date_dir, file_name)
shutil.copy(src_file, dst_file)
copied_files_patients += 1
print(f"病人: {patient_id}, 日期: {date_folder}, 文件已成功複製")
print(f"缺少所需OAR文件的資料總數: {missing_files_patients}")
print(f"文件完整且已複製的資料總數: {copied_files_patients}")
def count_patients_and_records(target_dir):
patient_count = 0
record_count = 0
for patient_id in os.listdir(target_dir):
patient_dir = os.path.join(target_dir, patient_id)
if os.path.isdir(patient_dir):
patient_count += 1
record_count += len(os.listdir(patient_dir))
print(f"完整OAR文件的病人總數: {patient_count}")
print(f"完整OAR文件的資料總數: {record_count}")
if __name__ == "__main__":
# 第一部分:複製 ORGAN 文件
source_root = '/mnt/1218/onlylian/M6_24Q3/nii'
intermediate_target = '/mnt/1218/onlylian/NEW_ORGAN_Files'
copy_organ_files(source_root, intermediate_target)
# 第二部分:檢查並複製完整的 OAR 文件
required_file_groups = [
['Struct_Brain_Stem.nii.gz'],
['Struct_Left_Optic_Nerve.nii.gz'],
['Struct_Right_Optic_Nerve.nii.gz'],
['Struct_Left_Eye.nii.gz'],
['Struct_Right_Eye.nii.gz'],
['Struct_Optic_Chiasm.nii.gz']
]
final_target = '/mnt/1218/onlylian/Complete_NEW_OAR_Files'
check_and_copy_files(intermediate_target, required_file_groups, final_target)
# 計算總共的病人和記錄數量
count_patients_and_records(final_target)

View file

@ -0,0 +1,62 @@
import os
import shutil
import pandas as pd
# 讀取 Excel 文件
file_path = '/home/onlylian/patient_modalities_files.xlsx'
df = pd.read_excel(file_path)
# 定義來源和目標資料夾
source_dir = '/home/onlylian/Complete_NEW_ORGAN_Target_Files'
target_dir = '/home/onlylian/sorted_OAR_Target_files'
modalities = ['CT', 'CTC', 'T1', 'T1C', 'T2', 'FLAIR', 'CT+CTC']
# 創建目標資料夾結構
for modality in modalities:
modality_path = os.path.join(target_dir, modality)
os.makedirs(modality_path, exist_ok=True)
# 遍歷每個病人ID和日期
for _, row in df.iterrows():
modality = row['Modalities']
patient_id = row['patient_id']
date = str(row['date'])
if modality in modalities:
# 構建來源資料夾路徑
source_path = os.path.join(source_dir, patient_id, date)
print(f"Checking source path: {source_path}") # 檢查路徑
if os.path.exists(source_path):
# 分別處理 ORGAN 和 RT 目錄
organ_path = os.path.join(source_path, 'ORGAN')
rt_path = os.path.join(source_path, 'RT')
# 構建目標資料夾路徑
modality_path = os.path.join(target_dir, modality, patient_id, date)
os.makedirs(modality_path, exist_ok=True)
# 遍歷 ORGAN 目錄中的所有檔案
if os.path.exists(organ_path):
for file_name in os.listdir(organ_path):
if file_name.endswith('.nii.gz'): # 檢查檔案格式
source_file = os.path.join(organ_path, file_name)
target_file = os.path.join(modality_path, file_name)
print(f"Copying {source_file} to {target_file}")
shutil.copy2(source_file, target_file)
else:
print(f"ORGAN folder not found: {organ_path}")
# 遍歷 RT 目錄中的所有檔案
if os.path.exists(rt_path):
for file_name in os.listdir(rt_path):
if file_name.endswith('.nii.gz'): # 檢查檔案格式
source_file = os.path.join(rt_path, file_name)
target_file = os.path.join(modality_path, file_name)
print(f"Copying {source_file} to {target_file}")
shutil.copy2(source_file, target_file)
else:
print(f"RT folder not found: {rt_path}")
else:
print(f"Source path not found: {source_path}")
else:
print(f"Unknown modality {modality} for patient {patient_id} on {date}")

View file

@ -0,0 +1,47 @@
import os
import shutil
def merge_directories(src1, src2, dest):
if not os.path.exists(dest):
os.makedirs(dest)
def copy_or_merge(src, dest):
for patient in os.listdir(src):
patient_src_path = os.path.join(src, patient)
patient_dest_path = os.path.join(dest, patient)
if os.path.isdir(patient_src_path):
if os.path.exists(patient_dest_path):
# If destination folder exists, merge the contents
for item in os.listdir(patient_src_path):
s = os.path.join(patient_src_path, item)
d = os.path.join(patient_dest_path, item)
if os.path.isdir(s):
shutil.copytree(s, d, dirs_exist_ok=True)
else:
shutil.copy2(s, d)
else:
# If destination folder does not exist, copy the whole folder
shutil.copytree(patient_src_path, patient_dest_path)
# Copy or merge from both source directories to destination directory
copy_or_merge(src1, dest)
copy_or_merge(src2, dest)
print("Directories merged successfully.")
# Define source and destination paths
src1 = '/mnt/1220/Public/dataset2/G4'
src2 = '/mnt/1220/Public/dataset2/M6'
dest = "/home/onlylian/dataset2_G4_M6"
# Call the function to merge the directories
merge_directories(src1, src2, dest)

View file

@ -0,0 +1,46 @@
import os
import shutil
import re
def extract_patient_id(filename):
# 假設文件名格式為 "something_PATIENTID.nii.gz"
match = re.search(r'_([A-Z0-9]+)\.nii\.gz$', filename)
if match:
return match.group(1)
return None
def merge_folders(folder1, folder2, output_folder):
# 確保輸出資料夾存在
if not os.path.exists(output_folder):
os.makedirs(output_folder)
# 獲取兩個資料夾中的所有文件
files1 = set(os.listdir(folder1))
files2 = set(os.listdir(folder2))
# 合併所有唯一的病人ID
all_files = files1.union(files2)
for file in all_files:
patient_id = extract_patient_id(file)
if patient_id:
new_filename = f"label_{patient_id}.nii.gz"
source_folder = folder1 if file in files1 else folder2
source_path = os.path.join(source_folder, file)
dest_path = os.path.join(output_folder, new_filename)
# 複製文件到輸出資料夾並重命名
shutil.copy2(source_path, dest_path)
print(f"Copied and renamed {file} to {new_filename}")
else:
print(f"Skipped {file} - could not extract patient ID")
# 設定資料夾路徑
folder1 = '/home/onlylian/resampled_T1C_TV_label'
folder2 = '/home/onlylian/resampled_CT+CTC_TV_label'
output_folder = '/home/onlylian/merged_TV_label_folder'
# 執行合併
merge_folders(folder1, folder2, output_folder)
print("Merging complete!")

View file

@ -0,0 +1,156 @@
import os
import shutil
import difflib
import time
def copy_organ_and_tv_files(source_root, target_root):
# 確保目標資料夾存在
if not os.path.exists(target_root):
os.makedirs(target_root)
print(f"Created directory {target_root}")
# 複製 'ORGAN' 資料夾的檔案
for root, dirs, files in os.walk(source_root):
for dir in dirs:
if dir == 'ORGAN':
source_folder_path = os.path.join(root, dir)
case_folder = os.path.basename(os.path.dirname(os.path.dirname(os.path.dirname(source_folder_path))))
date_folder = os.path.basename(os.path.dirname(os.path.dirname(source_folder_path)))
target_folder_path = os.path.join(target_root, case_folder, date_folder, 'ORGAN')
if not os.path.exists(target_folder_path):
os.makedirs(target_folder_path)
print(f"Created directory {target_folder_path}")
for file_name in os.listdir(source_folder_path):
source_file_path = os.path.join(source_folder_path, file_name)
target_file_path = os.path.join(target_folder_path, file_name)
copy_with_retry(source_file_path, target_file_path)
# 複製 TV 檔案
for root, dirs, files in os.walk(source_root):
for file_name in files:
if file_name.startswith('TV') and file_name.endswith('.nii.gz'):
source_file_path = os.path.join(root, file_name)
case_folder = os.path.basename(os.path.dirname(os.path.dirname(os.path.dirname(source_file_path))))
date_folder = os.path.basename(os.path.dirname(os.path.dirname(source_file_path)))
target_folder_path = os.path.join(target_root, case_folder, date_folder, 'RT')
if not os.path.exists(target_folder_path):
os.makedirs(target_folder_path)
print(f"Created directory {target_folder_path}")
target_file_path = os.path.join(target_folder_path, file_name)
copy_with_retry(source_file_path, target_file_path)
print("All ORGAN and TV files have been copied.")
def check_and_copy_files(root_dir, required_oar_files, target_dir):
os.makedirs(target_dir, exist_ok=True)
missing_files_patients = 0
copied_files_patients = 0
for patient_id in os.listdir(root_dir):
patient_dir = os.path.join(root_dir, patient_id)
if os.path.isdir(patient_dir):
for date_folder in os.listdir(patient_dir):
date_dir = os.path.join(patient_dir, date_folder)
organ_dir = os.path.join(date_dir, 'ORGAN')
rt_dir = os.path.join(date_dir, 'RT')
if os.path.isdir(organ_dir) and os.path.isdir(rt_dir):
organ_files = os.listdir(organ_dir)
rt_files = os.listdir(rt_dir)
missing_oar_files = []
for oar_file in required_oar_files:
if 'Left_Eye' in oar_file:
matches = [f for f in organ_files if 'Left_Eye' in f]
elif 'Right_Eye' in oar_file:
matches = [f for f in organ_files if 'Right_Eye' in f]
else:
matches = difflib.get_close_matches(oar_file, organ_files, n=1, cutoff=0.8)
if not matches:
missing_oar_files.append(oar_file)
target_files = [f for f in rt_files if f.endswith('.nii.gz')]
if not target_files:
missing_oar_files.append('Target File')
if missing_oar_files:
missing_files_patients += 1
print(f"病人: {patient_id}, 日期: {date_folder}, 缺失的文件: {missing_oar_files}")
else:
new_patient_dir = os.path.join(target_dir, patient_id, date_folder)
os.makedirs(new_patient_dir, exist_ok=True)
for file_name in organ_files:
src_file = os.path.join(organ_dir, file_name)
dst_file = os.path.join(new_patient_dir, 'ORGAN', file_name)
os.makedirs(os.path.join(new_patient_dir, 'ORGAN'), exist_ok=True)
copy_with_retry(src_file, dst_file)
for file_name in rt_files:
src_file = os.path.join(rt_dir, file_name)
dst_file = os.path.join(new_patient_dir, 'RT', file_name)
os.makedirs(os.path.join(new_patient_dir, 'RT'), exist_ok=True)
copy_with_retry(src_file, dst_file)
copied_files_patients += 1
print(f"病人: {patient_id}, 日期: {date_folder}, 文件已成功複製")
else:
missing_files_patients += 1
print(f"病人: {patient_id}, 日期: {date_folder}, 缺少 ORGAN 或 RT 目錄")
print(f"缺少所需文件的資料總數: {missing_files_patients}")
print(f"文件完整且已複製的資料總數: {copied_files_patients}")
def copy_with_retry(src, dst, retries=3, delay=5):
for attempt in range(retries):
try:
shutil.copy(src, dst)
print(f"Copied {src} to {dst}")
return
except BlockingIOError as e:
if attempt < retries - 1:
print(f"資源暫時不可用,等待 {delay} 秒後重試... (第 {attempt + 1} 次)")
time.sleep(delay)
else:
raise e
def count_patients_and_records(target_dir):
patient_count = 0
record_count = 0
for patient_id in os.listdir(target_dir):
patient_dir = os.path.join(target_dir, patient_id)
if os.path.isdir(patient_dir):
patient_count += 1
record_count += len(os.listdir(patient_dir))
print(f"完整OAR和Target文件的病人總數: {patient_count}")
print(f"完整OAR和Target文件的資料總數: {record_count}")
if __name__ == "__main__":
# 設定路徑
source_root = '/home/onlylian/dataset2_G4_M6'
intermediate_target = '/home/onlylian/NEW_ORGAN_Target_Files'
final_target = '/home/onlylian/Complete_NEW_ORGAN_Target_Files'
# 指定要檢查的OAR文件
required_oar_files = [
'Struct_Brain_Stem.nii.gz',
'Struct_Left_Optic_Nerve.nii.gz',
'Struct_Right_Optic_Nerve.nii.gz',
'Struct_Left_Eye.nii.gz',
'Struct_Right_Eye.nii.gz',
'Struct_Optic_Chiasm.nii.gz'
]
# 執行複製和檢查過程
copy_organ_and_tv_files(source_root, intermediate_target)
check_and_copy_files(intermediate_target, required_oar_files, final_target)
count_patients_and_records(final_target)

View file

@ -0,0 +1,107 @@
import os
import glob
import SimpleITK as sitk
from difflib import get_close_matches
def process_images(input_base_path, output_base_path, output_prefix):
# 定義組織標籤對應關係
labels = {
"background": "0",
"Struct_Brain_Stem": "1",
"Struct_Right_Eye": "2",
"Struct_Left_Eye": "3",
"Struct_Optic_Chiasm": "4",
"Struct_Right_Optic_Nerve": "5",
"Struct_Left_Optic_Nerve": "6",
"TV": "7" # Tumor
}
# 確保輸出資料夾存在
os.makedirs(output_base_path, exist_ok=True)
# 定義重新取樣函數
def resample_image(image, reference_image):
resample = sitk.ResampleImageFilter()
resample.SetReferenceImage(reference_image)
resample.SetInterpolator(sitk.sitkNearestNeighbor) # 使用最近鄰插值避免標籤變化
resample.SetDefaultPixelValue(0)
resampled_image = resample.Execute(image)
return resampled_image
# 遍歷所有病人的資料夾
for patient_id in os.listdir(input_base_path):
patient_folder_path = os.path.join(input_base_path, patient_id)
if not os.path.isdir(patient_folder_path):
continue # 跳過非資料夾的項目
# 確定病人資料夾下的日期資料夾
date_folder = os.listdir(patient_folder_path)[0]
ORGAN_folder_path = os.path.join(patient_folder_path, date_folder)
# 創建一個空的影像來合併所有標籤
combined_image = None
# 列出病人資料夾內的所有檔案
all_files = os.listdir(ORGAN_folder_path)
# 遍歷每個標籤並合併
for organ_name, label in labels.items():
if organ_name == "TV":
# 動態搜尋 TV 檔案
tv_files = glob.glob(os.path.join(ORGAN_folder_path, "TV-*.nii.gz"))
if len(tv_files) == 0:
print(f"TV file not found for patient {patient_id}, skipping TV...")
continue
organ_path = tv_files[0] # 取第一個找到的檔案
else:
# 使用模糊比對尋找最接近的檔案名
matches = get_close_matches(organ_name, all_files, n=1, cutoff=0.6)
if len(matches) == 0:
print(f"No close match found for {organ_name} for patient {patient_id}, skipping...")
continue
organ_filename = matches[0]
organ_path = os.path.join(ORGAN_folder_path, organ_filename)
if not os.path.exists(organ_path):
print(f"File {organ_path} not found for patient {patient_id}, skipping...")
continue
organ_image = sitk.ReadImage(organ_path)
if combined_image is None:
# 初始化合併影像的大小和參考
combined_image = sitk.Image(organ_image.GetSize(), sitk.sitkUInt8)
combined_image.CopyInformation(organ_image)
else:
# 如果器官影像的尺寸和合併影像不同,進行重新取樣
if organ_image.GetSize() != combined_image.GetSize():
print(f"Resampling organ {organ_name} for patient {patient_id} due to size mismatch...")
organ_image = resample_image(organ_image, combined_image)
# 將器官影像添加到合併影像中
organ_array = sitk.GetArrayFromImage(organ_image)
combined_array = sitk.GetArrayFromImage(combined_image)
# 更新合併影像的陣列,根據標籤值來設定非背景區域
combined_array[organ_array > 0] = int(label)
# 將更新的陣列轉回 SimpleITK 影像
combined_image = sitk.GetImageFromArray(combined_array)
combined_image.CopyInformation(organ_image)
# 保存合併後的影像
output_filename = f'{output_prefix}_label_{patient_id}.nii.gz'
output_path = os.path.join(output_base_path, output_filename)
sitk.WriteImage(combined_image, output_path)
print(f'Saved combined image for patient {patient_id} to {output_path}')
if __name__ == "__main__":
# 處理 T1C 影像
T1C_input_path = '/home/onlylian/sorted_OAR_Target_files/T1C'
T1C_output_path = '/home/onlylian/T1C_OAR_Target_label_folder'
process_images(T1C_input_path, T1C_output_path, "T1C")
# 處理 CT+CTC 影像
CT_CTC_input_path = '/home/onlylian/sorted_OAR_Target_files/T1C' # 注意:這裡使用相同的輸入路徑,如果需要不同的路徑請修改
CT_CTC_output_path = '/home/onlylian/CT+CTC_OAR_Target_label_folder'
process_images(CT_CTC_input_path, CT_CTC_output_path, "CT+CTC")

View file

@ -0,0 +1,227 @@
# import SimpleITK as sitk
# import os
# import numpy as np
# def standardize_origin(image, target_origin):
# """將影像的原點標準化為目標原點"""
# return sitk.Resample(
# image,
# image.GetSize(),
# sitk.Transform(),
# sitk.sitkLinear,
# target_origin,
# image.GetSpacing(),
# image.GetDirection(),
# 0,
# image.GetPixelID()
# )
# def resample_image(image, reference_image, is_label=False):
# resampler = sitk.ResampleImageFilter()
# resampler.SetReferenceImage(reference_image)
# resampler.SetTransform(sitk.Transform())
# resampler.SetOutputSpacing(reference_image.GetSpacing())
# resampler.SetSize(reference_image.GetSize())
# resampler.SetOutputDirection(reference_image.GetDirection())
# resampler.SetOutputOrigin(reference_image.GetOrigin())
# if is_label:
# resampler.SetInterpolator(sitk.sitkNearestNeighbor)
# resampler.SetDefaultPixelValue(0)
# else:
# resampler.SetInterpolator(sitk.sitkLinear)
# resampler.SetDefaultPixelValue(image.GetPixelIDValue())
# return resampler.Execute(image)
# def process_patient(t1c_image_path, ctctc_image_path, label_path, output_t1c_dir, output_ctctc_dir, output_label_dir, patient_id):
# print(f"\nProcessing patient: {patient_id}")
# # 讀取影像
# t1c_image = sitk.ReadImage(t1c_image_path)
# ctctc_image = sitk.ReadImage(ctctc_image_path)
# label = sitk.ReadImage(label_path)
# print(f" Original T1C image size: {t1c_image.GetSize()}, origin: {t1c_image.GetOrigin()}")
# print(f" Original CT+CTC image size: {ctctc_image.GetSize()}, origin: {ctctc_image.GetOrigin()}")
# print(f" Original label size: {label.GetSize()}, origin: {label.GetOrigin()}")
# # 使用 T1C 影像作為參考
# reference_image = t1c_image
# # 重採樣 CT+CTC 影像以匹配 T1C 影像
# resampled_ctctc = resample_image(ctctc_image, reference_image)
# # 重採樣 label 以匹配 T1C 影像
# resampled_label = resample_image(label, reference_image, is_label=True)
# print(f" Resampled CT+CTC image size: {resampled_ctctc.GetSize()}, origin: {resampled_ctctc.GetOrigin()}")
# print(f" Resampled label size: {resampled_label.GetSize()}, origin: {resampled_label.GetOrigin()}")
# # 保存重採樣後的影像和標籤
# t1c_output_path = os.path.join(output_t1c_dir, f"T1C_{patient_id}.nii.gz")
# ctctc_output_path = os.path.join(output_ctctc_dir, f"CT+CTC_{patient_id}.nii.gz")
# label_output_path = os.path.join(output_label_dir, f"label_{patient_id}.nii.gz")
# sitk.WriteImage(t1c_image, t1c_output_path)
# sitk.WriteImage(resampled_ctctc, ctctc_output_path)
# sitk.WriteImage(resampled_label, label_output_path)
# print(f" T1C image saved to: {t1c_output_path}")
# print(f" Resampled CT+CTC image saved to: {ctctc_output_path}")
# print(f" Resampled label saved to: {label_output_path}")
# def process_directory(t1c_dir, ctctc_dir, label_dir, output_t1c_dir, output_ctctc_dir, output_label_dir):
# print("Starting directory processing")
# os.makedirs(output_t1c_dir, exist_ok=True)
# os.makedirs(output_ctctc_dir, exist_ok=True)
# os.makedirs(output_label_dir, exist_ok=True)
# # 獲取所有病人 ID
# patient_ids = set()
# for filename in os.listdir(t1c_dir):
# if filename.startswith("T1C_") and filename.endswith(".nii.gz"):
# patient_id = filename[4:-7] # 去掉 "T1C_" 和 ".nii.gz"
# patient_ids.add(patient_id)
# for patient_id in patient_ids:
# t1c_path = os.path.join(t1c_dir, f"T1C_{patient_id}.nii.gz")
# ctctc_path = os.path.join(ctctc_dir, f"CT+CTC_{patient_id}.nii.gz")
# label_path = os.path.join(label_dir, f"T1C_label_{patient_id}.nii.gz") # 假設 label 文件名與 T1C 相同
# if os.path.exists(t1c_path) and os.path.exists(ctctc_path) and os.path.exists(label_path):
# process_patient(t1c_path, ctctc_path, label_path, output_t1c_dir, output_ctctc_dir, output_label_dir, patient_id)
# else:
# print(f" WARNING: Missing files for patient {patient_id}")
# # 主程序
# t1c_dir = '/home/onlylian/T1C_image_folder'
# ctctc_dir = '/home/onlylian/CT+CTC_image_folder'
# label_dir = '/home/onlylian/T1C_label_folder' # 使用 T1C 的 label
# output_t1c_dir = '/home/onlylian/resampled_T1C_image_2'
# output_ctctc_dir = '/home/onlylian/resampled_CT+CTC_image_2'
# output_label_dir = '/home/onlylian/resampled_label_2'
# print("Starting image preprocessing")
# process_directory(t1c_dir, ctctc_dir, label_dir, output_t1c_dir, output_ctctc_dir, output_label_dir)
# print("All processing completed.")
import SimpleITK as sitk
import os
import numpy as np
def standardize_origin(image, target_origin):
"""將影像的原點標準化為目標原點"""
return sitk.Resample(
image,
image.GetSize(),
sitk.Transform(),
sitk.sitkLinear,
target_origin,
image.GetSpacing(),
image.GetDirection(),
0,
image.GetPixelID()
)
def resample_image(image, reference_image, is_label=False):
"""重採樣影像以匹配參考影像"""
resampler = sitk.ResampleImageFilter()
resampler.SetReferenceImage(reference_image)
resampler.SetTransform(sitk.Transform())
resampler.SetOutputSpacing(reference_image.GetSpacing())
resampler.SetSize(reference_image.GetSize())
resampler.SetOutputDirection(reference_image.GetDirection())
resampler.SetOutputOrigin(reference_image.GetOrigin())
if is_label:
resampler.SetInterpolator(sitk.sitkNearestNeighbor)
resampler.SetDefaultPixelValue(0)
else:
resampler.SetInterpolator(sitk.sitkLinear)
resampler.SetDefaultPixelValue(image.GetPixelIDValue())
return resampler.Execute(image)
def process_patient(t1_image_path, label_path, output_t1_dir, output_label_dir, patient_id):
"""處理單個病人的影像"""
print(f"\nProcessing patient: {patient_id}")
# 讀取影像
t1_image = sitk.ReadImage(t1_image_path)
label = sitk.ReadImage(label_path)
print(f" Original T1 image size: {t1_image.GetSize()}, origin: {t1_image.GetOrigin()}")
print(f" Original label size: {label.GetSize()}, origin: {label.GetOrigin()}")
# 使用 T1 影像作為參考
reference_image = t1_image
# 重採樣 label 以匹配 T1 影像
resampled_label = resample_image(label, reference_image, is_label=True)
print(f" Resampled label size: {resampled_label.GetSize()}, origin: {resampled_label.GetOrigin()}")
# 保存處理後的影像和標籤
t1_output_path = os.path.join(output_t1_dir, f"T1_{patient_id}.nii.gz")
label_output_path = os.path.join(output_label_dir, f"label_{patient_id}.nii.gz")
sitk.WriteImage(t1_image, t1_output_path)
sitk.WriteImage(resampled_label, label_output_path)
print(f" T1 image saved to: {t1_output_path}")
print(f" Resampled label saved to: {label_output_path}")
def process_directory(t1_dir, label_dir, output_t1_dir, output_label_dir):
"""處理整個目錄的影像"""
print("Starting directory processing")
# 創建輸出目錄
os.makedirs(output_t1_dir, exist_ok=True)
os.makedirs(output_label_dir, exist_ok=True)
# 獲取所有病人 ID
patient_ids = set()
for filename in os.listdir(t1_dir):
if filename.startswith("T1_") and filename.endswith(".nii.gz"):
patient_id = filename[3:-7] # 去掉 "T1_" 和 ".nii.gz"
patient_ids.add(patient_id)
total_patients = len(patient_ids)
processed_count = 0
for patient_id in patient_ids:
processed_count += 1
print(f"\nProcessing patient {processed_count}/{total_patients}: {patient_id}")
t1_path = os.path.join(t1_dir, f"T1_{patient_id}.nii.gz")
label_path = os.path.join(label_dir, f"T1_label_{patient_id}.nii.gz")
if os.path.exists(t1_path) and os.path.exists(label_path):
try:
process_patient(t1_path, label_path, output_t1_dir, output_label_dir, patient_id)
except Exception as e:
print(f" ERROR processing patient {patient_id}: {str(e)}")
else:
print(f" WARNING: Missing files for patient {patient_id}")
if not os.path.exists(t1_path):
print(f" Missing T1 image: {t1_path}")
if not os.path.exists(label_path):
print(f" Missing label: {label_path}")
# 主程序
if __name__ == "__main__":
# 設定輸入輸出路徑
t1_dir = '/mnt/1218/onlylian/T1_image_folder_test'
label_dir = '/mnt/1218/onlylian/T1_label_folder_test'
output_t1_dir = '/mnt/1218/onlylian/resampled_T1_image_test'
output_label_dir = '/mnt/1218/onlylian/resampled_T1_label_test'
print("Starting image preprocessing")
try:
process_directory(t1_dir, label_dir, output_t1_dir, output_label_dir)
print("\nAll processing completed successfully.")
except Exception as e:
print(f"\nERROR: Processing failed with error: {str(e)}")

View file

@ -0,0 +1,119 @@
import json
import os
import shutil
import glob
import re
import pandas as pd
# 創建 dataset.json 內容
dataset_json = {
"channel_names": {
"0": "T1C"
},
"labels": {
"background": "0",
"Brainstem": "1",
"Right_Eye": "2",
"Left_Eye": "3",
"Optic_Chiasm": "4",
"Right_Optic_Nerve": "5",
"Left_Optic_Nerve": "6",
"TV": "7"
},
"numTraining": 0,
"file_ending": ".nii.gz"
}
# 定義目錄
source_image_dir = "/home/onlylian/resampled_T1C_TV_image"
source_label_dir = "/home/onlylian/resampled_tv_label"
target_base_dir = "/home/onlylian/nnUNet/nnUNet_raw/Dataset012_OAR_TV"
imagesTr_dir = os.path.join(target_base_dir, "imagesTr")
labelsTr_dir = os.path.join(target_base_dir, "labelsTr")
# 創建目錄
os.makedirs(imagesTr_dir, exist_ok=True)
os.makedirs(labelsTr_dir, exist_ok=True)
def extract_patient_id(filename):
match = re.search(r'(T1C|label)_([^.]+)\.nii\.gz', os.path.basename(filename))
return match.group(2) if match else None
# 獲取並排序原始圖像文件
source_image_files = sorted(glob.glob(os.path.join(source_image_dir, "*.nii.gz")))
processed_files = 0
patient_mapping = []
# 創建病歷號到文件名的映射
label_files = glob.glob(os.path.join(source_label_dir, "*.nii.gz"))
label_map = {extract_patient_id(f): f for f in label_files}
print(f"找到 {len(label_map)} 個標籤文件")
print(f"找到 {len(source_image_files)} 個圖像文件")
# 處理所有資料
for index, source_image_file in enumerate(source_image_files, start=1):
if index % 10 == 0: # 每處理10個文件顯示一次進度
print(f"\n正在處理檔案 {index}/{len(source_image_files)}:")
patient_id = extract_patient_id(source_image_file)
if patient_id is None:
print(f"警告: 無法提取病歷號,跳過文件: {source_image_file}")
continue
# 使用新的文件名格式
new_filename = f"OAR_TV_{index:03d}"
# 複製並重命名圖像文件
target_image_file = os.path.join(imagesTr_dir, f"{new_filename}_0000.nii.gz")
shutil.copyfile(source_image_file, target_image_file)
# 處理標籤文件
if patient_id in label_map:
source_label_file = label_map[patient_id]
target_label_file = os.path.join(labelsTr_dir, f"{new_filename}.nii.gz")
shutil.copyfile(source_label_file, target_label_file)
processed_files += 1
# 添加到映射列表
patient_mapping.append({
"編號": f"{index:03d}",
"病歷號": patient_id
})
# 輸出處理細節
print(f"T1C: {source_image_file}")
print(f"複製圖像檔案: {source_image_file}{target_image_file}")
print(f"複製標籤檔案: {source_label_file}{target_label_file}")
print() # 空行分隔每筆記錄
else:
print(f"警告: 未找到 {patient_id} 的標籤檔案。")
print("\n所有資料處理完畢。")
# 更新 dataset.json
dataset_json["numTraining"] = processed_files
# 寫入 dataset.json 檔案
dataset_json_path = os.path.join(target_base_dir, "dataset.json")
with open(dataset_json_path, 'w') as f:
json.dump(dataset_json, f, indent=4)
print(f"dataset.json 已寫入到 {dataset_json_path}")
print(f"總共處理了 {processed_files} 個完整的資料集(圖像加標籤)")
# 創建 Excel 文件
df = pd.DataFrame(patient_mapping)
excel_path = os.path.join(target_base_dir, "patient_mapping.xlsx")
df.to_excel(excel_path, index=False)
print(f"病人映射表已保存到 {excel_path}")
# 輸出處理摘要
print("\n處理摘要:")
print(f"總共找到的圖像: {len(source_image_files)}")
print(f"總共找到的標籤文件: {len(label_map)}")
print(f"成功處理的完整資料集: {processed_files}")
print(f"跳過的文件數量: {len(source_image_files) - processed_files}")

View file

@ -0,0 +1,128 @@
import json
import os
import shutil
import glob
import re
import pandas as pd
# 創建 dataset.json 內容
dataset_json = {
"channel_names": {
"0": "CT+CTC",
"1": "T1C"
},
"labels": {
"background": "0",
"Brainstem": "1",
"Right_Eye": "2",
"Left_Eye": "3",
"Optic_Chiasm": "4",
"Right_Optic_Nerve": "5",
"Left_Optic_Nerve": "6"
},
"numTraining": 0,
"file_ending": ".nii.gz"
}
# 定義目錄
source_image1_dir = "/home/onlylian/resampled_CT+CTC_image"
source_image2_dir = "/home/onlylian/resampled_T1C_image"
source_label_dir = "/home/onlylian/resampled_label"
target_base_dir = "/home/onlylian/nnUNet/nnUNet_raw/Dataset021_OAR"
imagesTr_dir = os.path.join(target_base_dir, "imagesTr")
labelsTr_dir = os.path.join(target_base_dir, "labelsTr")
# 創建目錄
os.makedirs(imagesTr_dir, exist_ok=True)
os.makedirs(labelsTr_dir, exist_ok=True)
def extract_patient_id(filename):
match = re.search(r'(CT\+CTC|T1C|label)_([^.]+)\.nii\.gz', os.path.basename(filename))
return match.group(2) if match else None
# 獲取並排序原始圖像文件
source_image1_files = sorted(glob.glob(os.path.join(source_image1_dir, "*.nii.gz")))
source_image2_files = sorted(glob.glob(os.path.join(source_image2_dir, "*.nii.gz")))
processed_files = 0
patient_mapping = []
# 創建病歷號到文件名的映射
label_files = glob.glob(os.path.join(source_label_dir, "*.nii.gz"))
label_map = {extract_patient_id(f): f for f in label_files}
print(f"找到 {len(label_map)} 個標籤文件")
print(f"找到 {len(source_image1_files)} 個 CT+CTC 圖像文件")
print(f"找到 {len(source_image2_files)} 個 T1C 圖像文件")
# 處理所有資料
for index, (source_image1_file, source_image2_file) in enumerate(zip(source_image1_files, source_image2_files), start=1):
if index % 10 == 0: # 每處理10個文件顯示一次進度
print(f"\n正在處理檔案 {index}/{len(source_image1_files)}:")
patient_id1 = extract_patient_id(source_image1_file)
patient_id2 = extract_patient_id(source_image2_file)
if patient_id1 != patient_id2 or patient_id1 is None or patient_id2 is None:
print(f"警告: 病歷號不匹配或無法提取,跳過這對文件: {source_image1_file}, {source_image2_file}")
continue
# 使用新的文件名格式
new_filename = f"OAR_{index:03d}"
# 複製並重命名圖像文件
target_image1_file = os.path.join(imagesTr_dir, f"{new_filename}_0000.nii.gz")
target_image2_file = os.path.join(imagesTr_dir, f"{new_filename}_0001.nii.gz")
shutil.copyfile(source_image1_file, target_image1_file)
shutil.copyfile(source_image2_file, target_image2_file)
# 處理標籤文件
if patient_id1 in label_map:
source_label_file = label_map[patient_id1]
target_label_file = os.path.join(labelsTr_dir, f"{new_filename}.nii.gz")
shutil.copyfile(source_label_file, target_label_file)
processed_files += 1
# 添加到映射列表
patient_mapping.append({
"編號": f"{index:03d}",
"病歷號": patient_id1
})
# 輸出處理細節
print(f"CT+CTC: {source_image1_file}")
print(f"T1C: {source_image2_file}")
print(f"複製圖像檔案: {source_image1_file}{target_image1_file}")
print(f"複製圖像檔案: {source_image2_file}{target_image2_file}")
print(f"複製標籤檔案: {source_label_file}{target_label_file}")
print() # 空行分隔每筆記錄
else:
print(f"警告: 未找到 {patient_id1} 的標籤檔案。")
print("\n所有資料處理完畢。")
# 更新 dataset.json
dataset_json["numTraining"] = processed_files
# 寫入 dataset.json 檔案
dataset_json_path = os.path.join(target_base_dir, "dataset.json")
with open(dataset_json_path, 'w') as f:
json.dump(dataset_json, f, indent=4)
print(f"dataset.json 已寫入到 {dataset_json_path}")
print(f"總共處理了 {processed_files} 個完整的資料集(圖像對加標籤)")
# 創建 Excel 文件
df = pd.DataFrame(patient_mapping)
excel_path = os.path.join(target_base_dir, "patient_mapping.xlsx")
df.to_excel(excel_path, index=False)
print(f"病人映射表已保存到 {excel_path}")
# 輸出處理摘要
print("\n處理摘要:")
print(f"總共找到的 CT+CTC 圖像: {len(source_image1_files)}")
print(f"總共找到的 T1C 圖像: {len(source_image2_files)}")
print(f"總共找到的標籤文件: {len(label_map)}")
print(f"成功處理的完整資料集: {processed_files}")
print(f"跳過的文件對數量: {len(source_image1_files) - processed_files}")

View file

@ -0,0 +1,130 @@
import json
import os
import shutil
import glob
import re
import pandas as pd
# 創建 dataset.json 內容
dataset_json = {
"channel_names": {
"0": "CT+CTC",
"1": "T1C"
},
"labels": {
"background": "0",
"Brainstem": "1",
"Right_Eye": "2",
"Left_Eye": "3",
"Optic_Chiasm": "4",
"Right_Optic_Nerve": "5",
"Left_Optic_Nerve": "6",
"TV": "7"
},
"numTraining": 0,
"file_ending": ".nii.gz"
}
# 定義目錄
source_image1_dir = "/home/onlylian/resampled_CT+CTC_TV_image"
source_image2_dir = "/home/onlylian/resampled_T1C_TV_image"
source_label_dir = "/home/onlylian/resampled_tv_label"
target_base_dir = "/home/onlylian/nnUNet/nnUNet_raw/Dataset022_OAR_TV"
imagesTr_dir = os.path.join(target_base_dir, "imagesTr")
labelsTr_dir = os.path.join(target_base_dir, "labelsTr")
# 創建目錄
os.makedirs(imagesTr_dir, exist_ok=True)
os.makedirs(labelsTr_dir, exist_ok=True)
def extract_patient_id(filename):
match = re.search(r'(CT\+CTC|T1C|label)_([^.]+)\.nii\.gz', os.path.basename(filename))
return match.group(2) if match else None
# 獲取並排序原始圖像文件
source_image1_files = sorted(glob.glob(os.path.join(source_image1_dir, "*.nii.gz")))
source_image2_files = sorted(glob.glob(os.path.join(source_image2_dir, "*.nii.gz")))
processed_files = 0
patient_mapping = []
# 創建病歷號到文件名的映射
label_files = glob.glob(os.path.join(source_label_dir, "*.nii.gz"))
label_map = {extract_patient_id(f): f for f in label_files}
print(f"找到 {len(label_map)} 個標籤文件")
print(f"找到 {len(source_image1_files)} 個 CT+CTC 圖像文件")
print(f"找到 {len(source_image2_files)} 個 T1C 圖像文件")
# 處理所有資料
for index, (source_image1_file, source_image2_file) in enumerate(zip(source_image1_files, source_image2_files), start=1):
if index % 10 == 0: # 每處理10個文件顯示一次進度
print(f"\n正在處理檔案 {index}/{len(source_image1_files)}:")
patient_id1 = extract_patient_id(source_image1_file)
patient_id2 = extract_patient_id(source_image2_file)
if patient_id1 != patient_id2 or patient_id1 is None or patient_id2 is None:
print(f"警告: 病歷號不匹配或無法提取,跳過這對文件: {source_image1_file}, {source_image2_file}")
continue
# 使用新的文件名格式
new_filename = f"OAR_TV_{index:03d}"
# 複製並重命名圖像文件
target_image1_file = os.path.join(imagesTr_dir, f"{new_filename}_0000.nii.gz")
target_image2_file = os.path.join(imagesTr_dir, f"{new_filename}_0001.nii.gz")
shutil.copyfile(source_image1_file, target_image1_file)
shutil.copyfile(source_image2_file, target_image2_file)
# 處理標籤文件
if patient_id1 in label_map:
source_label_file = label_map[patient_id1]
target_label_file = os.path.join(labelsTr_dir, f"{new_filename}.nii.gz")
shutil.copyfile(source_label_file, target_label_file)
processed_files += 1
# 添加到映射列表
patient_mapping.append({
"編號": f"{index:03d}",
"病歷號": patient_id1
})
# 輸出處理細節
print(f"CT+CTC: {source_image1_file}")
print(f"T1C: {source_image2_file}")
print(f"複製圖像檔案: {source_image1_file}{target_image1_file}")
print(f"複製圖像檔案: {source_image2_file}{target_image2_file}")
print(f"複製標籤檔案: {source_label_file}{target_label_file}")
print() # 空行分隔每筆記錄
else:
print(f"警告: 未找到 {patient_id1} 的標籤檔案。")
print("\n所有資料處理完畢。")
# 更新 dataset.json
dataset_json["numTraining"] = processed_files
# 寫入 dataset.json 檔案
dataset_json_path = os.path.join(target_base_dir, "dataset.json")
with open(dataset_json_path, 'w') as f:
json.dump(dataset_json, f, indent=4)
print(f"dataset.json 已寫入到 {dataset_json_path}")
print(f"總共處理了 {processed_files} 個完整的資料集(圖像對加標籤)")
# 創建 Excel 文件
df = pd.DataFrame(patient_mapping)
excel_path = os.path.join(target_base_dir, "patient_mapping.xlsx")
df.to_excel(excel_path, index=False)
print(f"病人映射表已保存到 {excel_path}")
# 輸出處理摘要
print("\n處理摘要:")
print(f"總共找到的 CT+CTC 圖像: {len(source_image1_files)}")
print(f"總共找到的 T1C 圖像: {len(source_image2_files)}")
print(f"總共找到的標籤文件: {len(label_map)}")
print(f"成功處理的完整資料集: {processed_files}")
print(f"跳過的文件對數量: {len(source_image1_files) - processed_files}")

View file

@ -0,0 +1,110 @@
import SimpleITK as sitk
import os
import numpy as np
def standardize_origin(image, target_origin):
"""將影像的原點標準化為目標原點"""
return sitk.Resample(
image,
image.GetSize(),
sitk.Transform(),
sitk.sitkLinear,
target_origin,
image.GetSpacing(),
image.GetDirection(),
0,
image.GetPixelID()
)
def resample_image(image, reference_image, is_label=False):
resampler = sitk.ResampleImageFilter()
resampler.SetReferenceImage(reference_image)
resampler.SetTransform(sitk.Transform())
resampler.SetOutputSpacing(reference_image.GetSpacing())
resampler.SetSize(reference_image.GetSize())
resampler.SetOutputDirection(reference_image.GetDirection())
resampler.SetOutputOrigin(reference_image.GetOrigin())
if is_label:
resampler.SetInterpolator(sitk.sitkNearestNeighbor)
resampler.SetDefaultPixelValue(0)
else:
resampler.SetInterpolator(sitk.sitkLinear)
resampler.SetDefaultPixelValue(image.GetPixelIDValue())
return resampler.Execute(image)
def process_patient(t1c_image_path, ctctc_image_path, label_path, output_t1c_dir, output_ctctc_dir, output_label_dir, patient_id):
print(f"\nProcessing patient: {patient_id}")
# 讀取影像
t1c_image = sitk.ReadImage(t1c_image_path)
ctctc_image = sitk.ReadImage(ctctc_image_path)
label = sitk.ReadImage(label_path)
print(f" Original T1C image size: {t1c_image.GetSize()}, origin: {t1c_image.GetOrigin()}")
print(f" Original CT+CTC image size: {ctctc_image.GetSize()}, origin: {ctctc_image.GetOrigin()}")
print(f" Original label size: {label.GetSize()}, origin: {label.GetOrigin()}")
# 使用 T1C 影像作為參考
reference_image = t1c_image
# 重採樣 CT+CTC 影像以匹配 T1C 影像
resampled_ctctc = resample_image(ctctc_image, reference_image)
# 重採樣 label 以匹配 T1C 影像
resampled_label = resample_image(label, reference_image, is_label=True)
print(f" Resampled CT+CTC image size: {resampled_ctctc.GetSize()}, origin: {resampled_ctctc.GetOrigin()}")
print(f" Resampled label size: {resampled_label.GetSize()}, origin: {resampled_label.GetOrigin()}")
# 保存重採樣後的影像和標籤
t1c_output_path = os.path.join(output_t1c_dir, f"T1C_{patient_id}.nii.gz")
ctctc_output_path = os.path.join(output_ctctc_dir, f"CT+CTC_{patient_id}.nii.gz")
label_output_path = os.path.join(output_label_dir, f"label_{patient_id}.nii.gz")
sitk.WriteImage(t1c_image, t1c_output_path)
sitk.WriteImage(resampled_ctctc, ctctc_output_path)
sitk.WriteImage(resampled_label, label_output_path)
print(f" T1C image saved to: {t1c_output_path}")
print(f" Resampled CT+CTC image saved to: {ctctc_output_path}")
print(f" Resampled label saved to: {label_output_path}")
def process_directory(t1c_dir, ctctc_dir, label_dir, output_t1c_dir, output_ctctc_dir, output_label_dir):
print("Starting directory processing")
os.makedirs(output_t1c_dir, exist_ok=True)
os.makedirs(output_ctctc_dir, exist_ok=True)
os.makedirs(output_label_dir, exist_ok=True)
# 獲取所有病人 ID
patient_ids = set()
for filename in os.listdir(t1c_dir):
if filename.startswith("T1C_") and filename.endswith(".nii.gz"):
patient_id = filename[4:-7] # 去掉 "T1C_" 和 ".nii.gz"
patient_ids.add(patient_id)
for patient_id in patient_ids:
t1c_path = os.path.join(t1c_dir, f"T1C_{patient_id}.nii.gz")
ctctc_path = os.path.join(ctctc_dir, f"CT+CTC_{patient_id}.nii.gz")
label_path = os.path.join(label_dir, f"T1C_label_{patient_id}.nii.gz") # 假設 label 文件名與 T1C 相同
if os.path.exists(t1c_path) and os.path.exists(ctctc_path) and os.path.exists(label_path):
process_patient(t1c_path, ctctc_path, label_path, output_t1c_dir, output_ctctc_dir, output_label_dir, patient_id)
else:
print(f" WARNING: Missing files for patient {patient_id}")
# 主程序
t1c_dir = '/home/onlylian/T1C_image_folder'
ctctc_dir = '/home/onlylian/CT+CTC_image_folder'
label_dir = '/home/onlylian/T1C_OAR_Target_label_folder' # 使用 T1C 的 label
output_t1c_dir = '/home/onlylian/resampled_T1C_TV_image_2'
output_ctctc_dir = '/home/onlylian/resampled_CT+CTC_TV_image_2'
output_label_dir = '/home/onlylian/resampled_tv_label'
print("Starting image preprocessing")
process_directory(t1c_dir, ctctc_dir, label_dir, output_t1c_dir, output_ctctc_dir, output_label_dir)
print("All processing completed.")

View file

@ -0,0 +1,147 @@
import os
import shutil
# 定义所有需要的路径
final_image_folder = '/mnt/1218/onlylian/resampled_T1_image_test'
final_label_folder = '/mnt/1218/onlylian/resampled_label_test'
mapping_file = '/mnt/1218/onlylian/patient_mapping.txt'
# nnUNet路径
base_nnunet_path = '/home/onlylian/nnUNet/nnUNet_raw/Dataset888'
nnunet_image_folder = os.path.join(base_nnunet_path, 'imagesTs')
nnunet_label_folder = os.path.join(base_nnunet_path, 'labelsTs')
# 打印初始状态
print("\nChecking directories:")
print(f"Source image folder exists: {os.path.exists(final_image_folder)}")
print(f"Source label folder exists: {os.path.exists(final_label_folder)}")
# 确保目录存在
try:
os.makedirs(base_nnunet_path, exist_ok=True)
os.makedirs(nnunet_image_folder, exist_ok=True)
os.makedirs(nnunet_label_folder, exist_ok=True)
print(f"Successfully created/verified nnUNet directories")
except Exception as e:
print(f"Error creating directories: {e}")
def get_patient_ids(folder_path, prefix='T1_'):
"""從檔案名稱中提取病人ID"""
if not os.path.exists(folder_path):
print(f"WARNING: Folder does not exist: {folder_path}")
return set()
patient_ids = set()
try:
for filename in os.listdir(folder_path):
if filename.endswith('.nii.gz'):
if filename.startswith(prefix):
# 對於T1影像: T1_XXXXX.nii.gz -> XXXXX
patient_id = filename[len(prefix):-7]
patient_ids.add(patient_id)
elif prefix == 'T1_' and filename.startswith('label_'):
# 對於標籤: label_XXXXX.nii.gz -> XXXXX
patient_id = filename[6:-7]
patient_ids.add(patient_id)
print(f"Found {len(patient_ids)} patients in {folder_path}")
except Exception as e:
print(f"Error reading directory {folder_path}: {e}")
return patient_ids
def create_filtered_folder(image_folder, label_folder, output_image_folder, output_label_folder):
"""过滤并重命名文件,同时创建映射文件"""
print("\nStarting file processing...")
# 获取病人ID
print("\nGetting patient IDs...")
image_patients = get_patient_ids(image_folder, 'T1_')
label_patients = get_patient_ids(label_folder, 'label_') # 修改為正確的前綴
# 打印一些檔案範例以供驗證
print("\nSample files in directories:")
print("Images directory:")
for f in sorted(os.listdir(image_folder))[:3]:
print(f" - {f}")
print("Labels directory:")
for f in sorted(os.listdir(label_folder))[:3]:
print(f" - {f}")
common_patients = image_patients.intersection(label_patients)
print(f"\nFound {len(common_patients)} common patients")
mapping_list = []
processed_files = {"images": 0, "labels": 0}
for index, patient_id in enumerate(sorted(common_patients), 1):
new_filename = f"T1_{index:03d}"
mapping_list.append(f"Original ID: {patient_id} -> New ID: {new_filename}")
# 处理图像文件
image_file = f'T1_{patient_id}.nii.gz'
src_image_path = os.path.join(image_folder, image_file)
dst_image_path = os.path.join(output_image_folder, f"{new_filename}_0000.nii.gz")
# 处理标签文件
label_file = f'label_{patient_id}.nii.gz' # 修改為正確的命名格式
src_label_path = os.path.join(label_folder, label_file)
dst_label_path = os.path.join(output_label_folder, f"{new_filename}.nii.gz")
# 複製檔案並記錄結果
if os.path.exists(src_image_path):
try:
shutil.copy2(src_image_path, dst_image_path)
processed_files["images"] += 1
print(f"Copied image: {os.path.basename(dst_image_path)}")
except Exception as e:
print(f"Error copying image {src_image_path}: {e}")
else:
print(f"Source image not found: {src_image_path}")
if os.path.exists(src_label_path):
try:
shutil.copy2(src_label_path, dst_label_path)
processed_files["labels"] += 1
print(f"Copied label: {os.path.basename(dst_label_path)}")
except Exception as e:
print(f"Error copying label {src_label_path}: {e}")
else:
print(f"Source label not found: {src_label_path}")
# 写入映射文件
try:
with open(mapping_file, 'w') as f:
f.write("Patient ID Mapping:\n")
f.write("=================\n\n")
for mapping in mapping_list:
f.write(f"{mapping}\n")
f.write(f"\nTotal number of patients: {len(mapping_list)}")
print(f"\nMapping file created: {mapping_file}")
except Exception as e:
print(f"Error writing mapping file: {e}")
return len(common_patients), processed_files
print("\nStarting file processing...")
common_count, processed_files = create_filtered_folder(
final_image_folder, final_label_folder,
nnunet_image_folder, nnunet_label_folder
)
print(f"\nProcessing completed:")
print(f"- Total number of common patients: {common_count}")
print(f"- Files processed: {processed_files['images']} images, {processed_files['labels']} labels")
print(f"- Patient ID mapping has been saved to: {mapping_file}")
# 验证最终结果
print("\nFinal verification:")
if os.path.exists(nnunet_label_folder):
label_files = os.listdir(nnunet_label_folder)
print(f"Files in labels directory: {len(label_files)}")
if len(label_files) > 0:
print("Sample files:")
for f in sorted(label_files)[:5]:
print(f" - {f}")
else:
print("WARNING: Labels directory is empty!")
else:
print("WARNING: Labels directory does not exist!")