diff --git a/Wrappers/Python/cil/framework/__init__.py b/Wrappers/Python/cil/framework/__init__.py index ab99ffced3..8a37b5767a 100644 --- a/Wrappers/Python/cil/framework/__init__.py +++ b/Wrappers/Python/cil/framework/__init__.py @@ -27,4 +27,4 @@ from .processors import DataProcessor, Processor, AX, PixelByPixelDataProcessor, CastDataContainer from .block import BlockDataContainer, BlockGeometry from .partitioner import Partitioner -from .labels import AcquisitionDimensionLabels, ImageDimensionLabels, FillTypes, UnitsAngles, AcquisitionTypes, AcquisitionDimensions +from .labels import AcquisitionDimensionLabels, ImageDimensionLabels, FillTypes, UnitsAngles, AcquisitionType diff --git a/Wrappers/Python/cil/framework/acquisition_geometry.py b/Wrappers/Python/cil/framework/acquisition_geometry.py index 20e55bdae0..36799f94a4 100644 --- a/Wrappers/Python/cil/framework/acquisition_geometry.py +++ b/Wrappers/Python/cil/framework/acquisition_geometry.py @@ -22,7 +22,7 @@ import numpy -from .labels import AcquisitionDimensionLabels, UnitsAngles, AcquisitionTypes, FillTypes, AcquisitionDimensions +from .labels import AcquisitionDimensionLabels, UnitsAngles, AcquisitionType, FillTypes from .acquisition_data import AcquisitionData from .image_geometry import ImageGeometry @@ -186,10 +186,7 @@ class SystemConfiguration(object): @property def dimension(self): - if self._dimension == 2: - return AcquisitionDimensions.DIM2.value - else: - return AcquisitionDimensions.DIM3.value + return AcquisitionType.DIM2 if self._dimension == 2 else AcquisitionType.DIM3 @dimension.setter def dimension(self,val): @@ -203,8 +200,8 @@ def geometry(self): return self._geometry @geometry.setter - def geometry(self,val): - self._geometry = AcquisitionTypes(val) + def geometry(self, val): + self._geometry = AcquisitionType(val) def __init__(self, dof, geometry, units='units'): """Initialises the system component attributes for the acquisition type @@ -213,7 +210,7 @@ def __init__(self, dof, geometry, units='units'): self.geometry = geometry self.units = units - if self.geometry == AcquisitionTypes.PARALLEL: + if AcquisitionType.PARALLEL & self.geometry: self.ray = DirectionVector(dof) else: self.source = PositionVector(dof) @@ -344,7 +341,7 @@ class Parallel2D(SystemConfiguration): def __init__ (self, ray_direction, detector_pos, detector_direction_x, rotation_axis_pos, units='units'): """Constructor method """ - super(Parallel2D, self).__init__(dof=2, geometry=AcquisitionTypes.PARALLEL, units=units) + super(Parallel2D, self).__init__(dof=2, geometry=AcquisitionType.PARALLEL, units=units) #source self.ray.direction = ray_direction @@ -518,7 +515,7 @@ class Parallel3D(SystemConfiguration): def __init__ (self, ray_direction, detector_pos, detector_direction_x, detector_direction_y, rotation_axis_pos, rotation_axis_direction, units='units'): """Constructor method """ - super(Parallel3D, self).__init__(dof=3, geometry=AcquisitionTypes.PARALLEL, units=units) + super(Parallel3D, self).__init__(dof=3, geometry=AcquisitionType.PARALLEL, units=units) #source self.ray.direction = ray_direction @@ -803,7 +800,7 @@ class Cone2D(SystemConfiguration): def __init__ (self, source_pos, detector_pos, detector_direction_x, rotation_axis_pos, units='units'): """Constructor method """ - super(Cone2D, self).__init__(dof=2, geometry=AcquisitionTypes.CONE, units=units) + super(Cone2D, self).__init__(dof=2, geometry=AcquisitionType.CONE, units=units) #source self.source.position = source_pos @@ -982,7 +979,7 @@ class Cone3D(SystemConfiguration): def __init__ (self, source_pos, detector_pos, detector_direction_x, detector_direction_y, rotation_axis_pos, rotation_axis_direction, units='units'): """Constructor method """ - super(Cone3D, self).__init__(dof=3, geometry=AcquisitionTypes.CONE, units=units) + super(Cone3D, self).__init__(dof=3, geometry=AcquisitionType.CONE, units=units) #source self.source.position = source_pos @@ -1819,7 +1816,7 @@ def get_centre_of_rotation(self, distance_units='default', angle_units='radian') offset = offset_distance/ self.config.panel.pixel_size[0] offset_units = 'pixels' - if self.dimension == '3D' and self.config.panel.pixel_size[0] != self.config.panel.pixel_size[1]: + if AcquisitionType.DIM3 & self.dimension and self.config.panel.pixel_size[0] != self.config.panel.pixel_size[1]: #if aspect ratio of pixels isn't 1:1 need to convert angle by new ratio y_pix = 1 /self.config.panel.pixel_size[1] x_pix = math.tan(angle_rad)/self.config.panel.pixel_size[0] @@ -1884,7 +1881,7 @@ def set_centre_of_rotation(self, offset=0.0, distance_units='default', angle=0.0 else: raise ValueError("`distance_units` is not recognised. Must be 'default' or 'pixels'. Got {}".format(distance_units)) - if self.dimension == '2D': + if AcquisitionType.DIM2 & self.dimension: self.config.system.set_centre_of_rotation(offset_distance) else: self.config.system.set_centre_of_rotation(offset_distance, angle_rad) @@ -1921,7 +1918,7 @@ def set_centre_of_rotation_by_slice(self, offset1, slice_index1=None, offset2=No if not hasattr(self.config.system, 'set_centre_of_rotation'): raise NotImplementedError() - if self.dimension == '2D': + if AcquisitionType.DIM2 & self.dimension: if offset2 is not None: warnings.warn("2D so offset2 is ingored", UserWarning, stacklevel=2) self.set_centre_of_rotation(offset1) @@ -2118,7 +2115,7 @@ def copy(self): def get_centre_slice(self): '''returns a 2D AcquisitionGeometry that corresponds to the centre slice of the input''' - if self.dimension == '2D': + if AcquisitionType.DIM2 & self.dimension: return self AG_2D = copy.deepcopy(self) @@ -2133,7 +2130,7 @@ def get_ImageGeometry(self, resolution=1.0): num_voxel_xy = int(numpy.ceil(self.config.panel.num_pixels[0] * resolution)) voxel_size_xy = self.config.panel.pixel_size[0] / (resolution * self.magnification) - if self.dimension == '3D': + if AcquisitionType.DIM3 & self.dimension: num_voxel_z = int(numpy.ceil(self.config.panel.num_pixels[1] * resolution)) voxel_size_z = self.config.panel.pixel_size[1] / (resolution * self.magnification) else: @@ -2161,7 +2158,7 @@ def get_slice(self, channel=None, angle=None, vertical=None, horizontal=None): geometry_new.config.angles.angle_data = geometry_new.config.angles.angle_data[angle] if vertical is not None: - if geometry_new.geom_type == AcquisitionTypes.PARALLEL or vertical == 'centre' or abs(geometry_new.pixel_num_v/2 - vertical) < 1e-6: + if AcquisitionType.PARALLEL & geometry_new.geom_type or vertical == 'centre' or abs(geometry_new.pixel_num_v/2 - vertical) < 1e-6: geometry_new = geometry_new.get_centre_slice() else: raise ValueError("Can only subset centre slice geometry on cone-beam data. Expected vertical = 'centre'. Got vertical = {0}".format(vertical)) diff --git a/Wrappers/Python/cil/framework/labels.py b/Wrappers/Python/cil/framework/labels.py index 420bf7136e..af267a2910 100644 --- a/Wrappers/Python/cil/framework/labels.py +++ b/Wrappers/Python/cil/framework/labels.py @@ -15,7 +15,7 @@ # # Authors: # CIL Developers, listed at: https://github.com/TomographicImaging/CIL/blob/master/NOTICE.txt -from enum import Enum, auto, unique +from enum import Enum, Flag as _Flag, auto, unique try: from enum import EnumType except ImportError: # Python<3.11 @@ -40,7 +40,6 @@ def _missing_(cls, value: str): return cls.__members__.get(value.upper(), None) def __eq__(self, value: str) -> bool: - """Uses value.upper() for case-insensitivity""" try: value = self.__class__[value.upper()] except (KeyError, ValueError, AttributeError): @@ -228,27 +227,42 @@ class UnitsAngles(StrEnum): RADIAN = auto() -class AcquisitionTypes(StrEnum): +class _FlagMeta(EnumType): + """Python<3.12 requires this in a metaclass (rather than directly in Flag)""" + def __contains__(self, item) -> bool: + return item.upper() in self.__members__ if isinstance(item, str) else super().__contains__(item) + + +@unique +class Flag(_Flag, metaclass=_FlagMeta): + """Case-insensitive Flag""" + @classmethod + def _missing_(cls, value): + return cls.__members__.get(value.upper(), None) if isinstance(value, str) else super()._missing_(value) + + def __eq__(self, value: str) -> bool: + return super().__eq__(self.__class__[value.upper()] if isinstance(value, str) else value) + + +class AcquisitionType(Flag): """ - Available acquisition types. + Available acquisition types & dimensions. Attributes ---------- PARALLEL: Parallel beam. CONE: Cone beam. + DIM2: 2D acquisition. + DIM3: 3D acquisition. """ PARALLEL = auto() CONE = auto() + DIM2 = auto() + DIM3 = auto() - -class AcquisitionDimensions(StrEnum): - """ - Available acquisition dimensions. - - Attributes - ---------- - DIM2 ('2D'): 2D acquisition. - DIM3 ('3D'): 3D acquisition. - """ - DIM2 = "2D" - DIM3 = "3D" + @classmethod + def _missing_(cls, value): + """2D/3D aliases""" + if isinstance(value, str): + value = {'2D': 'DIM2', '3D': 'DIM3'}.get(value.upper(), value) + return super()._missing_(value) diff --git a/Wrappers/Python/cil/io/NEXUSDataWriter.py b/Wrappers/Python/cil/io/NEXUSDataWriter.py index a3839d702c..cc0aec0c0b 100644 --- a/Wrappers/Python/cil/io/NEXUSDataWriter.py +++ b/Wrappers/Python/cil/io/NEXUSDataWriter.py @@ -18,7 +18,8 @@ import numpy as np import os -from cil.framework import AcquisitionData, AcquisitionGeometry, ImageData, ImageGeometry +from cil.framework import AcquisitionData, ImageData +from cil.framework.labels import AcquisitionType from cil.version import version import datetime from cil.io import utilities @@ -192,7 +193,7 @@ def write(self): ds_data.attrs['pixel_size_h'] = self.data.geometry.config.panel.pixel_size[0] ds_data.attrs['panel_origin'] = self.data.geometry.config.panel.origin - if self.data.geometry.config.system.dimension == '3D': + if AcquisitionType.DIM3 & self.data.geometry.config.system.dimension: f.create_dataset('entry1/tomo_entry/config/detector/direction_y', (self.data.geometry.config.system.detector.direction_y.shape), dtype = 'float32', diff --git a/Wrappers/Python/cil/io/NikonDataReader.py b/Wrappers/Python/cil/io/NikonDataReader.py index ca7962d795..51b72ba847 100644 --- a/Wrappers/Python/cil/io/NikonDataReader.py +++ b/Wrappers/Python/cil/io/NikonDataReader.py @@ -16,9 +16,9 @@ # Authors: # CIL Developers, listed at: https://github.com/TomographicImaging/CIL/blob/master/NOTICE.txt -from cil.framework import AcquisitionData, AcquisitionGeometry +from cil.framework import AcquisitionGeometry +from cil.framework.labels import AcquisitionType from cil.io.TIFF import TIFFStackReader -import warnings import numpy as np import os @@ -334,7 +334,7 @@ def get_geometry(self): def get_roi(self): '''returns the roi''' roi = self._roi_par[:] - if self._ag.dimension == '2D': + if AcquisitionType.DIM2 & self._ag.dimension: roi.pop(1) roidict = {} diff --git a/Wrappers/Python/cil/plugins/astra/operators/ProjectionOperator.py b/Wrappers/Python/cil/plugins/astra/operators/ProjectionOperator.py index af7a70fa7d..d62d7e4281 100644 --- a/Wrappers/Python/cil/plugins/astra/operators/ProjectionOperator.py +++ b/Wrappers/Python/cil/plugins/astra/operators/ProjectionOperator.py @@ -18,7 +18,7 @@ import logging -from cil.framework import BlockGeometry, AcquisitionDimensionLabels, ImageDimensionLabels +from cil.framework import BlockGeometry, AcquisitionDimensionLabels, ImageDimensionLabels, AcquisitionType from cil.optimisation.operators import BlockOperator, LinearOperator, ChannelwiseOperator from cil.plugins.astra.operators import AstraProjector2D, AstraProjector3D @@ -127,7 +127,7 @@ def __init__(self, if device == 'gpu': operator = AstraProjector3D(volume_geometry_sc, sinogram_geometry_sc) - elif self.sinogram_geometry.dimension == '2D': + elif AcquisitionType.DIM2 & self.sinogram_geometry.dimension: operator = AstraProjector2D(volume_geometry_sc, sinogram_geometry_sc, device=device) diff --git a/Wrappers/Python/cil/plugins/astra/processors/FBP.py b/Wrappers/Python/cil/plugins/astra/processors/FBP.py index c52006eef9..b750412e3c 100644 --- a/Wrappers/Python/cil/plugins/astra/processors/FBP.py +++ b/Wrappers/Python/cil/plugins/astra/processors/FBP.py @@ -15,9 +15,7 @@ # # Authors: # CIL Developers, listed at: https://github.com/TomographicImaging/CIL/blob/master/NOTICE.txt -import warnings - -from cil.framework import DataProcessor, ImageDimensionLabels, AcquisitionDimensionLabels +from cil.framework import DataProcessor, ImageDimensionLabels, AcquisitionDimensionLabels, AcquisitionType from cil.plugins.astra.processors.FBP_Flexible import FBP_Flexible from cil.plugins.astra.processors.FDK_Flexible import FDK_Flexible from cil.plugins.astra.processors.FBP_Flexible import FBP_CPU @@ -81,7 +79,7 @@ def __init__(self, image_geometry=None, acquisition_geometry=None, device='gpu') if acquisition_geometry.geom_type == 'cone': raise NotImplementedError("Cannot process cone-beam data without a GPU") - if acquisition_geometry.dimension == '2D': + if AcquisitionType.DIM2 & acquisition_geometry.dimension: processor = FBP_CPU(image_geometry, acquisition_geometry) else: raise NotImplementedError("Cannot process 3D data without a GPU") diff --git a/Wrappers/Python/cil/plugins/astra/processors/FBP_Flexible.py b/Wrappers/Python/cil/plugins/astra/processors/FBP_Flexible.py index 138caa1d05..4bed64064b 100644 --- a/Wrappers/Python/cil/plugins/astra/processors/FBP_Flexible.py +++ b/Wrappers/Python/cil/plugins/astra/processors/FBP_Flexible.py @@ -17,7 +17,7 @@ # CIL Developers, listed at: https://github.com/TomographicImaging/CIL/blob/master/NOTICE.txt -from cil.framework import AcquisitionGeometry, Processor, ImageData +from cil.framework import AcquisitionGeometry, Processor, ImageData, AcquisitionType from cil.plugins.astra.processors.FDK_Flexible import FDK_Flexible from cil.plugins.astra.utilities import convert_geometry_to_astra_vec_3D, convert_geometry_to_astra import logging @@ -76,7 +76,7 @@ def __init__(self, volume_geometry, detector_position = sino_geom_cone.config.system.detector.position detector_direction_x = sino_geom_cone.config.system.detector.direction_x - if sino_geom_cone.dimension == '2D': + if AcquisitionType.DIM2 & sino_geom_cone.dimension: tmp = AcquisitionGeometry.create_Cone2D(cone_source, detector_position, detector_direction_x) else: detector_direction_y = sino_geom_cone.config.system.detector.direction_y @@ -141,7 +141,7 @@ def check_input(self, dataset): raise ValueError("Expected input data to be parallel beam geometry , got {0}"\ .format(self.sinogram_geometry.geom_type)) - if self.sinogram_geometry.dimension != '2D': + if not AcquisitionType.DIM2 & self.sinogram_geometry.dimension: raise ValueError("Expected input data to be 2D , got {0}"\ .format(self.sinogram_geometry.dimension)) diff --git a/Wrappers/Python/cil/plugins/astra/utilities/convert_geometry_to_astra.py b/Wrappers/Python/cil/plugins/astra/utilities/convert_geometry_to_astra.py index 6a83ee0194..09526b581e 100644 --- a/Wrappers/Python/cil/plugins/astra/utilities/convert_geometry_to_astra.py +++ b/Wrappers/Python/cil/plugins/astra/utilities/convert_geometry_to_astra.py @@ -19,7 +19,7 @@ import astra import numpy as np -from cil.framework import UnitsAngles +from cil.framework import AcquisitionType, UnitsAngles def convert_geometry_to_astra(volume_geometry, sinogram_geometry): """ @@ -39,13 +39,8 @@ def convert_geometry_to_astra(volume_geometry, sinogram_geometry): The ASTRA vol_geom and proj_geom """ - # determine if the geometry is 2D or 3D - - if sinogram_geometry.pixel_num_v > 1: - dimension = '3D' - else: - dimension = '2D' + dimension = AcquisitionType.DIM3 if sinogram_geometry.pixel_num_v > 1 else AcquisitionType.DIM2 #get units @@ -54,7 +49,7 @@ def convert_geometry_to_astra(volume_geometry, sinogram_geometry): else: angles_rad = sinogram_geometry.config.angles.angle_data - if dimension == '2D': + if AcquisitionType.DIM2 & dimension: vol_geom = astra.create_vol_geom(volume_geometry.voxel_num_y, volume_geometry.voxel_num_x, volume_geometry.get_min_x(), @@ -77,7 +72,7 @@ def convert_geometry_to_astra(volume_geometry, sinogram_geometry): else: NotImplemented - elif dimension == '3D': + elif AcquisitionType.DIM3 & dimension: vol_geom = astra.create_vol_geom(volume_geometry.voxel_num_y, volume_geometry.voxel_num_x, volume_geometry.voxel_num_z, diff --git a/Wrappers/Python/cil/plugins/astra/utilities/convert_geometry_to_astra_vec_3D.py b/Wrappers/Python/cil/plugins/astra/utilities/convert_geometry_to_astra_vec_3D.py index 5a0e846b64..ca87c712a9 100644 --- a/Wrappers/Python/cil/plugins/astra/utilities/convert_geometry_to_astra_vec_3D.py +++ b/Wrappers/Python/cil/plugins/astra/utilities/convert_geometry_to_astra_vec_3D.py @@ -19,7 +19,7 @@ import astra import numpy as np -from cil.framework import UnitsAngles +from cil.framework import AcquisitionType, UnitsAngles def convert_geometry_to_astra_vec_3D(volume_geometry, sinogram_geometry_in): @@ -57,7 +57,7 @@ def convert_geometry_to_astra_vec_3D(volume_geometry, sinogram_geometry_in): #get units degrees = angles.angle_unit == UnitsAngles.DEGREE - if sinogram_geometry.dimension == '2D': + if AcquisitionType.DIM2 & sinogram_geometry.dimension: #create a 3D astra geom from 2D CIL geometry volume_geometry_temp = volume_geometry.copy() volume_geometry_temp.voxel_num_z = 1 diff --git a/Wrappers/Python/cil/plugins/tigre/Geometry.py b/Wrappers/Python/cil/plugins/tigre/Geometry.py index 403b3822b3..61c7a9cd3b 100644 --- a/Wrappers/Python/cil/plugins/tigre/Geometry.py +++ b/Wrappers/Python/cil/plugins/tigre/Geometry.py @@ -16,7 +16,7 @@ # Authors: # CIL Developers, listed at: https://github.com/TomographicImaging/CIL/blob/master/NOTICE.txt -from cil.framework import UnitsAngles +from cil.framework import AcquisitionType, UnitsAngles import numpy as np try: @@ -109,7 +109,7 @@ def __init__(self, ig, ag): self.sDetector = self.dDetector * self.nDetector # total size of the detector (mm) - if ag_in.dimension == '2D': + if AcquisitionType.DIM2 & ag_in.dimension: self.is2D = True #fix IG to single slice in z diff --git a/Wrappers/Python/cil/processors/CofR_image_sharpness.py b/Wrappers/Python/cil/processors/CofR_image_sharpness.py index 10cca07da0..7906d74741 100644 --- a/Wrappers/Python/cil/processors/CofR_image_sharpness.py +++ b/Wrappers/Python/cil/processors/CofR_image_sharpness.py @@ -16,15 +16,13 @@ # Authors: # CIL Developers, listed at: https://github.com/TomographicImaging/CIL/blob/master/NOTICE.txt -from cil.framework import Processor, AcquisitionData, AcquisitionDimensionLabels +from cil.framework import Processor, AcquisitionData, AcquisitionDimensionLabels, AcquisitionType import matplotlib.pyplot as plt import scipy import numpy as np -import inspect import logging import math import importlib -import warnings log = logging.getLogger(__name__) @@ -123,10 +121,7 @@ def check_input(self, data): raise ValueError('slice_index is out of range. Must be in range 0-{0}. Got {1}'.format(data.get_dimension_size('vertical'), self.slice_index)) #check order for single slice data - if data.geometry.dimension == '3D': - test_geom = data.geometry.get_slice(vertical='centre') - else: - test_geom = data.geometry + test_geom = data.geometry.get_slice(vertical='centre') if AcquisitionType.DIM3 & data.geometry.dimension else data.geometry if not AcquisitionDimensionLabels.check_order_for_engine(self.backend, test_geom): raise ValueError("Input data must be reordered for use with selected backend. Use input.reorder{'{0}')".format(self.backend)) @@ -250,16 +245,12 @@ def get_min(self, offsets, values, ind): w1 = ind_centre - ind0 return (1.0 - w1) * offsets[ind0] + w1 * offsets[ind0+1] - def process(self, out=None): - #get slice - data_full = self.get_input() + data = data_full = self.get_input() - if data_full.geometry.dimension == '3D': - data = data_full.get_slice(vertical=self.slice_index) - else: - data = data_full + if AcquisitionType.DIM3 & data_full.geometry.dimension: + data = data.get_slice(vertical=self.slice_index) data.geometry.config.system.align_reference_frame('cil') width = data.geometry.config.panel.num_pixels[0] diff --git a/Wrappers/Python/cil/processors/CofR_xcorrelation.py b/Wrappers/Python/cil/processors/CofR_xcorrelation.py index 62fe47f4a9..0feeaba2ce 100644 --- a/Wrappers/Python/cil/processors/CofR_xcorrelation.py +++ b/Wrappers/Python/cil/processors/CofR_xcorrelation.py @@ -16,7 +16,7 @@ # Authors: # CIL Developers, listed at: https://github.com/TomographicImaging/CIL/blob/master/NOTICE.txt -from cil.framework import Processor, AcquisitionData +from cil.framework import Processor, AcquisitionData, AcquisitionType import numpy as np import logging @@ -138,14 +138,9 @@ def _return_180_index(angles_deg, initial_index): return np.abs(angles_deg - 180).argmin() def process(self, out=None): - - data_full = self.get_input() - - if data_full.geometry.dimension == '3D': - - data = data_full.get_slice(vertical=self.slice_index) - else: - data = data_full + data = data_full = self.get_input() + if AcquisitionType.DIM3 & data_full.geometry.dimension: + data = data.get_slice(vertical=self.slice_index) geometry = data.geometry diff --git a/Wrappers/Python/cil/recon/FBP.py b/Wrappers/Python/cil/recon/FBP.py index c9392d7eba..8e2f1dcf39 100644 --- a/Wrappers/Python/cil/recon/FBP.py +++ b/Wrappers/Python/cil/recon/FBP.py @@ -17,7 +17,7 @@ # CIL Developers, listed at: https://github.com/TomographicImaging/CIL/blob/master/NOTICE.txt from cil.framework import cilacc -from cil.framework import AcquisitionTypes +from cil.framework import AcquisitionType from cil.recon import Reconstructor from scipy.fft import fftfreq @@ -254,13 +254,13 @@ def get_filter_array(self): elif self._filter == 'hann': filter_array = ramp * (0.5 + 0.5 * np.cos(freq*np.pi)) - return np.asarray(filter_array,dtype=np.float32).reshape(2**self.fft_order) - - + return np.asarray(filter_array,dtype=np.float32).reshape(2**self.fft_order) + + def plot_filter(self): """ Returns a plot of the filter array. - + Returns ------- matplotlib.pyplot @@ -376,7 +376,7 @@ def __init__ (self, input, image_geometry=None, filter='ram-lak'): #call parent initialiser super().__init__(input, image_geometry, filter, backend='tigre') - if input.geometry.geom_type != AcquisitionTypes.CONE: + if not AcquisitionType.CONE & input.geometry.geom_type: raise TypeError("This reconstructor is for cone-beam data only.") @@ -485,7 +485,7 @@ def __init__ (self, input, image_geometry=None, filter='ram-lak', backend='tigre super().__init__(input, image_geometry, filter, backend) self.set_split_processing(False) - if input.geometry.geom_type != AcquisitionTypes.PARALLEL: + if not AcquisitionType.PARALLEL & input.geometry.geom_type: raise TypeError("This reconstructor is for parallel-beam data only.") @@ -564,13 +564,11 @@ def run(self, out=None, verbose=1): ImageData The reconstructed volume. Suppressed if `out` is passed """ - if verbose: print(self) if self.slices_per_chunk: - - if self.acquisition_geometry.dimension == '2D': + if AcquisitionType.DIM2 & self.acquisition_geometry.dimension: raise ValueError("Only 3D datasets can be processed in chunks with `set_split_processing`") elif self.acquisition_geometry.system_description == 'advanced': raise ValueError("Only simple and offset geometries can be processed in chunks with `set_split_processing`") diff --git a/Wrappers/Python/cil/utilities/display.py b/Wrappers/Python/cil/utilities/display.py index cc7cc10fd4..7048028398 100644 --- a/Wrappers/Python/cil/utilities/display.py +++ b/Wrappers/Python/cil/utilities/display.py @@ -19,7 +19,7 @@ #%% -from cil.framework import AcquisitionGeometry, AcquisitionData, ImageData, DataContainer, BlockDataContainer +from cil.framework import AcquisitionGeometry, AcquisitionData, ImageData, DataContainer, BlockDataContainer, AcquisitionType import numpy as np import warnings @@ -700,10 +700,8 @@ def do_3d_projection(self, renderer=None): return np.min(zs) class _ShowGeometry(object): - def __init__(self, acquisition_geometry, image_geometry=None): - - if acquisition_geometry.dimension == "2D": + if AcquisitionType.DIM2 & acquisition_geometry.dimension: self.ndim = 2 sys = acquisition_geometry.config.system if acquisition_geometry.geom_type == 'cone': @@ -1051,8 +1049,7 @@ class show_geometry(show_base): def __init__(self,acquisition_geometry, image_geometry=None, elevation=20, azimuthal=-35, view_distance=10, grid=False, figsize=(10,10), fontsize=10): - - if acquisition_geometry.dimension == '2D': + if AcquisitionType.DIM2 & acquisition_geometry.dimension: elevation = 90 azimuthal = 0 diff --git a/Wrappers/Python/test/test_labels.py b/Wrappers/Python/test/test_labels.py index 3efdc57f65..0e5e34677f 100644 --- a/Wrappers/Python/test/test_labels.py +++ b/Wrappers/Python/test/test_labels.py @@ -20,34 +20,31 @@ import numpy as np from cil.framework import AcquisitionGeometry, ImageGeometry -from cil.framework.labels import (StrEnum, - FillTypes, UnitsAngles, - AcquisitionTypes, AcquisitionDimensions, - ImageDimensionLabels, AcquisitionDimensionLabels, Backends) +from cil.framework.labels import FillTypes, UnitsAngles, AcquisitionType, ImageDimensionLabels, AcquisitionDimensionLabels, Backends class Test_Lables(unittest.TestCase): def test_labels_strenum(self): - for item in ("3D", "DIM3", AcquisitionDimensions.DIM3): - out = AcquisitionDimensions(item) - self.assertEqual(out, AcquisitionDimensions.DIM3) - self.assertTrue(isinstance(out, AcquisitionDimensions)) - for item in ("bad_str", "4D", "DIM4", UnitsAngles.DEGREE): + for item in (UnitsAngles.DEGREE, "DEGREE", "degree"): + out = UnitsAngles(item) + self.assertEqual(out, UnitsAngles.DEGREE) + self.assertTrue(isinstance(out, UnitsAngles)) + for item in ("bad_str", FillTypes.RANDOM): with self.assertRaises(ValueError): - AcquisitionDimensions(item) + UnitsAngles(item) def test_labels_strenum_eq(self): - for i in ("3D", "DIM3", AcquisitionDimensions.DIM3): - self.assertEqual(AcquisitionDimensions.DIM3, i) - self.assertEqual(i, AcquisitionDimensions.DIM3) - for i in ("2D", "DIM2", AcquisitionDimensions.DIM2, AcquisitionDimensions): - self.assertNotEqual(AcquisitionDimensions.DIM3, i) + for i in (UnitsAngles.RADIAN, "RADIAN", "radian"): + self.assertEqual(UnitsAngles.RADIAN, i) + self.assertEqual(i, UnitsAngles.RADIAN) + for i in ("DEGREE", UnitsAngles.DEGREE, UnitsAngles): + self.assertNotEqual(UnitsAngles.RADIAN, i) def test_labels_contains(self): - for i in ("3D", "DIM3", AcquisitionDimensions.DIM3, AcquisitionDimensions.DIM2): - self.assertIn(i, AcquisitionDimensions) - for i in ("4D", "DIM4", AcquisitionDimensions): - self.assertNotIn(i, AcquisitionDimensions) + for i in ("RADIAN", "degree", UnitsAngles.RADIAN, UnitsAngles.DEGREE): + self.assertIn(i, UnitsAngles) + for i in ("bad_str", UnitsAngles): + self.assertNotIn(i, UnitsAngles) def test_backends(self): for i in ('ASTRA', 'CIL', 'TIGRE'): @@ -68,18 +65,10 @@ def test_units_angles(self): self.assertIn(getattr(UnitsAngles, i), UnitsAngles) def test_acquisition_type(self): - for i in ('PARALLEL', 'CONE'): - self.assertIn(i, AcquisitionTypes) - self.assertIn(i.lower(), AcquisitionTypes) - self.assertIn(getattr(AcquisitionTypes, i), AcquisitionTypes) - - def test_acquisition_dimension(self): - for i in ('2D', '3D'): - self.assertIn(i, AcquisitionDimensions) - for i in ('DIM2', 'DIM3'): - self.assertIn(i, AcquisitionDimensions) - self.assertIn(i.lower(), AcquisitionDimensions) - self.assertIn(getattr(AcquisitionDimensions, i), AcquisitionDimensions) + for i in ('PARALLEL', 'CONE', 'DIM2', 'DIM3'): + self.assertIn(i, AcquisitionType) + self.assertIn(i.lower(), AcquisitionType) + self.assertIn(getattr(AcquisitionType, i), AcquisitionType) def test_image_dimension_labels(self): for i in ('CHANNEL', 'VERTICAL', 'HORIZONTAL_X', 'HORIZONTAL_Y'): diff --git a/Wrappers/Python/test/utils_projectors.py b/Wrappers/Python/test/utils_projectors.py index 1ffe20a37c..c009c82e28 100644 --- a/Wrappers/Python/test/utils_projectors.py +++ b/Wrappers/Python/test/utils_projectors.py @@ -19,7 +19,7 @@ import numpy as np from cil.optimisation.operators import LinearOperator from cil.utilities import dataexample -from cil.framework import AcquisitionGeometry, AcquisitionDimensionLabels +from cil.framework import AcquisitionGeometry, AcquisitionDimensionLabels, AcquisitionType class SimData(object): @@ -375,7 +375,7 @@ def test_forward_projector(self): if (i + j)% 2 == 0: checker[j*4:(j+1)*4,i*4:(i+1)*4] = ones * 16 - if self.ag.dimension == '2D': + if AcquisitionType.DIM2 & self.ag.dimension: checker = checker[0] res = res[0] @@ -408,7 +408,7 @@ def test_backward_projector(self): if (i + k)% 2 == 0: res[k*4:(k+1)*4,:,i*4:(i+1)*4] = ones - if self.ag.dimension == '2D': + if AcquisitionType.DIM2 & self.ag.dimension: checker = checker[0] res = res[0] @@ -482,7 +482,7 @@ def test_FBP_roi(self): reco = FBP(self.acq_data) np.testing.assert_allclose(reco.as_array(), self.gold_roi, atol=self.tolerance_fbp_roi) - if self.ag.dimension == '3D': + if AcquisitionType.DIM3 & self.ag.dimension: FBP = self.FBP(self.ig_single_slice, self.ag, **self.FBP_args) reco = FBP(self.acq_data) np.testing.assert_allclose(reco.as_array(), self.gold_roi_single_slice, atol=self.tolerance_fbp_roi)