496 lines
18 KiB
Python
496 lines
18 KiB
Python
# 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)}")
|
||
|
||
|
||
|
||
|
||
|