def clip_plot(self, out_png_folder): in_img_obj = ScanWrapper(self._in_img_path) in_mask_obj = ScanWrapper(self._in_mask_path) in_img_data = in_img_obj.get_data() in_mask_data = in_mask_obj.get_data() masked_img_data = np.zeros(in_img_data.shape, dtype=float) masked_img_data.fill(np.nan) masked_img_data[in_mask_data == 1] = in_img_data[in_mask_data == 1] # masked_img_data[masked_img_data != masked_img_data] = self._vmin self._plot_view( self._num_clip, self._step_axial, masked_img_data, 'axial', out_png_folder ) self._plot_view( self._num_clip, self._step_sagittal, masked_img_data, 'sagittal', out_png_folder ) self._plot_view( self._num_clip, self._step_coronal, masked_img_data, 'coronal', out_png_folder )
def _run_single_scan(self, idx): mask1 = ScanWrapper(self._in_data_folder.get_file_path(idx)) mask2 = ScanWrapper(self._in_folder2_obj.get_file_path(idx)) mask_union = np.zeros(mask1.get_data().shape, dtype=int) mask_union[(mask1.get_data() == 1) | (mask2.get_data() == 1)] = 1 out_path = self._out_folder_obj.get_file_path(idx) mask1.save_scan_same_space(out_path, mask_union)
def clip_plot_3_view(self, out_png_folder): in_img_obj = ScanWrapper(self._in_img_path) in_mask_obj = ScanWrapper(self._in_mask_path) in_img_data = in_img_obj.get_data() in_mask_data = in_mask_obj.get_data() masked_img_data = np.zeros(in_img_data.shape, dtype=float) masked_img_data.fill(np.nan) masked_img_data[in_mask_data == 1] = in_img_data[in_mask_data == 1]
def _run_single_scan(self, idx): test_mask = ScanWrapper(self._in_data_folder.get_file_path(idx)) effective_mask = ScanWrapper( self._effective_mask_folder_obj.get_file_path(idx)) dice_val = get_dice_with_effective_mask(self._gt_mask.get_data(), test_mask.get_data(), effective_mask.get_data()) result = {'Scan': test_mask.get_file_name(), 'Dice': dice_val} return result
def _run_single_scan(self, idx): in_ori_image_obj = ScanWrapper(self._in_data_folder.get_file_path(idx)) in_affine_image_obj = ScanWrapper( self._in_affine_folder.get_file_path(idx)) in_warped_image_obj = ScanWrapper( self._in_warped_folder.get_file_path(idx)) num_view = 3 num_clip = 5 fig = plt.figure(figsize=(num_view * 100, num_clip * 30)) gs1 = gridspec.GridSpec(num_clip, self._num_plot_per_view * num_view) gs1.update(wspace=0.025, hspace=0.05) self._plot_view(num_clip, step_clip=self._step_axial, in_ori_data=in_ori_image_obj.get_data(), in_affine_data=in_affine_image_obj.get_data(), in_warped_data=in_warped_image_obj.get_data(), gs=gs1, plot_column=0, view_flag='Axial') self._plot_view(num_clip, step_clip=self._step_sagittal, in_ori_data=in_ori_image_obj.get_data(), in_affine_data=in_affine_image_obj.get_data(), in_warped_data=in_warped_image_obj.get_data(), gs=gs1, plot_column=1, view_flag='Sagittal') self._plot_view(num_clip, step_clip=self._step_coronal, in_ori_data=in_ori_image_obj.get_data(), in_affine_data=in_affine_image_obj.get_data(), in_warped_data=in_warped_image_obj.get_data(), gs=gs1, plot_column=2, view_flag='Coronal') fig.suptitle(f'{self._in_data_folder.get_file_name(idx)}', y=0.9, fontsize=2 * self._sub_title_font_size, va='center') out_png = self._out_png_folder.get_file_path(idx) logger.info(f'Save png to {out_png}') plt.savefig(out_png, bbox_inches='tight', pad_inches=0, dpi=self._out_dpi) plt.close(fig=fig)
class CalculateDice(AbstractParallelRoutine): def __init__(self, in_folder_obj, effective_mask_folder, num_process, gt_mask): super().__init__(in_folder_obj, num_process) self._effective_mask_folder_obj = effective_mask_folder self._gt_mask = ScanWrapper(gt_mask) self._df_dice = None def get_dice(self): logger.info(f'Calculating dice score') result_list = self.run_parallel() logger.info(f'Done.') self._df_dice = pd.DataFrame(result_list) # print(self._df_dice) def save_csv(self, csv_file): logger.info(f'Save dice table to csv {csv_file}') self._df_dice.to_csv(csv_file, index=False) def _run_single_scan(self, idx): test_mask = ScanWrapper(self._in_data_folder.get_file_path(idx)) effective_mask = ScanWrapper( self._effective_mask_folder_obj.get_file_path(idx)) dice_val = get_dice_with_effective_mask(self._gt_mask.get_data(), test_mask.get_data(), effective_mask.get_data()) result = {'Scan': test_mask.get_file_name(), 'Dice': dice_val} return result
def _run_chunk_get_variance(self, chunk_list): result_list = [] im_shape = self._ref_img.get_shape() sum_image_union = np.zeros(im_shape) for idx in chunk_list: self._in_data_folder.print_idx(idx) img_obj = ScanWrapper(self._in_data_folder.get_file_path(idx)) img_data = img_obj.get_data() valid_mask = np.logical_not(np.isnan(img_data)).astype(int) residue_map = np.zeros(img_data.shape) np.subtract(img_data, self._average_map, out=residue_map, where=valid_mask > 0) residue_map = np.power(residue_map, 2) np.add(residue_map, sum_image_union, out=sum_image_union, where=valid_mask > 0) result = {'sum_image': sum_image_union} result_list.append(result) return result_list
class GetDiceEffectiveRegion(AbstractParallelRoutine): def __init__(self, in_ori_folder_obj, in_ref_valid_mask, num_process, out_folder_obj, etch_radius): super().__init__(in_ori_folder_obj, num_process) self._in_ref_valid_mask = ScanWrapper(in_ref_valid_mask) self._out_folder_obj = out_folder_obj self._etch_radius = etch_radius def _run_single_scan(self, idx): in_ori_image = ScanWrapper(self._in_data_folder.get_file_path(idx)) in_ori_data = in_ori_image.get_data() in_effective_mask = (in_ori_data == in_ori_data).astype(int) effective_region_mask = in_effective_mask * self._in_ref_valid_mask.get_data( ) # We need to make sure the boundary elements are all 0 boundary_mask = np.zeros(in_ori_image.get_shape()) boundary_mask[1:-1, 1:-1, 1:-1] = 1 effective_region_mask = effective_region_mask * boundary_mask edt_img = ndi.distance_transform_edt(effective_region_mask) effective_region_mask = (edt_img > self._etch_radius).astype(int) out_mask_path = self._out_folder_obj.get_file_path(idx) in_ori_image.save_scan_same_space(out_mask_path, effective_region_mask)
def _run_single_scan(self, idx): in_img = ScanWrapper(self._in_data_folder.get_file_path(idx)) in_mask = None if self._in_mask_folder_obj is not None: in_mask = ScanWrapper(self._in_mask_folder_obj.get_file_path(idx)) if self._in_mask_file_obj is not None: in_mask = self._in_mask_file_obj out_path = self._out_folder_obj.get_file_path(idx) in_img_data = in_img.get_data() in_mask_data = in_mask.get_data() new_img_data = np.full(in_img.get_shape(), self._ambient_val) np.copyto(new_img_data, in_img_data, where=in_mask_data > 0) in_img.save_scan_same_space(out_path, new_img_data)
def _run_single_scan(self, idx): test_mask = ScanWrapper(self._in_data_folder.get_file_path(idx)) effective_mask = ScanWrapper(self._effective_mask_folder_obj.get_file_path(idx)) effective_data = effective_mask.get_data() test_data = test_mask.get_data() gt_data = self._gt_mask.get_data() edt_test, surf_test = self._get_edt_full(test_data) edt_gt, surf_gt = self._get_edt_full(gt_data) effective_surf_test = surf_test * effective_data effective_surf_gt = surf_gt * effective_data # For debug the effective surface. # effective_surf_combine_output_path = test_mask.get_path() + '_effsur.nii.gz' # combine_effective = effective_surf_test # np.copyto(combine_effective, 2 * effective_surf_gt, where=effective_surf_gt > 0.5) # test_mask.save_scan_same_space(effective_surf_combine_output_path, combine_effective) surf_dist_map_gt2test = effective_surf_gt * edt_test surf_dist_map_test2gt = effective_surf_test * edt_gt num_surface_test = effective_surf_test.sum() num_surface_gt = effective_surf_gt.sum() msd_sym = (surf_dist_map_gt2test.sum() / num_surface_gt) + \ (surf_dist_map_test2gt.sum() / num_surface_test) msd_sym /= 2 hd_sym = np.amax(surf_dist_map_gt2test) + np.amax(surf_dist_map_test2gt) hd_sym /= 2 result = { 'Scan': test_mask.get_file_name(), 'MSD': msd_sym, 'HD': hd_sym, 'effective surface points (test)': effective_surf_test.sum(), 'effective ratio (test)': effective_surf_test.sum() / surf_test.sum(), 'effective surface points (gt)': effective_surf_gt.sum(), 'effective ratio (gt)': effective_surf_gt.sum() / surf_gt.sum() } return result
def _run_single_scan(self, idx): in_img = ScanWrapper(self._in_data_folder.get_file_path(idx)) out_path = self._out_mask_folder_obj.get_file_path(idx) in_img_data = in_img.get_data() non_nan_mask = (in_img_data == in_img_data).astype(int) if self._if_reverse: non_nan_mask = 1 - non_nan_mask in_img.save_scan_same_space(out_path, non_nan_mask)
def _run_single_scan(self, idx): in_ori_path = self._in_data_folder.get_file_path(idx) out_path = self._out_folder.get_file_path(idx) im_obj = ScanWrapper(in_ori_path) im_data = im_obj.get_data() logger.info(f'Replace nan to valude {self._replace_val}') new_im_data = np.nan_to_num(im_data, nan=self._replace_val) im_obj.save_scan_same_space(out_path, new_im_data)
def clip_plot(self, out_png_folder): in_img_obj = ScanWrapper(self._in_img_path) in_mask_obj = ScanWrapper(self._in_mask_path) in_back_obj = ScanWrapper(self._in_back_img_path) in_trans_obj = ScanWrapper(self._in_trans_img_path) in_img_data = in_img_obj.get_data() in_mask_data = in_mask_obj.get_data() in_back_data = in_back_obj.get_data() in_trans_data = in_trans_obj.get_data() # masked_img_data = np.zeros(in_img_data.shape, dtype=float) # masked_img_data.fill(np.nan) # masked_img_data[in_mask_data == 1] = in_img_data[in_mask_data == 1] # masked_back_data = np.zeros(in_back_data.shape, dtype=float) # masked_back_data.fill(np.nan) # masked_back_data[in_mask_data == 1] = in_back_data[in_mask_data == 1] masked_img_data = in_img_data masked_back_data = in_back_data masked_trans_data = self._mask_in_trans_data(in_trans_data, in_mask_data) in_trans_data = masked_trans_data self._plot_view(self._num_clip, self._step_axial, masked_img_data, masked_back_data, in_trans_data, 'axial', out_png_folder) self._plot_view(self._num_clip, self._step_sagittal, masked_img_data, masked_back_data, in_trans_data, 'sagittal', out_png_folder) self._plot_view(self._num_clip, self._step_coronal, masked_img_data, masked_back_data, in_trans_data, 'coronal', out_png_folder)
def _get_img_data(self, idx): img_obj = ScanWrapper(self._in_data_folder.get_file_path(idx)) img_data = img_obj.get_data() img_data = np.abs(img_data) np.copyto(img_data, np.nan, where=img_data < 0.01) img_data = np.log10(img_data) in_omat_path = self._in_omat_folder_obj.get_file_path(idx) omat = np.loadtxt(in_omat_path) img_data = img_data + np.log10(np.linalg.det(omat)) save_corrected_path = self._out_corrected_folder_obj.get_file_path(idx) img_obj.save_scan_same_space(save_corrected_path, img_data) return img_data
def clip_plot(self, out_png_folder): in_img_obj = ScanWrapper(self._in_img_path) in_back_obj = ScanWrapper(self._in_back_img_path) in_img_data = in_img_obj.get_data() in_back_data = in_back_obj.get_data() masked_img_data = None masked_back_data = None if self._in_mask_path is not None: in_mask_obj = ScanWrapper(self._in_mask_path) in_mask_data = in_mask_obj.get_data() masked_img_data = np.zeros(in_img_data.shape, dtype=float) masked_img_data.fill(np.nan) masked_img_data[in_mask_data == 1] = in_img_data[in_mask_data == 1] masked_back_data = np.zeros(in_back_data.shape, dtype=float) masked_back_data.fill(np.nan) masked_back_data[in_mask_data == 1] = in_back_data[in_mask_data == 1] else: masked_img_data = in_img_data masked_back_data = in_back_data self._plot_view(self._num_clip, self._step_axial, masked_img_data, masked_back_data, 'axial', out_png_folder, 1) self._plot_view(self._num_clip, self._step_sagittal, masked_img_data, masked_back_data, 'sagittal', out_png_folder, 5.23438 / 2.28335) self._plot_view(self._num_clip, self._step_coronal, masked_img_data, masked_back_data, 'coronal', out_png_folder, 5.23438 / 2.17388)
def get_pad_mask2(in_native_nii, out_mask_nii): in_native_obj = ScanWrapper(in_native_nii) in_native_img = in_native_obj.get_data() print(in_native_img.shape) z_variance_map = np.var(in_native_img, axis=2) print(z_variance_map.shape) slice_pad_region = (z_variance_map == 0).astype(int) mask_img = np.zeros(in_native_img.shape, dtype=int) for z_idx in range(mask_img.shape[2]): mask_img[:, :, z_idx] = slice_pad_region in_native_obj.save_scan_same_space(out_mask_nii, mask_img) return np.sum(slice_pad_region) != 0
def _run_single_scan(self, idx): in_ori_image = ScanWrapper(self._in_data_folder.get_file_path(idx)) in_ori_data = in_ori_image.get_data() in_effective_mask = (in_ori_data == in_ori_data).astype(int) effective_region_mask = in_effective_mask * self._in_ref_valid_mask.get_data( ) # We need to make sure the boundary elements are all 0 boundary_mask = np.zeros(in_ori_image.get_shape()) boundary_mask[1:-1, 1:-1, 1:-1] = 1 effective_region_mask = effective_region_mask * boundary_mask edt_img = ndi.distance_transform_edt(effective_region_mask) effective_region_mask = (edt_img > self._etch_radius).astype(int) out_mask_path = self._out_folder_obj.get_file_path(idx) in_ori_image.save_scan_same_space(out_mask_path, effective_region_mask)
def get_pad_mask(in_nii, out_nii): circle_info = get_ct_pixel_pad_circle_fit(in_nii) in_obj = ScanWrapper(in_nii) in_img = in_obj.get_data() mask_img = np.zeros(in_img.shape, dtype=int) if circle_info is not None: c_x, c_y, c_r = circle_info mask_slice = np.zeros((in_img.shape[0], in_img.shape[1]), dtype=int) for i in range(in_img.shape[0]): for j in range(in_img.shape[1]): dist2center = np.sqrt((i - c_x)**2 + (j - c_y)**2) if dist2center > c_r - 1: mask_slice[i, j] = 1 for k in range(in_img.shape[2]): mask_img[:, :, k] = mask_slice in_obj.save_scan_same_space(out_nii, mask_img) return circle_info is not None
def _run_chunk_get_average(self, chunk_list): result_list = [] im_shape = self._ref_img.get_shape() sum_image_union = np.zeros(im_shape) region_mask_count_image = np.zeros(im_shape) for idx in chunk_list: self._in_data_folder.print_idx(idx) img_obj = ScanWrapper(self._in_data_folder.get_file_path(idx)) img_data = img_obj.get_data() valid_mask = np.logical_not(np.isnan(img_data)).astype(int) np.add(img_data, sum_image_union, out=sum_image_union, where=valid_mask > 0) region_mask_count_image += valid_mask result = { 'sum_image': sum_image_union, 'region_count': region_mask_count_image } result_list.append(result) return result_list
def _run_single_scan(self, idx): im_obj = ScanWrapper(self._in_data_folder.get_file_path(idx)) in_img_data = im_obj.get_data() out_png_prefix = self._out_folder_obj.get_file_path(idx) # self._plot_view( # self._offset_axial, # in_img_data, # 'axial', # out_png_prefix # ) # # self._plot_view( # self._offset_sagittal, # in_img_data, # 'sagittal', # out_png_prefix # ) self._plot_view(self._offset_coronal, in_img_data, 'coronal', out_png_prefix)
def _run_single_scan(self, idx): in_mask = ScanWrapper(self._in_data_folder.get_file_path(idx)) mask_diff = np.zeros(in_mask.get_shape(), dtype=int) mask_diff[in_mask.get_data() != self._ref_mask_obj.get_data()] = 1 out_path = self._out_folder_obj.get_file_path(idx) self._ref_mask_obj.save_scan_same_space(out_path, mask_diff)
def get_ct_pixel_pad_circle_fit(in_nii): in_obj = ScanWrapper(in_nii) in_data = in_obj.get_data() print(in_data.shape) middle_slice = in_data[:, :, int(in_data.shape[2] / 2)] print(middle_slice.shape) # Get 4 walls w_x_0 = in_data[0, :, :] w_x_1 = in_data[-1, :, :] w_y_0 = in_data[:, 0, :] w_y_1 = in_data[:, -1, :] w_x_0_p = (w_x_0 <= -1024).astype(int) w_x_1_p = (w_x_1 <= -1024).astype(int) w_y_0_p = (w_y_0 <= -1024).astype(int) w_y_1_p = (w_y_1 <= -1024).astype(int) w_x_0_acc = np.prod(w_x_0_p, axis=1) w_x_1_acc = np.prod(w_x_1_p, axis=1) w_y_0_acc = np.prod(w_y_0_p, axis=1) w_y_1_acc = np.prod(w_y_1_p, axis=1) if (np.sum(w_x_0_acc) + np.sum(w_x_1_acc) + np.sum(w_y_0_acc) + np.sum(w_y_1_acc)) == 0: print(f'No padding region detected') return None # Find the circle center coordinate. w_x_0_valid = np.where(w_x_0_acc == 0) w_x_1_valid = np.where(w_x_1_acc == 0) w_y_0_valid = np.where(w_y_0_acc == 0) w_y_1_valid = np.where(w_y_1_acc == 0) # Assume the valid region will reach the boundary on 4 sides. center_x = None center_y = None radius = 0 # Get the set of intersection intersect_pos_list = [] if len(w_x_0_valid) > 0: if np.max(w_x_0_valid) < in_data.shape[1] - 1: intersect_pos_list.append({ 'x': 0, 'y': np.max(w_x_0_valid) }) if np.min(w_x_0_valid) > 0: intersect_pos_list.append({ 'x': 0, 'y': np.min(w_x_0_valid) }) if len(w_x_1_valid) > 0: if np.max(w_x_1_valid) < in_data.shape[1] - 1: intersect_pos_list.append({ 'x': in_data.shape[0] - 1, 'y': np.max(w_x_1_valid) }) if np.min(w_x_1_valid) > 0: intersect_pos_list.append({ 'x': in_data.shape[0] - 1, 'y': np.min(w_x_1_valid) }) if len(w_y_0_valid) > 0: if np.max(w_y_0_valid) < in_data.shape[0] - 1: intersect_pos_list.append({ 'x': np.max(w_y_0_valid), 'y': 0 }) if np.min(w_y_0_valid) > 0: intersect_pos_list.append({ 'x': np.min(w_y_0_valid), 'y': 0 }) if len(w_y_1_valid) > 0: if np.max(w_y_1_valid) < in_data.shape[0] - 1: intersect_pos_list.append({ 'x': np.max(w_y_1_valid), 'y': 0 }) if np.min(w_y_1_valid) > 0: intersect_pos_list.append({ 'x': np.min(w_y_1_valid), 'y': 0 }) print(len(intersect_pos_list)) print(intersect_pos_list) if len(intersect_pos_list) < 3: print(f'Not enough intercept point to fit circle') return None c_x, c_y, c_r = fit_circle_algebraic(intersect_pos_list) print(f'Fitted circle: (x: {c_x}; y: {c_y}; r: {c_r})') return c_x, c_y, c_r
class GetMeanSurfaceDist(AbstractParallelRoutine): def __init__(self, in_folder_obj, effective_mask_folder_obj, num_process, gt_mask): super().__init__(in_folder_obj, num_process) self._effective_mask_folder_obj = effective_mask_folder_obj self._gt_mask = ScanWrapper(gt_mask) self._df_surface_metric = None def get_surface_metric(self): logger.info('Calculating surface metric') result_list = self.run_parallel() logger.info('Done') self._df_surface_metric = pd.DataFrame(result_list) def save_csv(self, csv_file): logger.info(f'Save surface metric table to csv {csv_file}') self._df_surface_metric.to_csv(csv_file, index=False) def _run_single_scan(self, idx): test_mask = ScanWrapper(self._in_data_folder.get_file_path(idx)) effective_mask = ScanWrapper(self._effective_mask_folder_obj.get_file_path(idx)) effective_data = effective_mask.get_data() test_data = test_mask.get_data() gt_data = self._gt_mask.get_data() edt_test, surf_test = self._get_edt_full(test_data) edt_gt, surf_gt = self._get_edt_full(gt_data) effective_surf_test = surf_test * effective_data effective_surf_gt = surf_gt * effective_data # For debug the effective surface. # effective_surf_combine_output_path = test_mask.get_path() + '_effsur.nii.gz' # combine_effective = effective_surf_test # np.copyto(combine_effective, 2 * effective_surf_gt, where=effective_surf_gt > 0.5) # test_mask.save_scan_same_space(effective_surf_combine_output_path, combine_effective) surf_dist_map_gt2test = effective_surf_gt * edt_test surf_dist_map_test2gt = effective_surf_test * edt_gt num_surface_test = effective_surf_test.sum() num_surface_gt = effective_surf_gt.sum() msd_sym = (surf_dist_map_gt2test.sum() / num_surface_gt) + \ (surf_dist_map_test2gt.sum() / num_surface_test) msd_sym /= 2 hd_sym = np.amax(surf_dist_map_gt2test) + np.amax(surf_dist_map_test2gt) hd_sym /= 2 result = { 'Scan': test_mask.get_file_name(), 'MSD': msd_sym, 'HD': hd_sym, 'effective surface points (test)': effective_surf_test.sum(), 'effective ratio (test)': effective_surf_test.sum() / surf_test.sum(), 'effective surface points (gt)': effective_surf_gt.sum(), 'effective ratio (gt)': effective_surf_gt.sum() / surf_gt.sum() } return result @staticmethod def _get_edt_full(mask_img_data): """ To get edt on both sides of the boundary, and the boundary itself as a second mask. :param img_data: :return: """ invert_mask = np.ones(mask_img_data.shape) invert_mask = invert_mask - mask_img_data edt_img = ndi.distance_transform_edt(mask_img_data) edt_invert_img = ndi.distance_transform_edt(invert_mask) edt_full = edt_img + edt_invert_img boundary_mask = (edt_full < 1.5).astype(int) return edt_full, boundary_mask