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