From ce15297f56366f6d8098f038c982a9bbe3b2cecc Mon Sep 17 00:00:00 2001 From: mollybuckley Date: Thu, 19 Oct 2023 11:00:09 +0100 Subject: [PATCH 1/3] Coil options Add command line options for coil type (head/body). --- hazenlib/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hazenlib/__init__.py b/hazenlib/__init__.py index 31ba44ed..7c00c714 100644 --- a/hazenlib/__init__.py +++ b/hazenlib/__init__.py @@ -85,7 +85,7 @@ Usage: hazen [options] - hazen snr [--measured_slice_width=] [options] + hazen snr [--measured_slice_width=] [--coil=] [options] hazen acr_snr [--measured_slice_width=] [--subtract=] [options] hazen relaxometry --calc= --plate_number=<4> [--verbose] [options] @@ -171,7 +171,8 @@ def main(): selected_task = 'snr' task = init_task(selected_task, files, report, report_dir) result = task.run( - measured_slice_width = arguments['--measured_slice_width']) + measured_slice_width = arguments['--measured_slice_width'], + coil = arguments['--coil']) elif arguments['acr_snr'] or arguments[''] == 'acr_snr': selected_task = 'acr_snr' task = init_task(selected_task, files, report, report_dir) From b08ca9bd0682d53e9b33e28908f967a4520d78ff Mon Sep 17 00:00:00 2001 From: mollybuckley Date: Wed, 25 Oct 2023 13:36:43 +0100 Subject: [PATCH 2/3] Update snr.py with coil options Updated snr.py to incorporate the coil choice (head/body) from the command line, which affects the choice of smoothing kernel size. --- hazenlib/tasks/snr.py | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/hazenlib/tasks/snr.py b/hazenlib/tasks/snr.py index acad7439..4dc251e6 100644 --- a/hazenlib/tasks/snr.py +++ b/hazenlib/tasks/snr.py @@ -29,9 +29,18 @@ class SNR(HazenTask): def __init__(self, **kwargs): super().__init__(**kwargs) - def run(self, measured_slice_width=None) -> dict: + def run(self, measured_slice_width=None, coil=None) -> dict: if measured_slice_width is not None: measured_slice_width = float(measured_slice_width) + + # Determining kernel size based on coil choice. Values of 9 and 25 come from McCann 2013 paper. + if coil is None: + kernel_size = int(9) + elif coil.lower() in ["bc", "body"]: + kernel_size = int(25) + elif coil.lower() in ["hc", "head"]: + kernel_size = int(9) + snr_results = {} if len(self.data) == 2: @@ -40,7 +49,7 @@ def run(self, measured_slice_width=None) -> dict: snr_results[f"snr_subtraction_normalised_{self.key(self.data[0])}"] = round(normalised_snr, 2) for idx, dcm in enumerate(self.data): - snr, normalised_snr = self.snr_by_smoothing(dcm, measured_slice_width) + snr, normalised_snr = self.snr_by_smoothing(dcm, measured_slice_width, kernel_size) snr_results[f"snr_smoothing_measured_{self.key(dcm)}"] = round(snr, 2) snr_results[f"snr_smoothing_normalised_{self.key(dcm)}"] = round(normalised_snr, 2) @@ -109,7 +118,7 @@ def get_normalised_snr_factor(self, dcm: pydicom.Dataset, measured_slice_width=N return normalised_snr_factor - def filtered_image(self, dcm: pydicom.Dataset) -> np.array: + def filtered_image(self, dcm: pydicom.Dataset, kernel_size) -> np.array: """ Performs a 2D convolution (for filtering images) uses uniform_filter SciPy function @@ -123,15 +132,15 @@ def filtered_image(self, dcm: pydicom.Dataset) -> np.array: filtered numpy array """ a = dcm.pixel_array.astype('int') - + filter_size=kernel_size # filter size = 9, following MATLAB code and McCann 2013 paper for head coil, although note McCann 2013 recommends 25x25 for body coil. - filter_size = 9 + # 9 for head coil, 25 for body coil # TODO make kernel size optional filtered_array = ndimage.uniform_filter(a, filter_size, mode='constant') return filtered_array - def get_noise_image(self, dcm: pydicom.Dataset) -> np.array: + def get_noise_image(self, dcm: pydicom.Dataset, kernel_size) -> np.array: """ Separates the image noise by smoothing the image and subtracting the smoothed image from the original. @@ -147,7 +156,7 @@ def get_noise_image(self, dcm: pydicom.Dataset) -> np.array: a = dcm.pixel_array.astype('int') # Convolve image with boxcar/uniform kernel - imsmoothed = self.filtered_image(dcm) + imsmoothed = self.filtered_image(dcm,kernel_size) # Subtract smoothed array from original imnoise = a - imsmoothed @@ -293,7 +302,7 @@ def get_object_centre(self, dcm) -> (int, int): return int(col), int(row) - def snr_by_smoothing(self, dcm: pydicom.Dataset, measured_slice_width=None) -> float: + def snr_by_smoothing(self, dcm: pydicom.Dataset, measured_slice_width=None, kernel_size=9) -> float: """ Parameters @@ -308,7 +317,7 @@ def snr_by_smoothing(self, dcm: pydicom.Dataset, measured_slice_width=None) -> f """ col, row = self.get_object_centre(dcm=dcm) - noise_img = self.get_noise_image(dcm=dcm) + noise_img = self.get_noise_image(dcm=dcm, kernel_size=kernel_size) signal = [np.mean(roi) for roi in self.get_roi_samples(ax=None, dcm=dcm, centre_col=col, centre_row=row)] From 2f4293fc6c1ebd7e83f32e22cacd275565944339 Mon Sep 17 00:00:00 2001 From: mollybuckley Date: Mon, 27 Nov 2023 13:06:34 +0000 Subject: [PATCH 3/3] Formatting changes Some minor formatting amendments as per the requested changes on the PR. --- hazenlib/__init__.py | 3 +-- hazenlib/tasks/snr.py | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hazenlib/__init__.py b/hazenlib/__init__.py index 59bb14da..d2a9e2d2 100644 --- a/hazenlib/__init__.py +++ b/hazenlib/__init__.py @@ -107,7 +107,6 @@ --plate_number= Which plate to use for measurement: 4 or 5 (required) """ - import importlib import inspect import logging @@ -197,7 +196,7 @@ def main(): selected_task = 'snr' task = init_task(selected_task, files, report, report_dir, measured_slice_width=arguments['--measured_slice_width'], - coil = arguments['--coil']) + coil=arguments['--coil']) result = task.run() elif arguments['acr_snr'] or arguments[''] == 'acr_snr': selected_task = 'acr_snr' diff --git a/hazenlib/tasks/snr.py b/hazenlib/tasks/snr.py index 0d833293..bef9af55 100644 --- a/hazenlib/tasks/snr.py +++ b/hazenlib/tasks/snr.py @@ -34,6 +34,7 @@ def __init__(self, **kwargs): self.measured_slice_width = float(kwargs["measured_slice_width"]) except: self.measured_slice_width = None + # Determining kernel size based on coil choice. Values of 9 and 25 come from McCann 2013 paper. try: coil = kwargs["coil"] @@ -142,7 +143,7 @@ def filtered_image(self, dcm: pydicom.Dataset) -> np.array: filtered numpy array """ a = dcm.pixel_array.astype('int') - + # filter size = 9, following MATLAB code and McCann 2013 paper for head coil, although note McCann 2013 recommends 25x25 for body coil. # 9 for head coil, 25 for body coil