def project(obj, theta, center=None, emission=True, sinogram_order=False, ncore=None, nchunk=None): """ Project x-rays through a given 3D object. Parameters ---------- obj : ndarray Voxelized 3D object. theta : array Projection angles in radian. center: array, optional Location of rotation axis. emission : bool, optional Determines whether output data is emission or transmission type. sinogram_order: bool, optional Determins whether output data is a stack of sinograms (True, y-axis first axis) or a stack of radiographs (False, theta first axis). ncore : int, optional Number of cores that will be assigned to jobs. nchunk : int, optional Chunk size for each core. Returns ------- ndarray 3D tomographic data. """ obj = dtype.as_float32(obj) theta = dtype.as_float32(theta) # Estimate data dimensions. oy, ox, oz = obj.shape dt = theta.size dy = oy dx = _round_to_even(np.sqrt(ox * ox + oz * oz) + 2) shape = dy, dt, dx tomo = dtype.empty_shared_array(shape) tomo[:] = 0.0 center = get_center(shape, center) tomo = mproc.distribute_jobs( (obj, center, tomo), func=extern.c_project, args=(theta,), axis=0, ncore=ncore, nchunk=nchunk) # NOTE: returns sinogram order with emmission=True if not emission: # convert data to be transmission type np.exp(-tomo, tomo) if not sinogram_order: # rotate to radiograph order tomo = np.swapaxes(tomo, 0, 1) #doesn't copy data # copy data to sharedmem tomo = dtype.as_sharedmem(tomo, copy=True) return tomo
def _init_recon(shape, init_recon, val=1e-6, sharedmem=True): if init_recon is None: if sharedmem: recon = dtype.empty_shared_array(shape) recon[:] = val else: recon = np.full(shape, val, dtype=np.float32) else: recon = np.require(init_recon, dtype=np.float32, requirements="AC") if sharedmem: recon = dtype.as_sharedmem(recon) return recon
def read_hdf5(fname, dataset, slc=None, dtype=None, shared=True): """ Read data from hdf5 file from a specific group. Parameters ---------- fname : str String defining the path of file or file name. dataset : str Path to the dataset inside hdf5 file where data is located. slc : sequence of tuples, optional Range of values for slicing data in each axis. ((start_1, end_1, step_1), ... , (start_N, end_N, step_N)) defines slicing parameters for each axis of the data matrix. dtype : numpy datatype (optional) Convert data to this datatype on read if specified. shared : bool (optional) If True, read data into shared memory location. Defaults to False. Returns ------- ndarray Data. """ try: fname = _check_read(fname) with h5py.File(fname, "r") as f: try: data = f[dataset] except KeyError: # NOTE: I think it would be better to raise an exception here. logger.error('Unrecognized hdf5 dataset: "%s"' % (str(dataset))) return None shape = _shape_after_slice(data.shape, slc) if dtype is None: dtype = data.dtype if shared: arr = empty_shared_array(shape, dtype) else: arr = np.empty(shape, dtype) data.read_direct(arr, _fix_slice(slc)) except KeyError: arr = None _log_imported_data(fname, arr) return arr
def read_hdf5(fname, dataset, slc=None, dtype=None, shared=True): """ Read data from hdf5 file from a specific group. Parameters ---------- fname : str String defining the path of file or file name. dataset : str Path to the dataset inside hdf5 file where data is located. slc : sequence of tuples, optional Range of values for slicing data in each axis. ((start_1, end_1, step_1), ... , (start_N, end_N, step_N)) defines slicing parameters for each axis of the data matrix. dtype : numpy datatype (optional) Convert data to this datatype on read if specified. shared : bool (optional) If True, read data into shared memory location. Defaults to False. Returns ------- ndarray Data. """ try: fname = _check_read(fname) with h5py.File(fname, "r") as f: try: data = f[dataset] except KeyError: # NOTE: I think it would be better to raise an exception here. logger.error('Unrecognized hdf5 dataset: "%s"'%(str(dataset))) return None shape = _shape_after_slice(data.shape, slc) if dtype is None: dtype = data.dtype if shared: arr = empty_shared_array(shape, dtype) else: arr = np.empty(shape, dtype) data.read_direct(arr, _fix_slice(slc)) except KeyError: arr = None _log_imported_data(fname, arr) return arr
def write_center(tomo, theta, dpath='tmp/center', cen_range=None, ind=None, mask=False, ratio=1., sinogram_order=False, algorithm='gridrec', filter_name='parzen'): """ Save images reconstructed with a range of rotation centers. Helps finding the rotation center manually by visual inspection of images reconstructed with a set of different centers.The output images are put into a specified folder and are named by the center position corresponding to the image. Parameters ---------- tomo : ndarray 3D tomographic data. theta : array Projection angles in radian. dpath : str, optional Folder name to save output images. cen_range : list, optional [start, end, step] Range of center values. ind : int, optional Index of the slice to be used for reconstruction. mask : bool, optional If ``True``, apply a circular mask to the reconstructed image to limit the analysis into a circular region. ratio : float, optional The ratio of the radius of the circular mask to the edge of the reconstructed image. sinogram_order: bool, optional Determins whether data is a stack of sinograms (True, y-axis first axis) or a stack of radiographs (False, theta first axis). algorithm : {str, function} One of the following string values. 'art' Algebraic reconstruction technique :cite:`Kak:98`. 'bart' Block algebraic reconstruction technique. 'fbp' Filtered back-projection algorithm. 'gridrec' Fourier grid reconstruction algorithm :cite:`Dowd:99`, :cite:`Rivers:06`. 'mlem' Maximum-likelihood expectation maximization algorithm :cite:`Dempster:77`. 'osem' Ordered-subset expectation maximization algorithm :cite:`Hudson:94`. 'ospml_hybrid' Ordered-subset penalized maximum likelihood algorithm with weighted linear and quadratic penalties. 'ospml_quad' Ordered-subset penalized maximum likelihood algorithm with quadratic penalties. 'pml_hybrid' Penalized maximum likelihood algorithm with weighted linear and quadratic penalties :cite:`Chang:04`. 'pml_quad' Penalized maximum likelihood algorithm with quadratic penalty. 'sirt' Simultaneous algebraic reconstruction technique. 'tv' Total Variation reconstruction technique :cite:`Chambolle:11`. 'grad' Gradient descent method with a constant step size filter_name : str, optional Name of the filter for analytic reconstruction. 'none' No filter. 'shepp' Shepp-Logan filter (default). 'cosine' Cosine filter. 'hann' Cosine filter. 'hamming' Hamming filter. 'ramlak' Ram-Lak filter. 'parzen' Parzen filter. 'butterworth' Butterworth filter. 'custom' A numpy array of size `next_power_of_2(num_detector_columns)/2` specifying a custom filter in Fourier domain. The first element of the filter should be the zero-frequency component. 'custom2d' A numpy array of size `num_projections*next_power_of_2(num_detector_columns)/2` specifying a custom angle-dependent filter in Fourier domain. The first element of each filter should be the zero-frequency component. """ tomo = dtype.as_float32(tomo) theta = dtype.as_float32(theta) if sinogram_order: dy, dt, dx = tomo.shape else: dt, dy, dx = tomo.shape if ind is None: ind = dy // 2 if cen_range is None: center = np.arange(dx / 2 - 5, dx / 2 + 5, 0.5) else: center = np.arange(*cen_range) stack = dtype.empty_shared_array((len(center), dt, dx)) for m in range(center.size): if sinogram_order: stack[m] = tomo[ind] else: stack[m] = tomo[:, ind, :] # Reconstruct the same slice with a range of centers. rec = recon(stack, theta, center=center, sinogram_order=True, algorithm=algorithm, filter_name=filter_name, nchunk=1) # Apply circular mask. if mask is True: rec = circ_mask(rec, axis=0) # Save images to a temporary folder. for m in range(len(center)): fname = os.path.join(dpath, str('{0:.2f}'.format(center[m]) + '.tiff')) dxchange.write_tiff(rec[m], fname=fname, overwrite=True)
def project(obj, theta, center=None, emission=True, pad=True, sinogram_order=False, ncore=None, nchunk=None): """ Project x-rays through a given 3D object. Parameters ---------- obj : ndarray Voxelized 3D object. theta : array Projection angles in radian. center: array, optional Location of rotation axis. emission : bool, optional Determines whether output data is emission or transmission type. pad : bool, optional Determines if the projection image width will be padded or not. If True, then the diagonal length of the object cross-section will be used for the output size of the projection image width. sinogram_order: bool, optional Determines whether output data is a stack of sinograms (True, y-axis first axis) or a stack of radiographs (False, theta first axis). ncore : int, optional Number of cores that will be assigned to jobs. nchunk : int, optional Chunk size for each core. Returns ------- ndarray 3D tomographic data. """ obj = dtype.as_float32(obj) theta = dtype.as_float32(theta) # Estimate data dimensions. oy, ox, oz = obj.shape dt = theta.size dy = oy if pad == True: dx = _round_to_even(np.sqrt(ox * ox + oz * oz) + 2) elif pad == False: dx = ox shape = dy, dt, dx tomo = dtype.empty_shared_array(shape) tomo[:] = 0.0 center = get_center(shape, center) tomo = mproc.distribute_jobs((obj, center, tomo), func=extern.c_project, args=(theta, ), axis=0, ncore=ncore, nchunk=nchunk) # NOTE: returns sinogram order with emmission=True if not emission: # convert data to be transmission type np.exp(-tomo, tomo) if not sinogram_order: # rotate to radiograph order tomo = np.swapaxes(tomo, 0, 1) #doesn't copy data # copy data to sharedmem tomo = dtype.as_sharedmem(tomo, copy=True) return tomo
def project2( objx, objy, theta, center=None, emission=True, pad=True, sinogram_order=False, ncore=None, nchunk=None): """ Project x-rays through a given 3D object. Parameters ---------- objx, objy : ndarray (x, y) components of vector of a voxelized 3D object. theta : array Projection angles in radian. center: array, optional Location of rotation axis. emission : bool, optional Determines whether output data is emission or transmission type. pad : bool, optional Determines if the projection image width will be padded or not. If True, then the diagonal length of the object cross-section will be used for the output size of the projection image width. sinogram_order: bool, optional Determines whether output data is a stack of sinograms (True, y-axis first axis) or a stack of radiographs (False, theta first axis). ncore : int, optional Number of cores that will be assigned to jobs. nchunk : int, optional Chunk size for each core. Returns ------- ndarray 3D tomographic data. """ objx = dtype.as_float32(objx) objy = dtype.as_float32(objy) theta = dtype.as_float32(theta) # Estimate data dimensions. oy, ox, oz = objx.shape dt = theta.size dy = oy if pad is True: dx = _round_to_even(np.sqrt(ox * ox + oz * oz) + 2) elif pad is False: dx = ox shape = dy, dt, dx tomo = dtype.empty_shared_array(shape) tomo[:] = 0.0 center = get_center(shape, center) extern.c_project2(objx, objy, center, tomo, theta) # NOTE: returns sinogram order with emmission=True if not emission: # convert data to be transmission type np.exp(-tomo, tomo) if not sinogram_order: # rotate to radiograph order tomo = np.swapaxes(tomo, 0, 1) # doesn't copy data # copy data to sharedmem tomo = dtype.as_sharedmem(tomo, copy=True) return tomo
def write_center( tomo, theta, dpath='tmp/center', cen_range=None, ind=None, mask=False, ratio=1., sinogram_order=False): """ Save images reconstructed with a range of rotation centers. Helps finding the rotation center manually by visual inspection of images reconstructed with a set of different centers.The output images are put into a specified folder and are named by the center position corresponding to the image. Parameters ---------- tomo : ndarray 3D tomographic data. theta : array Projection angles in radian. dpath : str, optional Folder name to save output images. cen_range : list, optional [start, end, step] Range of center values. ind : int, optional Index of the slice to be used for reconstruction. mask : bool, optional If ``True``, apply a circular mask to the reconstructed image to limit the analysis into a circular region. ratio : float, optional The ratio of the radius of the circular mask to the edge of the reconstructed image. sinogram_order: bool, optional Determins whether data is a stack of sinograms (True, y-axis first axis) or a stack of radiographs (False, theta first axis). """ tomo = dtype.as_float32(tomo) theta = dtype.as_float32(theta) if sinogram_order: dy, dt, dx = tomo.shape else: dt, dy, dx = tomo.shape if ind is None: ind = dy // 2 if cen_range is None: center = np.arange(dx / 2 - 5, dx / 2 + 5, 0.5) else: center = np.arange(*cen_range) stack = dtype.empty_shared_array((len(center), dt, dx)) for m in range(center.size): if sinogram_order: stack[m] = tomo[ind] else: stack[m] = tomo[:, ind, :] # Reconstruct the same slice with a range of centers. rec = recon(stack, theta, center=center, sinogram_order=True, algorithm='gridrec', nchunk=1) # Apply circular mask. if mask is True: rec = circ_mask(rec, axis=0) # Save images to a temporary folder. for m in range(len(center)): fname = os.path.join( dpath, str('{0:.2f}'.format(center[m]) + '.tiff')) dxchange.write_tiff(rec[m], fname=fname, overwrite=True)
def write_center( tomo, theta, dpath='tmp/center', cen_range=None, ind=None, mask=False, ratio=1., sinogram_order=False, algorithm='gridrec', filter_name='parzen'): """ Save images reconstructed with a range of rotation centers. Helps finding the rotation center manually by visual inspection of images reconstructed with a set of different centers.The output images are put into a specified folder and are named by the center position corresponding to the image. Parameters ---------- tomo : ndarray 3D tomographic data. theta : array Projection angles in radian. dpath : str, optional Folder name to save output images. cen_range : list, optional [start, end, step] Range of center values. ind : int, optional Index of the slice to be used for reconstruction. mask : bool, optional If ``True``, apply a circular mask to the reconstructed image to limit the analysis into a circular region. ratio : float, optional The ratio of the radius of the circular mask to the edge of the reconstructed image. sinogram_order: bool, optional Determins whether data is a stack of sinograms (True, y-axis first axis) or a stack of radiographs (False, theta first axis). algorithm : {str, function} One of the following string values. 'art' Algebraic reconstruction technique :cite:`Kak:98`. 'bart' Block algebraic reconstruction technique. 'fbp' Filtered back-projection algorithm. 'gridrec' Fourier grid reconstruction algorithm :cite:`Dowd:99`, :cite:`Rivers:06`. 'mlem' Maximum-likelihood expectation maximization algorithm :cite:`Dempster:77`. 'osem' Ordered-subset expectation maximization algorithm :cite:`Hudson:94`. 'ospml_hybrid' Ordered-subset penalized maximum likelihood algorithm with weighted linear and quadratic penalties. 'ospml_quad' Ordered-subset penalized maximum likelihood algorithm with quadratic penalties. 'pml_hybrid' Penalized maximum likelihood algorithm with weighted linear and quadratic penalties :cite:`Chang:04`. 'pml_quad' Penalized maximum likelihood algorithm with quadratic penalty. 'sirt' Simultaneous algebraic reconstruction technique. 'tv' Total Variation reconstruction technique :cite:`Chambolle:11`. 'grad' Gradient descent method with a constant step size filter_name : str, optional Name of the filter for analytic reconstruction. 'none' No filter. 'shepp' Shepp-Logan filter (default). 'cosine' Cosine filter. 'hann' Cosine filter. 'hamming' Hamming filter. 'ramlak' Ram-Lak filter. 'parzen' Parzen filter. 'butterworth' Butterworth filter. 'custom' A numpy array of size `next_power_of_2(num_detector_columns)/2` specifying a custom filter in Fourier domain. The first element of the filter should be the zero-frequency component. 'custom2d' A numpy array of size `num_projections*next_power_of_2(num_detector_columns)/2` specifying a custom angle-dependent filter in Fourier domain. The first element of each filter should be the zero-frequency component. """ tomo = dtype.as_float32(tomo) theta = dtype.as_float32(theta) if sinogram_order: dy, dt, dx = tomo.shape else: dt, dy, dx = tomo.shape if ind is None: ind = dy // 2 if cen_range is None: center = np.arange(dx / 2 - 5, dx / 2 + 5, 0.5) else: center = np.arange(*cen_range) stack = dtype.empty_shared_array((len(center), dt, dx)) for m in range(center.size): if sinogram_order: stack[m] = tomo[ind] else: stack[m] = tomo[:, ind, :] # Reconstruct the same slice with a range of centers. rec = recon(stack, theta, center=center, sinogram_order=True, algorithm=algorithm, filter_name=filter_name, nchunk=1) # Apply circular mask. if mask is True: rec = circ_mask(rec, axis=0) # Save images to a temporary folder. dpath = os.path.abspath(dpath) if not os.path.exists(dpath): os.makedirs(dpath) for m in range(len(center)): fname = os.path.join( dpath, str('{0:.2f}'.format(center[m]) + '.tiff')) tifffile.imsave(file=fname, data=rec[m])