...
 
Commits (2)
......@@ -12,7 +12,7 @@ import pandas as pd
import pydicom
from dicomgenerator.exceptions import InvalidGeometryError, InvalidDoseGradientError, InvalidRoiError, \
InvalidSpacingError
InvalidSpacingError, MultipleDicomFilesError
def _valid_dose_grading(dose_gradient: str):
......@@ -53,88 +53,77 @@ def _format_dose_gradient(dose_gradient: str) -> str:
elif dose_gradient == 'SI':
return 'SupInf'
elif dose_gradient == 'all':
return ''
raise InvalidDoseGradientError("Dose Gradient must be {'AP', 'SI', 'all'}")
def get_rtstruct(geometry: str = 'all', spacing='all') -> List[pydicom.FileDataset]:
def get_rtstruct(geometry: str, spacing: float) -> pydicom.FileDataset:
"""Get RTSTRUCT DICOM files.
Parameters
----------
geometry
Geometry: {'cone', 'sphere', 'cylinder', 'all'}.
Geometry: {'cone', 'sphere', 'cylinder'}.
spacing
Spacing in millimeters: {0.2, 1, 2, 3, 'all'}.
Spacing in millimeters: {0.2, 1, 2, 3}.
Returns
-------
List of DICOM file data set.
A RTSTRUCT DICOM file data set.
"""
if spacing != 'all':
_valid_spacing(spacing)
if geometry != 'all':
_valid_geometry(geometry)
_valid_geometry(geometry), _valid_spacing(spacing)
directory, _ = os.path.split(__file__)
if geometry == 'all':
geometry_path = join(directory, 'data', 'RTSTRUCT')
geometry_paths = [join(directory, 'data', 'RTSTRUCT', i) for i in os.listdir(geometry_path)]
else:
geometry_paths = [join(directory, 'data', 'RTSTRUCT', geometry)]
geometry_path = join(directory, 'data', 'RTSTRUCT', geometry)
if False in [os.path.exists(i) for i in geometry_paths]:
raise InvalidGeometryError("Geometry should be in {'cone', 'sphere', 'cylinder', 'all'}")
if not os.path.exists(geometry_path):
raise InvalidGeometryError("Geometry should be in {'cone', 'sphere', 'cylinder'}")
dicom_paths: List[str] = []
for geometry_path in geometry_paths:
dicom_files = filter(
lambda i: f'{_format_spacing(spacing)}_' in i or spacing == 'all',
os.listdir(geometry_path)
)
dicom_paths += [join(geometry_path, i) for i in dicom_files]
dicom_files = filter(
lambda i: f'{_format_spacing(spacing)}_' in i,
os.listdir(geometry_path)
)
dicom_paths = [join(geometry_path, i) for i in dicom_files]
return [pydicom.dcmread(i) for i in dicom_paths]
if len(dicom_paths) == 1:
return pydicom.dcmread(dicom_paths[0])
raise MultipleDicomFilesError()
def get_rtdose(dose_gradient: str = 'all', spacing='all') -> List[pydicom.FileDataset]:
def get_rtdose(dose_gradient: str, spacing: float) -> pydicom.FileDataset:
"""Get RTDOSE DICOM files.
Parameters
----------
dose_gradient
Dose gradient: {'AP', 'SI', 'all'}.
Dose gradient: {'AP', 'SI'}.
AP := Anterior Posterior, SI := Superior Inferior.
spacing
Spacing in millimeters: {1, 2, 3, 'all'}.
Spacing in millimeters: {1, 2, 3}.
Returns
-------
List of DICOM file data set.
"""
if spacing != 'all':
_valid_spacing(spacing)
if spacing == 0.2:
raise InvalidSpacingError("Should be {1, 2, 3, 'all'}")
_valid_dose_grading(dose_gradient), _valid_spacing(spacing)
if spacing == 0.2:
raise InvalidSpacingError("Should be {1, 2, 3}")
directory, _ = os.path.split(__file__)
rtdose_path = join(directory, 'data', 'RTDOSE')
dicom_files = filter(
lambda i: _format_dose_gradient(dose_gradient) in i or dose_gradient == 'all',
lambda i: _format_dose_gradient(dose_gradient) in i,
filter(
lambda i: f'{spacing}mm'.replace('.0', '') in i or spacing == 'all',
lambda i: f'{spacing}mm'.replace('.0', '') in i,
os.listdir(rtdose_path)
)
)
dicom_paths = [join(rtdose_path, i) for i in dicom_files]
return [pydicom.dcmread(i) for i in dicom_paths]
if len(dicom_paths) == 1:
return pydicom.dcmread(dicom_paths[0])
raise MultipleDicomFilesError()
def get_rtplan() -> pydicom.FileDataset:
......@@ -160,7 +149,7 @@ def get_analytic_dvhs(geometry: str, spacing: float, dose_gradient: str, roi: st
spacing
Spacing in millimeters: {0.2, 1, 2, 3}.
dose_gradient
Dose gradient: {'AP', 'SI', 'all'}.
Dose gradient: {'AP', 'SI'}.
AP := Anterior Posterior, SI := Superior Inferior.
roi: (Optional)
......@@ -169,7 +158,7 @@ def get_analytic_dvhs(geometry: str, spacing: float, dose_gradient: str, roi: st
Returns
-------
pandas.DataFrame
Analytic DVHs
Analytic DVHs.
"""
_valid_geometry(geometry), _valid_spacing(spacing), _valid_dose_grading(dose_gradient)
if geometry != 'sphere':
......
......@@ -16,3 +16,7 @@ class InvalidRoiError(ValueError):
class InvalidSpacingError(ValueError):
pass
class MultipleDicomFilesError(ValueError):
pass
# coding: utf-8
# author: Gabriel Couture
import os
from os.path import join
import pydicom
......@@ -7,13 +8,27 @@ import pydicom
from dicomgenerator import dao
def write_dicom_rt(target_directory: str, geometry: str, dose_gradient: str) -> None:
raise NotImplementedError
def write_dicom_rt(
target_directory: str,
geometry: str,
spacing: float,
dose_gradient: str,
roi: str):
# Getting Data
rtplan = dao.get_rtplan()
rtdoses = dao.get_rtdose(dose_gradient)
rtstructs = dao.get_rtstruct(geometry)
rtdose = dao.get_rtdose(dose_gradient, spacing)
rtstruct = dao.get_rtstruct(geometry, spacing)
dvh_data = dao.get_analytic_dvhs(geometry, spacing, dose_gradient, roi)
# Applying desired layers
# Writing results
os.makedirs(target_directory)
pydicom.dcmwrite(join(target_directory, 'RTPLAN-1.dcm'), rtplan)
pydicom.dcmwrite(join(target_directory, 'RTDOSE-1.dcm'), rtdoses[0])
pydicom.dcmwrite(join(target_directory, 'RTSTRUCT-1.dcm'), rtstructs[0])
pydicom.dcmwrite(join(target_directory, 'RTDOSE-1.dcm'), rtdose)
pydicom.dcmwrite(join(target_directory, 'RTSTRUCT-1.dcm'), rtstruct)
dvh_data.columns = ['Dose [cGy]', 'Volume [cc]']
dvh_data.to_csv(
join(target_directory, 'DVH.csv'),
index=False
)
......@@ -19,19 +19,10 @@ A_BAD_DOSE_GRADIENT = 'A_BAD_DOSE_GRADIENT'
class TestDao(unittest.TestCase):
def test_givenAGeometryAndASpacing_whenGettingRtstruct_thenResultIsOneRtstructFileDataSet(self):
def test_givenAGeometryAndASpacing_whenGettingRtstruct_thenResultIsARtstructFileDataSet(self):
result = dao.get_rtstruct(A_GEOMETRY, A_SPACING)
self.assertEqual(len(result), 1)
for i in result:
self.assert_is_rtstruct(i)
def test_givenAGeometry_whenGettingRtstruct_thenResultIsRtstructFileDataSetsWithAnySpacing(self):
result = dao.get_rtstruct(A_GEOMETRY)
self.assertEqual(len(result), 4)
for i in result:
self.assert_is_rtstruct(i)
self.assert_is_rtstruct(result)
def test_givenABadGeometryAndASpacing_whenGettingRtstruct_thenRaiseWrongGeometryError(self):
self.assertRaises(
......@@ -45,12 +36,10 @@ class TestDao(unittest.TestCase):
lambda: dao.get_rtstruct(A_GEOMETRY, A_BAD_SPACING)
)
def test_givenADoseGradientAndASpacing_whenGettingRtdose_thenResultIsOneRtdoseFileDataSet(self):
def test_givenADoseGradientAndASpacing_whenGettingRtdose_thenResultIsARtdoseFileDataSet(self):
result = dao.get_rtdose(A_DOSE_GRADIENT, A_SPACING)
self.assertEqual(len(result), 1)
for i in result:
self.assert_is_rtdose(i)
self.assert_is_rtdose(result)
def test_givenABadDoseGradientAndASpacing_whenGettingRtdose_thenRaiseWrongGeometryError(self):
self.assertRaises(
......
# coding: utf-8
# author: Gabriel Couture
import shutil
import unittest
from dicomgenerator import writer
A_TARGET_DIRECTORY = './tests/data'
A_GEOMETRY = 'cone'
A_SPACING = 1.0
A_DOSE_GRADIENT = 'AP'
A_ROI = 'axial'
class TestWriter(unittest.TestCase):
def tearDown(self) -> None:
shutil.rmtree(A_TARGET_DIRECTORY)
def test_givenTargetDirectoryAndASetOfValidParameters_whenWritingDicomRt_thenDirectoryWithDicomFilesAndDVHAndMetadataIsWorte(self):
writer.write_dicom_rt(
A_TARGET_DIRECTORY,
A_GEOMETRY,
A_SPACING,
A_DOSE_GRADIENT,
A_ROI
)
raise NotImplementedError