Beispiel #1
0
    def __init__(self,
                 volume_geometry=None,
                 sinogram_geometry=None,
                 proj_geom=None,
                 vol_geom=None):
        kwargs = {
            'volume_geometry': volume_geometry,
            'sinogram_geometry': sinogram_geometry,
            'proj_geom': proj_geom,
            'vol_geom': vol_geom,
        }

        super(AstraForwardProjector3D, self).__init__(**kwargs)

        self.set_ImageGeometry(volume_geometry)
        self.set_AcquisitionGeometry(sinogram_geometry)

        self.vol_geom, self.proj_geom = convert_geometry_to_astra(
            self.volume_geometry, self.sinogram_geometry)
    def __init__(self,
                 volume_geometry=None,
                 sinogram_geometry=None,
                 proj_id=None,
                 device='cpu'):
        kwargs = {
            'volume_geometry': volume_geometry,
            'sinogram_geometry': sinogram_geometry,
            'proj_id': proj_id,
            'device': device
        }

        #DataProcessor.__init__(self, **kwargs)
        super(AstraForwardProjector, self).__init__(**kwargs)

        self.set_ImageGeometry(volume_geometry)
        self.set_AcquisitionGeometry(sinogram_geometry)

        # Set up ASTRA Volume and projection geometry, not to be stored in self
        vol_geom, proj_geom = convert_geometry_to_astra(
            self.volume_geometry, self.sinogram_geometry)

        # ASTRA projector, to be stored
        if device == 'cpu':
            # Note that 'line' only one option
            if self.sinogram_geometry.geom_type == 'parallel':
                self.set_projector(
                    astra.create_projector('line', proj_geom, vol_geom))
            elif self.sinogram_geometry.geom_type == 'cone':
                self.set_projector(
                    astra.create_projector('line_fanflat', proj_geom,
                                           vol_geom))
            else:
                NotImplemented
        elif device == 'gpu':
            self.set_projector(
                astra.create_projector('cuda', proj_geom, vol_geom))
        else:
            NotImplemented
    def __init__(self,
                 volume_geometry,
                 sinogram_geometry,
                 device='cpu',
                 filter_type='ram-lak',
                 **kwargs):

        print(
            "Warning: FBP will use simple geometry only. Any configuration offsets or rotations will be ignored."
        )

        if sinogram_geometry.dimension == '3D':

            # For 3D FBP and parallel goemetry, we perform 2D FBP per slice
            # Therofore, we need vol_geom2D, porj_geom2D attributes to setup in the DataProcessor.
            # There are both CPU/GPU options for this case

            if sinogram_geometry.geom_type == 'parallel':

                # By default, we select GPU device
                super(FBP_Simple,
                      self).__init__(volume_geometry=volume_geometry,
                                     sinogram_geometry=sinogram_geometry,
                                     device='gpu',
                                     proj_id=None,
                                     vol_geom2D=None,
                                     proj_geom2D=None,
                                     filter_type=filter_type)

                # we need a 2D image and acquisition geometry
                ig2D = ImageGeometry(
                    voxel_num_x=self.volume_geometry.voxel_num_x,
                    voxel_num_y=self.volume_geometry.voxel_num_y,
                    voxel_size_x=self.volume_geometry.voxel_size_x,
                    voxel_size_y=self.volume_geometry.voxel_size_y)

                ag2D = AcquisitionGeometry(
                    geom_type=self.sinogram_geometry.geom_type,
                    dimension='2D',
                    angles=self.sinogram_geometry.angles,
                    pixel_num_h=self.sinogram_geometry.pixel_num_h,
                    pixel_size_h=self.sinogram_geometry.pixel_size_h)

                # Convert to Astra Geometries
                self.vol_geom2D, self.proj_geom2D = convert_geometry_to_astra(
                    ig2D, ag2D)

                if self.device == 'gpu':
                    self.set_projector(
                        astra.create_projector('cuda', self.proj_geom2D,
                                               self.vol_geom2D))
                else:
                    self.set_projector(
                        astra.create_projector('line', self.proj_geom2D,
                                               self.vol_geom2D))

            elif sinogram_geometry.geom_type == 'cone':

                # For 3D cone we do not need  ASTRA proj_id
                super(FBP_Simple,
                      self).__init__(volume_geometry=volume_geometry,
                                     sinogram_geometry=sinogram_geometry,
                                     device='gpu',
                                     filter_type='ram-lak')

                #Raise an error if the user select a filter other than ram-lak
                if self.filter_type != 'ram-lak':
                    raise NotImplementedError(
                        'Currently in astra, FDK has only ram-lak available')
        else:

            # Setup for 2D case
            super(FBP_Simple,
                  self).__init__(volume_geometry=volume_geometry,
                                 sinogram_geometry=sinogram_geometry,
                                 device=device,
                                 proj_id=None,
                                 filter_type=filter_type)

            # Raise an error if the user select a filter other than ram-lak, for 2D case
            if self.filter_type != 'ram-lak' and self.device == 'cpu':
                raise NotImplementedError(
                    'Currently in astra, 2D FBP is using only the ram-lak filter, switch to gpu for other filters'
                )

            if (self.sinogram_geometry.geom_type == 'cone' and self.device
                    == 'gpu') and self.sinogram_geometry.channels >= 1:
                warnings.warn(
                    "For FanFlat geometry there is a mismatch between CPU & GPU filtered back projection. Currently, only CPU case provides a reasonable result."
                )

            self.set_ImageGeometry(volume_geometry)
            self.set_AcquisitionGeometry(sinogram_geometry)

            # Set up ASTRA Volume and projection geometry, not to be stored in self
            vol_geom, proj_geom = convert_geometry_to_astra(
                self.volume_geometry, self.sinogram_geometry)

            # Here we define proj_id that will be used for ASTRA
            # ASTRA projector, to be stored
            if self.device == 'cpu':

                # Note that 'line' only one option
                if self.sinogram_geometry.geom_type == 'parallel':
                    self.set_projector(
                        astra.create_projector('line', proj_geom, vol_geom))

                elif self.sinogram_geometry.geom_type == 'cone':
                    self.set_projector(
                        astra.create_projector('line_fanflat', proj_geom,
                                               vol_geom))

                else:
                    NotImplemented

            elif self.device == 'gpu':

                self.set_projector(
                    astra.create_projector('cuda', proj_geom, vol_geom))

            else:
                NotImplemented
Beispiel #4
0
    def test_convert_geometry_to_astra(self):

        #2D parallel radians
        astra_vol, astra_sino = convert_geometry_to_astra(self.ig, self.ag)

        self.assertEqual(astra_sino['type'], 'parallel')
        self.assertEqual(astra_sino['DetectorCount'], self.ag.pixel_num_h)
        self.assertEqual(astra_sino['DetectorWidth'], self.ag.pixel_size_h)
        np.testing.assert_allclose(astra_sino['ProjectionAngles'],
                                   self.ag.angles)

        self.assertEqual(astra_vol['GridColCount'], self.ig.voxel_num_x)
        self.assertEqual(astra_vol['GridRowCount'], self.ig.voxel_num_y)
        self.assertEqual(astra_vol['option']['WindowMinX'],
                         -self.ig.voxel_num_x * self.ig.voxel_size_x * 0.5)
        self.assertEqual(astra_vol['option']['WindowMaxX'],
                         self.ig.voxel_num_x * self.ig.voxel_size_x * 0.5)
        self.assertEqual(astra_vol['option']['WindowMinY'],
                         -self.ig.voxel_num_y * self.ig.voxel_size_y * 0.5)
        self.assertEqual(astra_vol['option']['WindowMaxY'],
                         self.ig.voxel_num_y * self.ig.voxel_size_y * 0.5)

        #2D parallel degrees
        astra_vol, astra_sino = convert_geometry_to_astra(self.ig, self.ag_deg)
        np.testing.assert_allclose(astra_sino['ProjectionAngles'],
                                   self.ag.angles)

        #2D cone
        astra_vol, astra_sino = convert_geometry_to_astra(
            self.ig, self.ag_cone)

        self.assertEqual(astra_sino['type'], 'fanflat')
        self.assertEqual(astra_sino['DistanceOriginSource'],
                         -1 * self.ag_cone.dist_source_center)
        self.assertTrue(astra_sino['DistanceOriginDetector'],
                        -1 * self.ag_cone.dist_center_detector)

        #3D parallel
        astra_vol, astra_sino = convert_geometry_to_astra(self.ig3, self.ag3)
        self.assertEqual(astra_sino['type'], 'parallel3d')
        self.assertEqual(astra_sino['DetectorColCount'], self.ag3.pixel_num_h)
        self.assertEqual(astra_sino['DetectorRowCount'], self.ag3.pixel_num_v)
        self.assertEqual(astra_sino['DetectorSpacingX'], self.ag3.pixel_size_h)
        self.assertEqual(astra_sino['DetectorSpacingY'], self.ag3.pixel_size_h)
        np.testing.assert_allclose(astra_sino['ProjectionAngles'],
                                   -self.ag3.angles)

        self.assertEqual(astra_vol['GridColCount'], self.ig3.voxel_num_x)
        self.assertEqual(astra_vol['GridRowCount'], self.ig3.voxel_num_y)
        self.assertEqual(astra_vol['GridSliceCount'], self.ig3.voxel_num_z)

        self.assertEqual(astra_vol['option']['WindowMinX'],
                         -self.ig3.voxel_num_x * self.ig3.voxel_size_x * 0.5)
        self.assertEqual(astra_vol['option']['WindowMaxX'],
                         self.ig3.voxel_num_x * self.ig3.voxel_size_x * 0.5)
        self.assertEqual(astra_vol['option']['WindowMinY'],
                         -self.ig3.voxel_num_y * self.ig3.voxel_size_y * 0.5)
        self.assertEqual(astra_vol['option']['WindowMaxY'],
                         self.ig3.voxel_num_y * self.ig3.voxel_size_y * 0.5)
        self.assertEqual(astra_vol['option']['WindowMinZ'],
                         -self.ig3.voxel_num_z * self.ig3.voxel_size_z * 0.5)
        self.assertEqual(astra_vol['option']['WindowMaxZ'],
                         self.ig3.voxel_num_z * self.ig3.voxel_size_z * 0.5)

        #3D cone
        astra_vol, astra_sino = convert_geometry_to_astra(
            self.ig3, self.ag3_cone)
        self.assertEqual(astra_sino['type'], 'cone')
        self.assertEqual(astra_sino['DistanceOriginSource'],
                         self.ag_cone.dist_source_center)
        self.assertEqual(astra_sino['DistanceOriginDetector'],
                         self.ag_cone.dist_center_detector)
        self.assertEqual(astra_sino['DetectorColCount'], self.ag3.pixel_num_h)
        self.assertEqual(astra_sino['DetectorRowCount'], self.ag3.pixel_num_v)
        self.assertEqual(astra_sino['DetectorSpacingX'], self.ag3.pixel_size_h)
        self.assertEqual(astra_sino['DetectorSpacingY'], self.ag3.pixel_size_h)
        np.testing.assert_allclose(astra_sino['ProjectionAngles'],
                                   -self.ag3.angles)
    def process(self):

        # Get DATA
        DATA = self.get_input()

        # Allocate FBP reconstruction
        IM = self.volume_geometry.allocate()

        # Set up ASTRA Volume and projection geometry, not to be stored in self
        vol_geom, proj_geom = convert_geometry_to_astra(
            self.volume_geometry, self.sinogram_geometry)
        # Get number of channels
        num_chan = DATA.geometry.channels

        #######################################################################
        #######################################################################
        #######################################################################

        # Run FBP for one channel data
        if num_chan == 1:

            if self.sinogram_geometry.dimension == '2D':

                # Create a data object for the reconstruction and to hold sinogram for 2D
                rec_id = astra.data2d.create('-vol', vol_geom)
                sinogram_id = astra.data2d.create('-sino', proj_geom,
                                                  DATA.as_array())

                # ASTRA configuration for reconstruction algorithm
                if self.device == 'cpu':
                    cfg = astra.astra_dict('FBP')
                    cfg['ProjectorId'] = self.proj_id
                else:
                    cfg = astra.astra_dict('FBP_CUDA')
                cfg['ReconstructionDataId'] = rec_id
                cfg['ProjectionDataId'] = sinogram_id
                cfg['FilterType'] = self.filter_type
                alg_id = astra.algorithm.create(cfg)
                astra.algorithm.run(alg_id)

                # Get the result
                IM.array = astra.data2d.get(rec_id)
                astra.data2d.delete(rec_id)
                astra.data2d.delete(sinogram_id)
                astra.algorithm.delete(alg_id)

                return IM

            elif self.sinogram_geometry.dimension == '3D':

                # Here we use 2D geometries to run FBP for parallel geometry for each slice
                if self.sinogram_geometry.geom_type == 'parallel':

                    rec_id = astra.data2d.create('-vol', self.vol_geom2D)

                    for i in range(DATA.shape[0]):

                        sinogram_id = astra.data2d.create(
                            '-sino', self.proj_geom2D,
                            DATA.as_array()[i])

                        cfg = astra.astra_dict('FBP_CUDA')
                        cfg['ReconstructionDataId'] = rec_id
                        cfg['ProjectionDataId'] = sinogram_id
                        cfg['FilterType'] = self.filter_type
                        alg_id = astra.algorithm.create(cfg)
                        astra.algorithm.run(alg_id)

                        # since we run gpu we need to rescale and flip it
                        IM.array[i] = np.flip(
                            astra.data2d.get(rec_id) *
                            self.volume_geometry.voxel_size_x, 0)

                        astra.algorithm.delete(alg_id)
                        astra.data2d.delete(sinogram_id)

                    astra.data2d.delete(rec_id)

                    return IM

                elif self.sinogram_geometry.geom_type == 'cone':

                    rec_id = astra.data3d.create('-vol', vol_geom)
                    sinogram_id = astra.data3d.create('-sino', proj_geom,
                                                      DATA.as_array())

                    cfg = astra.astra_dict('FDK_CUDA')
                    cfg['ReconstructionDataId'] = rec_id
                    cfg['ProjectionDataId'] = sinogram_id
                    alg_id = astra.algorithm.create(cfg)
                    astra.algorithm.run(alg_id)
                    IM.array = astra.data3d.get(rec_id)
                    astra.data3d.delete(rec_id)
                    astra.data3d.delete(sinogram_id)
                    astra.algorithm.delete(alg_id)
                    return IM / (self.volume_geometry.voxel_size_x**4)

        #######################################################################
        #######################################################################
        #######################################################################

        # Here we preform FBP for each channel for the 2D/3D cases
        else:

            if self.sinogram_geometry.dimension == '2D':

                if self.device == 'cpu':
                    cfg = astra.astra_dict('FBP')
                    cfg['ProjectorId'] = self.proj_id
                else:
                    cfg = astra.astra_dict('FBP_CUDA')
                cfg['FilterType'] = self.filter_type

                for i in range(num_chan):

                    sinogram_id = astra.data2d.create('-sino', proj_geom,
                                                      DATA.as_array()[i])
                    rec_id = astra.data2d.create('-vol', vol_geom)

                    cfg['ReconstructionDataId'] = rec_id
                    cfg['ProjectionDataId'] = sinogram_id

                    alg_id = astra.algorithm.create(cfg)
                    astra.algorithm.run(alg_id)

                    # Get the result
                    IM.array[i] = astra.data2d.get(rec_id)

                    astra.algorithm.delete(alg_id)
                    astra.data2d.delete(rec_id)
                    astra.data2d.delete(sinogram_id)

                    if self.device == 'cpu':
                        IM.array[i] /= (self.volume_geometry.voxel_size_x**2)
                    else:
                        IM.array[i] *= self.volume_geometry.voxel_size_x
                return IM

            elif self.sinogram_geometry.dimension == '3D':

                if self.sinogram_geometry.geom_type == 'parallel':

                    cfg = astra.astra_dict('FBP_CUDA')
                    cfg['FilterType'] = self.filter_type

                    for i in range(num_chan):

                        for j in range(DATA.shape[1]):

                            sinogram_id = astra.data2d.create(
                                '-sino', self.proj_geom2D,
                                DATA.as_array()[i, j])
                            rec_id = astra.data2d.create(
                                '-vol', self.vol_geom2D)

                            cfg['ReconstructionDataId'] = rec_id
                            cfg['ProjectionDataId'] = sinogram_id

                            alg_id = astra.algorithm.create(cfg)
                            astra.algorithm.run(alg_id)

                            # Get the result
                            IM.array[i, j] = np.flip(
                                astra.data2d.get(rec_id) *
                                self.volume_geometry.voxel_size_x, 0)

                            astra.algorithm.delete(alg_id)
                            astra.data2d.delete(rec_id)
                            astra.data2d.delete(sinogram_id)

                    return IM

                elif self.sinogram_geometry.geom_type == 'cone':

                    for i in range(num_chan):

                        rec_id = astra.data3d.create('-vol', vol_geom)
                        sinogram_id = astra.data3d.create(
                            '-sino', proj_geom,
                            DATA.as_array()[i])

                        cfg = astra.astra_dict('FDK_CUDA')
                        cfg['ReconstructionDataId'] = rec_id
                        cfg['ProjectionDataId'] = sinogram_id
                        alg_id = astra.algorithm.create(cfg)
                        astra.algorithm.run(alg_id)
                        IM.array[i] = astra.data3d.get(
                            rec_id) / self.volume_geometry.voxel_size_x**4

                    astra.data3d.delete(rec_id)
                    astra.data3d.delete(sinogram_id)
                    astra.algorithm.delete(alg_id)

                    return IM