def test_discrete_gradient(): """Discretized spatial gradient operator.""" discr_space = Rn(1) with pytest.raises(TypeError): DiscreteGradient(discr_space) # Check result of operator with explicit summation # phantom data data = np.array([[0., 1., 2., 3., 4.], [1., 2., 3., 4., 5.], [2., 3., 4., 5., 6.]]) data = np.array([[0., 1., 2., 3., 4.], [0., 1., 2., 3., 4.], [0., 1., 2., 3., 4.]]) # DiscreteLp Vector discr_space = uniform_discr([0, 0], [6, 2.5], data.shape) dom_vec = discr_space.element(data) # computation of gradient components with helper function dx0, dx1 = discr_space.grid.stride df0 = finite_diff(data, axis=0, dx=dx0, zero_padding=True, edge_order=2) df1 = finite_diff(data, axis=1, dx=dx1, zero_padding=True, edge_order=2) # gradient grad = DiscreteGradient(discr_space) grad_vec = grad(dom_vec) assert len(grad_vec) == data.ndim assert all_equal(grad_vec[0].asarray(), df0) assert all_equal(grad_vec[1].asarray(), df1) # adjoint operator ran_vec = grad.range.element([data, data ** 2]) adj_vec = grad.adjoint(ran_vec) lhs = ran_vec.inner(grad_vec) rhs = dom_vec.inner(adj_vec) assert lhs != 0 assert rhs != 0 assert lhs == rhs # higher dimensional arrays lin_size = 3 for ndim in range(1, 6): # DiscreteLp Vector discr_space = uniform_discr([0.] * ndim, [lin_size] * ndim, [lin_size] * ndim) dom_vec = discr_space.element(ndvolume(lin_size, ndim)) # gradient grad = DiscreteGradient(discr_space) grad(dom_vec)
def test_discrete_divergence(): """Discretized spatial divergence operator.""" # Invalid arguments discr_space = Rn(1) with pytest.raises(TypeError): DiscreteDivergence(discr_space) # Check result of operator with explicit summation data = np.array([[0., 1., 2., 3., 4.], [1., 2., 3., 4., 5.], [2., 3., 4., 5., 6.]]) # DiscreteLp discr_space = uniform_discr([0, 0], [6, 2.5], data.shape) # Operator instance div = DiscreteDivergence(discr_space) # Apply operator dom_vec = div.domain.element([data, data]) div_dom_vec = div(dom_vec) # computation of divergence with helper function dx0, dx1 = discr_space.grid.stride df0 = finite_diff(data, axis=0, dx=dx0, zero_padding=True, edge_order=2) df1 = finite_diff(data, axis=1, dx=dx1, zero_padding=True, edge_order=2) assert all_equal(df0 + df1, div_dom_vec.asarray()) # Adjoint operator adj_div = div.adjoint ran_vec = div.range.element(data ** 2) adj_div_ran_vec = adj_div(ran_vec) # Adjoint condition lhs = ran_vec.inner(div_dom_vec) rhs = dom_vec.inner(adj_div_ran_vec) assert lhs != 0 assert rhs != 0 assert almost_equal(lhs, rhs) # Higher dimensional arrays for ndim in range(1, 6): # DiscreteLp Vector lin_size = 3 discr_space = uniform_discr([0.] * ndim, [lin_size] * ndim, [lin_size] * ndim) # Divergence div = DiscreteDivergence(discr_space) dom_vec = div.domain.element([ndvolume(lin_size, ndim)] * ndim) div(dom_vec)
def stir_projector_from_file(volume_file, projection_file): """Create a STIR projector from given template files. Parameters ---------- volume_file : `str` Full file path to the STIR input file containing information on the volume. This is usually a '.hv' file. For STIR reasons, a '.v' file is also needed. projection_file : `str` Full file path to the STIR input file with information on the projection data. This is usually a '.hs' file. For STIR reasons, a '.s' file is also needed. Returns ------- projector : `ForwardProjectorByBinWrapper` A STIR forward projector. """ volume = stir.FloatVoxelsOnCartesianGrid.read_from_file(volume_file) proj_data_in = stir.ProjData.read_from_file(projection_file) proj_data = stir.ProjDataInMemory(proj_data_in.get_exam_info(), proj_data_in.get_proj_data_info()) origin = volume.get_origin() grid_spacing = volume.get_grid_spacing() grid_shape = [ volume.get_z_size(), volume.get_y_size(), volume.get_x_size() ] min_corner = [origin[1], origin[2], origin[3]] max_corner = [ origin[1] + grid_spacing[1] * grid_shape[0], origin[2] + grid_spacing[2] * grid_shape[1], origin[3] + grid_spacing[3] * grid_shape[2] ] # reverse to handle STIR bug? See: # https://github.com/UCL/STIR/issues/7 recon_sp = uniform_discr(min_corner, max_corner, grid_shape, dtype='float32') # TODO: set correct projection space. Currently, a default grid with # stride (1, 1, 1) is used. proj_shape = proj_data.to_array().shape() data_sp = uniform_discr([0, 0, 0], proj_shape, proj_shape, dtype='float32') return ForwardProjectorByBinWrapper(recon_sp, data_sp, volume, proj_data)
def test_discrete_gradient_cuda(): """Discretized spatial gradient operator using CUDA.""" # Check result of operator with explicit summation # phantom data data = np.array([[0., 1., 2., 3., 4.], [1., 2., 3., 4., 5.], [2., 3., 4., 5., 6.]]) # DiscreteLp Vector discr_space = uniform_discr([0, 0], [6, 2.5], data.shape, impl='cuda') dom_vec = discr_space.element(data) # computation of gradient components with helper function dx0, dx1 = discr_space.grid.stride df0 = finite_diff(data, axis=0, dx=dx0, zero_padding=True, edge_order=2) df1 = finite_diff(data, axis=1, dx=dx1, zero_padding=True, edge_order=2) # gradient grad = DiscreteGradient(discr_space) grad_vec = grad(dom_vec) assert len(grad_vec) == data.ndim assert all_equal(grad_vec[0].asarray(), df0) assert all_equal(grad_vec[1].asarray(), df1) # adjoint operator ran_vec = grad.range.element([data, data ** 2]) adj_vec = grad.adjoint(ran_vec) lhs = ran_vec.inner(grad_vec) rhs = dom_vec.inner(adj_vec) assert lhs != 0 assert rhs != 0 assert lhs == rhs
def test_discr_part_deriv_cuda(): """Discretized partial derivative using CUDA.""" # phantom data data = np.array([0., 1., 2., 3., 4., 16., 25., 36.]) # explicit calculation of finite difference dfe = np.zeros_like(data) # interior: second-order accurate differences dfe[1:-1] = (data[2:] - data[:-2]) / 2.0 # boundary: second-order accurate central differences with zero padding dfe[0] = data[1] / 2.0 dfe[-1] = -data[-2] / 2.0 # discretized space using CUDA discr_space = uniform_discr(0, data.size, data.shape, impl='cuda') # operator partial = DiscretePartDeriv(discr_space, zero_padding=True) # discretized space vector discr_vec = partial.domain.element(data) # apply operator partial_vec = partial(discr_vec) assert all_equal(partial_vec, dfe)
def __init__(self, min_pt=None, max_pt=None, return_val='mu_normed'): """Construct the ellipses dataset. Parameters ---------- return_val : str, optional Values to return. Options are ``'mu_normed'`` Normalized linear attenuation, values are in [0, 1]. ``'mu'`` Linear attenuation in m^-1, values are in ~[-0.46, 81.4]. ``'hu'`` Values in Hounsfield unit in [-1024, 3071]. The default is ``'mu_normed'``. min_pt : [int, int], optional Minimum values of the lp space. Default: [-181, -181]. max_pt : [int, int], optional Maximum values of the lp space. Default: [181, 181]. """ self.return_val = return_val self.shape = (362, 362) if min_pt is None: min_pt = [-self.shape[0] / 2, -self.shape[1] / 2] if max_pt is None: max_pt = [self.shape[0] / 2, self.shape[1] / 2] space = uniform_discr(min_pt, max_pt, self.shape, dtype=np.float32) with open(FILE_LIST_FILE, 'r') as json_file: self.dcm_files_dict = json.load(json_file) self.train_len = len(self.dcm_files_dict['train']) self.validation_len = len(self.dcm_files_dict['validation']) self.test_len = len(self.dcm_files_dict['test']) super().__init__(space=space)
def test_discrete_divergence_cuda(): """Discretized spatial divergence operator using CUDA.""" # Check result of operator with explicit summation # phantom data data = np.array([[0.0, 1.0, 2.0, 3.0, 4.0], [1.0, 2.0, 3.0, 4.0, 5.0], [2.0, 3.0, 4.0, 5.0, 6.0]]) # DiscreteLp discr_space = uniform_discr([0, 0], [1.5, 10], data.shape, impl="cuda") # operator instance div = DiscreteDivergence(discr_space) # apply operator dom_vec = div.domain.element([data, data]) div_dom_vec = div(dom_vec) # computation of divergence with helper function dx0, dx1 = discr_space.grid.stride df0 = finite_diff(data, axis=0, dx=dx0, zero_padding=True, edge_order=2) df1 = finite_diff(data, axis=1, dx=dx1, zero_padding=True, edge_order=2) assert all_equal(df0 + df1, div_dom_vec.asarray()) # Adjoint operator adj_div = div.adjoint ran_vec = div.range.element(data ** 2) adj_div_ran_vec = adj_div(ran_vec) # Adjoint condition lhs = ran_vec.inner(div_dom_vec) rhs = dom_vec.inner(adj_div_ran_vec) assert lhs != 0 assert rhs != 0 assert almost_equal(lhs, rhs)
def stir_projector_from_file(volume_file, projection_file): """Create a STIR projector from given template files. Parameters ---------- volume_file : `str` Full file path to the STIR input file containing information on the volume. This is usually a '.hv' file. For STIR reasons, a '.v' file is also needed. projection_file : `str` Full file path to the STIR input file with information on the projection data. This is usually a '.hs' file. For STIR reasons, a '.s' file is also needed. Returns ------- projector : `ForwardProjectorByBinWrapper` A STIR forward projector. """ volume = stir.FloatVoxelsOnCartesianGrid.read_from_file(volume_file) proj_data_in = stir.ProjData.read_from_file(projection_file) proj_data = stir.ProjDataInMemory(proj_data_in.get_exam_info(), proj_data_in.get_proj_data_info()) origin = volume.get_origin() grid_spacing = volume.get_grid_spacing() grid_shape = [volume.get_z_size(), volume.get_y_size(), volume.get_x_size()] min_corner = [origin[1], origin[2], origin[3]] max_corner = [origin[1] + grid_spacing[1] * grid_shape[0], origin[2] + grid_spacing[2] * grid_shape[1], origin[3] + grid_spacing[3] * grid_shape[2]] # reverse to handle STIR bug? See: # https://github.com/UCL/STIR/issues/7 recon_sp = uniform_discr(min_corner, max_corner, grid_shape, dtype='float32') # TODO: set correct projection space. Currently, a default grid with # stride (1, 1, 1) is used. proj_shape = proj_data.to_array().shape() data_sp = uniform_discr([0, 0, 0], proj_shape, proj_shape, dtype='float32') return ForwardProjectorByBinWrapper(recon_sp, data_sp, volume, proj_data)
def test_discr_part_deriv(): """Discretized partial derivative.""" discr_space = Rn(1) with pytest.raises(TypeError): DiscretePartDeriv(discr_space) # phantom data data = np.array([[0., 1., 2., 3., 4.], [1., 2., 3., 4., 5.], [2., 3., 4., 5., 6.]]) # explicit calculation of finite difference # axis: 0 dfe0 = np.zeros_like(data) # interior: second-order accurate differences dfe0[1:-1, :] = (data[2:, :] - data[:-2, :]) / 2.0 # boundary: second-order accurate central differences with zero padding dfe0[0, :] = data[1, :] / 2.0 dfe0[-1, :] = -data[-2, :] / 2.0 # axis: 1 dfe1 = np.zeros_like(data) # interior: second-order accurate differences dfe1[:, 1:-1] = (data[:, 2:] - data[:, :-2]) / 2.0 # boundary: second-order accurate central differences with zero padding dfe1[:, 0] = data[:, 1] / 2.0 dfe1[:, -1] = -data[:, -2] / 2.0 # assert `dfe0` and `dfe1` do differ assert (dfe0 != dfe1).any() # discretized space discr_space = uniform_discr([0, 0], [2, 1], data.shape) # operator partial_0 = DiscretePartDeriv(discr_space, axis=0, zero_padding=True) partial_1 = DiscretePartDeriv(discr_space, axis=1, zero_padding=True) # discretized space vector vec = partial_0.domain.element(data) # partial derivative partial_vec_0 = partial_0(vec) partial_vec_1 = partial_1(vec) assert partial_vec_0 != partial_vec_1 assert all_equal(partial_vec_0.asarray(), dfe0) assert all_equal(partial_vec_1.asarray(), dfe1) # operator partial_0 = DiscretePartDeriv(discr_space, axis=1, dx=0.2, edge_order=2, zero_padding=True) # adjoint not implemented with pytest.raises(NotImplementedError): partial_0.adjoint
def __init__(self, image_size=128, min_pt=None, max_pt=None, train_len=32000, validation_len=3200, test_len=3200, fixed_seeds=False): """ Parameters ---------- image_size : int, optional Number of pixels per image dimension. Default: ``128``. min_pt : [int, int], optional Minimum values of the lp space. Default: ``[-image_size/2, -image_size/2]``. max_pt : [int, int], optional Maximum values of the lp space. Default: ``[image_size/2, image_size/2]``. train_len : int or `None`, optional Length of training set. Default: ``32000``. If `None`, infinitely many samples could be generated. validation_len : int, optional Length of training set. Default: ``3200``. test_len : int, optional Length of test set. Default: ``3200``. fixed_seeds : dict or bool, optional Seeds to use for random generation. The values of the keys ``'train'``, ``'validation'`` and ``'test'`` are used. If a seed is `None` or omitted, it is choosen randomly. If ``True`` is passed, the seeds ``fixed_seeds={'train': 42, 'validation': 2, 'test': 1}`` are used. If ``False`` is passed (the default), all seeds are chosen randomly. """ self.shape = (image_size, image_size) if min_pt is None: min_pt = [-self.shape[0] / 2, -self.shape[1] / 2] if max_pt is None: max_pt = [self.shape[0] / 2, self.shape[1] / 2] space = uniform_discr(min_pt, max_pt, self.shape, dtype=np.float32) self.train_len = train_len self.validation_len = validation_len self.test_len = test_len self.random_access = False if isinstance(fixed_seeds, bool): if fixed_seeds: self.fixed_seeds = {'train': 42, 'validation': 2, 'test': 1} else: self.fixed_seeds = {} else: self.fixed_seeds = fixed_seeds.copy() super().__init__(space=space)
def uniform_discr_element(inp, space=None): """Generate an element of a ODL space from an array-like. Parameters ---------- inp : array-like The input data from which the element is generated. space : :class:`odl.discr.DiscretizedSpace`, optional The space which the element will belong to. If not given, a uniform discretization space with cell size 1 centered around the origin is generated. """ inp = np.asarray(inp) if space is None: space = uniform_discr(-np.array(inp.shape) / 2, np.array(inp.shape) / 2, inp.shape) element = space.element(inp) return element
def __init__(self, min_pt=None, max_pt=None): """Construct the ellipses dataset. Parameters ---------- min_pt : [int, int], optional Minimum values of the lp space. Default: [-64, -64]. max_pt : [int, int], optional Maximum values of the lp space. Default: [64, 64]. """ self.shape = (128, 128) if min_pt is None: min_pt = [-self.shape[0] / 2, -self.shape[1] / 2] if max_pt is None: max_pt = [self.shape[0] / 2, self.shape[1] / 2] space = uniform_discr(min_pt, max_pt, self.shape, dtype=np.float32) self.train_len = 50000 self.validation_len = 5000 self.test_len = 5000 super().__init__(space=space)
def __init__(self, min_pt=None, max_pt=None, observation_model='post-log', min_photon_count=None, impl='astra_cuda'): """ Parameters ---------- min_pt : [float, float], optional Minimum values of the lp space. Default: ``[-0.13, -0.13]``. max_pt : [float, float], optional Maximum values of the lp space. Default: ``[0.13, 0.13]``. observation_model : {'post-log', 'pre-log'}, optional The observation model to use. The default is ``'post-log'``. ``'post-log'`` Observations are linearly related to the normalized ground truth via the ray transform, ``obs = ray_trafo(gt) + noise``. Note that the scaling of the observations matches the normalized ground truth, i.e., they are divided by the linear attenuation of 3071 HU. ``'pre-log'`` Observations are non-linearly related to the ground truth, as given by the Beer-Lambert law. The model is ``obs = exp(-ray_trafo(gt * MU(3071 HU))) + noise``, where `MU(3071 HU)` is the factor, by which the ground truth was normalized. min_photon_count : float, optional Replacement value for a simulated photon count of zero. If ``observation_model == 'post-log'``, a value greater than zero is required in order to avoid undefined values. The default is 0.1, both for ``'post-log'`` and ``'pre-log'`` model. impl : {``'skimage'``, ``'astra_cpu'``, ``'astra_cuda'``},\ optional Implementation passed to :class:`odl.tomo.RayTransform` to construct :attr:`ray_trafo`. """ global DATA_PATH NUM_ANGLES = 1000 NUM_DET_PIXELS = 513 self.shape = ((NUM_ANGLES, NUM_DET_PIXELS), (362, 362)) self.num_elements_per_sample = 2 if min_pt is None: min_pt = MIN_PT if max_pt is None: max_pt = MAX_PT domain = uniform_discr(min_pt, max_pt, self.shape[1], dtype=np.float32) if observation_model == 'post-log': self.post_log = True elif observation_model == 'pre-log': self.post_log = False else: raise ValueError("`observation_model` must be 'post-log' or " "'pre-log', not '{}'".format(observation_model)) if min_photon_count is None or min_photon_count <= 1.: self.min_photon_count = min_photon_count else: self.min_photon_count = 1. warn('`min_photon_count` changed from {} to 1.'.format( min_photon_count)) self.train_len = 35820 self.validation_len = 3522 self.test_len = 3553 self.random_access = True while not self.check_for_lodopab(): print('The LoDoPaB-CT dataset could not be found under the ' "configured path '{}'.".format( CONFIG['lodopab_dataset']['data_path'])) print('Do you want to download it now? (y: download, n: input ' 'other path)') download = input_yes_no() if download: success = download_lodopab() if not success: raise RuntimeError('lodopab dataset not available, ' 'download failed') else: print('Path to LoDoPaB dataset:') DATA_PATH = input() set_config('lodopab_dataset/data_path', DATA_PATH) self.geometry = odl.tomo.parallel_beam_geometry( domain, num_angles=NUM_ANGLES, det_shape=(NUM_DET_PIXELS, )) range_ = uniform_discr(self.geometry.partition.min_pt, self.geometry.partition.max_pt, self.shape[0], dtype=np.float32) super().__init__(space=(range_, domain)) self.ray_trafo = self.get_ray_trafo(impl=impl)