LIENWEIYING/T1C_OAR_TV/visualize_medical_compare.py
2026-07-22 14:50:09 +08:00

496 lines
18 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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)}")