From fe1aa0f07a50347cb17c6ec8b4b981fd8e557627 Mon Sep 17 00:00:00 2001 From: Haleema A Date: Thu, 22 Dec 2022 17:30:16 +0000 Subject: [PATCH 01/22] Removes '-v' from docstring --- hazenlib/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hazenlib/__init__.py b/hazenlib/__init__.py index 3358a931..b8343c9e 100644 --- a/hazenlib/__init__.py +++ b/hazenlib/__init__.py @@ -77,7 +77,7 @@ hazen [--measured_slice_width=] [--report] [--output=] [--calc_t1 | --calc_t2] [--plate_number=] [--show_template_fit] [--show_relax_fits] [--show_rois] [--log=] [--verbose] hazen -h|--help - hazen -v|--version + hazen --version Options: snr | slice_position | slice_width | spatial_resolution | uniformity | ghosting | relaxometry | snr_map From 4d6ecafbde7e36f85d1e42566a7ab20853326bad Mon Sep 17 00:00:00 2001 From: Haleema A Date: Thu, 22 Dec 2022 17:32:36 +0000 Subject: [PATCH 02/22] Adds acr_ghosting and acr_uniformity tasks to docstring --- hazenlib/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hazenlib/__init__.py b/hazenlib/__init__.py index b8343c9e..edba639b 100644 --- a/hazenlib/__init__.py +++ b/hazenlib/__init__.py @@ -74,12 +74,14 @@ Welcome to the Hazen Command Line Interface Usage: - hazen [--measured_slice_width=] [--report] [--output=] [--calc_t1 | --calc_t2] [--plate_number=] [--show_template_fit] + hazen [--measured_slice_width=] [--report] [--output=] [--calc_t1 | --calc_t2] + [--plate_number=] [--show_template_fit] [--show_relax_fits] [--show_rois] [--log=] [--verbose] hazen -h|--help hazen --version Options: - snr | slice_position | slice_width | spatial_resolution | uniformity | ghosting | relaxometry | snr_map + snr | slice_position | slice_width | spatial_resolution | uniformity | ghosting | relaxometry | snr_map | + acr_ghosting | acr_uniformity --report From 4aab2dff2192c154735be8b6ea06fdabf5c77ca5 Mon Sep 17 00:00:00 2001 From: Haleema A Date: Thu, 22 Dec 2022 17:37:30 +0000 Subject: [PATCH 03/22] Removes 'task_' from acr_ghosting and acr_uniformity filenames, and updates import references in tests --- hazenlib/tasks/{task_acr_ghosting.py => acr_ghosting.py} | 0 hazenlib/tasks/{task_acr_uniformity.py => acr_uniformity.py} | 0 tests/test_acr_ghosting.py | 2 +- tests/test_acr_uniformity.py | 2 +- 4 files changed, 2 insertions(+), 2 deletions(-) rename hazenlib/tasks/{task_acr_ghosting.py => acr_ghosting.py} (100%) rename hazenlib/tasks/{task_acr_uniformity.py => acr_uniformity.py} (100%) diff --git a/hazenlib/tasks/task_acr_ghosting.py b/hazenlib/tasks/acr_ghosting.py similarity index 100% rename from hazenlib/tasks/task_acr_ghosting.py rename to hazenlib/tasks/acr_ghosting.py diff --git a/hazenlib/tasks/task_acr_uniformity.py b/hazenlib/tasks/acr_uniformity.py similarity index 100% rename from hazenlib/tasks/task_acr_uniformity.py rename to hazenlib/tasks/acr_uniformity.py diff --git a/tests/test_acr_ghosting.py b/tests/test_acr_ghosting.py index bd6c1d72..7828b6e1 100644 --- a/tests/test_acr_ghosting.py +++ b/tests/test_acr_ghosting.py @@ -3,7 +3,7 @@ import pathlib import pydicom -from hazenlib.tasks.task_acr_ghosting import ACRGhosting +from hazenlib.tasks.acr_ghosting import ACRGhosting from tests import TEST_DATA_DIR diff --git a/tests/test_acr_uniformity.py b/tests/test_acr_uniformity.py index 287fff3e..d02d2fd2 100644 --- a/tests/test_acr_uniformity.py +++ b/tests/test_acr_uniformity.py @@ -4,7 +4,7 @@ import pydicom import numpy as np -from hazenlib.tasks.task_acr_uniformity import ACRUniformity +from hazenlib.tasks.acr_uniformity import ACRUniformity from tests import TEST_DATA_DIR From ed46bda0246b185f11fcca0f3ba2f1ce3be34063 Mon Sep 17 00:00:00 2001 From: Haleema A Date: Thu, 22 Dec 2022 17:45:00 +0000 Subject: [PATCH 04/22] Updates version from 1.0.4 to 1.0.5 --- hazenlib/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hazenlib/_version.py b/hazenlib/_version.py index 7ce86c96..8412cb14 100644 --- a/hazenlib/_version.py +++ b/hazenlib/_version.py @@ -1 +1 @@ -__version__ = '1.0.4' \ No newline at end of file +__version__ = '1.0.5' \ No newline at end of file From 216f1cbb00a5d7653dec1718e58de6d02c2ee7e3 Mon Sep 17 00:00:00 2001 From: Haleema A Date: Thu, 22 Dec 2022 17:46:14 +0000 Subject: [PATCH 05/22] Updates version reference in ReadTheDocs config --- docs/source/conf.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index b4755053..c825a69a 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -12,6 +12,8 @@ # import os import sys +from hazenlib import __version__ + sys.path.insert(0, os.path.abspath("../..")) @@ -22,7 +24,7 @@ author = 'Haris Shuaib' # The full version, including alpha/beta/rc tags -release = '0.6.0' +release = __version__ # -- General configuration --------------------------------------------------- From 49829b453b9e1ff3185e67971e627b83cfb3f729 Mon Sep 17 00:00:00 2001 From: Haleema A Date: Thu, 22 Dec 2022 17:53:28 +0000 Subject: [PATCH 06/22] Standardises test directory structure for acr_ghosting and acr_uniformity, and updates directory path in tests --- tests/data/acr/GE/{Test => }/0.dcm | Bin tests/data/acr/GE/{Test => }/1.dcm | Bin tests/data/acr/GE/{Test => }/10.dcm | Bin tests/data/acr/GE/{Test => }/2.dcm | Bin tests/data/acr/GE/{Test => }/3.dcm | Bin tests/data/acr/GE/{Test => }/4.dcm | Bin tests/data/acr/GE/{Test => }/5.dcm | Bin tests/data/acr/GE/{Test => }/6.dcm | Bin tests/data/acr/GE/{Test => }/7.dcm | Bin tests/data/acr/GE/{Test => }/8.dcm | Bin tests/data/acr/GE/{Test => }/9.dcm | Bin tests/data/acr/Siemens/{Test => }/0.dcm | Bin tests/data/acr/Siemens/{Test => }/1.dcm | Bin tests/data/acr/Siemens/{Test => }/10.dcm | Bin tests/data/acr/Siemens/{Test => }/2.dcm | Bin tests/data/acr/Siemens/{Test => }/3.dcm | Bin tests/data/acr/Siemens/{Test => }/4.dcm | Bin tests/data/acr/Siemens/{Test => }/5.dcm | Bin tests/data/acr/Siemens/{Test => }/6.dcm | Bin tests/data/acr/Siemens/{Test => }/7.dcm | Bin tests/data/acr/Siemens/{Test => }/8.dcm | Bin tests/data/acr/Siemens/{Test => }/9.dcm | Bin tests/test_acr_ghosting.py | 4 ++-- tests/test_acr_uniformity.py | 4 ++-- 24 files changed, 4 insertions(+), 4 deletions(-) rename tests/data/acr/GE/{Test => }/0.dcm (100%) rename tests/data/acr/GE/{Test => }/1.dcm (100%) rename tests/data/acr/GE/{Test => }/10.dcm (100%) rename tests/data/acr/GE/{Test => }/2.dcm (100%) rename tests/data/acr/GE/{Test => }/3.dcm (100%) rename tests/data/acr/GE/{Test => }/4.dcm (100%) rename tests/data/acr/GE/{Test => }/5.dcm (100%) rename tests/data/acr/GE/{Test => }/6.dcm (100%) rename tests/data/acr/GE/{Test => }/7.dcm (100%) rename tests/data/acr/GE/{Test => }/8.dcm (100%) rename tests/data/acr/GE/{Test => }/9.dcm (100%) rename tests/data/acr/Siemens/{Test => }/0.dcm (100%) rename tests/data/acr/Siemens/{Test => }/1.dcm (100%) rename tests/data/acr/Siemens/{Test => }/10.dcm (100%) rename tests/data/acr/Siemens/{Test => }/2.dcm (100%) rename tests/data/acr/Siemens/{Test => }/3.dcm (100%) rename tests/data/acr/Siemens/{Test => }/4.dcm (100%) rename tests/data/acr/Siemens/{Test => }/5.dcm (100%) rename tests/data/acr/Siemens/{Test => }/6.dcm (100%) rename tests/data/acr/Siemens/{Test => }/7.dcm (100%) rename tests/data/acr/Siemens/{Test => }/8.dcm (100%) rename tests/data/acr/Siemens/{Test => }/9.dcm (100%) diff --git a/tests/data/acr/GE/Test/0.dcm b/tests/data/acr/GE/0.dcm similarity index 100% rename from tests/data/acr/GE/Test/0.dcm rename to tests/data/acr/GE/0.dcm diff --git a/tests/data/acr/GE/Test/1.dcm b/tests/data/acr/GE/1.dcm similarity index 100% rename from tests/data/acr/GE/Test/1.dcm rename to tests/data/acr/GE/1.dcm diff --git a/tests/data/acr/GE/Test/10.dcm b/tests/data/acr/GE/10.dcm similarity index 100% rename from tests/data/acr/GE/Test/10.dcm rename to tests/data/acr/GE/10.dcm diff --git a/tests/data/acr/GE/Test/2.dcm b/tests/data/acr/GE/2.dcm similarity index 100% rename from tests/data/acr/GE/Test/2.dcm rename to tests/data/acr/GE/2.dcm diff --git a/tests/data/acr/GE/Test/3.dcm b/tests/data/acr/GE/3.dcm similarity index 100% rename from tests/data/acr/GE/Test/3.dcm rename to tests/data/acr/GE/3.dcm diff --git a/tests/data/acr/GE/Test/4.dcm b/tests/data/acr/GE/4.dcm similarity index 100% rename from tests/data/acr/GE/Test/4.dcm rename to tests/data/acr/GE/4.dcm diff --git a/tests/data/acr/GE/Test/5.dcm b/tests/data/acr/GE/5.dcm similarity index 100% rename from tests/data/acr/GE/Test/5.dcm rename to tests/data/acr/GE/5.dcm diff --git a/tests/data/acr/GE/Test/6.dcm b/tests/data/acr/GE/6.dcm similarity index 100% rename from tests/data/acr/GE/Test/6.dcm rename to tests/data/acr/GE/6.dcm diff --git a/tests/data/acr/GE/Test/7.dcm b/tests/data/acr/GE/7.dcm similarity index 100% rename from tests/data/acr/GE/Test/7.dcm rename to tests/data/acr/GE/7.dcm diff --git a/tests/data/acr/GE/Test/8.dcm b/tests/data/acr/GE/8.dcm similarity index 100% rename from tests/data/acr/GE/Test/8.dcm rename to tests/data/acr/GE/8.dcm diff --git a/tests/data/acr/GE/Test/9.dcm b/tests/data/acr/GE/9.dcm similarity index 100% rename from tests/data/acr/GE/Test/9.dcm rename to tests/data/acr/GE/9.dcm diff --git a/tests/data/acr/Siemens/Test/0.dcm b/tests/data/acr/Siemens/0.dcm similarity index 100% rename from tests/data/acr/Siemens/Test/0.dcm rename to tests/data/acr/Siemens/0.dcm diff --git a/tests/data/acr/Siemens/Test/1.dcm b/tests/data/acr/Siemens/1.dcm similarity index 100% rename from tests/data/acr/Siemens/Test/1.dcm rename to tests/data/acr/Siemens/1.dcm diff --git a/tests/data/acr/Siemens/Test/10.dcm b/tests/data/acr/Siemens/10.dcm similarity index 100% rename from tests/data/acr/Siemens/Test/10.dcm rename to tests/data/acr/Siemens/10.dcm diff --git a/tests/data/acr/Siemens/Test/2.dcm b/tests/data/acr/Siemens/2.dcm similarity index 100% rename from tests/data/acr/Siemens/Test/2.dcm rename to tests/data/acr/Siemens/2.dcm diff --git a/tests/data/acr/Siemens/Test/3.dcm b/tests/data/acr/Siemens/3.dcm similarity index 100% rename from tests/data/acr/Siemens/Test/3.dcm rename to tests/data/acr/Siemens/3.dcm diff --git a/tests/data/acr/Siemens/Test/4.dcm b/tests/data/acr/Siemens/4.dcm similarity index 100% rename from tests/data/acr/Siemens/Test/4.dcm rename to tests/data/acr/Siemens/4.dcm diff --git a/tests/data/acr/Siemens/Test/5.dcm b/tests/data/acr/Siemens/5.dcm similarity index 100% rename from tests/data/acr/Siemens/Test/5.dcm rename to tests/data/acr/Siemens/5.dcm diff --git a/tests/data/acr/Siemens/Test/6.dcm b/tests/data/acr/Siemens/6.dcm similarity index 100% rename from tests/data/acr/Siemens/Test/6.dcm rename to tests/data/acr/Siemens/6.dcm diff --git a/tests/data/acr/Siemens/Test/7.dcm b/tests/data/acr/Siemens/7.dcm similarity index 100% rename from tests/data/acr/Siemens/Test/7.dcm rename to tests/data/acr/Siemens/7.dcm diff --git a/tests/data/acr/Siemens/Test/8.dcm b/tests/data/acr/Siemens/8.dcm similarity index 100% rename from tests/data/acr/Siemens/Test/8.dcm rename to tests/data/acr/Siemens/8.dcm diff --git a/tests/data/acr/Siemens/Test/9.dcm b/tests/data/acr/Siemens/9.dcm similarity index 100% rename from tests/data/acr/Siemens/Test/9.dcm rename to tests/data/acr/Siemens/9.dcm diff --git a/tests/test_acr_ghosting.py b/tests/test_acr_ghosting.py index 7828b6e1..82f75bda 100644 --- a/tests/test_acr_ghosting.py +++ b/tests/test_acr_ghosting.py @@ -14,7 +14,7 @@ class TestACRGhostingSiemens(unittest.TestCase): def setUp(self): self.acr_ghosting_task = ACRGhosting(data_paths=[os.path.join(TEST_DATA_DIR, 'acr')]) - self.dcm = pydicom.read_file(os.path.join(TEST_DATA_DIR, 'acr', 'Siemens', 'Test', '6.dcm')) + self.dcm = pydicom.read_file(os.path.join(TEST_DATA_DIR, 'acr', 'Siemens', '6.dcm')) def test_object_centre(self): assert self.acr_ghosting_task.centroid_com(self.dcm.pixel_array)[1] == self.centre @@ -30,7 +30,7 @@ class TestACRGhostingGE(unittest.TestCase): def setUp(self): self.acr_ghosting_task = ACRGhosting(data_paths=[os.path.join(TEST_DATA_DIR, 'acr')]) - self.dcm = pydicom.read_file(os.path.join(TEST_DATA_DIR, 'acr', 'GE', 'Test', '4.dcm')) + self.dcm = pydicom.read_file(os.path.join(TEST_DATA_DIR, 'acr', 'GE', '4.dcm')) def test_object_centre(self): assert self.acr_ghosting_task.centroid_com(self.dcm.pixel_array)[1] == self.centre diff --git a/tests/test_acr_uniformity.py b/tests/test_acr_uniformity.py index d02d2fd2..b1f20115 100644 --- a/tests/test_acr_uniformity.py +++ b/tests/test_acr_uniformity.py @@ -21,7 +21,7 @@ class TestACRUniformitySiemens(unittest.TestCase): def setUp(self): self.acr_uniformity_task = ACRUniformity(data_paths=[os.path.join(TEST_DATA_DIR, 'acr')]) - self.dcm = pydicom.read_file(os.path.join(TEST_DATA_DIR, 'acr', 'Siemens', 'Test', '6.dcm')) + self.dcm = pydicom.read_file(os.path.join(TEST_DATA_DIR, 'acr', 'Siemens', '6.dcm')) def test_object_centre(self): assert self.acr_uniformity_task.centroid_com(self.dcm.pixel_array) == self.centre @@ -44,7 +44,7 @@ class TestACRUniformityGE(unittest.TestCase): def setUp(self): self.acr_uniformity_task = ACRUniformity(data_paths=[os.path.join(TEST_DATA_DIR, 'acr')]) - self.dcm = pydicom.read_file(os.path.join(TEST_DATA_DIR, 'acr', 'GE', 'Test', '4.dcm')) + self.dcm = pydicom.read_file(os.path.join(TEST_DATA_DIR, 'acr', 'GE', '4.dcm')) def test_object_centre(self): assert self.acr_uniformity_task.centroid_com(self.dcm.pixel_array) == self.centre From 5efa2c61599adc1f62f5b28849e3813dfef3141d Mon Sep 17 00:00:00 2001 From: Haleema A Date: Thu, 29 Dec 2022 11:26:23 +0000 Subject: [PATCH 07/22] Updates version --- CITATION.cff | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CITATION.cff b/CITATION.cff index 19064dc0..0d36d791 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -43,5 +43,5 @@ date-released: 2022-02-24 message: "If you use hazen in your work, please cite it using these metadata." repository-code: "https://github.com/GSTT-CSC/hazen" title: hazen -version: "0.6.0" +version: "1.0.5" ... From 8c139381fe64934f0abcda630a5ddae9892d7307 Mon Sep 17 00:00:00 2001 From: Haleema A Date: Thu, 29 Dec 2022 13:58:22 +0000 Subject: [PATCH 08/22] Updates .gitignore --- .gitignore | 51 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/.gitignore b/.gitignore index 836efba2..7941cabd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,34 +1,45 @@ +# virtual environments hazen-venv -.scannerwork +/venv/ + +# tests .coverage coverage.xml +pytest-coverage.txt +pytest.xml + +# cache **/__pycache__ .idea +.DS_Store +/.cache/ +/.bash_history +.pytest_cache + +# logs logs *.log +Hazen_logger.log +hazen.egg-info +dist + +# data uploads +*.png +# TODO: Remove the following if no longer needed tests/data/slicepos/results tests/data/uniformity/results -rods.png -tra_slicewidth5_Bottom_Profile.png -tra_slicewidth5_Slice_Width_and_Distortion_Results.txt -tra_slicewidth5_Slice_Width_and_Distortion_ROIs.png -tra_slicewidth5_Top_Profile.png -nodeids -htmlcov -build -dist -hazen.egg-info +hazenlib/data + +# docs docs/_build docs/build +build + +# mischellaneous +# TODO: Remove the following if no longer needed hazen_getting_started.pdf -.DS_Store -/.cache/ -/.bash_history /junit.xml -pytest-coverage.txt -pytest.xml -Hazen_logger.log -*.png -newdocs/_build -/venv/ +.scannerwork +nodeids +htmlcov \ No newline at end of file From 6e01eddeb23aa1b6dbdbe6324ce62807ff0bde56 Mon Sep 17 00:00:00 2001 From: Haleema A Date: Thu, 29 Dec 2022 14:04:26 +0000 Subject: [PATCH 09/22] Updates release instructions in README.md --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 358bd7e2..97aea7c0 100644 --- a/README.md +++ b/README.md @@ -161,11 +161,10 @@ We have used hazen with MRI data from a handful of different MRI scanners, inclu The Release Manager should ensure: - All outstanding issues for the current release have been closed, or, transferred to future release. -- All tests are passing on Github Actions. +- All tests are passing on GitHub Actions. - All documentation has been updated with correct version numbers: - - Version number in `docs/conf.py` - - Version number in `hazenlib/__init__.py` - - Version number in `CITATION.cff` + - Update version number `hazenlib/_version.py`, i.e. imported into `docs/conf.py` and `hazenlib/__init__.py` + - Update version number in `CITATION.cff` - The `release` branch has been merged into `main` branch - A new release has been created with a new version tag (tag = version number) From d26eff1561320dff6adc0d83fa4d4de59d84a015 Mon Sep 17 00:00:00 2001 From: Haleema A Date: Thu, 29 Dec 2022 21:09:42 +0000 Subject: [PATCH 10/22] Updates .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 7941cabd..845305e2 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ dist # data uploads *.png +report # TODO: Remove the following if no longer needed tests/data/slicepos/results tests/data/uniformity/results From b848596b071a9d3b0825c543ec6424195c1fe0d5 Mon Sep 17 00:00:00 2001 From: Haleema A Date: Thu, 29 Dec 2022 21:11:24 +0000 Subject: [PATCH 11/22] Updates comments and adds new snr_map arg in hazenlib init --- hazenlib/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hazenlib/__init__.py b/hazenlib/__init__.py index edba639b..435b2cee 100644 --- a/hazenlib/__init__.py +++ b/hazenlib/__init__.py @@ -371,12 +371,14 @@ def main(): measured_slice_width = float(arguments['--measured_slice_width']) logger.info(f'Calculating SNR with measured slice width {measured_slice_width}') result = task.run(measured_slice_width) - # Relaxometry not currently converted to HazenTask object - this task accessible in the CLI using the old syntax until it can be refactored + # TODO: Refactor Relaxometry task into HazenTask object Relaxometry not currently converted to HazenTask object - + # this task accessible in the CLI using the old syntax until it can be refactored elif arguments[''] == 'relaxometry': task = importlib.import_module(f"hazenlib.{arguments['']}") dicom_objects = [pydicom.read_file(x, force=True) for x in files if is_dicom_file(x)] result = parse_relaxometry_data(task, arguments, dicom_objects, report=True) - # Relaxometry not currently converted to HazenTask object - this task accessible in the CLI using the old syntax until it can be refactored + # TODO: Refactor SNR Map into HazenTask object (if not already) Relaxometry not currently converted to HazenTask + # object - this task accessible in the CLI using the old syntax until it can be refactored elif arguments[''] == 'snr_map': task = importlib.import_module(f"hazenlib.{arguments['']}") dicom_objects = [pydicom.read_file(x, force=True) for x in files if is_dicom_file(x)] From 9767c67cc55924f7e818fc37a1096eb7383b84f3 Mon Sep 17 00:00:00 2001 From: Haleema A Date: Thu, 29 Dec 2022 21:12:12 +0000 Subject: [PATCH 12/22] Updates report directory name in HazenTask to match hazenlib init --- hazenlib/HazenTask.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hazenlib/HazenTask.py b/hazenlib/HazenTask.py index fc4d582e..96e8f40d 100644 --- a/hazenlib/HazenTask.py +++ b/hazenlib/HazenTask.py @@ -10,7 +10,7 @@ class HazenTask: - def __init__(self, data_paths: list, report: bool = False, report_dir: str = os.path.join(os.getcwd(), 'hazen_reports')): + def __init__(self, data_paths: list, report: bool = False, report_dir: str = os.path.join(os.getcwd(), 'report')): self.data_paths = sorted(data_paths) self.report: bool = report self.report_path = os.path.join(report_dir, type(self).__name__) From 928b81a97bf8e265a794a08445be232ae61eddb4 Mon Sep 17 00:00:00 2001 From: Haleema A Date: Thu, 29 Dec 2022 21:13:03 +0000 Subject: [PATCH 13/22] Adds tests report directory --- tests/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/__init__.py b/tests/__init__.py index 23e23cca..cc3fa090 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -2,3 +2,4 @@ import pathlib TEST_DATA_DIR = pathlib.Path(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data')) +TEST_REPORT_DIR = pathlib.Path(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'report')) From 4b2a7acf3d6ec55f1323b0481fe6f22cf68690ff Mon Sep 17 00:00:00 2001 From: Haleema A Date: Thu, 29 Dec 2022 21:14:10 +0000 Subject: [PATCH 14/22] Adds new arg and refactors report directory creation/reference in snr_map --- hazenlib/snr_map.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/hazenlib/snr_map.py b/hazenlib/snr_map.py index 356fb4ee..9ab628bb 100644 --- a/hazenlib/snr_map.py +++ b/hazenlib/snr_map.py @@ -28,6 +28,7 @@ method for measurement of signal-to-noise ratio in MRI. Physics in Medicine & Biology, 58(11), 3775. """ +import pathlib import pydicom import numpy as np @@ -361,7 +362,7 @@ def plot_summary(original_image, snr_map, roi_corners, roi_size): def main(dcm_list, kernel_len=9, roi_size=20, roi_distance=40, - report_path=False): + report_path=False, report_dir=pathlib.Path.joinpath(pathlib.Path.cwd(), 'report', 'SNRMap')): """ Returns SNR parametric map on flood phantom DICOM file. @@ -384,17 +385,21 @@ def main(dcm_list, kernel_len=9, roi_size=20, roi_distance=40, roi_distance : int, optional Distance from centre of image to centre of each ROI along both dimensions. The default is 40. + report_path: + report_dir: Returns ------- results : dict """ - # TODO # ---- # * Scale ROI distance to account for different image sizes. # * Pass kernel_len and roi_size parameters from command line. results = {} + if report_path: + # Create nested report folder and ignore if already exists + pathlib.Path.mkdir(report_dir, parents=True, exist_ok=True) for dcm in dcm_list: @@ -463,8 +468,8 @@ def main(dcm_list, kernel_len=9, roi_size=20, roi_distance=40, # Save images # =========== if report_path: - detailed_image_path = f'{report_path}_snr_map_detailed.png' - summary_image_path = f'{report_path}_snr_map.png' + detailed_image_path = pathlib.Path.joinpath(report_dir, f'{report_path}_snr_map_detailed.png') + summary_image_path = pathlib.Path.joinpath(report_dir, f'{report_path}_snr_map.png') fig_detailed.savefig(detailed_image_path, dpi=300) fig_summary.savefig(summary_image_path, dpi=300) From 2b5742a856ab1ece2fd37cf8858bfe0e781e90b9 Mon Sep 17 00:00:00 2001 From: Haleema A Date: Thu, 29 Dec 2022 21:15:15 +0000 Subject: [PATCH 15/22] Updates tests report directory --- tests/test_acr_ghosting.py | 8 ++- tests/test_acr_uniformity.py | 14 ++-- tests/test_ghosting.py | 62 ++++++++++-------- tests/test_hazenlib.py | 2 +- tests/test_slice_width.py | 120 ++++++++++++++++++++++------------- tests/test_snr.py | 23 ++++--- tests/test_snr_map.py | 32 +++++----- 7 files changed, 156 insertions(+), 105 deletions(-) diff --git a/tests/test_acr_ghosting.py b/tests/test_acr_ghosting.py index 82f75bda..3160cd2d 100644 --- a/tests/test_acr_ghosting.py +++ b/tests/test_acr_ghosting.py @@ -4,7 +4,7 @@ import pydicom from hazenlib.tasks.acr_ghosting import ACRGhosting -from tests import TEST_DATA_DIR +from tests import TEST_DATA_DIR, TEST_REPORT_DIR class TestACRGhostingSiemens(unittest.TestCase): @@ -13,7 +13,8 @@ class TestACRGhostingSiemens(unittest.TestCase): psg = 0.056 def setUp(self): - self.acr_ghosting_task = ACRGhosting(data_paths=[os.path.join(TEST_DATA_DIR, 'acr')]) + self.acr_ghosting_task = ACRGhosting(data_paths=[os.path.join(TEST_DATA_DIR, 'acr')], + report_dir=pathlib.PurePath.joinpath(TEST_REPORT_DIR)) self.dcm = pydicom.read_file(os.path.join(TEST_DATA_DIR, 'acr', 'Siemens', '6.dcm')) def test_object_centre(self): @@ -29,7 +30,8 @@ class TestACRGhostingGE(unittest.TestCase): psg = 0.487 def setUp(self): - self.acr_ghosting_task = ACRGhosting(data_paths=[os.path.join(TEST_DATA_DIR, 'acr')]) + self.acr_ghosting_task = ACRGhosting(data_paths=[os.path.join(TEST_DATA_DIR, 'acr')], + report_dir=pathlib.PurePath.joinpath(TEST_REPORT_DIR)) self.dcm = pydicom.read_file(os.path.join(TEST_DATA_DIR, 'acr', 'GE', '4.dcm')) def test_object_centre(self): diff --git a/tests/test_acr_uniformity.py b/tests/test_acr_uniformity.py index b1f20115..47f88aef 100644 --- a/tests/test_acr_uniformity.py +++ b/tests/test_acr_uniformity.py @@ -5,14 +5,14 @@ import numpy as np from hazenlib.tasks.acr_uniformity import ACRUniformity -from tests import TEST_DATA_DIR +from tests import TEST_DATA_DIR, TEST_REPORT_DIR class TestACRUniformitySiemens(unittest.TestCase): ACR_UNIFORMITY_DATA = pathlib.Path(TEST_DATA_DIR / 'acr') centre = [129, 128] piu = 68.66 - array = np.zeros((256,256), dtype=int) + array = np.zeros((256, 256), dtype=int) array[127][126] = 1 array[126][127] = 1 array[127][127] = 1 @@ -20,7 +20,8 @@ class TestACRUniformitySiemens(unittest.TestCase): array[127][128] = 1 def setUp(self): - self.acr_uniformity_task = ACRUniformity(data_paths=[os.path.join(TEST_DATA_DIR, 'acr')]) + self.acr_uniformity_task = ACRUniformity(data_paths=[os.path.join(TEST_DATA_DIR, 'acr')], + report_dir=pathlib.PurePath.joinpath(TEST_REPORT_DIR)) self.dcm = pydicom.read_file(os.path.join(TEST_DATA_DIR, 'acr', 'Siemens', '6.dcm')) def test_object_centre(self): @@ -32,7 +33,7 @@ def test_circular_mask(self): def test_uniformity(self): results = self.acr_uniformity_task.get_integral_uniformity(self.dcm) - assert round(results,2) == self.piu + assert round(results, 2) == self.piu # class TestACRUniformityPhilips(unittest.TestCase): @@ -43,7 +44,8 @@ class TestACRUniformityGE(unittest.TestCase): piu = 84.76 def setUp(self): - self.acr_uniformity_task = ACRUniformity(data_paths=[os.path.join(TEST_DATA_DIR, 'acr')]) + self.acr_uniformity_task = ACRUniformity(data_paths=[os.path.join(TEST_DATA_DIR, 'acr')], + report_dir=pathlib.PurePath.joinpath(TEST_REPORT_DIR)) self.dcm = pydicom.read_file(os.path.join(TEST_DATA_DIR, 'acr', 'GE', '4.dcm')) def test_object_centre(self): @@ -51,4 +53,4 @@ def test_object_centre(self): def test_uniformity(self): results = self.acr_uniformity_task.get_integral_uniformity(self.dcm) - assert round(results,2) == self.piu + assert round(results, 2) == self.piu diff --git a/tests/test_ghosting.py b/tests/test_ghosting.py index 8b1975b3..d5f58113 100644 --- a/tests/test_ghosting.py +++ b/tests/test_ghosting.py @@ -4,8 +4,8 @@ import pydicom import pytest import os - -from tests import TEST_DATA_DIR +import pathlib +from tests import TEST_DATA_DIR, TEST_REPORT_DIR from hazenlib.tasks.ghosting import Ghosting from hazenlib.tools import get_dicom_files @@ -17,22 +17,23 @@ class TestGhosting(unittest.TestCase): PADDING_FROM_BOX = 30 SLICE_RADIUS = 5 PE = 'ROW' - ELIGIBLE_GHOST_AREA = range(5, SIGNAL_BOUNDING_BOX[0] - PADDING_FROM_BOX), range(SIGNAL_BOUNDING_BOX[2], SIGNAL_BOUNDING_BOX[3]) + ELIGIBLE_GHOST_AREA = range(5, SIGNAL_BOUNDING_BOX[0] - PADDING_FROM_BOX), range(SIGNAL_BOUNDING_BOX[2], + SIGNAL_BOUNDING_BOX[3]) - SIGNAL_SLICE = np.array(range(SIGNAL_CENTRE[0] - SLICE_RADIUS, SIGNAL_CENTRE[0] + SLICE_RADIUS), dtype=np.intp)[:, np.newaxis], np.array( - range(SIGNAL_CENTRE[1] - SLICE_RADIUS, SIGNAL_CENTRE[1] + SLICE_RADIUS), dtype=np.intp) + SIGNAL_SLICE = np.array(range(SIGNAL_CENTRE[0] - SLICE_RADIUS, SIGNAL_CENTRE[0] + SLICE_RADIUS), dtype=np.intp)[:, + np.newaxis], np.array( + range(SIGNAL_CENTRE[1] - SLICE_RADIUS, SIGNAL_CENTRE[1] + SLICE_RADIUS), dtype=np.intp) GHOST_SLICE = np.array( range(min(ELIGIBLE_GHOST_AREA[1]), max(ELIGIBLE_GHOST_AREA[1])), dtype=np.intp)[:, np.newaxis], np.array( range(min(ELIGIBLE_GHOST_AREA[0]), max(ELIGIBLE_GHOST_AREA[0]))) - GHOSTING = (None, 0.11803264099090763) - + GHOSTING = (None, 0.11803264099090763) def setUp(self): self.dcm = pydicom.read_file(os.path.join(TEST_DATA_DIR, 'ghosting', 'GHOSTING', 'IM_0001.dcm')) - self.ghosting = Ghosting(data_paths=get_dicom_files(os.path.join(TEST_DATA_DIR, 'ghosting', 'GHOSTING'))) - + self.ghosting = Ghosting(data_paths=get_dicom_files(os.path.join(TEST_DATA_DIR, 'ghosting', 'GHOSTING')), + report_dir=pathlib.PurePath.joinpath(TEST_REPORT_DIR)) def test_calculate_ghost_intensity(self): with pytest.raises(Exception): @@ -43,14 +44,12 @@ def test_calculate_ghost_intensity(self): with pytest.raises(Exception): self.ghosting.calculate_ghost_intensity(ghost=np.asarray([-10]), - phantom=np.asarray([-100]), - noise=np.asarray([-5])) + phantom=np.asarray([-100]), + noise=np.asarray([-5])) assert 5.0 == self.ghosting.calculate_ghost_intensity(ghost=np.asarray([10]), - phantom=np.asarray([100]), - noise=np.asarray([5])) - - + phantom=np.asarray([100]), + noise=np.asarray([5])) def test_get_signal_bounding_box(self): (left_column, right_column, upper_row, lower_row,) = self.ghosting.get_signal_bounding_box(self.dcm.pixel_array) @@ -84,40 +83,47 @@ class TestCOLPEGhosting(TestGhosting): PADDING_FROM_BOX = 30 SLICE_RADIUS = 5 ELIGIBLE_GHOST_AREA = range(SIGNAL_BOUNDING_BOX[0], SIGNAL_BOUNDING_BOX[1]), range( - SLICE_RADIUS, SIGNAL_BOUNDING_BOX[2] - PADDING_FROM_BOX) + SLICE_RADIUS, SIGNAL_BOUNDING_BOX[2] - PADDING_FROM_BOX) - SIGNAL_SLICE = np.array(range(SIGNAL_CENTRE[0] - SLICE_RADIUS, SIGNAL_CENTRE[0] + SLICE_RADIUS), dtype=np.intp)[:,np.newaxis],\ - np.array(range(SIGNAL_CENTRE[1] - SLICE_RADIUS, SIGNAL_CENTRE[1] + SLICE_RADIUS),dtype=np.intp) + SIGNAL_SLICE = np.array(range(SIGNAL_CENTRE[0] - SLICE_RADIUS, SIGNAL_CENTRE[0] + SLICE_RADIUS), dtype=np.intp)[:, + np.newaxis], \ + np.array(range(SIGNAL_CENTRE[1] - SLICE_RADIUS, SIGNAL_CENTRE[1] + SLICE_RADIUS), dtype=np.intp) GHOST_SLICE = np.array( range(min(ELIGIBLE_GHOST_AREA[1]), max(ELIGIBLE_GHOST_AREA[1])), dtype=np.intp)[:, np.newaxis], np.array( range(min(ELIGIBLE_GHOST_AREA[0]), max(ELIGIBLE_GHOST_AREA[0]))) PE = "COL" - GHOSTING = (None, 0.015138960417776908) + GHOSTING = (None, 0.015138960417776908) def setUp(self): - self.dcm = pydicom.read_file(os.path.join(TEST_DATA_DIR, 'ghosting', 'PE_COL_PHANTOM_BOTTOM_RIGHT' , 'PE_COL_PHANTOM_BOTTOM_RIGHT.IMA')) - self.ghosting = Ghosting(data_paths=get_dicom_files(os.path.join(TEST_DATA_DIR, 'ghosting', 'PE_COL_PHANTOM_BOTTOM_RIGHT'))) + self.dcm = pydicom.read_file( + os.path.join(TEST_DATA_DIR, 'ghosting', 'PE_COL_PHANTOM_BOTTOM_RIGHT', 'PE_COL_PHANTOM_BOTTOM_RIGHT.IMA')) + self.ghosting = Ghosting( + data_paths=get_dicom_files(os.path.join(TEST_DATA_DIR, 'ghosting', 'PE_COL_PHANTOM_BOTTOM_RIGHT')), + report_dir=pathlib.PurePath.joinpath(TEST_REPORT_DIR)) class TestAxialPhilipsBroomfields(TestGhosting): SIGNAL_BOUNDING_BOX = (217, 299, 11, 93) SIGNAL_CENTRE = [(SIGNAL_BOUNDING_BOX[0] + SIGNAL_BOUNDING_BOX[1]) // 2, - (SIGNAL_BOUNDING_BOX[2] + SIGNAL_BOUNDING_BOX[3]) // 2] + (SIGNAL_BOUNDING_BOX[2] + SIGNAL_BOUNDING_BOX[3]) // 2] BACKGROUND_ROIS = [(258, 264), (194, 264), (130, 264), (66, 264)] PADDING_FROM_BOX = 30 SLICE_RADIUS = 5 ELIGIBLE_GHOST_AREA = range(SLICE_RADIUS, SIGNAL_BOUNDING_BOX[0] - PADDING_FROM_BOX), range( - SIGNAL_BOUNDING_BOX[2], SIGNAL_BOUNDING_BOX[3]) - SIGNAL_SLICE = np.array(range(SIGNAL_CENTRE[0] - SLICE_RADIUS, SIGNAL_CENTRE[0] + SLICE_RADIUS), dtype=np.intp)[:,np.newaxis],\ - np.array(range(SIGNAL_CENTRE[1] - SLICE_RADIUS, SIGNAL_CENTRE[1] + SLICE_RADIUS),dtype=np.intp) + SIGNAL_BOUNDING_BOX[2], SIGNAL_BOUNDING_BOX[3]) + SIGNAL_SLICE = np.array(range(SIGNAL_CENTRE[0] - SLICE_RADIUS, SIGNAL_CENTRE[0] + SLICE_RADIUS), dtype=np.intp)[:, + np.newaxis], \ + np.array(range(SIGNAL_CENTRE[1] - SLICE_RADIUS, SIGNAL_CENTRE[1] + SLICE_RADIUS), dtype=np.intp) GHOST_SLICE = np.array( range(min(ELIGIBLE_GHOST_AREA[1]), max(ELIGIBLE_GHOST_AREA[1])), dtype=np.intp)[:, np.newaxis], np.array( range(min(ELIGIBLE_GHOST_AREA[0]), max(ELIGIBLE_GHOST_AREA[0]))) - GHOSTING = (None, 0.007246960909896829) + GHOSTING = (None, 0.007246960909896829) def setUp(self): - self.dcm = pydicom.read_file(os.path.join(TEST_DATA_DIR, 'ghosting', 'GHOSTING' , 'axial_philips_broomfields.dcm')) - self.ghosting = Ghosting(data_paths=get_dicom_files(os.path.join(TEST_DATA_DIR, 'ghosting', 'GHOSTING'))) + self.dcm = pydicom.read_file( + os.path.join(TEST_DATA_DIR, 'ghosting', 'GHOSTING', 'axial_philips_broomfields.dcm')) + self.ghosting = Ghosting(data_paths=get_dicom_files(os.path.join(TEST_DATA_DIR, 'ghosting', 'GHOSTING')), + report_dir=pathlib.PurePath.joinpath(TEST_REPORT_DIR)) diff --git a/tests/test_hazenlib.py b/tests/test_hazenlib.py index fdab9d0e..bc9aacbc 100644 --- a/tests/test_hazenlib.py +++ b/tests/test_hazenlib.py @@ -1,5 +1,5 @@ import pydicom -from tests import TEST_DATA_DIR +from tests import TEST_DATA_DIR, TEST_REPORT_DIR import hazenlib import unittest import numpy as np diff --git a/tests/test_slice_width.py b/tests/test_slice_width.py index 2e74629a..6d29b99e 100644 --- a/tests/test_slice_width.py +++ b/tests/test_slice_width.py @@ -5,7 +5,7 @@ import pydicom # import hazenlib.slice_width as hazen_slice_width -from tests import TEST_DATA_DIR +from tests import TEST_DATA_DIR, TEST_REPORT_DIR from hazenlib.tasks.slice_width import SliceWidth from hazenlib.shapes import Rod from hazenlib.tools import get_dicom_files @@ -76,44 +76,50 @@ class TestSliceWidth(unittest.TestCase): def setUp(self): # self.file = str(self.SLICE_WIDTH_DATA / 'SLICEWIDTH' / 'ANNUALQA.MR.HEAD_GENERAL.tra.slice_width.IMA') # self.dcm = pydicom.read_file(self.file) - self.slice_width = SliceWidth(data_paths=get_dicom_files(os.path.join(TEST_DATA_DIR, 'slicewidth', 'SLICEWIDTH'), sort=True)) + self.slice_width = SliceWidth( + data_paths=get_dicom_files(os.path.join(TEST_DATA_DIR, 'slicewidth', 'SLICEWIDTH'), sort=True), + report_dir=pathlib.PurePath.joinpath(TEST_REPORT_DIR)) def test_get_rods(self): rods, _ = self.slice_width.get_rods(self.slice_width.data[0]) - #print("rods") - #print(rods) + # print("rods") + # print(rods) for n in range(len(rods)): np.testing.assert_almost_equal(self.rods[n].centroid, rods[n].centroid, 3) def test_get_rod_distances(self): # From MATLAB Rods distances = self.slice_width.get_rod_distances(self.matlab_rods) - #print("distances") - #print(distances) + # print("distances") + # print(distances) assert distances == self.DISTANCES def test_get_rod_distortion_correction_coefficients(self): distances = self.slice_width.get_rod_distances(self.matlab_rods) - #print("rod distortion correction coefficient") - #print(hazen_slice_width.get_rod_distortion_correction_coefficients(distances[0], self.dcm.PixelSpacing[0])) - assert self.slice_width.get_rod_distortion_correction_coefficients(distances[0], self.slice_width.data[0].PixelSpacing[0]) == self.DIST_CORR_COEFF + # print("rod distortion correction coefficient") + # print(hazen_slice_width.get_rod_distortion_correction_coefficients(distances[0], self.dcm.PixelSpacing[0])) + assert self.slice_width.get_rod_distortion_correction_coefficients(distances[0], + self.slice_width.data[0].PixelSpacing[ + 0]) == self.DIST_CORR_COEFF def test_rod_distortions(self): - horizontal_distortion, vertical_distortion = self.slice_width.get_rod_distortions(self.matlab_rods, self.slice_width.data[0]) - #print("rod distortion") - #print(horizontal_distortion, vertical_distortion) + horizontal_distortion, vertical_distortion = self.slice_width.get_rod_distortions(self.matlab_rods, + self.slice_width.data[0]) + # print("rod distortion") + # print(horizontal_distortion, vertical_distortion) assert (round(horizontal_distortion, 2), round(vertical_distortion, 2)) == self.ROD_DIST def test_get_ramp_profiles(self): - ramp_profiles = self.slice_width.get_ramp_profiles(self.slice_width.data[0].pixel_array, self.matlab_rods, self.slice_width.data[0].PixelSpacing[0]) + ramp_profiles = self.slice_width.get_ramp_profiles(self.slice_width.data[0].pixel_array, self.matlab_rods, + self.slice_width.data[0].PixelSpacing[0]) bottom_profiles = ramp_profiles["bottom"] mean_bottom_profile = np.mean(bottom_profiles, axis=0).tolist() - #print("bottom centre ramp profile") - #print(ramp_profiles["bottom-centre"]) - #print("top centre ramp profile") - #print(ramp_profiles["top-centre"]) - #print("mean bottom profile") - #print(mean_bottom_profile) + # print("bottom centre ramp profile") + # print(ramp_profiles["bottom-centre"]) + # print("top centre ramp profile") + # print(ramp_profiles["top-centre"]) + # print("mean bottom profile") + # print(mean_bottom_profile) assert ramp_profiles["bottom-centre"] == self.BOTTOM_CENTRE assert ramp_profiles["top-centre"] == self.TOP_CENTRE assert mean_bottom_profile == self.MATLAB_PROFILE @@ -122,18 +128,19 @@ def test_baseline_correction(self): # matlab top 0.0215 -2.9668 602.4568 # matlab bottom [0.0239, -2.9349, 694.9520] - ramps = self.slice_width.get_ramp_profiles(self.slice_width.data[0].pixel_array, self.matlab_rods, self.slice_width.data[0].PixelSpacing[0]) + ramps = self.slice_width.get_ramp_profiles(self.slice_width.data[0].pixel_array, self.matlab_rods, + self.slice_width.data[0].PixelSpacing[0]) top_mean_ramp = np.mean(ramps["top"], axis=0) top_coefficients = list(self.slice_width.baseline_correction(top_mean_ramp, sample_spacing=0.25)["f"]) - #print("top bline corr coeff") - #print(round(top_coefficients[0], 4)) + # print("top bline corr coeff") + # print(round(top_coefficients[0], 4)) assert round(top_coefficients[0], 4) == self.BLINE_TOP bottom_mean_ramp = np.mean(ramps["bottom"], axis=0) bottom_coefficients = list(self.slice_width.baseline_correction(bottom_mean_ramp, sample_spacing=0.25)["f"]) - #print("bottom bline corr coeff") - #print(round(bottom_coefficients[0], 4)) + # print("bottom bline corr coeff") + # print(round(bottom_coefficients[0], 4)) assert round(bottom_coefficients[0], 4) == self.BLINE_BOT def test_trapezoid(self): @@ -157,7 +164,8 @@ def test_get_initial_trapezoid_fit_and_coefficients(self): """ sample_spacing = 0.25 slice_thickness = self.slice_width.data[0].SliceThickness - ramps = self.slice_width.get_ramp_profiles(self.slice_width.data[0].pixel_array, self.matlab_rods, self.slice_width.data[0].PixelSpacing[0]) + ramps = self.slice_width.get_ramp_profiles(self.slice_width.data[0].pixel_array, self.matlab_rods, + self.slice_width.data[0].PixelSpacing[0]) top_mean_ramp = np.mean(ramps["top"], axis=0) bottom_mean_ramp = np.mean(ramps["bottom"], axis=0) ramps_baseline_corrected = { @@ -167,21 +175,22 @@ def test_get_initial_trapezoid_fit_and_coefficients(self): trapezoid_fit, trapezoid_fit_coefficients = self.slice_width.get_initial_trapezoid_fit_and_coefficients( ramps_baseline_corrected["top"]["profile_corrected_interpolated"], slice_thickness) - #print("trap fit coeff top initial") - #print(trapezoid_fit_coefficients[:4]) + # print("trap fit coeff top initial") + # print(trapezoid_fit_coefficients[:4]) assert trapezoid_fit_coefficients[:4] == self.TRAP_FIT_COEFF_TOP trapezoid_fit, trapezoid_fit_coefficients = self.slice_width.get_initial_trapezoid_fit_and_coefficients( ramps_baseline_corrected["bottom"]["profile_corrected_interpolated"], slice_thickness) - #print("trap fit coeff bottom initial") - #print(trapezoid_fit_coefficients[:4]) + # print("trap fit coeff bottom initial") + # print(trapezoid_fit_coefficients[:4]) assert trapezoid_fit_coefficients[:4] == self.TRAP_FIT_COEFF_BOT def test_fit_trapezoid(self): sample_spacing = 0.25 slice_thickness = self.slice_width.data[0].SliceThickness - ramps = self.slice_width.get_ramp_profiles(self.slice_width.data[0].pixel_array, self.matlab_rods, self.slice_width.data[0].PixelSpacing[0]) + ramps = self.slice_width.get_ramp_profiles(self.slice_width.data[0].pixel_array, self.matlab_rods, + self.slice_width.data[0].PixelSpacing[0]) top_mean_ramp = np.mean(ramps["top"], axis=0) bottom_mean_ramp = np.mean(ramps["bottom"], axis=0) @@ -191,11 +200,11 @@ def test_fit_trapezoid(self): } trapezoid_fit_coefficients, baseline_fit_coefficients = self.slice_width.fit_trapezoid( profiles=ramps_baseline_corrected["top"], slice_thickness=slice_thickness) - #print("top trap fit coeff") - #print(trapezoid_fit_coefficients) + # print("top trap fit coeff") + # print(trapezoid_fit_coefficients) - #print("top bline fit coeff") - #print(baseline_fit_coefficients) + # print("top bline fit coeff") + # print(baseline_fit_coefficients) # check top profile first matlab_trapezoid_fit_coefficients = self.MATLAB_TRAP_FIT_COEFF @@ -212,10 +221,10 @@ def test_fit_trapezoid(self): trapezoid_fit_coefficients, baseline_fit_coefficients = self.slice_width.fit_trapezoid( profiles=ramps_baseline_corrected["bottom"], slice_thickness=slice_thickness) - #print("bottom trap fit coeff") - #print(trapezoid_fit_coefficients) - #print("bottom bline fit coeff") - #print(baseline_fit_coefficients) + # print("bottom trap fit coeff") + # print(trapezoid_fit_coefficients) + # print("bottom bline fit coeff") + # print(baseline_fit_coefficients) matlab_trapezoid_fit_coefficients = self.MATLAB_TRAP_FIT_COEFF_BOT matlab_baseline_fit_coefficients = self.MATLAB_BLINE_FIT_COEFF_BOT @@ -229,8 +238,8 @@ def test_slice_width(self): results = self.slice_width.run() self.slice_width.key(self.slice_width.data[0]) # key = f"{self.slice_width.data[0].SeriesDescription}_{self.slice_width.data[0].SeriesNumber}_{self.slice_width.data[0].InstanceNumber}" - #print("slice width") - #print(results[key]['slice_width_mm']) + # print("slice width") + # print(results[key]['slice_width_mm']) # assert abs(results[key]['slice_width_mm'] - self.SW_MATLAB) < 0.1 assert abs(results[self.slice_width.key(self.slice_width.data[0])]['slice_width_mm'] - self.SW_MATLAB) < 0.1 @@ -272,7 +281,30 @@ class Test512Matrix(TestSliceWidth): TOP_CENTRE = 196 BOTTOM_CENTRE = 316 - MATLAB_PROFILE = [7970.0, 7912.7, 7884.8, 7858.8, 7812.95, 7759.8, 7717.925, 7686.575, 7658.275, 7625.375, 7578.45, 7522.275, 7472.325, 7439.35, 7408.325, 7364.875, 7327.475, 7311.0, 7299.025, 7265.475, 7213.525, 7166.7, 7122.625, 7070.875, 7022.25, 6998.95, 6990.05, 6965.325, 6923.2, 6891.6, 6880.05, 6856.425, 6809.975, 6770.95, 6759.55, 6752.15, 6721.55, 6682.175, 6658.075, 6642.175, 6611.75, 6562.325, 6512.85, 6485.575, 6479.45, 6478.3, 6467.8, 6439.45, 6396.675, 6351.825, 6320.7, 6309.525, 6310.175, 6303.0, 6267.3, 6214.1, 6173.725, 6167.0, 6176.825, 6171.4, 6152.0, 6146.4, 6147.425, 6127.85, 6090.75, 6065.175, 6055.675, 6031.15, 5991.975, 5977.125, 5996.25, 5998.8, 5951.0, 5895.325, 5883.475, 5903.65, 5899.7, 5861.825, 5843.075, 5860.825, 5872.95, 5857.625, 5835.2, 5828.275, 5816.95, 5783.475, 5752.975, 5753.025, 5766.5, 5768.75, 5773.775, 5794.775, 5802.7, 5757.25, 5684.375, 5638.1, 5624.1, 5593.575, 5519.275, 5428.525, 5351.5, 5274.45, 5169.925, 5045.75, 4932.65, 4839.15, 4757.825, 4684.975, 4621.675, 4577.3, 4547.775, 4529.55, 4515.15, 4495.7, 4479.775, 4484.425, 4501.25, 4487.35, 4437.025, 4392.075, 4386.55, 4401.175, 4401.925, 4386.75, 4388.975, 4402.075, 4395.4, 4376.25, 4384.05, 4419.075, 4443.875, 4436.9, 4423.475, 4437.25, 4467.7, 4485.775, 4490.65, 4499.55, 4522.525, 4556.525, 4594.4, 4634.55, 4662.325, 4658.45, 4645.775, 4675.325, 4746.475, 4802.05, 4809.95, 4813.95, 4874.025, 4968.925, 5048.425, 5119.975, 5236.2, 5398.2, 5542.675, 5632.375, 5706.4, 5813.375, 5943.575, 6056.1, 6135.025, 6193.7, 6246.75, 6291.075, 6325.35, 6353.55, 6371.325, 6382.85, 6403.925, 6445.9, 6493.45, 6522.275, 6531.15, 6535.175, 6541.625, 6544.275, 6557.0, 6593.725, 6651.325, 6703.0, 6725.95, 6732.325, 6745.5, 6774.625, 6811.225, 6843.275, 6863.175, 6874.65, 6893.3, 6934.7, 6985.0, 7019.45, 7049.125, 7095.475, 7143.35, 7165.65, 7169.85, 7197.3, 7255.65, 7304.35, 7319.925, 7333.55, 7385.35, 7466.05, 7530.7, 7559.15, 7579.95, 7614.85, 7657.725, 7699.05, 7747.975, 7805.1, 7855.1, 7888.925, 7913.6, 7947.425, 7996.65, 8051.65, 8105.075, 8160.575, 8215.6, 8258.975, 8284.975, 8312.85, 8364.925, 8438.1, 8501.675, 8547.175, 8597.825, 8665.9, 8731.175, 8776.475, 8814.125, 8864.925, 8922.575, 8972.85, 9023.675, 9093.4, 9167.25, 9216.7] + MATLAB_PROFILE = [7970.0, 7912.7, 7884.8, 7858.8, 7812.95, 7759.8, 7717.925, 7686.575, 7658.275, 7625.375, 7578.45, + 7522.275, 7472.325, 7439.35, 7408.325, 7364.875, 7327.475, 7311.0, 7299.025, 7265.475, 7213.525, + 7166.7, 7122.625, 7070.875, 7022.25, 6998.95, 6990.05, 6965.325, 6923.2, 6891.6, 6880.05, + 6856.425, 6809.975, 6770.95, 6759.55, 6752.15, 6721.55, 6682.175, 6658.075, 6642.175, 6611.75, + 6562.325, 6512.85, 6485.575, 6479.45, 6478.3, 6467.8, 6439.45, 6396.675, 6351.825, 6320.7, + 6309.525, 6310.175, 6303.0, 6267.3, 6214.1, 6173.725, 6167.0, 6176.825, 6171.4, 6152.0, 6146.4, + 6147.425, 6127.85, 6090.75, 6065.175, 6055.675, 6031.15, 5991.975, 5977.125, 5996.25, 5998.8, + 5951.0, 5895.325, 5883.475, 5903.65, 5899.7, 5861.825, 5843.075, 5860.825, 5872.95, 5857.625, + 5835.2, 5828.275, 5816.95, 5783.475, 5752.975, 5753.025, 5766.5, 5768.75, 5773.775, 5794.775, + 5802.7, 5757.25, 5684.375, 5638.1, 5624.1, 5593.575, 5519.275, 5428.525, 5351.5, 5274.45, + 5169.925, 5045.75, 4932.65, 4839.15, 4757.825, 4684.975, 4621.675, 4577.3, 4547.775, 4529.55, + 4515.15, 4495.7, 4479.775, 4484.425, 4501.25, 4487.35, 4437.025, 4392.075, 4386.55, 4401.175, + 4401.925, 4386.75, 4388.975, 4402.075, 4395.4, 4376.25, 4384.05, 4419.075, 4443.875, 4436.9, + 4423.475, 4437.25, 4467.7, 4485.775, 4490.65, 4499.55, 4522.525, 4556.525, 4594.4, 4634.55, + 4662.325, 4658.45, 4645.775, 4675.325, 4746.475, 4802.05, 4809.95, 4813.95, 4874.025, 4968.925, + 5048.425, 5119.975, 5236.2, 5398.2, 5542.675, 5632.375, 5706.4, 5813.375, 5943.575, 6056.1, + 6135.025, 6193.7, 6246.75, 6291.075, 6325.35, 6353.55, 6371.325, 6382.85, 6403.925, 6445.9, + 6493.45, 6522.275, 6531.15, 6535.175, 6541.625, 6544.275, 6557.0, 6593.725, 6651.325, 6703.0, + 6725.95, 6732.325, 6745.5, 6774.625, 6811.225, 6843.275, 6863.175, 6874.65, 6893.3, 6934.7, + 6985.0, 7019.45, 7049.125, 7095.475, 7143.35, 7165.65, 7169.85, 7197.3, 7255.65, 7304.35, + 7319.925, 7333.55, 7385.35, 7466.05, 7530.7, 7559.15, 7579.95, 7614.85, 7657.725, 7699.05, + 7747.975, 7805.1, 7855.1, 7888.925, 7913.6, 7947.425, 7996.65, 8051.65, 8105.075, 8160.575, + 8215.6, 8258.975, 8284.975, 8312.85, 8364.925, 8438.1, 8501.675, 8547.175, 8597.825, 8665.9, + 8731.175, 8776.475, 8814.125, 8864.925, 8922.575, 8972.85, 9023.675, 9093.4, 9167.25, 9216.7] BLINE_TOP = 0.1686 BLINE_BOT = 0.2072 @@ -288,6 +320,8 @@ class Test512Matrix(TestSliceWidth): SW_MATLAB = 4.972852917690252 def setUp(self): - self.slice_width = SliceWidth(data_paths=get_dicom_files(os.path.join(TEST_DATA_DIR, 'slicewidth', '512_matrix'), sort=True)) + self.slice_width = SliceWidth( + data_paths=get_dicom_files(os.path.join(TEST_DATA_DIR, 'slicewidth', '512_matrix'), sort=True), + report_dir=pathlib.PurePath.joinpath(TEST_REPORT_DIR)) # self.file = str(TEST_DATA_DIR / 'slicewidth' / 'SLICEWIDTH' / '512_matrix') - # self.dcm = pydicom.read_file(self.file) \ No newline at end of file + # self.dcm = pydicom.read_file(self.file) diff --git a/tests/test_snr.py b/tests/test_snr.py index 0cdf8ced..83852283 100644 --- a/tests/test_snr.py +++ b/tests/test_snr.py @@ -2,7 +2,7 @@ import pathlib import pydicom import os -from tests import TEST_DATA_DIR +from tests import TEST_DATA_DIR, TEST_REPORT_DIR from hazenlib.tools import get_dicom_files from hazenlib.tasks.snr import SNR @@ -28,7 +28,8 @@ class TestSnr(unittest.TestCase): LOWER_SUBTRACT_SNR = IMAGE_SUBTRACT_SNR * 0.98 def setUp(self): - self.snr = SNR(data_paths=get_dicom_files(os.path.join(TEST_DATA_DIR, 'snr', 'Siemens'), sort=True)) + self.snr = SNR(data_paths=get_dicom_files(os.path.join(TEST_DATA_DIR, 'snr', 'Siemens'), sort=True), + report_dir=pathlib.PurePath.joinpath(TEST_REPORT_DIR)) def test_get_object_centre(self): assert self.snr.get_object_centre(self.snr.data[0]) == self.OBJECT_CENTRE @@ -68,11 +69,13 @@ class TestSnrPhilips(TestSnr): def setUp(self): # self.test_file = pydicom.read_file(str(self.SNR_DATA / 'Philips' / 'Philips_IM-0011-0005.dcm'), force=True) # self.test_file_2 = pydicom.read_file(str(self.SNR_DATA / 'Philips' / 'Philips_IM-0011-0006.dcm'), force=True) - self.snr = SNR(data_paths=get_dicom_files(os.path.join(TEST_DATA_DIR, 'snr', 'Philips'), sort=True)) + self.snr = SNR(data_paths=get_dicom_files(os.path.join(TEST_DATA_DIR, 'snr', 'Philips'), sort=True), + report_dir=pathlib.PurePath.joinpath(TEST_REPORT_DIR)) def test_image_snr(self): val = self.snr.run() - self.assertTrue(self.LOWER_SMOOTHED_SNR <= val[self.snr.key(self.snr.data[0])][f"snr_smoothing_normalised_{self.snr.key(self.snr.data[0])}"] <= self.UPPER_SMOOTHED_SNR) + self.assertTrue(self.LOWER_SMOOTHED_SNR <= val[self.snr.key(self.snr.data[0])][ + f"snr_smoothing_normalised_{self.snr.key(self.snr.data[0])}"] <= self.UPPER_SMOOTHED_SNR) self.assertTrue(self.LOWER_SUBTRACT_SNR <= val[self.snr.key(self.snr.data[0])][ f"snr_subtraction_normalised_{self.snr.key(self.snr.data[0])}"] <= self.UPPER_SUBTRACT_SNR) @@ -100,15 +103,18 @@ class TestSnrGE(TestSnr): def setUp(self): # self.test_file = pydicom.read_file(str(self.SNR_DATA / 'GE' / 'IM-0003-0001.dcm'), force=True) # self.test_file_2 = pydicom.read_file(str(self.SNR_DATA / 'GE' / 'IM-0004-0001.dcm'), force=True) - self.snr = SNR(data_paths=get_dicom_files(os.path.join(TEST_DATA_DIR, 'snr', 'GE'), sort=True)) + self.snr = SNR(data_paths=get_dicom_files(os.path.join(TEST_DATA_DIR, 'snr', 'GE'), sort=True), + report_dir=pathlib.PurePath.joinpath(TEST_REPORT_DIR)) def test_image_snr(self): # val = self.snr.run(data=[self.test_file, self.test_file_2]) val = self.snr.run() self.assertTrue( - self.LOWER_SMOOTHED_SNR <= val[self.snr.key(self.snr.data[0])][f"snr_smoothing_normalised_{self.snr.key(self.snr.data[0])}"] <= self.UPPER_SMOOTHED_SNR) + self.LOWER_SMOOTHED_SNR <= val[self.snr.key(self.snr.data[0])][ + f"snr_smoothing_normalised_{self.snr.key(self.snr.data[0])}"] <= self.UPPER_SMOOTHED_SNR) self.assertTrue( - self.LOWER_SUBTRACT_SNR <= val[self.snr.key(self.snr.data[0])][f"snr_subtraction_normalised_{self.snr.key(self.snr.data[0])}"] <= self.UPPER_SUBTRACT_SNR) + self.LOWER_SUBTRACT_SNR <= val[self.snr.key(self.snr.data[0])][ + f"snr_subtraction_normalised_{self.snr.key(self.snr.data[0])}"] <= self.UPPER_SUBTRACT_SNR) class TestSnrThreshold(TestSnr): @@ -132,7 +138,8 @@ class TestSnrThreshold(TestSnr): def setUp(self): # self.test_file = pydicom.read_file(str(self.SNR_DATA / 'VIDA' / 'HC_SNR_SAG_1.dcm'), force=True) # self.test_file_2 = pydicom.read_file(str(self.SNR_DATA / 'VIDA' / 'HC_SNR_SAG_2.dcm'), force=True) - self.snr = SNR(data_paths=get_dicom_files(os.path.join(TEST_DATA_DIR, 'snr_threshold', 'VIDA'), sort=True)) + self.snr = SNR(data_paths=get_dicom_files(os.path.join(TEST_DATA_DIR, 'snr_threshold', 'VIDA'), sort=True), + report_dir=pathlib.PurePath.joinpath(TEST_REPORT_DIR)) def test_get_object_centre(self): assert self.snr.get_object_centre(self.snr.data[0]) == self.OBJECT_CENTRE diff --git a/tests/test_snr_map.py b/tests/test_snr_map.py index 7d8f29e0..1803f36d 100644 --- a/tests/test_snr_map.py +++ b/tests/test_snr_map.py @@ -1,3 +1,4 @@ +import pathlib import unittest import pydicom import numpy as np @@ -5,14 +6,14 @@ import matplotlib import hazenlib.snr_map as hazen_snr_map -from tests import TEST_DATA_DIR +from tests import TEST_DATA_DIR, TEST_REPORT_DIR -class TestSnrMap(unittest.TestCase): +class TestSnrMap(unittest.TestCase): siemens_1 = os.path.join(TEST_DATA_DIR, 'snr', 'Siemens', 'tra_250_2meas_1.IMA') - RANDOM_DATA = np.array([-39.5905228 , 9.56115628, 21.34564697, - 65.52582681, 49.27813827, 14.395829 , - 77.88067287, 78.14606583, -55.1427819 , + RANDOM_DATA = np.array([-39.5905228, 9.56115628, 21.34564697, + 65.52582681, 49.27813827, 14.395829, + 77.88067287, 78.14606583, -55.1427819, 16.77284764, -45.36429801, -41.69026038]) ROI_CORNERS_TEST = [np.array([114, 121]), np.array([74, 81]), np.array([154, 81]), @@ -24,22 +25,23 @@ def setUp(self): self.DCM = pydicom.read_file(self.siemens_1) original, smooth, noise = hazen_snr_map.smooth(self.DCM) - self.images = {'original' : original, - 'smooth' : smooth, - 'noise' : noise} + self.images = {'original': original, + 'smooth': smooth, + 'noise': noise} def test_snr_value(self): dcms = [pydicom.dcmread(self.siemens_1)] - results = hazen_snr_map.main(dcms, report_path=True) + results = hazen_snr_map.main(dcms, report_path=True, report_dir=pathlib.Path.joinpath(TEST_REPORT_DIR, 'SNRMap')) np.testing.assert_almost_equal(192.88188017908504, - results['snr_map_snr_seFoV250_2meas_slice5mm_tra_repeat_PSN_noDC_2_1_tra_250_2meas_1.IMA']) + results[ + 'snr_map_snr_seFoV250_2meas_slice5mm_tra_repeat_PSN_noDC_2_1_tra_250_2meas_1.IMA']) def test_sample_std(self): np.testing.assert_almost_equal(49.01842637544699, hazen_snr_map.sample_std(self.RANDOM_DATA)) def test_smooth(self): - #original, smooth, noise = hazen_snr_map.smooth(self.DCM) + # original, smooth, noise = hazen_snr_map.smooth(self.DCM) assert self.images['original'].cumsum().sum() == 1484467722691 np.testing.assert_almost_equal( self.images['smooth'].cumsum().sum(), 1484468146211.5635) @@ -56,16 +58,16 @@ def test_get_rois(self): assert mask.sum() == 29444 def test_calc_snr(self): - #original, smooth, noise = hazen_snr_map.smooth(self.DCM) + # original, smooth, noise = hazen_snr_map.smooth(self.DCM) snr = hazen_snr_map.calc_snr( self.images['original'], self.images['noise'], self.ROI_CORNERS_TEST, 20) np.testing.assert_approx_equal(snr, 192.8818801790859) def test_calc_snr_map(self): - #original, smooth, noise = hazen_snr_map.smooth(self.DCM) + # original, smooth, noise = hazen_snr_map.smooth(self.DCM) snr_map = hazen_snr_map.calc_snr_map( self.images['original'], self.images['noise'], 20) - np.testing.assert_almost_equal(snr_map.cumsum().sum(),128077116718.40483) + np.testing.assert_almost_equal(snr_map.cumsum().sum(), 128077116718.40483) def test_plot_detailed(self): # Just check a valid figure handle is returned @@ -91,7 +93,5 @@ def test_plot_summary(self): assert isinstance(fig, matplotlib.figure.Figure) - - if __name__ == '__main__': unittest.main() From 0083d7b0750aefe7da5aff3b5e7f18761b4a4060 Mon Sep 17 00:00:00 2001 From: Haleema A Date: Thu, 29 Dec 2022 21:22:02 +0000 Subject: [PATCH 16/22] Updates tests report directory --- tests/test_relaxometry.py | 73 +++++++++++++++----------------- tests/test_slice_position.py | 8 ++-- tests/test_spatial_resolution.py | 9 ++-- tests/test_uniformity.py | 11 +++-- 4 files changed, 51 insertions(+), 50 deletions(-) diff --git a/tests/test_relaxometry.py b/tests/test_relaxometry.py index 847263ba..699e773c 100644 --- a/tests/test_relaxometry.py +++ b/tests/test_relaxometry.py @@ -13,21 +13,21 @@ import hazenlib.relaxometry as hazen_relaxometry from hazenlib.exceptions import ArgumentCombinationError -from tests import TEST_DATA_DIR +from tests import TEST_DATA_DIR, TEST_REPORT_DIR class TestRelaxometry(unittest.TestCase): # test parameters here - + # Values for transform_coords tests TEST_COORDS = [[0, 0], [0, 1], [1, 2]] COORDS_FLIP = [[0, 0], [1, 0], [2, 1]] COORDS_TRANS = [[3, 1], [3, 2], [4, 3]] COORDS_TRANS_FLIP = [[3, 1], [4, 1], [5, 2]] COORDS_TRANS_COL_ROW = [[1, 3], [1, 4], [2, 5]] - COORDS_TRANS_ROTATE = [[10, 20], [10.5, 20 + np.sqrt(3)/2], - [11 + np.sqrt(3)/2, 19.5 + np.sqrt(3)]] - + COORDS_TRANS_ROTATE = [[10, 20], [10.5, 20 + np.sqrt(3) / 2], + [11 + np.sqrt(3) / 2, 19.5 + np.sqrt(3)]] + # T1_FILES are in random order to test sorting T1_DIR = os.path.join(TEST_DATA_DIR, 'relaxometry', 'T1', 'site1_20200218', 'plate5') @@ -66,16 +66,16 @@ class TestRelaxometry(unittest.TestCase): [73, 127], [60, 109], [60, 84], [73, 65], [81, 81], [112, 81], [112, 112], [81, 111], [43, 97]] - + # Mask generation MASK_POI_TEMPLATE = np.zeros((14, 192, 192)) - + for i in range(14): - MASK_POI_TEMPLATE[i, TEMPLATE_TEST_COORDS_ROW_COL[i][0], + MASK_POI_TEMPLATE[i, TEMPLATE_TEST_COORDS_ROW_COL[i][0], TEMPLATE_TEST_COORDS_ROW_COL[i][1]] = 1 MASK_POI_TARGET = np.zeros((14, 192, 192)) for i in range(14): - MASK_POI_TARGET[i, TEMPLATE_TARGET_COORDS_COL_ROW[i][1], + MASK_POI_TARGET[i, TEMPLATE_TARGET_COORDS_COL_ROW[i][1], TEMPLATE_TARGET_COORDS_COL_ROW[i][0]] = 1 ROI0_TEMPLATE_PIXELS = [-620, -706, -678, -630, -710, -672, -726, -684, @@ -85,15 +85,15 @@ class TestRelaxometry(unittest.TestCase): # Only check first three ROIs ROI_TEMPLATE_MEANS_T0 = [-683.840, -819.28, -1019.84] - + # Values from IDL routine PLATE5_T1 = [1862.6, 1435.8, 999.9, 740.7, 498.2, 351.6, 255.2, 178.0, 131.7, 93.3, 66.6, 45.1, 32.5, 22.4, 2632] # Values from testing (to check for variations) PLATE4_T2 = [816.633328, 590.521423, 430.902617, 310.985158, 217.258533, - 156.093083, 109.839424, 79.269721, 55.998743, 39.044842, - 27.143183, 18.448322, 12.514836, 9.351455, 2376] + 156.093083, 109.839424, 79.269721, 55.998743, 39.044842, + 27.143183, 18.448322, 12.514836, 9.351455, 2376] TEMPLATE_PATH_T2 = os.path.join(TEST_DATA_DIR, 'relaxometry', 'T2', 'Template_plate4_T2') @@ -101,9 +101,9 @@ class TestRelaxometry(unittest.TestCase): TEMPLATE_P4_TEST_COORDS_ROW_COL = [[56, 95], [62, 117], [81, 133], [104, 134], [124, 121], [133, 98], [127, 75], [109, 61], [84, 60], - [64, 72], [80, 81], [78, 111], + [64, 72], [80, 81], [78, 111], [109, 113], [110, 82], [148, 118]] - + # Values from IDL routine. Site 2 T1 values are signed not magnitude SITE2_PLATE5_T1 = [1880.5, 1432.3, 1012.0, 742.5, 504.0, 354.4, 256.3, 178.9, 131.9, 93.8, 67.9, 45.8, 33.4, 23.6, 2700] @@ -147,7 +147,6 @@ class TestRelaxometry(unittest.TestCase): SITE4_T2_P4 = [830.93, 597.30, 437.69, 313.22, 220.64, 157.19, 110.05, 78.48, 55.53, 39.33, 27.20, 18.24, 13.17, 9.38, 2254] - SITE4_T2_P5_DIR = os.path.join(TEST_DATA_DIR, 'relaxometry', 'T2', 'site4_philips', 'plate5') @@ -166,10 +165,10 @@ class TestRelaxometry(unittest.TestCase): PATH_256_MATRIX = os.path.join(TEST_DATA_DIR, 'relaxometry', 'T1', 'site3_ge', 'plate4', 'Z675') - TARGET_COORDS_256 = np.array([[ 75, 125], [ 85, 155], [111, 173], + TARGET_COORDS_256 = np.array([[75, 125], [85, 155], [111, 173], [142, 175], [168, 155], [177, 127], - [168, 95], [142, 76], [110, 76], - [ 85, 95], [106, 106], [105, 146], + [168, 95], [142, 76], [110, 76], + [85, 95], [106, 106], [105, 146], [146, 147], [147, 105], [198, 151]]) # Site 3 from GE scanner @@ -230,11 +229,9 @@ class TestRelaxometry(unittest.TestCase): 'IM_0557', 'IM_0558', 'IM_0559', 'IM_0560', 'IM_0561', 'IM_0562', 'IM_0563'] - SITE5_T2_P4 = [625.5, 435.9, 309.3, 216.8, 149.7, 106.3, 74.3, 51.6, 35.7, 25.4, 16.5, 11.0, 7.1, 4.2, 2497.2] - SITE5_T2_P5_DIR = os.path.join(TEST_DATA_DIR, 'relaxometry', 'T2', 'site5_philips_3T', 'plate5') @@ -249,7 +246,6 @@ class TestRelaxometry(unittest.TestCase): SITE5_T2_P5 = [1591.4, 1119.2, 742.7, 524.5, 370.7, 256.0, 180.5, 125.9, 91.2, 64.4, 45.2, 30.8, 22.0, 15.4, 2706.2] - def test_transform_coords(self): # no translation, no rotation, input = yx, output = yx warp_matrix = np.array([[1, 0, 0], [0, 1, 0]]) @@ -266,7 +262,7 @@ def test_transform_coords(self): op = hazen_relaxometry.transform_coords(self.TEST_COORDS, warp_matrix, input_row_col=False, output_row_col=True) - + np.testing.assert_allclose(op, self.COORDS_FLIP) # no translation, no rotation, input = col_row (xy), @@ -306,8 +302,8 @@ def test_transform_coords(self): # rotation (-30) degrees, translation col=10, row=20, # input = col_row (xy), output = col_row (xy) - warp_matrix = np.array([[np.sqrt(3)/2, 0.5, 10], - [-0.5, np.sqrt(3)/2, 20]]) + warp_matrix = np.array([[np.sqrt(3) / 2, 0.5, 10], + [-0.5, np.sqrt(3) / 2, 20]]) # use float64 rather than int32 for coordinates to better test rotation op = hazen_relaxometry.transform_coords(np.array(self.TEST_COORDS, dtype=np.float64), @@ -345,7 +341,7 @@ def test_image_stack_T1_sort(self): def test_image_stack_T2_sort(self): # read list of un-ordered T2 files, sort by TE, test sorted - t2_dcms = [pydicom.dcmread(os.path.join(self.T2_DIR, fname)) + t2_dcms = [pydicom.dcmread(os.path.join(self.T2_DIR, fname)) for fname in self.T2_FILES] t2_image_stack = hazen_relaxometry.T2ImageStack(t2_dcms) @@ -353,7 +349,7 @@ def test_image_stack_T2_sort(self): t2_image_stack.images] assert sorted_output == self.T2_TE_SORTED - + def test_generate_time_series_template_POIs(self): # Test on template first, no image fitting needed # Need image to get correct size @@ -362,7 +358,7 @@ def test_generate_time_series_template_POIs(self): template_image_stack.generate_time_series( self.TEMPLATE_TEST_COORDS_ROW_COL, fit_coords=False) - + for i in range(np.size(self.MASK_POI_TEMPLATE, 0)): np.testing.assert_equal( template_image_stack.ROI_time_series[i].POI_mask, @@ -402,7 +398,7 @@ def test_extract_single_roi(self): np.testing.assert_equal( template_image_stack.ROI_time_series[0].pixel_values[0], self.ROI0_TEMPLATE_PIXELS) - + def test_template_roi_means(self): # Check mean of first 3 ROIs in template match with ImageJ calculations template_dcm = pydicom.read_file(self.TEMPLATE_PATH_T1_P5) @@ -424,7 +420,6 @@ def test_template_roi_means(self): np.mean(template_image_stack.ROI_time_series[i].pixel_values), self.ROI_TEMPLATE_MEANS_T0[i]) - def test_t1_calc_magnitude_image(self): """Test T1 value for plate 5 spheres.""" template_dcm = pydicom.read_file(self.TEMPLATE_PATH_T1_P5) @@ -440,7 +435,7 @@ def test_t1_calc_magnitude_image(self): t1_image_stack.initialise_fit_parameters(t1_estimates=t1_published) t1_image_stack.find_relax_times() - + np.testing.assert_allclose(t1_image_stack.t1s, self.PLATE5_T1, rtol=0.02, atol=1) @@ -458,7 +453,7 @@ def test_t2_calc_magnitude_image(self): t2_image_stack.initialise_fit_parameters(t2_estimates=t2_published) t2_image_stack.initialise_fit_parameters(t2_published) t2_image_stack.find_relax_times() - + np.testing.assert_allclose(t2_image_stack.t2s, self.PLATE4_T2, rtol=0.01, atol=1) @@ -476,7 +471,7 @@ def test_t1_calc_signed_image(self): hazen_relaxometry.TEMPLATE_VALUES['plate5']['t1']['relax_times']['1.5T'] t1_image_stack.initialise_fit_parameters(t1_estimates=t1_published) t1_image_stack.find_relax_times() - + np.testing.assert_allclose(t1_image_stack.t1s, self.SITE2_PLATE5_T1, rtol=0.02, atol=1) @@ -491,7 +486,7 @@ def test_both_t1_or_t2(self): self.assertRaises(ArgumentCombinationError, hazen_relaxometry.main, [], calc_t1=True, calc_t2=True, plate_number=5) - + def test_t1_siemens(self): """Test T1 values on Siemens images.""" dcms = [pydicom.dcmread(os.path.join(self.T1_DIR, fname)) for fname in @@ -573,22 +568,22 @@ def test_scale_up_template(self): def test_ge(self): """Test relaxometry.py values on GE.""" - for plate in (4,5): + for plate in (4, 5): for tparam in ('T1', 'T2'): calc_t1 = tparam == 'T1' calc_t2 = tparam == 'T2' dcms = [pydicom.dcmread(os.path.join( - getattr(self,f'SITE3_{tparam}_P{plate}_DIR'), fname)) - for fname in getattr(self, f'SITE3_{tparam}_P{plate}_FILES')] + getattr(self, f'SITE3_{tparam}_P{plate}_DIR'), fname)) + for fname in getattr(self, f'SITE3_{tparam}_P{plate}_FILES')] t_results = hazen_relaxometry.main(dcms, plate_number=plate, - calc_t2=calc_t2, - calc_t1=calc_t1, + calc_t2=calc_t2, + calc_t1=calc_t1, verbose=True) results, = t_results.values() np.testing.assert_allclose( results['calc_times'], - getattr(self,f'SITE3_{tparam}_P{plate}_VALS'), + getattr(self, f'SITE3_{tparam}_P{plate}_VALS'), rtol=0.02, atol=1) def test_plate_number_not_specified(self): diff --git a/tests/test_slice_position.py b/tests/test_slice_position.py index 363f8351..87787759 100644 --- a/tests/test_slice_position.py +++ b/tests/test_slice_position.py @@ -3,7 +3,7 @@ import os import pydicom -from tests import TEST_DATA_DIR +from tests import TEST_DATA_DIR, TEST_REPORT_DIR from hazenlib.tasks.slice_position import SlicePosition from hazenlib.tools import get_dicom_files import copy @@ -25,7 +25,8 @@ class TestSlicePosition(unittest.TestCase): def setUp(self): self.hazen_slice_position = SlicePosition( - data_paths=get_dicom_files(os.path.join(self.SLICE_POS, 'SLICEPOSITION'))) + data_paths=get_dicom_files(os.path.join(self.SLICE_POS, 'SLICEPOSITION')), + report_dir=pathlib.PurePath.joinpath(TEST_REPORT_DIR)) self.sorted_slices = copy.deepcopy(self.hazen_slice_position.data) self.sorted_slices.sort(key=lambda x: x.SliceLocation) # sort by slice location @@ -61,6 +62,7 @@ class CanonTestSlicePosition(TestSlicePosition): def setUp(self): # self.test_files = [pydicom.read_file(str(i), force=True) for i in (self.SLICE_POS / 'canon').iterdir()] # self.test_files.sort(key=lambda x: x.SliceLocation) - self.hazen_slice_position = SlicePosition(data_paths=get_dicom_files(os.path.join(self.SLICE_POS, 'canon'))) + self.hazen_slice_position = SlicePosition(data_paths=get_dicom_files(os.path.join(self.SLICE_POS, 'canon')), + report_dir=pathlib.PurePath.joinpath(TEST_REPORT_DIR)) self.sorted_slices = copy.deepcopy(self.hazen_slice_position.data) self.sorted_slices.sort(key=lambda x: x.SliceLocation) # sort by slice location diff --git a/tests/test_spatial_resolution.py b/tests/test_spatial_resolution.py index 89af6e2b..cd332498 100644 --- a/tests/test_spatial_resolution.py +++ b/tests/test_spatial_resolution.py @@ -7,12 +7,11 @@ import hazenlib from hazenlib.tasks.spatial_resolution import SpatialResolution -from tests import TEST_DATA_DIR +from tests import TEST_DATA_DIR, TEST_REPORT_DIR from hazenlib.tools import get_dicom_files class TestSpatialResolution(unittest.TestCase): - files = get_dicom_files(os.path.join(TEST_DATA_DIR, 'resolution', 'RESOLUTION')) TEST_SQUARE = [[300, 220], [219, 206], [205, 287], [286, 301]] CIRCLE = [[[254, 256, 197]]] @@ -312,7 +311,8 @@ class TestSpatialResolution(unittest.TestCase): bisecting_normal = (273, 257, 313, 263) def setUp(self) -> None: - self.hazen_spatial_resolution = SpatialResolution(data_paths=self.files) + self.hazen_spatial_resolution = SpatialResolution(data_paths=self.files, + report_dir=pathlib.PurePath.joinpath(TEST_REPORT_DIR)) def test_get_roi(self): pixels = np.zeros((256, 256)) @@ -397,7 +397,8 @@ def test_get_edge_angle_intercept(self): self.x_edge, self.y_edge) def test_get_edge_profile_coords(self): - a, b = self.hazen_spatial_resolution.get_edge_profile_coords(self.angle, self.intercept, self.hazen_spatial_resolution.data[0].PixelSpacing) + a, b = self.hazen_spatial_resolution.get_edge_profile_coords(self.angle, self.intercept, + self.hazen_spatial_resolution.data[0].PixelSpacing) assert self.x, self.y == (a.flatten(), b.flatten()) def test_get_esf(self): diff --git a/tests/test_uniformity.py b/tests/test_uniformity.py index 4b337313..5481fab7 100644 --- a/tests/test_uniformity.py +++ b/tests/test_uniformity.py @@ -3,7 +3,7 @@ import unittest from hazenlib.tasks.uniformity import Uniformity -from tests import TEST_DATA_DIR +from tests import TEST_DATA_DIR, TEST_REPORT_DIR class TestUniformity(unittest.TestCase): @@ -12,7 +12,8 @@ class TestUniformity(unittest.TestCase): IPEM_VERTICAL = 0.98125 def setUp(self): - self.uniformity_task = Uniformity(data_paths=[os.path.join(self.UNIFORMITY_DATA, 'axial_oil.IMA')]) + self.uniformity_task = Uniformity(data_paths=[os.path.join(self.UNIFORMITY_DATA, 'axial_oil.IMA')], + report_dir=pathlib.PurePath.joinpath(TEST_REPORT_DIR)) def test_uniformity(self): results = self.uniformity_task.run() @@ -26,7 +27,8 @@ class TestSagUniformity(TestUniformity): IPEM_VERTICAL = 0.5125 def setUp(self): - self.uniformity_task = Uniformity(data_paths=[os.path.join(self.UNIFORMITY_DATA, 'sag.dcm')]) + self.uniformity_task = Uniformity(data_paths=[os.path.join(self.UNIFORMITY_DATA, 'sag.dcm')], + report_dir=pathlib.PurePath.joinpath(TEST_REPORT_DIR)) class TestCorUniformity(TestUniformity): @@ -34,4 +36,5 @@ class TestCorUniformity(TestUniformity): IPEM_VERTICAL = 0.45 def setUp(self): - self.uniformity_task = Uniformity(data_paths=[os.path.join(self.UNIFORMITY_DATA, 'cor.dcm')]) + self.uniformity_task = Uniformity(data_paths=[os.path.join(self.UNIFORMITY_DATA, 'cor.dcm')], + report_dir=pathlib.PurePath.joinpath(TEST_REPORT_DIR)) From ab23415309489d2871fbed19fe2e60928890d786 Mon Sep 17 00:00:00 2001 From: Haleema A Date: Thu, 29 Dec 2022 21:26:45 +0000 Subject: [PATCH 17/22] Updates version reference in setup.py and release instructions in README.md --- README.md | 2 +- setup.py | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 97aea7c0..a71ecac4 100644 --- a/README.md +++ b/README.md @@ -163,7 +163,7 @@ The Release Manager should ensure: - All outstanding issues for the current release have been closed, or, transferred to future release. - All tests are passing on GitHub Actions. - All documentation has been updated with correct version numbers: - - Update version number `hazenlib/_version.py`, i.e. imported into `docs/conf.py` and `hazenlib/__init__.py` + - Update version number `hazenlib/_version.py`, i.e. imported into `docs/conf.py`, `hazenlib/__init__.py` and `setup.py` - Update version number in `CITATION.cff` - The `release` branch has been merged into `main` branch - A new release has been created with a new version tag (tag = version number) diff --git a/setup.py b/setup.py index b93584f5..270aef83 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,5 @@ import setuptools -import os - -__version__ = '1.0.4' +from hazenlib import __version__ install_requires = ['pydicom==2.2.2', 'numpy==1.21.4', From 2a1d9ec95a359fc3ddbc25c5be39e8ef898c7055 Mon Sep 17 00:00:00 2001 From: Haleema A Date: Thu, 29 Dec 2022 21:57:47 +0000 Subject: [PATCH 18/22] Adds TODO comment in __init__.py for report_dir arg --- hazenlib/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/hazenlib/__init__.py b/hazenlib/__init__.py index 435b2cee..26c7574a 100644 --- a/hazenlib/__init__.py +++ b/hazenlib/__init__.py @@ -362,6 +362,7 @@ def main(): task = getattr(task_module, class_list[0].__name__)(data_paths=files, report=arguments['--report'], + # TODO: Is this necessary? See HazenTask __init__() report_dir=[arguments['--output'] if arguments[ '--output'] else os.path.join(os.getcwd(), 'report')][0]) From 81af26da2fe2fc523ce789ca766d38def0bbcbc6 Mon Sep 17 00:00:00 2001 From: Haleema A Date: Thu, 29 Dec 2022 22:09:46 +0000 Subject: [PATCH 19/22] Updates acr_ghosting and acr_uniformity task names in CLI Tests GitHub Action --- .github/workflows/test_cli.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_cli.yml b/.github/workflows/test_cli.yml index 7fe7caff..2309795b 100644 --- a/.github/workflows/test_cli.yml +++ b/.github/workflows/test_cli.yml @@ -42,12 +42,12 @@ jobs: - name: test acr_uniformity if: always() # will always run regardless of whether previous step fails - useful to ensure all CLI functions tested run: | - hazen task_acr_uniformity tests/data/acr/Siemens/Test --report + hazen acr_uniformity tests/data/acr/Siemens/Test --report - name: test acr_ghosting if: always() # will always run regardless of whether previous step fails - useful to ensure all CLI functions tested run: | - hazen task_acr_ghosting tests/data/acr/Siemens/Test --report + hazen acr_ghosting tests/data/acr/Siemens/Test --report - name: test slice_position if: always() # will always run regardless of whether previous step fails - useful to ensure all CLI functions tested From 0b50eb18a19d126884b35fd896debe87406212db Mon Sep 17 00:00:00 2001 From: Haleema A Date: Thu, 29 Dec 2022 22:13:58 +0000 Subject: [PATCH 20/22] Updates acr_ghosting and acr_uniformity test data directories in CLI Tests GitHub Action --- .github/workflows/test_cli.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_cli.yml b/.github/workflows/test_cli.yml index 2309795b..ae16797b 100644 --- a/.github/workflows/test_cli.yml +++ b/.github/workflows/test_cli.yml @@ -42,12 +42,12 @@ jobs: - name: test acr_uniformity if: always() # will always run regardless of whether previous step fails - useful to ensure all CLI functions tested run: | - hazen acr_uniformity tests/data/acr/Siemens/Test --report + hazen acr_uniformity tests/data/acr/Siemens --report - name: test acr_ghosting if: always() # will always run regardless of whether previous step fails - useful to ensure all CLI functions tested run: | - hazen acr_ghosting tests/data/acr/Siemens/Test --report + hazen acr_ghosting tests/data/acr/Siemens --report - name: test slice_position if: always() # will always run regardless of whether previous step fails - useful to ensure all CLI functions tested From fa858e9ee238401f8924bad5f4c1f69505cdc540 Mon Sep 17 00:00:00 2001 From: Tom Roberts Date: Fri, 30 Dec 2022 09:53:08 +0000 Subject: [PATCH 21/22] PEP8 end of line change --- hazenlib/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hazenlib/_version.py b/hazenlib/_version.py index 8412cb14..858de170 100644 --- a/hazenlib/_version.py +++ b/hazenlib/_version.py @@ -1 +1 @@ -__version__ = '1.0.5' \ No newline at end of file +__version__ = '1.0.5' From ca06714b84701c9553ba02bbda52aa9942194db0 Mon Sep 17 00:00:00 2001 From: Haleema A Date: Fri, 30 Dec 2022 10:57:20 +0000 Subject: [PATCH 22/22] Adds condition for task report directory creation --- hazenlib/HazenTask.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hazenlib/HazenTask.py b/hazenlib/HazenTask.py index 96e8f40d..9e9d813c 100644 --- a/hazenlib/HazenTask.py +++ b/hazenlib/HazenTask.py @@ -14,7 +14,10 @@ def __init__(self, data_paths: list, report: bool = False, report_dir: str = os. self.data_paths = sorted(data_paths) self.report: bool = report self.report_path = os.path.join(report_dir, type(self).__name__) - pathlib.Path(self.report_path).mkdir(parents=True, exist_ok=True) + if report: + pathlib.Path(self.report_path).mkdir(parents=True, exist_ok=True) + else: + pass self.report_files = [] @property