[6fe801]: / utils / metrics.py

Download this file

125 lines (96 with data), 5.1 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import numpy as np
import os
import pandas as pd
import matplotlib.pyplot as plt
from glob import glob
def plot_boxplot(experiment_name, output_dir, exclude=[], title="Boxplot"):
'''
Plot boxplot for the given data.
Args:
experiment_name (str): Name of the experiment.
output_dir (str): Path to the output directory.
exclude (list): List of columns to exclude from the boxplot.
title (str): Title of the plot.
Note:
The dataframe holds the data in columns. Each column represents an experiment (single box plot) that we want to plot.
We add the data to specific column of the experiment in the datafraame.
Each row in the dataframe represents the result obtained from each subject in the experiment.
>> df.head()
>> Par0003.affine Par0003.bs-R1-fg Par0003.bs-R6-ug experiment_name
>> copd1 10.62 26.25 1.34 ..
>> copd2 10.07 21.45 2.68 ..
>> copd3 03.57 12.04 1.27 ..
>> copd4 07.48 29.45 1.53 ..
If we describe the dataframe, we get the following:
>> stats = df.describe()
>> stats
>> Par0003.affine Par0003.bs-R1-fg Par0003.bs-R6-ug
>> count 4.000000 4.000000 4.000000
>> mean 7.935000 22.795000 1.705000
>> std 3.417692 7.221071 0.700713
>> min 3.570000 12.040000 1.270000
>> 25% 6.345000 19.522500 1.330000
>> 50% 8.775000 23.850000 1.435000
>> 75% 10.365000 27.122500 2.060000
>> max 10.620000 29.450000 2.680000
Returns:
None. The function generates and displays the box plot.
'''
# Get the data
columns = os.listdir(f'../output/{experiment_name}/')
TRE_sample_results = [path.replace('\\', '/') for path in glob(os.path.join(output_dir, experiment_name, "***", "points", "TRE_sample_results.csv"), recursive=True)]
# Remove the excluded columns
for column in exclude:
if column in columns:
columns.remove(column)
TRE_sample_results = [item for item in TRE_sample_results if column not in item]
# debugging
# columns = columns[:5]
# TRE_sample_results = TRE_sample_results[:5]
# print(columns)
# print(TRE_sample_results)
# assert len(columns) == len(TRE_sample_results)
assert len(columns) == len(TRE_sample_results), f"Number of columns ({len(columns)}) does not match number of results ({len(TRE_sample_results)})"
# Create a dataframe
df = pd.DataFrame(columns=columns)
for i, path in enumerate(TRE_sample_results):
# Read the csv file
data = pd.read_csv(path, index_col=0)
# Add the data to the dataframe
columns[i] = columns[i] + f" ({data['TRE_mean'].mean():.3f})"
df[columns[i]] = data['TRE_mean']
# Plot the boxplot
boxplot = df.boxplot(column=columns, rot=90)
# Get the lowest values for each column
lowest_values = df.mean()
# Get the column with the overall lowest minimum value
lowest_column = lowest_values.idxmin()
# Highlight the entire boxplot for the column with the lowest minimum value in red
position = columns.index(lowest_column) + 1
# 7 is the number of data that represents a single boxplot (divide len(boxplot.get_lines())//len(columns) to get the number of data per boxplot)
boxplot.get_lines()[position * 7 - 7].set(color='red', linewidth=3)
# Set plot title
plt.title(title)
# Show the plot
plt.show()
def compute_TRE(pts_exhale_file, pts_inhale_file, voxel_size):
"""
Computes the Target Registration Error (TRE) to quantify the accuracy of the registration process. The TRE is calculated using 3D Euclidean
distance between the keypoints in the reference image (File 1) and the transformed keypoints in the registered image (File 2).
Args:
pts_exhale_file (str): path to the file containing the coordinates of the moving points
pts_inhale_file (str): path to the file containing the coordinates of the fixed points
voxel_size (tuple): voxel size in mm
Returns:
mean_TRE (float): mean TRE in mm
std_TRE (float): standard deviation of the TRE in mm
"""
# load the files if paths are provided
pts_inhale = np.loadtxt(pts_inhale_file)
pts_exhale = np.loadtxt(pts_exhale_file)
# Check if the number of points in both files is the same
if len(pts_inhale) != len(pts_exhale):
raise ValueError("The number of points in the fixed and moving files must be the same.")
# Compute the TRE - square root of the sum of the squared elements
TRE = np.linalg.norm((pts_inhale - pts_exhale) * voxel_size, axis=1)
return np.round(np.mean(TRE),2), np.round(np.std(TRE),2)