615 lines
22 KiB
Python
615 lines
22 KiB
Python
|
|
# 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之間的數字。")
|
|||
|
|
|