def _mesh_crystal_thin_ring(self, block_shape: List(int, 3), average_radius: int, block_size: List(float, 3), nb_blocks_per_ring: int, gap: float, d: int): if gap > 0.0: raise ValueError('Please use _mesh_crystal_ring instead') nb_crystals_per_thin_ring = block_shape[1] * nb_blocks_per_ring block_unit_size = [s1 / s2 for s1, s2 in zip(block_size, block_shape)] lors = np.zeros((nb_crystals_per_thin_ring * nb_crystals_per_thin_ring, 6), dtype = np.float32) x = np.ones(block_shape[1], ) * average_radius y = (np.arange(block_shape[1]) + 0.5) * block_unit_size[1] - \ block_size[1] / 2 z = 0.5 * block_unit_size[2] - block_unit_size[2] * (d + 1) / 2 xx = np.kron(x, [1] * nb_blocks_per_ring) yy = np.kron(y, [1] * nb_blocks_per_ring) theta = 2 * np.pi / nb_blocks_per_ring * np.arange(nb_blocks_per_ring) theta1 = np.kron(theta, [[1]] * block_shape[1]).ravel() xd = xx * np.cos(theta1) - yy * np.sin(theta1) yd = xx * np.sin(theta1) + yy * np.cos(theta1) zd = np.kron(z, [1] * block_shape[1] * nb_blocks_per_ring) lors[:, 0] = np.kron(xd, [1] * nb_crystals_per_thin_ring) lors[:, 1] = np.kron(yd, [1] * nb_crystals_per_thin_ring) lors[:, 2] = np.kron(zd, [1] * nb_crystals_per_thin_ring) lors[:, 3] = np.kron(xd, [[1]] * nb_crystals_per_thin_ring).ravel() lors[:, 4] = np.kron(yd, [[1]] * nb_crystals_per_thin_ring).ravel() lors[:, 5] = np.kron(zd, [[1]] * nb_crystals_per_thin_ring).ravel() + d * \ block_unit_size[2] return lors
def __call__(self, listmodes: List(Listmode), dvfs: List(Dvf)) -> Image: declare_eager_execution() x = self.emap.update(data=np.ones(self.emap.shape, dtype=np.float32)) x_tf = x.update(data=tf.Variable(x.data)) emap_tf = self.emap.update(data=tf.constant(self.emap.data)) for _ in tqdm(range(self.n_iter)): for listmode, dvf in zip(listmodes, dvfs): dvf_x_tf = tf.constant(dvf.dvf_x.data) dvf_y_tf = tf.constant(dvf.dvf_y.data) dvf_z_tf = tf.constant(dvf.dvf_z.data) lors_tf = listmode.lors.update( data=tf.constant(listmode.lors.data)) listmode_tf = listmode.update(data=tf.constant(listmode.data), lors=lors_tf) _x_tf = x_tf.update(data=self._deform_invert_tf( x_tf.data, dvf_x_tf, dvf_y_tf, dvf_z_tf)) _listmode_tf = Project('tf')(_x_tf, lors_tf) _listmode_tf = _listmode_tf + 1e-8 _bp = BackProject('tf')(listmode_tf / _listmode_tf, emap_tf) emap_tf2 = emap_tf.update(data=self._deform_tf( emap_tf.data, dvf_x_tf, dvf_y_tf, dvf_z_tf)) + 1e-8 _bp2 = _bp.update(data=self._deform_tf(_bp.data, dvf_x_tf, dvf_y_tf, dvf_z_tf)) x_tf = x_tf * _bp2 / emap_tf2 return x_tf.update(data=x_tf.data.numpy())
def _mesh_crystal_full(self, nb_rings: int, block_shape: List(int, 3), average_radius: int, block_size: List(float, 3), nb_blocks_per_ring: int): if nb_rings > 1: raise ValueError('Please use _mesh_crystal_ring instead') block_unit_size = [s1 / s2 for s1, s2 in zip(block_size, block_shape)] nb_crystals_per_ring = nb_blocks_per_ring * block_shape[1] * block_shape[2] nb_crystals = nb_crystals_per_ring * nb_rings lors = np.zeros((nb_crystals * nb_crystals, 6), dtype = np.float32) x = np.ones(block_shape[1], ) * average_radius y = (np.arange(block_shape[1]) + 0.5) * block_unit_size[1] - \ block_size[1] / 2 z = (np.arange(block_shape[2]) + 0.5) * block_unit_size[2] - \ block_size[2] / 2 xx = np.kron(x, [1] * nb_blocks_per_ring) yy = np.kron(y, [1] * nb_blocks_per_ring) theta = 2 * np.pi / nb_blocks_per_ring * np.arange(nb_blocks_per_ring) theta1 = np.kron(theta, [[1]] * block_shape[1]).ravel() xx1 = xx * np.cos(theta1) - yy * np.sin(theta1) yy1 = xx * np.sin(theta1) + yy * np.cos(theta1) xd = np.kron(xx1, [[1]] * block_shape[2]).ravel() yd = np.kron(yy1, [[1]] * block_shape[2]).ravel() zd = np.kron(z, [1] * block_shape[1] * nb_blocks_per_ring) lors[:, 0] = np.kron(xd, [1] * nb_crystals_per_ring) lors[:, 1] = np.kron(yd, [1] * nb_crystals_per_ring) lors[:, 2] = np.kron(zd, [1] * nb_crystals_per_ring) lors[:, 3] = np.kron(xd, [[1]] * nb_crystals_per_ring).ravel() lors[:, 4] = np.kron(yd, [[1]] * nb_crystals_per_ring).ravel() lors[:, 5] = np.kron(zd, [[1]] * nb_crystals_per_ring).ravel() return lors
def __call__(self, images: List(Image), fov_centers: List(float)) -> Image: '''sort images according to their fov centers''' if len(images) == 1: return images[0] border_left = np.min( [image.center[2] - image.size[2] / 2 for image in images]) border_right = np.max( [image.center[2] + image.size[2] / 2 for image in images]) unit_size_z = images[0].unit_size[2] length = border_right - border_left shape_z = int(np.round(length / unit_size_z)) size_z = shape_z * unit_size_z center_z = (border_left + border_right) / 2 shape = images[0].shape[:2] + [shape_z] center = images[0].center[:2] + [center_z] size = images[0].size[:2] + [size_z] trans = (np.array(fov_centers[1:]) + np.array(fov_centers[:-1])) / 2 trans_ind = np.round( (trans - center[2] + size[2] / 2) / 2.05).astype(int).tolist() trans_ind = [0] + trans_ind + [-1] print(trans_ind) img_out = Image(np.zeros(shape, np.float32), center, size) img_out_data = img_out.data for ind, img in enumerate(images): img_data = copy(img.data) img_data[:, :, :trans_ind[ind]] = 0 img_data[:, :, trans_ind[ind + 1]:] = 0 img_out_data += img_data return img_out.update(data=img_out_data)
class ImageConfig(UnitSizePropertyMixin): """ Image data with center and size info. """ shape: List(int, 3) center: List(float, 3) size: List(float, 3)
class Block(UnitSizePropertyMixin): size: List(float, 3) shape: List(int, 3) interval: List(float, 3) def __attrs_post_init__(self): if self.interval is None: object.__setattr__(self, 'interval', [0, 0, 0])
class PointSource(ShapePropertyMixin, UnitSizePropertyMixin, GetItemMixin, CentralSlicesPropertyMixin, CentralProfilesPropertyMixin, LoadMixin, SaveMixin, ImshowMixin, ArithmeticMixin): data: Any center: List(float, 3) size: List(float, 3) pos: List(int) psf_type: str = attr.ib(default='xy')
class Image(ShapePropertyMixin, UnitSizePropertyMixin, GetItemMixin, CentralSlicesPropertyMixin, CentralProfilesPropertyMixin, AverageProfilesPropertyMixin, LoadMixin, SaveMixin, ImshowMixin, ArithmeticMixin): """ Image data with center and size info. """ data: Any center: List(float, 3) size: List(float, 3)
def _mesh_crystal_ring_full(self, block_shape: List(int, 3), average_radius: int, block_size: List(float, 3), nb_blocks_per_ring: int, axial_length: float): nb_crystals_per_block = block_shape[1] * block_shape[2] nb_crystals_per_ring = nb_blocks_per_ring * nb_crystals_per_block nb_crystals_per_ring2 = (nb_blocks_per_ring - 1) * nb_crystals_per_block lors = np.zeros((nb_crystals_per_ring * nb_crystals_per_ring2 // 2, 6), dtype = np.float32) block_unit_size = [s1 / s2 for s1, s2 in zip(block_size, block_shape)] x = np.ones(block_shape[1], ) * average_radius y = (np.arange(block_shape[1]) + 0.5) * block_unit_size[1] - \ block_size[1] / 2 z = (np.arange(block_shape[2]) + 0.5) * block_unit_size[2] xx = np.kron(x, [[1]] * block_shape[2]).ravel() yy = np.kron(y, [[1]] * block_shape[2]).ravel() zz = np.kron(z, [1] * block_shape[1]) theta_list = 2 * np.pi / nb_blocks_per_ring * np.arange(nb_blocks_per_ring) c = 0 N = nb_crystals_per_block ** 2 for theta1 in theta_list: xx1 = xx * np.cos(theta1) - yy * np.sin(theta1) yy1 = xx * np.sin(theta1) + yy * np.cos(theta1) zz1 = zz for theta2 in theta_list: if theta2 <= theta1: continue xx2 = xx * np.cos(theta2) - yy * np.sin(theta2) yy2 = xx * np.sin(theta2) + yy * np.cos(theta2) zz2 = zz lors[c: c + N, 0] = np.kron(xx1, [1] * nb_crystals_per_block) lors[c: c + N, 1] = np.kron(yy1, [1] * nb_crystals_per_block) lors[c: c + N, 2] = np.kron(zz1, [1] * nb_crystals_per_block) - axial_length / 2 lors[c: c + N, 3] = np.kron(xx2, [[1]] * nb_crystals_per_block).ravel() lors[c: c + N, 4] = np.kron(yy2, [[1]] * nb_crystals_per_block).ravel() lors[c: c + N, 5] = np.kron(zz2, [ [1]] * nb_crystals_per_block).ravel() - axial_length / 2 c += N return lors
def __call__(self, scanner: PetEcatScanner, config: ImageConfig, fov_centers: List(float)) -> Emap: emap = Emap(np.zeros(config.shape, np.float32), config.center, config.size) for i in range(len(fov_centers)): scanner = scanner.update(center=[0, 0, fov_centers[i]]) emap += EmapGenerator('block-full', scanner)(emap) return emap
def test_isinstance(self): list_int_3 = List(int, 3) for any_type in basic_type_list: if issubclass(any_type, list): for sample in basic_type_samples[any_type]: if len(sample) == 3 and all( map(lambda x: isinstance(x, int), sample)): assert list_int_3.isinstance(sample) else: assert not list_int_3.isinstance(sample) else: for sample in basic_type_samples[any_type]: assert not list_int_3.isinstance(sample)
def test_generic_type_isinstance_(self): list_int_3 = List(int, 3) tuple_sample = Tuple((int, float, str)) for type_ in basic_type_list: for sample in basic_type_samples[type_]: if list_int_3.isinstance(sample): assert isinstance_(sample, list_int_3) else: assert not isinstance_(sample, list_int_3) if tuple_sample.isinstance(sample): assert isinstance_(sample, tuple_sample) else: assert not isinstance_(sample, tuple_sample)
def _mesh_crystal_ring(self, block_shape: List(int, 3), average_radius: int, block_size: List(float, 3), nb_blocks_per_ring: int, gap: float, d: int): block_unit_size = [s1 / s2 for s1, s2 in zip(block_size, block_shape)] nb_crystals_per_ring = nb_blocks_per_ring * block_shape[1] * block_shape[2] lors = np.zeros((nb_crystals_per_ring * nb_crystals_per_ring, 6), dtype = np.float32) x = np.ones(block_shape[1], ) * average_radius y = (np.arange(block_shape[1]) + 0.5) * block_unit_size[1] - \ block_size[1] / 2 z = (np.arange(block_shape[2]) + 0.5) * block_unit_size[2] - \ block_size[2] * (d + 1) / 2 xx = np.kron(x, [1] * nb_blocks_per_ring) yy = np.kron(y, [1] * nb_blocks_per_ring) theta = 2 * np.pi / nb_blocks_per_ring * np.arange(nb_blocks_per_ring) theta1 = np.kron(theta, [[1]] * block_shape[1]).ravel() xx1 = xx * np.cos(theta1) - yy * np.sin(theta1) yy1 = xx * np.sin(theta1) + yy * np.cos(theta1) xd = np.kron(xx1, [[1]] * block_shape[2]).ravel() yd = np.kron(yy1, [[1]] * block_shape[2]).ravel() zd = np.kron(z, [1] * block_shape[1] * nb_blocks_per_ring) lors[:, 0] = np.kron(xd, [1] * nb_crystals_per_ring) lors[:, 1] = np.kron(yd, [1] * nb_crystals_per_ring) lors[:, 2] = np.kron(zd, [1] * nb_crystals_per_ring) lors[:, 3] = np.kron(xd, [[1]] * nb_crystals_per_ring).ravel() lors[:, 4] = np.kron(yd, [[1]] * nb_crystals_per_ring).ravel() lors[:, 5] = np.kron(zd + d * (block_size[2] + gap), [[1]] * nb_crystals_per_ring).ravel() return lors
class PetCylindricalScanner(PetScanner, PropertyClass): inner_radius: float outer_radius: float nb_rsector: int nb_module: List(int, 2) # module grid in each rsector, [trans, z] mv_module: List(float, 2) # movement per module nb_submodule: List(int, 2) # submodule grid in each module, [trans, z] mv_submodule: List(float, 2) # movement per submodule nb_crystal: List(int, 2) # crystal grid in each submodule, [trans, z] mv_crystal: List(float, 2) # movement per crystal sz_crystal: List(float, 2) # crystaml voxel-size tof: TofConfig
def test_create(self): list_int_3 = List(int, 3) assert isinstance(list_int_3, type) assert issubclass(list_int_3, list) for any_type in basic_type_list: if issubclass(any_type, list): for sample in basic_type_samples[any_type]: if len(sample) == 3 and all( map(lambda x: isinstance(x, int), sample)): pass else: with pytest.raises(TypeError): list_int_3(sample) else: for sample in basic_type_samples[any_type]: with pytest.raises(TypeError): list_int_3(sample) assert list_int_3.length == 3 assert list_int_3.dtype == int
def __call__(self, listmodes: List(Listmode)) -> Listmode: lors = Lors(np.vstack([lm.lors.data for lm in listmodes])) listmode = Listmode(np.hstack([lm.data for lm in listmodes]), lors) return listmode