|
from scipy.signal import correlate as cc |
|
from scipy.spatial.distance import euclidean |
|
from skimage.metrics import mean_squared_error as mse |
|
from skimage.metrics import structural_similarity as ssim |
|
from medpy.metric.binary import dc, hd95 |
|
import numpy as np |
|
import pandas as pd |
|
import os |
|
from ddmr.utils.constants import EPS |
|
from ddmr.utils.nifti_utils import save_nifti |
|
from skimage.transform import resize |
|
from skimage.measure import regionprops, label |
|
|
|
|
|
def ncc(y_true, y_pred, eps=EPS): |
|
f_yt = np.reshape(y_true, [-1]) |
|
f_yp = np.reshape(y_pred, [-1]) |
|
mean_yt = np.mean(f_yt) |
|
mean_yp = np.mean(f_yp) |
|
|
|
n_f_yt = f_yt - mean_yt |
|
n_f_yp = f_yp - mean_yp |
|
norm_yt = np.linalg.norm(f_yt, ord=2) |
|
norm_yp = np.linalg.norm(f_yp, ord=2) |
|
numerator = np.sum(np.multiply(n_f_yt, n_f_yp)) |
|
denominator = norm_yt * norm_yp + eps |
|
return np.divide(numerator, denominator) |
|
|
|
|
|
class EvaluationFigures: |
|
def __init__(self, output_folder): |
|
pd.set_option('display.max_columns', None) |
|
self.__metrics_df = pd.DataFrame(columns=['Name', 'Train_ds', 'Eval_ds', 'MSE', 'NCC', 'SSIM', |
|
'DICE_PAR', 'DICE_TUM', 'DICE_VES', |
|
'HD95_PAR', 'HD95_TUM', 'HD95_VES', 'TRE']) |
|
self.__output_folder = output_folder |
|
|
|
def add_sample(self, name, train_ds, eval_ds, fix_t, par_t, tum_t, ves_t, centroid_t, fix_p, par_p, tum_p, ves_p, centroid_p, scale_transform=None): |
|
|
|
n_fix_t = self.__mean_centred_img(fix_t) |
|
n_fix_p = self.__mean_centred_img(fix_p) |
|
|
|
if scale_transform is not None: |
|
s_centroid_t = self.__scale_point(centroid_t, scale_transform) |
|
s_centroid_p = self.__scale_point(centroid_p, scale_transform) |
|
else: |
|
s_centroid_t = centroid_t |
|
s_centroid_p = centroid_p |
|
|
|
new_row = {'Name': name, |
|
'Train_ds': train_ds, |
|
'Eval_ds': eval_ds, |
|
'MSE': mse(fix_t, fix_p), |
|
'NCC': ncc(n_fix_t, n_fix_p), |
|
'SSIM': ssim(fix_t, fix_p, multichannel=True), |
|
'DICE_PAR': dc(par_p, par_t), |
|
'DICE_TUM': dc(tum_p, tum_t), |
|
'DICE_VES': dc(ves_p, ves_t), |
|
'HD95_PAR': hd95(par_p, par_t) if np.sum(par_p) else 64, |
|
'HD95_TUM': hd95(tum_p, tum_t) if np.sum(tum_p) else 64, |
|
'HD95_VES': hd95(ves_p, ves_t) if np.sum(ves_p) else 64, |
|
'TRE': euclidean(s_centroid_t, s_centroid_p)} |
|
|
|
self.__metrics_df = self.__metrics_df.append(new_row, ignore_index=True) |
|
|
|
@staticmethod |
|
def __mean_centred_img(img): |
|
return img - np.mean(img) |
|
|
|
@staticmethod |
|
def __scale_point(point, scale_matrix): |
|
assert scale_matrix.shape == (4, 4), 'Transformation matrix is expected to have shape (4, 4)' |
|
aux_aug = np.ones((4,)) |
|
aux_aug[:3] = point |
|
return np.matmul(scale_matrix, aux_aug)[:1] |
|
|
|
def save_metrics(self, dest_folder=None): |
|
if dest_folder is None: |
|
dest_folder = self.__output_folder |
|
self.__metrics_df.to_csv(os.path.join(dest_folder, 'metrics.csv')) |
|
self.__metrics_df.to_latex(os.path.join(dest_folder, 'table.txt'), sparsify=True) |
|
print('Metrics saved in: ' + os.path.join(dest_folder)) |
|
|
|
def print_summary(self): |
|
print(self.__metrics_df[['MSE', 'NCC', 'SSIM', |
|
'DICE_PAR', 'DICE_TUM', 'DICE_VES', |
|
'HD95_PAR', 'HD95_TUM', 'HD95_VES', 'TRE']].describe()) |
|
|
|
|
|
def resize_img_to_original_space(img, bb, first_reshape, original_shape, clip_img=False, flow=False): |
|
first_reshape = first_reshape.astype(int) |
|
bb = bb.astype(int) |
|
original_shape = original_shape.astype(int) |
|
|
|
if flow: |
|
|
|
img = _rescale_flow_values(img, bb, img.shape, first_reshape, original_shape) |
|
|
|
min_i, min_j, min_k, bb_i, bb_j, bb_k = bb |
|
max_i = min_i + bb_i |
|
max_j = min_j + bb_j |
|
max_k = min_k + bb_k |
|
|
|
img_bb = resize(img, (bb_i, bb_j, bb_k)) |
|
|
|
|
|
img_copy = np.zeros((*first_reshape, img.shape[-1]) if len(img.shape) > 3 else first_reshape) |
|
img_copy[min_i:max_i, min_j:max_j, min_k:max_k, ...] = img_bb |
|
|
|
|
|
resized_img = resize(img_copy, original_shape, preserve_range=True, anti_aliasing=False) |
|
if clip_img or flow: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
S = resize_transformation(img.shape, bb=None, first_reshape=first_reshape, original_shape=original_shape, translate=True) |
|
bb_coords = np.asarray([[min_i, min_j, min_k], [max_i, max_j, max_k]]) |
|
bb_coords = np.hstack([bb_coords, np.ones((2, 1))]) |
|
|
|
upsamp_bbox_coords = np.around(np.matmul(S, bb_coords.T)[:-1, :].T).astype(np.int) |
|
min_i = upsamp_bbox_coords[0][0] |
|
min_j = upsamp_bbox_coords[0][1] |
|
min_k = upsamp_bbox_coords[0][2] |
|
max_i = upsamp_bbox_coords[1][0] |
|
max_j = upsamp_bbox_coords[1][1] |
|
max_k = upsamp_bbox_coords[1][2] |
|
resized_img = resized_img[min_i:max_i, min_j:max_j, min_k:max_k, ...] |
|
|
|
if flow: |
|
|
|
return resized_img, np.asarray([min_i, min_j, min_k]) |
|
return resized_img |
|
|
|
|
|
|
|
def _rescale_flow_values(flow, bb, current_img_shape, first_reshape, original_shape): |
|
S = resize_transformation(current_img_shape, bb, first_reshape, original_shape, translate=False) |
|
|
|
[si, sj, sk] = np.diag(S[:3, :3]) |
|
flow[..., 0] *= si |
|
flow[..., 1] *= sj |
|
flow[..., 2] *= sk |
|
|
|
return flow |
|
|
|
|
|
def resize_pts_to_original_space(pt, bb, current_img_shape, first_reshape, original_shape): |
|
T = resize_transformation(current_img_shape, bb, first_reshape, original_shape) |
|
if len(pt.shape) > 1: |
|
pt_aug = np.ones((4, pt.shape[0])) |
|
pt_aug[0:3, :] = pt.T |
|
else: |
|
pt_aug = np.ones((4,)) |
|
pt_aug[0:3] = pt |
|
trf_pt = np.matmul(T, pt_aug)[:-1, ...].T |
|
|
|
return trf_pt |
|
|
|
|
|
def resize_transformation(current_img_shape, bb=None, first_reshape=None, original_shape=None, translate=True): |
|
first_reshape = first_reshape.astype(int) |
|
original_shape = original_shape.astype(int) |
|
|
|
first_resize_trf = np.eye(4) |
|
if bb is not None: |
|
bb = bb.astype(int) |
|
min_i, min_j, min_k, bb_i, bb_j, bb_k = bb |
|
np.fill_diagonal(first_resize_trf, [bb_i / current_img_shape[0], bb_j / current_img_shape[1], bb_k / current_img_shape[2], 1]) |
|
if translate: |
|
first_resize_trf[:3, -1] = np.asarray([min_i, min_j, min_k]) |
|
|
|
original_resize_trf = np.eye(4) |
|
np.fill_diagonal(original_resize_trf, [original_shape[0] / first_reshape[0], original_shape[1] / first_reshape[1], original_shape[2] / first_reshape[2], 1]) |
|
|
|
return np.matmul(original_resize_trf, first_resize_trf) |
|
|
|
|