コード例 #1
0
 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)
コード例 #2
0
    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__, ))
コード例 #3
0
    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
コード例 #4
0
    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
コード例 #5
0
    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
コード例 #6
0
    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)
コード例 #7
0
    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)
コード例 #8
0
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())
コード例 #9
0
    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