class TestAstraSimple(unittest.TestCase): def setUp(self): N = 128 angles = np.linspace(0, np.pi, 180, dtype='float32') ag = AcquisitionGeometry.create_Parallel2D()\ .set_angles(angles, angle_unit='radian')\ .set_panel(N, 0.1)\ .set_labels(['angle', 'horizontal']) ig = ag.get_ImageGeometry() ag3 = AcquisitionGeometry.create_Parallel3D()\ .set_angles(angles, angle_unit='radian')\ .set_panel((N, N), (0.1, 0.1))\ .set_labels(['vertical', 'angle', 'horizontal']) ig3 = ag3.get_ImageGeometry() self.ig = ig self.ag = ag self.ig3 = ig3 self.ag3 = ag3 self.norm = 14.85 @unittest.skipUnless(has_astra and astra.use_cuda(), "Astra not built with CUDA") def test_norm_simple2D_gpu(self): # test exists # Create projection operator using Astra-Toolbox. Available CPU/CPU device = 'gpu' A = AstraProjectorSimple(self.ig, self.ag, device = device) n = A.norm() print ("norm A GPU", n) self.assertTrue(True) self.assertAlmostEqual(n, self.norm, places=2) @unittest.skipUnless(has_astra, "Astra not built with CUDA") def test_norm_simple2D_cpu(self): # test exists # Create projection operator using Astra-Toolbox. Available CPU/CPU device = 'cpu' A = AstraProjectorSimple(self.ig, self.ag, device = device) n = A.norm() print ("norm A CPU", n) self.assertTrue(True) self.assertAlmostEqual(n, self.norm, places=2) @unittest.skipUnless(has_astra and astra.use_cuda(), "Astra not built with CUDA") def test_norm_simple3D_gpu(self): # test exists A3 = AstraProjector3DSimple(self.ig3, self.ag3) n = A3.norm() print ("norm A3", n) self.assertTrue(True) self.assertAlmostEqual(n, self.norm, places=2)
def test(): """Perform a very basic functionality test""" import astra if astra.use_cuda(): test_CUDA() else: print("No GPU support available") test_noCUDA()
class BasicAstraTests(unittest.TestCase): @unittest.skipUnless(has_astra and astra.use_cuda(), "Astra not built with CUDA") def test_CUDA(self): astra.test_CUDA() assert True @unittest.skipUnless(has_astra, "Astra not built with CUDA") def test_noCUDA(self): astra.test_noCUDA() assert True
def check_cuda(): try: import astra import sys if not astra.use_cuda(): print('No GPU support available') print('Please have a GPU to run these scripts') exit(1) except: print('Please have ASTRA installed!')
def get_torch_ray_trafo_parallel_2d_adjoint(ray_trafo, z_shape=1): """ Create a torch autograd-enabled function from a 2D parallel-beam :class:`odl.tomo.RayTransform` using tomosipo that calls the direct backward projection routine of astra, which avoids copying between GPU and CPU (available in 1.9.9.dev4). Parameters ---------- ray_trafo : :class:`odl.tomo.RayTransform` Ray transform z_shape : int, optional Batch dimension. Default: ``1``. Returns ------- torch_ray_trafo_adjoint : callable Torch autograd-enabled function applying the parallel-beam backward projection. Input and output have a trivial leading batch dimension and a channel dimension specified by `z_shape` (default ``1``), i.e. the input shape is ``(1, z_shape) + ray_trafo.range.shape`` and the output shape is ``(1, z_shape) + ray_trafo.domain.shape``. """ if not TOMOSIPO_AVAILABLE: raise ImportError(MISSING_TOMOSIPO_MESSAGE) if not ASTRA_AVAILABLE: raise RuntimeError('Astra is not available.') if not astra.use_cuda(): raise RuntimeError('Astra is not able to use CUDA.') vg = from_odl(discretized_space_2d_to_3d(ray_trafo.domain, z_shape=z_shape)) pg = from_odl( parallel_2d_to_3d_geometry(ray_trafo.geometry, det_z_shape=z_shape)) ts_op = ts.operator(vg, pg) torch_ray_trafo_adjoint_ts = to_autograd(ts_op.T) scaling_factor = astra_cuda_bp_scaling_factor(ray_trafo.range, ray_trafo.domain, ray_trafo.geometry) def torch_ray_trafo_adjoint(y): return scaling_factor * torch_ray_trafo_adjoint_ts(y) return torch_ray_trafo_adjoint
def __init__(self, ray_trafo, init_z_shape=1): """ Parameters ---------- ray_trafo : :class:`odl.tomo.RayTransform` Ray transform init_z_shape : int, optional Initial guess for the number of 2D transforms per batch, i.e. the product of batch and channel dimensions. """ if not TOMOSIPO_AVAILABLE: raise ImportError(MISSING_TOMOSIPO_MESSAGE) if not ASTRA_AVAILABLE: raise RuntimeError('Astra is not available.') if not astra.use_cuda(): raise RuntimeError('Astra is not able to use CUDA.') super().__init__() self.ray_trafo = ray_trafo self._construct_operator(init_z_shape)
TORCH_AVAILABLE = True from dival.util.torch_utility import ( RandomAccessTorchDataset, GeneratorTorchDataset, load_state_dict_convert_data_parallel, TOMOSIPO_AVAILABLE, TorchRayTrafoParallel2DModule, TorchRayTrafoParallel2DAdjointModule) import numpy as np import odl from dival import get_standard_dataset from dival.datasets import Dataset from dival.datasets.ellipses_dataset import EllipsesDataset try: import astra except ImportError: ASTRA_CUDA_AVAILABLE = False else: ASTRA_CUDA_AVAILABLE = astra.use_cuda() def get_parallel_beam_dataset(): ellipses_dataset = EllipsesDataset(image_size=128, min_pt=[-20., -20.], max_pt=[20., 20.], fixed_seeds=True) NUM_ANGLES = 30 geometry = odl.tomo.parallel_beam_geometry(ellipses_dataset.space, num_angles=NUM_ANGLES) ray_trafo = odl.tomo.RayTransform(ellipses_dataset.space, geometry,
class TestAstraConeBeamProjectors(unittest.TestCase): def setUp(self): #%% Setup Geometry voxel_num_xy = 255 voxel_num_z = 15 self.cs_ind = (voxel_num_z - 1) // 2 mag = 2 src_to_obj = 50 src_to_det = src_to_obj * mag pix_size = 0.2 det_pix_x = voxel_num_xy det_pix_y = voxel_num_z num_projections = 180 angles = np.linspace(0, np.pi, num=num_projections, endpoint=False) self.ag = AcquisitionGeometry.create_Cone3D([0,-src_to_obj,0],[0,src_to_det-src_to_obj,0])\ .set_angles(angles, angle_unit='radian')\ .set_panel((det_pix_x,det_pix_y), (pix_size,pix_size))\ .set_labels(['vertical','angle','horizontal']) self.ag_slice = AcquisitionGeometry.create_Cone2D([0,-src_to_obj],[0,src_to_det-src_to_obj])\ .set_angles(angles, angle_unit='radian')\ .set_panel(det_pix_x, pix_size)\ .set_labels(['angle','horizontal']) self.ig_2D = self.ag_slice.get_ImageGeometry() self.ig_3D = self.ag.get_ImageGeometry() #%% Create phantom kernel_size = voxel_num_xy kernel_radius = (kernel_size - 1) // 2 y, x = np.ogrid[-kernel_radius:kernel_radius + 1, -kernel_radius:kernel_radius + 1] circle1 = [5, 0, 0] #r,x,y dist1 = ((x - circle1[1])**2 + (y - circle1[2])**2)**0.5 circle2 = [5, 100, 0] #r,x,y dist2 = ((x - circle2[1])**2 + (y - circle2[2])**2)**0.5 circle3 = [25, 0, 100] #r,x,y dist3 = ((x - circle3[1])**2 + (y - circle3[2])**2)**0.5 mask1 = (dist1 - circle1[0]).clip(0, 1) mask2 = (dist2 - circle2[0]).clip(0, 1) mask3 = (dist3 - circle3[0]).clip(0, 1) phantom = 1 - np.logical_and(np.logical_and(mask1, mask2), mask3) self.golden_data = self.ig_3D.allocate(0) for i in range(4): self.golden_data.fill(array=phantom, vertical=7 + i) self.golden_data_cs = self.golden_data.subset(vertical=self.cs_ind, force=True) @unittest.skipUnless(has_astra, "Astra not available") def test_consistency(self): # #%% AstraProjectorSimple cpu ig = self.ig_2D.copy() ag = self.ag_slice.copy() A = AstraProjectorSimple(ig, ag, device='cpu') fp = A.direct(self.golden_data_cs) bp = A.adjoint(fp) # #%% AstraProjectorFlexible ig = self.ig_3D.copy() ag = self.ag.copy() A = AstraProjector3DSimple(ig, ag) flex_fp = A.direct(self.golden_data) flex_bp = A.adjoint(flex_fp) #comparision foward projection fp_flex_0 = flex_fp.subset(vertical=self.cs_ind, force=True) fp_flex_2 = flex_fp.subset(vertical=self.cs_ind - 3, force=True) zeros = self.ag_slice.allocate(0) np.testing.assert_allclose(fp_flex_0.as_array(), fp.as_array(), atol=0.8) np.testing.assert_allclose(fp_flex_2.as_array(), zeros.as_array()) #comparision back projection bp_flex_0 = flex_bp.subset(vertical=self.cs_ind, force=True) bp_flex_1 = flex_bp.subset(vertical=self.cs_ind + 3, force=True) bp_flex_2 = flex_bp.subset(vertical=self.cs_ind - 3, force=True) zeros = self.ig_2D.allocate(0) np.testing.assert_allclose(bp_flex_0.as_array(), bp.as_array(), atol=12) np.testing.assert_allclose(bp_flex_1.as_array(), bp.as_array(), atol=25) np.testing.assert_allclose(bp_flex_2.as_array(), zeros.as_array()) @unittest.skipUnless(has_astra and astra.use_cuda(), "Astra GPU not available") def test_3D(self): #%% AstraProjectorSimple gpu ig = self.ig_3D.copy() ag = self.ag.copy() A = AstraProjector3DSimple(ig, ag) fp = A.direct(self.golden_data) bp = A.adjoint(fp) # #%% AstraProjectorFlexible ig = self.ig_3D.copy() ag = self.ag.copy() A = AstraProjector3DSimple(ig, ag) flex_fp = A.direct(self.golden_data) flex_bp = A.adjoint(fp) #comparision foward projection fp_0 = fp.subset(vertical=self.cs_ind, force=True) fp_1 = fp.subset(vertical=self.cs_ind + 3, force=True) fp_2 = fp.subset(vertical=self.cs_ind - 3, force=True) fp_flex_0 = flex_fp.subset(vertical=self.cs_ind, force=True) fp_flex_1 = flex_fp.subset(vertical=self.cs_ind + 3, force=True) fp_flex_2 = flex_fp.subset(vertical=self.cs_ind - 3, force=True) np.testing.assert_allclose(fp_flex_0.as_array(), fp_0.as_array()) np.testing.assert_allclose(fp_flex_1.as_array(), fp_1.as_array()) np.testing.assert_allclose(fp_flex_2.as_array(), fp_2.as_array()) #comparision back projection bp_0 = bp.subset(vertical=self.cs_ind, force=True) bp_1 = bp.subset(vertical=self.cs_ind + 3, force=True) bp_2 = bp.subset(vertical=self.cs_ind - 3, force=True) bp_flex_0 = flex_bp.subset(vertical=self.cs_ind, force=True) bp_flex_1 = flex_bp.subset(vertical=self.cs_ind + 3, force=True) bp_flex_2 = flex_bp.subset(vertical=self.cs_ind - 3, force=True) np.testing.assert_allclose(bp_flex_0.as_array(), bp_0.as_array()) np.testing.assert_allclose(bp_flex_1.as_array(), bp_1.as_array()) np.testing.assert_allclose(bp_flex_2.as_array(), bp_2.as_array()) @unittest.skipUnless(has_astra and astra.use_cuda(), "Astra not built with CUDA") def test_2D(self): # #%% AstraProjectorSimple cpu ig = self.ig_2D.copy() ag = self.ag_slice.copy() A = AstraProjectorSimple(ig, ag, device='cpu') fp = A.direct(self.golden_data_cs) bp = A.adjoint(fp) #%% AstraProjectorSimple gpu ig = self.ig_2D.copy() ag = self.ag_slice.copy() A = AstraProjectorSimple(ig, ag, device='gpu') fp_gpu = A.direct(self.golden_data_cs) bp_gpu = A.adjoint(fp_gpu) np.testing.assert_allclose(fp_gpu.as_array(), fp.as_array(), atol=0.8) np.testing.assert_allclose(bp_gpu.as_array(), bp.as_array(), atol=12) # #%% AstraProjectorFlexible as a 2D ig = self.ig_2D.copy() ag = self.ag_slice.copy() A = AstraProjectorFlexible(ig, ag) fp_flex = A.direct(self.golden_data_cs) bp_flex = A.adjoint(fp_flex) np.testing.assert_allclose(fp_flex.as_array(), fp.as_array(), atol=0.8) np.testing.assert_allclose(bp_flex.as_array(), bp.as_array(), atol=12)
def test_align(self, xshift=0.0, angle=0.0, slices=None, thickness=None, method='FBP', iterations=50, constrain=True, cuda=None, thresh=0): """ Reconstruct three slices from the input data for visual inspection. Args ---------- xshift : float Number of pixels by which to shift the input data. angle : float Angle by which to rotate stack prior to reconstruction slices : list Position of slices to use for the reconstruction. If None, positions at 1/4, 1/2, and 3/4 of the full size of the stack are chosen. thickness : integer Size of the output volume (in pixels) in the projection direction. cuda : bool If True, use CUDA-accelerated Astra algorithms. If None, use CUDA if astra.use_cuda() is True. """ if slices is None: mid = np.int32(self.data.shape[1] / 2) slices = np.int32([mid / 2, mid, mid + mid / 2]) if (xshift != 0.0) or (angle != 0.0): shifted = self.trans_stack(xshift=xshift, yshift=0, angle=angle) else: shifted = self.deepcopy() shifted.data = shifted.data[:, slices, :] shifted.axes_manager[0].axis = self.axes_manager[0].axis if not cuda: if astra.use_cuda(): logger.info('CUDA detected with Astra') cuda = True else: cuda = False logger.info('CUDA not detected with Astra') rec = shifted.reconstruct(method=method, iterations=iterations, constrain=constrain, thickness=thickness, cuda=cuda, thresh=thresh) if 'ipympl' in mpl.get_backend().lower(): fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(7, 3)) elif 'nbagg' in mpl.get_backend().lower(): fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(8, 4)) else: fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(12, 4)) minvals = rec.data.mean((1, 2)) - 3 * rec.data.std((1, 2)) minvals[minvals < 0] = 0 maxvals = rec.data.mean((1, 2)) + 3 * rec.data.std((1, 2)) for i in range(0, 3): if maxvals[i] > rec.data[i].max(): maxvals[i] = rec.data[i].max() ax1.imshow(rec.data[0, :, :], cmap='afmhot', vmin=minvals[0], vmax=maxvals[0]) ax1.set_title('Slice %s' % str(slices[0])) ax1.set_axis_off() ax2.imshow(rec.data[1, :, :], cmap='afmhot', vmin=minvals[1], vmax=maxvals[1]) ax2.set_title('Slice %s' % str(slices[1])) ax2.set_axis_off() ax3.imshow(rec.data[2, :, :], cmap='afmhot', vmin=minvals[2], vmax=maxvals[2]) ax3.set_title('Slice %s' % str(slices[2])) ax3.set_axis_off() fig.tight_layout() return
def reconstruct(self, method='FBP', iterations=None, constrain=False, thresh=0, cuda=None, thickness=None): """ Reconstruct a TomoStack series using one of the available methods. astraWBP, astraSIRT, astraSIRT_GPU Args ---------- method : string Reconstruction algorithm to use. Must be either 'FBP' (default) or 'SIRT' iterations : integer Number of iterations for the SIRT reconstruction (for astraSIRT and astraSIRT_GPU, methods only) constrain : boolean If True, output reconstruction is constrained above value given by 'thresh' thresh : integer or float Value above which to constrain the reconstructed data cuda : boolean If True, use the CUDA-accelerated reconstruction algorithm thickness : integer Size of the output volume (in pixels) in the projection direction. Returns ---------- out : TomoStack object TomoStack containing the reconstructed volume Examples ---------- Filtered backprojection (FBP) reconstruction >>> import tomotools.datasets as ds >>> stack = ds.get_needle_data(True) >>> slices = stack.isig[:, 120:121].deepcopy() >>> rec = slices.reconstruct('FBP') Simultaneous iterative reconstruction technique (SIRT) reconstruction >>> import tomotools.datasets as ds >>> stack = ds.get_needle_data(True) >>> slices = stack.isig[:, 120:121].deepcopy() >>> rec = slices.reconstruct('SIRT',iterations=5) Simultaneous iterative reconstruction technique (SIRT) reconstruction with positivity constraint >>> import tomotools.datasets as ds >>> stack = ds.get_needle_data(True) >>> slices = stack.isig[:, 120:121].deepcopy() >>> iterations = 5 >>> constrain = True >>> thresh = 0 >>> rec = slices.reconstruct('SIRT',iterations, constrain, thresh) """ if not cuda: if astra.use_cuda(): logger.info('CUDA detected with Astra') cuda = True else: cuda = False logger.info('CUDA not detected with Astra') out = copy.deepcopy(self) out.data = recon.run(self, method, iterations, constrain, thresh, cuda, thickness) out.axes_manager[0].name = 'y' out.axes_manager[0].size = out.data.shape[0] out.axes_manager[0].offset = self.axes_manager['y'].offset out.axes_manager[0].scale = self.axes_manager['y'].scale out.axes_manager[0].units = self.axes_manager['y'].units out.axes_manager[2].name = 'z' out.axes_manager[2].size = out.data.shape[1] out.axes_manager[2].offset = self.axes_manager['x'].offset out.axes_manager[2].scale = self.axes_manager['x'].scale out.axes_manager[2].units = self.axes_manager['x'].units out.axes_manager[1].name = 'x' out.axes_manager[1].size = out.data.shape[2] out.axes_manager[1].offset = self.axes_manager['x'].offset out.axes_manager[1].scale = self.axes_manager['x'].scale out.axes_manager[1].units = self.axes_manager['x'].units return out
def tilt_align(self, method, limit=10, delta=0.3, locs=None, axis=0, show_progressbar=False): """ Align the tilt axis of a TomoStack. Uses either a center-of-mass approach or a maximum image approach Available options are 'CoM' and 'Error' CoM: track the center of mass (CoM) of the projections at three locations. Fit the motion of the CoM as a function of tilt to that expected for an ideal cylinder to calculate an X-shift at each location. Perform a linear fit of the three X-shifts to calculate an ideal rotation. MaxImage: Perform automated determination of the tilt axis of a TomoStack by measuring the rotation of the projected maximum image. Maximum image is rotated positively and negatively, filtered using a Hamming window, and the rotation angle is determined by iterative histogram analysis minimize: Perform automated determination of the tilt axis of a TomoStack by minimizing the difference between the reconstruction and the input dataset usign scipy.optimize.differential_evolution. Args ---------- method : string Algorithm to use for registration alignment. Must be either 'CoM', 'MaxImage', or 'minimize'. limit : integer Position in tilt series to use as starting point for the alignment. If None, the central projection is used. delta : integer Position i limit : integer or float Maximum rotation angle to use for MaxImage calculation delta : float Angular increment for MaxImage calculation locs : list Image coordinates indicating the locations at which to calculate the alignment axis : integer Axis along which to extract sinograms. Value of 0 means tilt axis is horizontally oriented. 1 means vertically oriented. show_progressbar : boolean Enable/disable progress bar Returns ---------- out : TomoStack object Copy of the input stack rotated by calculated angle Examples ---------- Align tilt axis using the center of mass (CoM) method >>> import tomotools.datasets as ds >>> stack = ds.get_needle_data() >>> reg = stack.stack_register('ECC',show_progressbar=False) >>> method = 'CoM' >>> ali = reg.tilt_align(method, locs=[50,100,160]) Align tilt axis using the maximum image method >>> import tomotools.datasets as ds >>> stack = ds.get_needle_data() >>> reg = stack.stack_register('ECC',show_progressbar=False) >>> method = 'MaxImage' >>> ali = reg.tilt_align(method, show_progressbar=False) """ method = method.lower() if axis == 1: self = self.swap_axes(1, 2) if method == 'com': out = align.tilt_com(self, locs) elif method == 'maximage': out = align.tilt_maximage(self, limit, delta, show_progressbar) elif method == 'minimize': out = align.tilt_minimize(self, cuda=astra.use_cuda()) else: raise ValueError("Invalid alignment method: %s." "Must be 'CoM', 'MaxImage', or 'Minimize'" % method) if axis == 1: self = self.swap_axes(2, 1) return out
def recon_error(self, nslice=None, iterations=50, constrain=True, cuda=None, thresh=0): """ Determine the optimum number of iterations for reconstruction. Evaluates the difference between reconstruction and input data at each iteration and terminates when the change between iterations is below tolerance. Args ---------- algorithm : str Reconstruction algorithm use. nslice : int Location at which to perform the evaluation. constrain : boolean If True, perform SIRT reconstruction with a non-negativity constraint. Default is True cuda : boolean If True, perform reconstruction using the GPU-accelrated algorithm. Default is True thresh : integer or float Value above which to constrain the reconstructed data Returns ---------- rec_stack : Hyperspy Signal2D Signal containing the SIRT reconstruction at each iteration for visual inspection. error : Hyperspy Signal1D Sum of squared difference between the forward-projected reconstruction and the input sinogram at each iteration Examples ---------- >>> import tomotools.datasets as ds >>> stack = ds.get_needle_data(True) >>> rec_stack, error = stack.recon_error(iterations=5) """ if not nslice: nslice = np.int32(self.data.shape[1] / 2) if not cuda: if astra.use_cuda(): logger.info('CUDA detected with Astra') cuda = True else: cuda = False logger.info('CUDA not detected with Astra') sinogram = self.isig[:, nslice:nslice + 1].deepcopy() angles = self.metadata.Tomography.tilts rec_stack, error = recon.astra_sirt_error(sinogram, angles, iterations=iterations, constrain=constrain, thresh=thresh, cuda=cuda) rec_stack = Signal2D(rec_stack) rec_stack.axes_manager[0].name = 'z' rec_stack.axes_manager[0].scale = self.axes_manager[1].scale rec_stack.axes_manager[0].scale = self.axes_manager[1].scale rec_stack.axes_manager[1].name = 'x' rec_stack.axes_manager[1].scale = self.axes_manager[1].scale rec_stack.axes_manager[1].scale = self.axes_manager[1].scale error = Signal1D(error) error.axes_manager[0].name = 'SIRT Iteration' error.metadata.Signal.quantity = 'Sum of Squared Difference' return rec_stack, error
class TestAstraFlexible(unittest.TestCase): def setUp(self): N = 128 angles = np.linspace(0, np.pi, 180, dtype='float32') ag = AcquisitionGeometry.create_Parallel2D()\ .set_angles(angles, angle_unit='radian')\ .set_panel(N, 0.1)\ .set_labels(['angle', 'horizontal']) ig = ag.get_ImageGeometry() ag3 = AcquisitionGeometry.create_Parallel3D()\ .set_angles(angles, angle_unit='radian')\ .set_panel((N, N), (0.1, 0.1))\ .set_labels(['vertical', 'angle', 'horizontal']) ig3 = ag3.get_ImageGeometry() self.ig = ig self.ag = ag self.ig3 = ig3 self.ag3 = ag3 self.norm = 14.85 @unittest.skipUnless(has_astra and astra.use_cuda(), "Astra not built with CUDA") def test_norm_flexible2D_gpu(self): # test exists # Create projection operator using Astra-Toolbox. Available CPU/CPU A = AstraProjectorFlexible(self.ig, self.ag) n = A.norm() print ("norm A GPU", n) self.assertTrue(True) self.assertAlmostEqual(n, self.norm, places=2) ag_2 = self.ag.copy() ag_2.dimension_labels = ['horizontal','angle'] with self.assertRaises(ValueError): A = AstraProjectorFlexible(self.ig, ag_2) ig_2 = self.ig3.copy() ig_2.dimension_labels = ['horizontal_x','horizontal_y'] with self.assertRaises(ValueError): A = AstraProjectorFlexible(ig_2, self.ag) @unittest.skipUnless(has_astra and astra.use_cuda(), "Astra not built with CUDA") def test_norm_flexible3D_gpu(self): # test exists A3 = AstraProjectorFlexible(self.ig3, self.ag3) n = A3.norm() print ("norm A3", n) self.assertTrue(True) self.assertAlmostEqual(n, self.norm, places=2) ag3_2 = self.ag3.copy() ag3_2.dimension_labels = ['angle','vertical','horizontal'] with self.assertRaises(ValueError): A3 = AstraProjectorFlexible(self.ig3, ag3_2) ig3_2 = self.ig3.copy() ig3_2.dimension_labels = ['horizontal_y','vertical','horizontal_x'] with self.assertRaises(ValueError): A3 = AstraProjectorFlexible(ig3_2, self.ag3)
class TestProjectionOperator(unittest.TestCase): def setUp(self): # Define image geometry. N = 128 angles = np.linspace(0, np.pi, 180, dtype='float32') ag = AcquisitionGeometry.create_Parallel2D()\ .set_angles(angles, angle_unit='radian')\ .set_panel(N, 0.1)\ .set_labels(['angle', 'horizontal']) ig = ag.get_ImageGeometry() ag3 = AcquisitionGeometry.create_Parallel3D()\ .set_angles(angles, angle_unit='radian')\ .set_panel((N, N), (0.1, 0.1))\ .set_labels(['vertical', 'angle', 'horizontal']) ig3 = ag3.get_ImageGeometry() ag_channel = AcquisitionGeometry.create_Parallel2D()\ .set_angles(angles, angle_unit='radian')\ .set_panel(N, 0.1)\ .set_labels(['channel', 'angle', 'horizontal'])\ .set_channels(2) ig_channel = ag_channel.get_ImageGeometry() ag3_channel = AcquisitionGeometry.create_Parallel3D()\ .set_angles(angles, angle_unit='radian')\ .set_panel((N, N), (0.1, 0.1))\ .set_labels(['channel','vertical', 'angle', 'horizontal'])\ .set_channels(2) ig3_channel = ag3_channel.get_ImageGeometry() self.ig = ig self.ag = ag self.ig_channel = ig_channel self.ag_channel = ag_channel self.ig3 = ig3 self.ag3 = ag3 self.ig3_channel = ig3_channel self.ag3_channel = ag3_channel self.norm = 14.85 @unittest.skipUnless(has_astra, "Astra not built with CUDA") def test_cpu(self): A = ProjectionOperator(self.ig, self.ag, device='cpu') n = A.norm() print ("norm A GPU", n) self.assertAlmostEqual(n, self.norm, places=2) A = ProjectionOperator(self.ig_channel, self.ag_channel, device='cpu') n = A.norm() print ("norm A GPU", n) self.assertAlmostEqual(n, self.norm, places=2) with self.assertRaises(NotImplementedError): A = ProjectionOperator(self.ig3, self.ag3, device='cpu') @unittest.skipUnless(has_astra and astra.use_cuda(), "Astra not built with CUDA") def test_gpu(self): A = ProjectionOperator(self.ig, self.ag) n = A.norm() print ("norm A GPU", n) self.assertAlmostEqual(n, self.norm, places=2) A = ProjectionOperator(self.ig_channel, self.ag_channel) n = A.norm() print ("norm A GPU", n) self.assertAlmostEqual(n, self.norm, places=2) A3 = ProjectionOperator(self.ig3, self.ag3) n = A3.norm() print ("norm A3", n) self.assertAlmostEqual(n, self.norm, places=2) A3_channel = ProjectionOperator(self.ig3_channel, self.ag3_channel) n = A3_channel.norm() print ("norm A4", n) self.assertAlmostEqual(n, self.norm, places=2)
class TestProcessors(unittest.TestCase): def setUp(self): #%% Setup Geometry voxel_num_xy = 255 voxel_num_z = 15 self.cs_ind = (voxel_num_z-1)//2 src_to_obj = 500 src_to_det = src_to_obj pix_size = 0.2 det_pix_x = voxel_num_xy det_pix_y = voxel_num_z num_projections = 360 angles = np.linspace(0, 2*np.pi, num=num_projections, endpoint=False) self.ag_cone = AcquisitionGeometry.create_Cone3D([0,-src_to_obj,0],[0,src_to_det-src_to_obj,0])\ .set_angles(angles, angle_unit='radian')\ .set_panel((det_pix_x,det_pix_y), (pix_size,pix_size))\ .set_labels(['vertical','angle','horizontal']) self.ag_parallel = AcquisitionGeometry.create_Parallel3D()\ .set_angles(angles, angle_unit='radian')\ .set_panel((det_pix_x,det_pix_y), (pix_size,pix_size))\ .set_labels(['vertical','angle','horizontal']) self.ig_3D = self.ag_parallel.get_ImageGeometry() #%% Create phantom kernel_size = voxel_num_xy kernel_radius = (kernel_size - 1) // 2 y, x = np.ogrid[-kernel_radius:kernel_radius+1, -kernel_radius:kernel_radius+1] circle1 = [5,0,0] #r,x,y dist1 = ((x - circle1[1])**2 + (y - circle1[2])**2)**0.5 circle2 = [5,100,0] #r,x,y dist2 = ((x - circle2[1])**2 + (y - circle2[2])**2)**0.5 circle3 = [25,0,100] #r,x,y dist3 = ((x - circle3[1])**2 + (y - circle3[2])**2)**0.5 mask1 =(dist1 - circle1[0]).clip(0,1) mask2 =(dist2 - circle2[0]).clip(0,1) mask3 =(dist3 - circle3[0]).clip(0,1) phantom = 1 - np.logical_and(np.logical_and(mask1, mask2),mask3) self.golden_data = self.ig_3D.allocate(0) for i in range(4): self.golden_data.fill(array=phantom, vertical=7+i) self.golden_data_cs = self.golden_data.subset(vertical=self.cs_ind) @unittest.skipUnless(has_astra and astra.use_cuda(), "Astra not built with CUDA") def test_FBPgpu(self): #2D cone ag = self.ag_cone.subset(vertical='centre') ig = ag.get_ImageGeometry() A = ProjectionOperator(ig, ag, device='gpu') fp_2D = A.direct(self.golden_data_cs) fbp = FBP(ig, ag, 'gpu') fbp.set_input(fp_2D) fbp_2D_cone = fbp.get_output() np.testing.assert_allclose(fbp_2D_cone.as_array(),self.golden_data_cs.as_array(),atol=0.6) #3D cone ag = self.ag_cone ig = ag.get_ImageGeometry() A = ProjectionOperator(ig, ag, device='gpu') fp_3D = A.direct(self.golden_data) fbp = FBP(ig, ag, 'gpu') fbp.set_input(fp_3D) fbp_3D_cone = fbp.get_output() np.testing.assert_allclose(fbp_3D_cone.as_array(),self.golden_data.as_array(),atol=0.6) #2D parallel ag = self.ag_parallel.subset(vertical='centre') ig = ag.get_ImageGeometry() A = ProjectionOperator(ig, ag, device='gpu') fp_2D = A.direct(self.golden_data_cs) fbp = FBP(ig, ag, 'gpu') fbp.set_input(fp_2D) fbp_2D_parallel = fbp.get_output() np.testing.assert_allclose(fbp_2D_parallel.as_array(),self.golden_data_cs.as_array(), atol=0.6) #3D parallel ag = self.ag_parallel ig = ag.get_ImageGeometry() A = ProjectionOperator(ig, ag, device='gpu') fp_3D = A.direct(self.golden_data) fbp = FBP(ig, ag, 'gpu') fbp.set_input(fp_3D) fbp_3D_parallel = fbp.get_output() np.testing.assert_allclose(fbp_3D_parallel.as_array(),self.golden_data.as_array(),atol=0.6) @unittest.skipUnless(has_astra, "Astra not built with CUDA") def test_FBPcpu(self): #2D parallel ag = self.ag_parallel.subset(vertical='centre') ig = ag.get_ImageGeometry() A = ProjectionOperator(ig, ag, device='cpu') fp_2D = A.direct(self.golden_data_cs) fbp = FBP(ig, ag, 'cpu') fbp.set_input(fp_2D) fbp_2D_parallel = fbp.get_output() np.testing.assert_allclose(fbp_2D_parallel.as_array(),self.golden_data_cs.as_array(),atol=0.6)