def test_IndicatorBox(self): ig = ImageGeometry(10, 10) im = ig.allocate(-1) ib = IndicatorBox(lower=0) a = ib(im) numpy.testing.assert_equal(a, numpy.inf) ib = IndicatorBox(lower=-2) a = ib(im) numpy.testing.assert_array_equal(0, a) ib = IndicatorBox(lower=-5, upper=-2) a = ib(im) numpy.testing.assert_equal(a, numpy.inf)
def set_up(self, initial, operator, data, lower=None, upper=None, constraint=None): '''initialisation of the algorithm :param initial: Initial guess :param operator: Linear operator for the inverse problem :param data: Acquired data to reconstruct :param lower: Scalar specifying lower bound constraint on pixel values, default -inf :param upper: Scalar specifying upper bound constraint on pixel values, default +inf :param constraint: More general constraint, given as Function proximal method e.g. :math:`x\in[0, 1]`, :code:`IndicatorBox` to enforce box constraints Default is :code:`None`). constraint takes priority over lower and upper.''' print("{} setting up".format(self.__class__.__name__, )) self.x = initial.copy() self.operator = operator self.data = data self.r = data.copy() self.relax_par = 1.0 # Set constraints. If "constraint" is given, it should be an indicator # function, and in that case "lower" and "upper" inputs are ignored. If # "constraint" is not given, then "lower" and "upper" are looked at, # and if at least one is not None, then an IndicatorBox is set up which # provides the proximal mapping to enforce lower and upper bounds. self.constraint = constraint if constraint is None: if lower is not None or upper is not None: if lower is None: lower=-inf if upper is None: upper=inf self.constraint=IndicatorBox(lower=lower,upper=upper) # Set up scaling matrices D and M. self.M = 1/self.operator.direct(self.operator.domain_geometry().allocate(value=1.0)) self.D = 1/self.operator.adjoint(self.operator.range_geometry().allocate(value=1.0)) self.configured = True print("{} configured".format(self.__class__.__name__, ))
def __init__(self, max_iteration=100, tolerance=None, correlation="Space", backend="c", lower=-np.inf, upper=np.inf, isotropic=True, split=False, info=False): super(TotalVariation, self).__init__(L=None) # Regularising parameter = alpha self.regularisation_parameter = 1. # Iterations for FGP_TV self.iterations = max_iteration # Tolerance for FGP_TV self.tolerance = tolerance # Total variation correlation (isotropic=Default) self.isotropic = isotropic # correlation space or spacechannels self.correlation = correlation self.backend = backend # Define orthogonal projection onto the convex set C self.lower = lower self.upper = upper self.tmp_proj_C = IndicatorBox(lower, upper).proximal # Setup GradientOperator as None. This is to avoid domain argument in the __init__ self._gradient = None self._domain = None self.pptmp = None self.pptmp1 = None # Print stopping information (iterations and tolerance error) of FGP_TV self.info = info # splitting Gradient self.split = split
def __init__(self, max_iteration=100, tolerance = None, correlation = "Space", backend = "c", lower = -numpy.inf, upper = numpy.inf, info = False): super(TotalVariation, self).__init__(L = None) # Regularising parameter = alpha self.regularisation_parameter = 1. # Iterations for FGP_TV self.iterations = max_iteration # Tolerance for FGP_TV self.tolerance = tolerance # Define (ISOTROPIC) Total variation penalty ( Note it is without the regularisation paremeter) # TODO add anisotropic??? self.TV = MixedL21Norm() # correlation space or spacechannels self.correlation = correlation self.backend = backend # Define orthogonal projection onto the convex set C self.lower = lower self.upper = upper self.tmp_proj_C = IndicatorBox(lower, upper).proximal # Setup GradientOperator as None. This is to avoid domain argument in the __init__ self._gradient = None self._domain = None self.pptmp = None self.pptmp1 = None # Print stopping information (iterations and tolerance error) of FGP_TV self.info = info
def test_SPDHG_vs_SPDHG_explicit_axpby(self): data = dataexample.SIMPLE_PHANTOM_2D.get(size=(128, 128)) if debug_print: print("test_SPDHG_vs_SPDHG_explicit_axpby here") ig = data.geometry ig.voxel_size_x = 0.1 ig.voxel_size_y = 0.1 detectors = ig.shape[0] angles = np.linspace(0, np.pi, 180) ag = AcquisitionGeometry('parallel', '2D', angles, detectors, pixel_size_h=0.1, angle_unit='radian') # Select device # device = input('Available device: GPU==1 / CPU==0 ') # if device=='1': # dev = 'gpu' # else: # dev = 'cpu' dev = 'cpu' Aop = AstraProjectorSimple(ig, ag, dev) sin = Aop.direct(data) # Create noisy data. Apply Gaussian noise noises = ['gaussian', 'poisson'] noise = noises[1] if noise == 'poisson': np.random.seed(10) scale = 5 eta = 0 noisy_data = AcquisitionData( np.random.poisson(scale * (eta + sin.as_array())) / scale, geometry=ag) elif noise == 'gaussian': np.random.seed(10) n1 = np.random.normal(0, 0.1, size=ag.shape) noisy_data = AcquisitionData(n1 + sin.as_array(), geometry=ag) else: raise ValueError('Unsupported Noise ', noise) #%% 'explicit' SPDHG, scalar step-sizes subsets = 10 size_of_subsets = int(len(angles) / subsets) # create GradientOperator operator op1 = GradientOperator(ig) # take angles and create uniform subsets in uniform+sequential setting list_angles = [ angles[i:i + size_of_subsets] for i in range(0, len(angles), size_of_subsets) ] # create acquisitioin geometries for each the interval of splitting angles list_geoms = [ AcquisitionGeometry('parallel', '2D', list_angles[i], detectors, pixel_size_h=0.1, angle_unit='radian') for i in range(len(list_angles)) ] # create with operators as many as the subsets A = BlockOperator(*[ AstraProjectorSimple(ig, list_geoms[i], dev) for i in range(subsets) ] + [op1]) ## number of subsets #(sub2ind, ind2sub) = divide_1Darray_equally(range(len(A)), subsets) # ## acquisisiton data ## acquisisiton data AD_list = [] for sub_num in range(subsets): for i in range(0, len(angles), size_of_subsets): arr = noisy_data.as_array()[i:i + size_of_subsets, :] AD_list.append( AcquisitionData(arr, geometry=list_geoms[sub_num])) g = BlockDataContainer(*AD_list) alpha = 0.5 ## block function F = BlockFunction(*[ *[KullbackLeibler(b=g[i]) for i in range(subsets)] + [alpha * MixedL21Norm()] ]) G = IndicatorBox(lower=0) prob = [1 / (2 * subsets)] * (len(A) - 1) + [1 / 2] algos = [] algos.append( SPDHG(f=F, g=G, operator=A, max_iteration=1000, update_objective_interval=200, prob=prob.copy(), use_axpby=True)) algos[0].run(1000, verbose=0) algos.append( SPDHG(f=F, g=G, operator=A, max_iteration=1000, update_objective_interval=200, prob=prob.copy(), use_axpby=False)) algos[1].run(1000, verbose=0) # np.testing.assert_array_almost_equal(algos[0].get_output().as_array(), algos[1].get_output().as_array()) from cil.utilities.quality_measures import mae, mse, psnr qm = (mae(algos[0].get_output(), algos[1].get_output()), mse(algos[0].get_output(), algos[1].get_output()), psnr(algos[0].get_output(), algos[1].get_output())) if debug_print: print("Quality measures", qm) assert qm[0] < 0.005 assert qm[1] < 3.e-05
def test_SPDHG_vs_PDHG_explicit(self): data = dataexample.SIMPLE_PHANTOM_2D.get(size=(128, 128)) ig = data.geometry ig.voxel_size_x = 0.1 ig.voxel_size_y = 0.1 detectors = ig.shape[0] angles = np.linspace(0, np.pi, 180) ag = AcquisitionGeometry('parallel', '2D', angles, detectors, pixel_size_h=0.1, angle_unit='radian') # Select device dev = 'cpu' Aop = AstraProjectorSimple(ig, ag, dev) sin = Aop.direct(data) # Create noisy data. Apply Gaussian noise noises = ['gaussian', 'poisson'] noise = noises[1] if noise == 'poisson': scale = 5 noisy_data = scale * applynoise.poisson(sin / scale, seed=10) # np.random.seed(10) # scale = 5 # eta = 0 # noisy_data = AcquisitionData(np.random.poisson( scale * (eta + sin.as_array()))/scale, ag) elif noise == 'gaussian': noisy_data = noise.gaussian(sin, var=0.1, seed=10) # np.random.seed(10) # n1 = np.random.normal(0, 0.1, size = ag.shape) # noisy_data = AcquisitionData(n1 + sin.as_array(), ag) else: raise ValueError('Unsupported Noise ', noise) #%% 'explicit' SPDHG, scalar step-sizes subsets = 10 size_of_subsets = int(len(angles) / subsets) # create Gradient operator op1 = GradientOperator(ig) # take angles and create uniform subsets in uniform+sequential setting list_angles = [ angles[i:i + size_of_subsets] for i in range(0, len(angles), size_of_subsets) ] # create acquisitioin geometries for each the interval of splitting angles list_geoms = [ AcquisitionGeometry('parallel', '2D', list_angles[i], detectors, pixel_size_h=0.1, angle_unit='radian') for i in range(len(list_angles)) ] # create with operators as many as the subsets A = BlockOperator(*[ AstraProjectorSimple(ig, list_geoms[i], dev) for i in range(subsets) ] + [op1]) ## number of subsets #(sub2ind, ind2sub) = divide_1Darray_equally(range(len(A)), subsets) # ## acquisisiton data ## acquisisiton data AD_list = [] for sub_num in range(subsets): for i in range(0, len(angles), size_of_subsets): arr = noisy_data.as_array()[i:i + size_of_subsets, :] AD_list.append( AcquisitionData(arr, geometry=list_geoms[sub_num])) g = BlockDataContainer(*AD_list) alpha = 0.5 ## block function F = BlockFunction(*[ *[KullbackLeibler(b=g[i]) for i in range(subsets)] + [alpha * MixedL21Norm()] ]) G = IndicatorBox(lower=0) prob = [1 / (2 * subsets)] * (len(A) - 1) + [1 / 2] spdhg = SPDHG(f=F, g=G, operator=A, max_iteration=1000, update_objective_interval=200, prob=prob) spdhg.run(1000, verbose=0) #%% 'explicit' PDHG, scalar step-sizes op1 = GradientOperator(ig) op2 = Aop # Create BlockOperator operator = BlockOperator(op1, op2, shape=(2, 1)) f2 = KullbackLeibler(b=noisy_data) g = IndicatorBox(lower=0) normK = operator.norm() sigma = 1 / normK tau = 1 / normK f1 = alpha * MixedL21Norm() f = BlockFunction(f1, f2) # Setup and run the PDHG algorithm pdhg = PDHG(f=f, g=g, operator=operator, tau=tau, sigma=sigma) pdhg.max_iteration = 1000 pdhg.update_objective_interval = 200 pdhg.run(1000, verbose=0) #%% show diff between PDHG and SPDHG # plt.imshow(spdhg.get_output().as_array() -pdhg.get_output().as_array()) # plt.colorbar() # plt.show() from cil.utilities.quality_measures import mae, mse, psnr qm = (mae(spdhg.get_output(), pdhg.get_output()), mse(spdhg.get_output(), pdhg.get_output()), psnr(spdhg.get_output(), pdhg.get_output())) if debug_print: print("Quality measures", qm) np.testing.assert_almost_equal(mae(spdhg.get_output(), pdhg.get_output()), 0.00150, decimal=3) np.testing.assert_almost_equal(mse(spdhg.get_output(), pdhg.get_output()), 1.68590e-05, decimal=3)
def test_PDHG_vs_PDHG_explicit_axpby(self): data = dataexample.SIMPLE_PHANTOM_2D.get(size=(128, 128)) if debug_print: print("test_PDHG_vs_PDHG_explicit_axpby here") ig = data.geometry ig.voxel_size_x = 0.1 ig.voxel_size_y = 0.1 detectors = ig.shape[0] angles = np.linspace(0, np.pi, 180) ag = AcquisitionGeometry('parallel', '2D', angles, detectors, pixel_size_h=0.1, angle_unit='radian') dev = 'cpu' Aop = AstraProjectorSimple(ig, ag, dev) sin = Aop.direct(data) # Create noisy data. Apply Gaussian noise noises = ['gaussian', 'poisson'] noise = noises[1] if noise == 'poisson': np.random.seed(10) scale = 5 eta = 0 noisy_data = AcquisitionData( np.random.poisson(scale * (eta + sin.as_array())) / scale, geometry=ag) elif noise == 'gaussian': np.random.seed(10) n1 = np.random.normal(0, 0.1, size=ag.shape) noisy_data = AcquisitionData(n1 + sin.as_array(), geometry=ag) else: raise ValueError('Unsupported Noise ', noise) alpha = 0.5 op1 = GradientOperator(ig) op2 = Aop # Create BlockOperator operator = BlockOperator(op1, op2, shape=(2, 1)) f2 = KullbackLeibler(b=noisy_data) g = IndicatorBox(lower=0) normK = operator.norm() sigma = 1. / normK tau = 1. / normK f1 = alpha * MixedL21Norm() f = BlockFunction(f1, f2) # Setup and run the PDHG algorithm algos = [] algos.append( PDHG(f=f, g=g, operator=operator, tau=tau, sigma=sigma, max_iteration=1000, update_objective_interval=200, use_axpby=True)) algos[0].run(1000, verbose=0) algos.append( PDHG(f=f, g=g, operator=operator, tau=tau, sigma=sigma, max_iteration=1000, update_objective_interval=200, use_axpby=False)) algos[1].run(1000, verbose=0) from cil.utilities.quality_measures import mae, mse, psnr qm = (mae(algos[0].get_output(), algos[1].get_output()), mse(algos[0].get_output(), algos[1].get_output()), psnr(algos[0].get_output(), algos[1].get_output())) if debug_print: print("Quality measures", qm) np.testing.assert_array_less(qm[0], 0.005) np.testing.assert_array_less(qm[1], 3e-05)
class SIRT(Algorithm): r'''Simultaneous Iterative Reconstruction Technique Problem: .. math:: A x = b :param initial: Initial guess :param operator: Linear operator for the inverse problem :param data: Acquired data to reconstruct :param constraint: Function proximal method e.g. :math:`x\in[0, 1]`, :code:`IndicatorBox` to enforce box constraints Default is :code:`None`). ''' def __init__(self, initial=None, operator=None, data=None, lower=None, upper=None, constraint=None, **kwargs): '''SIRT algorithm creator Optional parameters: :param initial: Initial guess :param operator: Linear operator for the inverse problem :param data: Acquired data to reconstruct :param lower: Scalar specifying lower bound constraint on pixel values, default -inf :param upper: Scalar specifying upper bound constraint on pixel values, default +inf :param constraint: More general constraint, given as Function proximal method e.g. :math:`x\in[0, 1]`, :code:`IndicatorBox` to enforce box constraints Default is :code:`None`). constraint takes priority over lower and upper.''' super(SIRT, self).__init__(**kwargs) if kwargs.get('x_init', None) is not None: if initial is None: warnings.warn('The use of the x_init parameter is deprecated and will be removed in following version. Use initial instead', DeprecationWarning, stacklevel=4) initial = kwargs.get('x_init', None) else: raise ValueError('{} received both initial and the deprecated x_init parameter. It is not clear which one we should use.'\ .format(self.__class__.__name__)) if initial is not None and operator is not None and data is not None: self.set_up(initial=initial, operator=operator, data=data, lower=lower, upper=upper, constraint=constraint) def set_up(self, initial, operator, data, lower=None, upper=None, constraint=None): '''initialisation of the algorithm :param initial: Initial guess :param operator: Linear operator for the inverse problem :param data: Acquired data to reconstruct :param lower: Scalar specifying lower bound constraint on pixel values, default -inf :param upper: Scalar specifying upper bound constraint on pixel values, default +inf :param constraint: More general constraint, given as Function proximal method e.g. :math:`x\in[0, 1]`, :code:`IndicatorBox` to enforce box constraints Default is :code:`None`). constraint takes priority over lower and upper.''' print("{} setting up".format(self.__class__.__name__, )) self.x = initial.copy() self.operator = operator self.data = data self.r = data.copy() self.relax_par = 1.0 # Set constraints. If "constraint" is given, it should be an indicator # function, and in that case "lower" and "upper" inputs are ignored. If # "constraint" is not given, then "lower" and "upper" are looked at, # and if at least one is not None, then an IndicatorBox is set up which # provides the proximal mapping to enforce lower and upper bounds. self.constraint = constraint if constraint is None: if lower is not None or upper is not None: if lower is None: lower=-inf if upper is None: upper=inf self.constraint=IndicatorBox(lower=lower,upper=upper) # Set up scaling matrices D and M. self.M = 1/self.operator.direct(self.operator.domain_geometry().allocate(value=1.0)) self.D = 1/self.operator.adjoint(self.operator.range_geometry().allocate(value=1.0)) self.configured = True print("{} configured".format(self.__class__.__name__, )) def update(self): self.r = self.data - self.operator.direct(self.x) self.x += self.relax_par * (self.D*self.operator.adjoint(self.M*self.r)) if self.constraint is not None: self.x = self.constraint.proximal(self.x, None) # self.constraint.proximal(self.x,None, out=self.x) def update_objective(self): self.loss.append(self.r.squared_norm())
cProfile.run('algo.run(1)') # %% from cil.optimisation.algorithms import PDHG from cil.optimisation.functions import MixedL21Norm, BlockFunction, L2NormSquared, IndicatorBox from cil.optimisation.operators import GradientOperator, BlockOperator nabla = GradientOperator(ig_cs, backend='c') F = BlockFunction(L2NormSquared(b=ldata), alpha * MixedL21Norm()) BK = BlockOperator(K, nabla) # normK = BK.norm() normK = 191.54791313753265 pdhg = PDHG(f=F, g=IndicatorBox(lower=0.), operator=BK, max_iteration=1000, update_objective_interval=100) #%% pdhg.run(100, verbose=2, print_interval=10) #%% plotter2D(pdhg.solution, cmap='gist_earth') # %% subsets = AcquisitionGeometrySubsetGenerator.generate_subset( ag_shift, num_subsets=8, method='stagger') print(subsets[0]) #%% from cil.optimisation.algorithms import SPDHG