def evaluate(self): self.tomo.value = tomopy.remove_outlier(self.tomo.value, self.dif.value, size=self.size.value, axis=self.axis.value, ncore=self.ncore.value, out=self.tomo.value)
def filter_one_use_tomopy(img, maxdiff=500, **kwds): """does gamma filtering on the given image This algorithm uses tomopy. maxdiff is the max diff between a pixel value and median value to be considered normal (not an outlier) - img: image npy array. must be of integer data type - kwds: additional kwd args to pass to remove_outliers_bymedian """ import tomopy return tomopy.remove_outlier(img, maxdiff)
def reconstruct(sname, rot_center, ovlpfind, s_start, s_end): fname = dfolder + sname + '.h5' print(fname) start = s_start end = s_end chunks = 24 num_sino = (end - start) // chunks for m in range(chunks): sino_start = start + num_sino * m sino_end = start + num_sino * (m + 1) start_read_time = time.time() proj, flat, dark, thetat = dxchange.read_aps_2bm(fname, sino=(sino_start, sino_end)) print(' done read in %0.1f min' % ((time.time() - start_read_time) / 60)) dark = proj[9001:9002] flat = proj[0:1] proj = proj[1:9000] theta = tomopy.angles(proj.shape[0], 0., 360.) proj = tomopy.sino_360_to_180(proj, overlap=ovlpfind, rotation='right') proj = tomopy.remove_outlier(proj, dif=0.4) proj = tomopy.normalize_bg(proj, air=10) proj = tomopy.minus_log(proj) center = rot_center start_ring_time = time.time() proj = tomopy.remove_stripe_fw(proj, wname='sym5', sigma=4, pad=False) proj = tomopy.remove_stripe_sf(proj, size=3) print(' done pre-process in %0.1f min' % ((time.time() - start_ring_time) / 60)) start_phase_time = time.time() proj = tomopy.retrieve_phase(proj, pixel_size=detector_pixel_size_x, dist=sample_detector_distance, energy=energy, alpha=alpha, pad=True, ncore=None, nchunk=None) print(' done phase retrieval in %0.1f min' % ((time.time() - start_phase_time) / 60)) start_recon_time = time.time() rec = tomopy.recon(proj, theta, center=center, algorithm='gridrec', filter_name='ramalk') tomopy.circ_mask(rec, axis=0, ratio=0.95) print("Reconstructed", rec.shape) dxchange.write_tiff_stack(rec, fname=dfolder + '/' + sname + '/' + sname, overwrite=True, start=sino_start) print(' Chunk reconstruction done in %0.1f min' % ((time.time() - start_recon_time) / 60)) print("Done!")
def recon_hdf5_mpi(src_fanme, dest_folder, sino_range, sino_step, center_vec, shift_grid, dtype='float32', algorithm='gridrec', tolerance=1, save_sino=False, sino_blur=None, **kwargs): """ Reconstruct a single tile, or fused HDF5 created using util/total_fusion. MPI supported. """ raise DeprecationWarning if rank == 0: if not os.path.exists(dest_folder): os.mkdir(dest_folder) sino_ini = int(sino_range[0]) sino_end = int(sino_range[1]) f = h5py.File(src_fanme) dset = f['exchange/data'] full_shape = dset.shape theta = tomopy.angles(full_shape[0]) center_vec = np.asarray(center_vec) sino_ls = np.arange(sino_ini, sino_end, sino_step, dtype='int') grid_bins = np.ceil(shift_grid[:, 0, 0]) t0 = time.time() alloc_set = allocate_mpi_subsets(sino_ls.size, size, task_list=sino_ls) for slice in alloc_set[rank]: print(' Rank {:d}: reconstructing {:d}'.format(rank, slice)) grid_line = np.digitize(slice, grid_bins) grid_line = grid_line - 1 center = center_vec[grid_line] data = dset[:, slice, :] if sino_blur is not None: data = gaussian_filter(data, sino_blur) data = data.reshape([full_shape[0], 1, full_shape[2]]) data[np.isnan(data)] = 0 data = data.astype('float32') if save_sino: dxchange.write_tiff(data[:, slice, :], fname=os.path.join(dest_folder, 'sino/recon_{:05d}_{:d}.tiff').format(slice, center)) # data = tomopy.remove_stripe_ti(data) rec = tomopy.recon(data, theta, center=center, algorithm=algorithm, **kwargs) # rec = tomopy.remove_ring(rec) rec = tomopy.remove_outlier(rec, tolerance) rec = tomopy.circ_mask(rec, axis=0, ratio=0.95) dxchange.write_tiff(rec, fname='{:s}/recon/recon_{:05d}_{:d}'.format(dest_folder, slice, center), dtype=dtype) print('Rank {:d} finished in {:.2f} s.'.format(rank, time.time()-t0)) return
def testOutlierRemoval(size): '''test with random array, loop outside with a 2d cuda kernel''' # prjsize is z, imsize is x, y. # size = 13 # window size for the filter imsizex = 2048 # image size for the input imsizey = 2048 prjsize = 1 combinedMed = np.zeros(shape=(prjsize, imsizey, imsizex), dtype=np.float32) resultscuda = np.zeros(shape=(prjsize, imsizey, imsizex), dtype=np.float32) resultscpu = np.zeros(shape=(prjsize, imsizey, imsizex), dtype=np.float32) diff = 100 # create combined noise matrix 3D for step in range(5, 5 + prjsize): im_noise = np.arange(10, imsizey * imsizex * step + 10, step).reshape(imsizey, imsizex) # im_noise = np.random.rand(imsizey, imsizex) im_noise = im_noise.astype(np.float32) combinedMed[step - 5] = im_noise start = timeit.default_timer() resultscpu = tomopy.remove_outlier_cuda(combinedMed, diff, size=size) stop = timeit.default_timer() diff2 = stop - start print("end cuda outlier removal", diff2) start = timeit.default_timer() resultscuda = tomopy.remove_outlier(combinedMed, diff, size=size, ncore=1) stop = timeit.default_timer() diff1 = stop - start print("end cpu outlier removal", diff1) print("the times gpu over cpu is", diff1 / diff2) print not np.any(resultscuda - resultscpu)
def testOutlierRemoval(size): '''test with random array, loop outside with a 2d cuda kernel''' # prjsize is z, imsize is x, y. # size = 13 # window size for the filter imsizex =2048 # image size for the input imsizey = 2048 prjsize= 1 combinedMed = np.zeros(shape=(prjsize,imsizey,imsizex), dtype=np.float32) resultscuda = np.zeros(shape=(prjsize,imsizey,imsizex), dtype=np.float32) resultscpu = np.zeros(shape=(prjsize,imsizey,imsizex), dtype=np.float32) diff = 100 # create combined noise matrix 3D for step in range (5, 5 + prjsize): im_noise = np.arange( 10, imsizey*imsizex*step+10, step ).reshape(imsizey, imsizex) # im_noise = np.random.rand(imsizey, imsizex) im_noise = im_noise.astype(np.float32) combinedMed[step-5]=im_noise start = timeit.default_timer() resultscpu= tomopy.remove_outlier_cuda(combinedMed, diff, size=size) stop = timeit.default_timer() diff2 = stop - start print("end cuda outlier removal", diff2) start = timeit.default_timer() resultscuda = tomopy.remove_outlier(combinedMed, diff, size=size, ncore = 1) stop = timeit.default_timer() diff1 = stop - start print("end cpu outlier removal", diff1 ) print("the times gpu over cpu is", diff1/diff2) print not np.any(resultscuda-resultscpu)
def reconstruct(sname, rot_center, ovlpfind, s_start, s_end): fname = dfolder + sname + '.h5' print (fname) start = s_start end = s_end chunks = 24 num_sino = (end - start) // chunks for m in range(chunks): sino_start = start + num_sino * m sino_end = start + num_sino * (m + 1) start_read_time = time.time() proj, flat, dark, thetat = dxchange.read_aps_2bm(fname, sino=(sino_start, sino_end)) print(' done read in %0.1f min' % ((time.time() - start_read_time)/60)) dark = proj[9001:9002] flat = proj[0:1] proj = proj[1:9000] theta = tomopy.angles(proj.shape[0], 0., 360.) proj = tomopy.sino_360_to_180(proj, overlap=ovlpfind, rotation='right') proj = tomopy.remove_outlier(proj, dif=0.4) proj = tomopy.normalize_bg(proj, air=10) proj = tomopy.minus_log(proj) center = rot_center start_ring_time = time.time() proj = tomopy.remove_stripe_fw(proj, wname='sym5', sigma=4, pad=False) proj = tomopy.remove_stripe_sf(proj, size=3) print(' done pre-process in %0.1f min' % ((time.time() - start_ring_time)/60)) start_phase_time = time.time() proj = tomopy.retrieve_phase(proj, pixel_size=detector_pixel_size_x, dist=sample_detector_distance, energy=energy, alpha=alpha, pad=True, ncore=None, nchunk=None) print(' done phase retrieval in %0.1f min' % ((time.time() - start_phase_time)/60)) start_recon_time = time.time() rec = tomopy.recon(proj, theta, center=center, algorithm='gridrec', filter_name='ramalk') tomopy.circ_mask(rec, axis=0, ratio=0.95) print ("Reconstructed", rec.shape) dxchange.write_tiff_stack(rec, fname = dfolder + '/' + sname + '/' + sname, overwrite=True, start=sino_start) print(' Chunk reconstruction done in %0.1f min' % ((time.time() - start_recon_time)/60)) print ("Done!")
def recon_block(grid, shift_grid, src_folder, dest_folder, slice_range, sino_step, center_vec, ds_level=0, blend_method='max', blend_options=None, tolerance=1, sinogram_order=False, algorithm='gridrec', init_recon=None, ncore=None, nchunk=None, dtype='float32', crop=None, save_sino=False, assert_width=None, sino_blur=None, color_correction=False, flattened_radius=120, normalize=True, test_mode=False, mode='180', phase_retrieval=None, **kwargs): """ Reconstruct dsicrete HDF5 tiles, blending sinograms only. """ raw_folder = os.getcwd() os.chdir(src_folder) sino_ini = int(slice_range[0]) sino_end = int(slice_range[1]) mod_start_slice = 0 center_vec = np.asarray(center_vec) center_pos_cache = 0 sino_ls = np.arange(sino_ini, sino_end, sino_step, dtype='int') pix_shift_grid = np.ceil(shift_grid) pix_shift_grid[pix_shift_grid < 0] = 0 alloc_set = allocate_mpi_subsets(sino_ls.size, size, task_list=sino_ls) for i_slice in alloc_set[rank]: print('############################################') print('Reconstructing ' + str(i_slice)) # judge from which tile to retrieve sinos grid_lines = np.zeros(grid.shape[1], dtype=np.int) slice_in_tile = np.zeros(grid.shape[1], dtype=np.int) for col in range(grid.shape[1]): bins = pix_shift_grid[:, col, 0] grid_lines[col] = int(np.squeeze(np.digitize(i_slice, bins)) - 1) if grid_lines[col] == -1: print("WARNING: The specified starting slice number does not allow for full sinogram construction. Trying next slice...") mod_start_slice = 1 break else: mod_start_slice = 0 slice_in_tile[col] = i_slice - bins[grid_lines[col]] if mod_start_slice == 1: continue center_pos = int(np.round(center_vec[grid_lines].mean())) if center_pos_cache == 0: center_pos_cache = center_pos center_diff = center_pos - center_pos_cache center_pos_0 = center_pos row_sino, center_pos = prepare_slice(grid, shift_grid, grid_lines, slice_in_tile, ds_level=ds_level, method=blend_method, blend_options=blend_options, rot_center=center_pos, assert_width=assert_width, sino_blur=sino_blur, color_correction=color_correction, normalize=normalize, mode=mode, phase_retrieval=phase_retrieval) rec0 = recon_slice(row_sino, center_pos, sinogram_order=sinogram_order, algorithm=algorithm, init_recon=init_recon, ncore=ncore, nchunk=nchunk, **kwargs) rec = tomopy.remove_ring(np.copy(rec0)) cent = int((rec.shape[1] - 1) / 2) xx, yy = np.meshgrid(np.arange(rec.shape[2]), np.arange(rec.shape[1])) mask0 = ((xx - cent) ** 2 + (yy - cent) ** 2 <= flattened_radius ** 2) mask = np.zeros(rec.shape, dtype='bool') for i in range(mask.shape[0]): mask[i, :, :] = mask0 rec[mask] = (rec[mask] + rec0[mask]) / 2 rec = tomopy.remove_outlier(rec, tolerance) rec = tomopy.circ_mask(rec, axis=0, ratio=0.95) print('Center: {:d}'.format(center_pos)) rec = np.squeeze(rec) if center_diff != 0: rec = np.roll(rec, -center_diff, axis=0) if not crop is None: crop = np.asarray(crop) rec = rec[crop[0, 0]:crop[1, 0], crop[0, 1]:crop[1, 1]] os.chdir(raw_folder) if test_mode: dxchange.write_tiff(rec, fname=os.path.join(dest_folder, 'recon/recon_{:05d}_{:04d}.tiff'.format(i_slice, center_pos)), dtype=dtype) else: dxchange.write_tiff(rec, fname=os.path.join(dest_folder, 'recon/recon_{:05d}.tiff'.format(i_slice)), dtype=dtype) if save_sino: dxchange.write_tiff(np.squeeze(row_sino), fname=os.path.join(dest_folder, 'sino/sino_{:05d}.tiff'.format(i_slice)), overwrite=True) os.chdir(src_folder) os.chdir(raw_folder) return
datapath = '/home/xf05id1/localdata/CHESSdata/SS_Cl/nf/' projname = 'SS_Cl_combine_590-664.tif' samplename = 'SS_Cl' wf_file_name = '/home/xf05id1/localdata/CHESSdata/darkfield_whitefield/white_field_test_NFA1300_664-590.tif' #%run /home/xf05id1/src/nsls2-xf-utils/chesstomo_parameters.py ################# print('loading data') proj, df, wf = srxfftomo_process.chess_fileio(datapath, projname, wf_file=wf_file_name) print(proj.shape, wf.shape) proj = tomopy.remove_outlier(proj, 400, size=6) proj = tomopy.remove_outlier(proj, 100, size=6) wf = np.expand_dims( wf, axis=0) #expand the initial 2D white field image into 3D image wf = tomopy.remove_outlier(wf, 400, size=6) wf = tomopy.remove_outlier(wf, 100, size=6) print('correcting background') proj = srxfftomo_process.srxfftomo_bkg_correction(df, wf, proj) print('taking negative natural log') proj = tomopy.minus_log(proj) print(' handling special values: negatives, Nan, infinite') proj = tomopy.misc.corr.remove_neg(proj, val=0.001) proj = tomopy.misc.corr.remove_nan(proj, val=0.001)
def recon_hdf5(src_fanme, dest_folder, sino_range, sino_step, shift_grid, center_vec=None, center_eq=None, dtype='float32', algorithm='gridrec', tolerance=1, chunk_size=20, save_sino=False, sino_blur=None, flattened_radius=120, mode='180', test_mode=False, phase_retrieval=None, ring_removal=True, **kwargs): """ center_eq: a and b parameters in fitted center position equation center = a*slice + b. """ if not os.path.exists(dest_folder): try: os.mkdir(dest_folder) except: pass sino_ini = int(sino_range[0]) sino_end = int(sino_range[1]) sino_ls_all = np.arange(sino_ini, sino_end, sino_step, dtype='int') alloc_set = allocate_mpi_subsets(sino_ls_all.size, size, task_list=sino_ls_all) sino_ls = alloc_set[rank] # prepare metadata f = h5py.File(src_fanme) dset = f['exchange/data'] full_shape = dset.shape theta = tomopy.angles(full_shape[0]) if center_eq is not None: a, b = center_eq center_ls = sino_ls * a + b center_ls = np.round(center_ls) for iblock in range(int(sino_ls.size/chunk_size)+1): print('Beginning block {:d}.'.format(iblock)) t0 = time.time() istart = iblock*chunk_size iend = np.min([(iblock+1)*chunk_size, sino_ls.size]) fstart = sino_ls[istart] fend = sino_ls[iend] center = center_ls[istart:iend] data = dset[:, fstart:fend:sino_step, :] data[np.isnan(data)] = 0 data = data.astype('float32') data = tomopy.remove_stripe_ti(data, alpha=4) if sino_blur is not None: for i in range(data.shape[1]): data[:, i, :] = gaussian_filter(data[:, i, :], sino_blur) rec = tomopy.recon(data, theta, center=center, algorithm=algorithm, **kwargs) rec = tomopy.remove_ring(rec) rec = tomopy.remove_outlier(rec, tolerance) rec = tomopy.circ_mask(rec, axis=0, ratio=0.95) for i in range(rec.shape[0]): slice = fstart + i*sino_step dxchange.write_tiff(rec[i, :, :], fname=os.path.join(dest_folder, 'recon/recon_{:05d}_{:05d}.tiff').format(slice, sino_ini)) if save_sino: dxchange.write_tiff(data[:, i, :], fname=os.path.join(dest_folder, 'sino/recon_{:05d}_{:d}.tiff').format(slice, int(center[i]))) iblock += 1 print('Block {:d} finished in {:.2f} s.'.format(iblock, time.time()-t0)) else: # divide chunks grid_bins = np.append(np.ceil(shift_grid[:, 0, 0]), full_shape[1]) chunks = [] center_ls = [] istart = 0 counter = 0 # irow should be 0 for slice 0 irow = np.searchsorted(grid_bins, sino_ls[0], side='right')-1 for i in range(sino_ls.size): counter += 1 sino_next = i+1 if i != sino_ls.size-1 else i if counter >= chunk_size or sino_ls[sino_next] >= grid_bins[irow+1] or sino_next == i: iend = i+1 chunks.append((istart, iend)) istart = iend center_ls.append(center_vec[irow]) if sino_ls[sino_next] >= grid_bins[irow+1]: irow += 1 counter = 0 # reconstruct chunks iblock = 1 for (istart, iend), center in izip(chunks, center_ls): print('Beginning block {:d}.'.format(iblock)) t0 = time.time() fstart = sino_ls[istart] fend = sino_ls[iend-1] print('Reading data...') data = dset[:, fstart:fend+1:sino_step, :] if mode == '360': overlap = 2 * (dset.shape[2] - center) data = tomosaic.morph.sino_360_to_180(data, overlap=overlap, rotation='right') theta = tomopy.angles(data.shape[0]) data[np.isnan(data)] = 0 data = data.astype('float32') if sino_blur is not None: for i in range(data.shape[1]): data[:, i, :] = gaussian_filter(data[:, i, :], sino_blur) if ring_removal: data = tomopy.remove_stripe_ti(data, alpha=4) if phase_retrieval: data = tomopy.retrieve_phase(data, kwargs['pixel_size'], kwargs['dist'], kwargs['energy'], kwargs['alpha']) rec0 = tomopy.recon(data, theta, center=center, algorithm=algorithm, **kwargs) rec = tomopy.remove_ring(np.copy(rec0)) cent = int((rec.shape[1]-1) / 2) xx, yy = np.meshgrid(np.arange(rec.shape[2]), np.arange(rec.shape[1])) mask0 = ((xx-cent)**2+(yy-cent)**2 <= flattened_radius**2) mask = np.zeros(rec.shape, dtype='bool') for i in range(mask.shape[0]): mask[i, :, :] = mask0 rec[mask] = (rec[mask] + rec0[mask])/2 else: rec = tomopy.recon(data, theta, center=center, algorithm=algorithm, **kwargs) rec = tomopy.remove_outlier(rec, tolerance) rec = tomopy.circ_mask(rec, axis=0, ratio=0.95) for i in range(rec.shape[0]): slice = fstart + i*sino_step if test_mode: dxchange.write_tiff(rec[i, :, :], fname=os.path.join(dest_folder, 'recon/recon_{:05d}_{:d}.tiff').format(slice, center), dtype=dtype) else: dxchange.write_tiff(rec[i, :, :], fname=os.path.join(dest_folder, 'recon/recon_{:05d}.tiff').format(slice), dtype=dtype) if save_sino: dxchange.write_tiff(data[:, i, :], fname=os.path.join(dest_folder, 'sino/recon_{:05d}_{:d}.tiff').format(slice, center), dtype=dtype) print('Block {:d} finished in {:.2f} s.'.format(iblock, time.time()-t0)) iblock += 1 return
def recon_block(grid, shift_grid, src_folder, dest_folder, slice_range, sino_step, center_vec, ds_level=0, blend_method='max', blend_options=None, tolerance=1, sinogram_order=False, algorithm='gridrec', init_recon=None, ncore=None, nchunk=None, dtype='float32', crop=None, save_sino=False, assert_width=None, sino_blur=None, color_correction=False, flattened_radius=120, normalize=True, test_mode=False, mode='180', phase_retrieval=None, data_format='aps_32id', read_theta=True, ring_removal=True, **kwargs): """ Reconstruct dsicrete HDF5 tiles, blending sinograms only. """ sino_ini = int(slice_range[0]) sino_end = int(slice_range[1]) mod_start_slice = 0 center_vec = np.asarray(center_vec) center_pos_cache = 0 sino_ls = np.arange(sino_ini, sino_end, sino_step, dtype='int') alloc_set = allocate_mpi_subsets(sino_ls.size, size, task_list=sino_ls) for i_slice in alloc_set[rank]: internal_print('############################################') internal_print('Reconstructing ' + str(i_slice)) row_sino, center_pos = create_row_sinogram( grid, shift_grid, src_folder, i_slice, center_vec, ds_level, blend_method, blend_options, assert_width, sino_blur, color_correction, normalize, mode, phase_retrieval, data_format) if row_sino is None: continue if read_theta: _, _, _, theta = read_data_adaptive(os.path.join( src_folder, grid[0, 0]), proj=(0, 1), return_theta=True) if ring_removal: rec0 = recon_slice(row_sino, theta, center_pos, sinogram_order=sinogram_order, algorithm=algorithm, init_recon=init_recon, ncore=ncore, nchunk=nchunk, **kwargs) rec = tomopy.remove_ring(np.copy(rec0)) cent = int((rec.shape[1] - 1) / 2) xx, yy = np.meshgrid(np.arange(rec.shape[2]), np.arange(rec.shape[1])) mask0 = ((xx - cent)**2 + (yy - cent)**2 <= flattened_radius**2) mask = np.zeros(rec.shape, dtype='bool') for i in range(mask.shape[0]): mask[i, :, :] = mask0 rec[mask] = (rec[mask] + rec0[mask]) / 2 else: rec = recon_slice(row_sino, theta, center_pos, sinogram_order=sinogram_order, algorithm=algorithm, init_recon=init_recon, ncore=ncore, nchunk=nchunk, **kwargs) rec = tomopy.remove_outlier(rec, tolerance) rec = tomopy.circ_mask(rec, axis=0, ratio=0.95) internal_print('Center: {:d}'.format(center_pos)) rec = np.squeeze(rec) # correct recon position shifting due to center misalignment if center_pos_cache == 0: center_pos_cache = center_pos center_diff = center_pos - center_pos_cache if center_diff != 0: rec = np.roll(rec, -center_diff, axis=0) if not crop is None: crop = np.asarray(crop) rec = rec[crop[0, 0]:crop[1, 0], crop[0, 1]:crop[1, 1]] if test_mode: dxchange.write_tiff(rec, fname=os.path.join( dest_folder, 'recon/recon_{:05d}_{:04d}.tiff'.format( i_slice, center_pos)), dtype=dtype) else: dxchange.write_tiff(rec, fname=os.path.join( dest_folder, 'recon/recon_{:05d}.tiff'.format(i_slice)), dtype=dtype) if save_sino: dxchange.write_tiff(np.squeeze(row_sino), fname=os.path.join( dest_folder, 'sino/sino_{:05d}.tiff'.format(i_slice)), overwrite=True) return
def reconstruct(filename, inputPath="", outputPath="", COR=COR, doOutliers=doOutliers, outlier_diff=outlier_diff, outlier_size=outlier_size, doFWringremoval=doFWringremoval, ringSigma=ringSigma, ringLevel=ringLevel, ringWavelet=ringWavelet, pad_sino=pad_sino, doPhaseRetrieval=doPhaseRetrieval, propagation_dist=propagation_dist, kev=kev, alphaReg=alphaReg, butterworthpars=butterworthpars, doPolarRing=doPolarRing, Rarc=Rarc, Rmaxwidth=Rmaxwidth, Rtmax=Rtmax, Rthr=Rthr, Rtmin=Rtmin, useAutoCOR=useAutoCOR, use360to180=use360to180, num_substacks=num_substacks, recon_slice=recon_slice): # Convert filename to list type if only one file name is given if type(filename) != list: filename = [filename] # If useAutoCor == true, a list of COR will be automatically calculated for all files # If a list of COR is given, only entries with boolean False will use automatic COR calculation if useAutoCOR == True or (len(COR) != len(filename)): logging.info('using auto COR for all input files') COR = [False] * len(filename) for x in range(len(filename)): logging.info('opening data set, checking metadata') fdata, gdata = read_als_832h5_metadata(inputPath[x] + filename[x] + '.h5') pxsize = float( gdata['pxsize'] ) / 10.0 # convert from metadata (mm) to this script (cm) numslices = int(gdata['nslices']) # recon_slice == True, only center slice will be reconstructed # if integer is given, a specific if recon_slice != False: if (type(recon_slice) == int) and (recon_slice <= numslices): sinorange[recon_slice - 1, recon_slice] else: sinorange = [numslices // 2 - 1, numslices // 2] else: sinorange = [0, numslices] # Calculate number of substacks (chunks) substacks = num_substacks #(sinorange[1]-sinorange[0]-1)//num_sino_per_substack+1 if (sinorange[1] - sinorange[0]) >= substacks: num_sino_per_substack = (sinorange[1] - sinorange[0]) // num_substacks else: num_sino_per_substack = 1 firstcor, lastcor = 0, int(gdata['nangles']) - 1 projs, flat, dark, floc = dxchange.read_als_832h5( inputPath[x] + filename[x] + '.h5', ind_tomo=(firstcor, lastcor)) projs = tomopy.normalize_nf(projs, flat, dark, floc) autocor = tomopy.find_center_pc(projs[0], projs[1], tol=0.25) if (type(COR[x]) == bool) or (COR[x] < 0) or (COR[x] == 'auto'): firstcor, lastcor = 0, int(gdata['nangles']) - 1 projs, flat, dark, floc = dxchange.read_als_832h5( inputPath[x] + filename[x] + '.h5', ind_tomo=(firstcor, lastcor)) projs = tomopy.normalize_nf(projs, flat, dark, floc) cor = tomopy.find_center_pc(projs[0], projs[1], tol=0.25) else: cor = COR[x] logging.info( 'Dataset %s, has %d total slices, reconstructing slices %d through %d in %d substack(s), using COR: %f', filename[x], int(gdata['nslices']), sinorange[0], sinorange[1] - 1, substacks, cor) for y in range(0, substacks): logging.info('Starting dataset %s (%d of %d), substack %d of %d', filename[x], x + 1, len(filename), y + 1, substacks) logging.info('Reading sinograms...') projs, flat, dark, floc = dxchange.read_als_832h5( inputPath[x] + filename[x] + '.h5', sino=(sinorange[0] + y * num_sino_per_substack, sinorange[0] + (y + 1) * num_sino_per_substack, 1)) logging.info( 'Doing remove outliers, norm (nearest flats), and -log...') if doOutliers: projs = tomopy.remove_outlier(projs, outlier_diff, size=outlier_size, axis=0) flat = tomopy.remove_outlier(flat, outlier_diff, size=outlier_size, axis=0) tomo = tomopy.normalize_nf(projs, flat, dark, floc) tomo = tomopy.minus_log(tomo, out=tomo) # in place logarithm # Use padding to remove halo in reconstruction if present if pad_sino: npad = int( np.ceil(tomo.shape[2] * np.sqrt(2)) - tomo.shape[2]) // 2 tomo = tomopy.pad(tomo, 2, npad=npad, mode='edge') cor_rec = cor + npad # account for padding else: cor_rec = cor if doFWringremoval: logging.info('Doing ring (Fourier-wavelet) function...') tomo = tomopy.remove_stripe_fw(tomo, sigma=ringSigma, level=ringLevel, pad=True, wname=ringWavelet) if doPhaseRetrieval: logging.info('Doing Phase retrieval...') #tomo = tomopy.retrieve_phase(tomo, pixel_size=pxsize, dist=propagation_dist, energy=kev, alpha=alphaReg, pad=True) tomo = tomopy.retrieve_phase(tomo, pixel_size=pxsize, dist=propagation_dist, energy=kev, alpha=alphaReg, pad=True) logging.info( 'Doing recon (gridrec) function and scaling/masking, with cor %f...', cor_rec) rec = tomopy.recon(tomo, tomopy.angles(tomo.shape[0], 270, 90), center=cor_rec, algorithm='gridrec', filter_name='butterworth', filter_par=butterworthpars) #rec = tomopy.recon(tomo, tomopy.angles(tomo.shape[0], 180+angularrange/2, 180-angularrange/2), center=cor_rec, algorithm='gridrec', filter_name='butterworth', filter_par=butterworthpars) rec /= pxsize # intensity values in cm^-1 if pad_sino: rec = tomopy.circ_mask(rec[:, npad:-npad, npad:-npad], 0) else: rec = tomopy.circ_mask(rec, 0, ratio=1.0, val=0.0) if doPolarRing: logging.info('Doing ring (polar mean filter) function...') rec = tomopy.remove_ring(rec, theta_min=Rarc, rwidth=Rmaxwidth, thresh_max=Rtmax, thresh=Rthr, thresh_min=Rtmin) logging.info('Writing reconstruction slices to %s', filename[x]) #dxchange.write_tiff_stack(rec, fname=outputPath+'alpha'+str(alphaReg)+'/rec'+filename[x]+'/rec'+filename[x], start=sinorange[0]+y*num_sino_per_substack) dxchange.write_tiff_stack(rec, fname=outputPath + 'recon_' + filename[x] + '/recon_' + filename[x], start=sinorange[0] + y * num_sino_per_substack) logging.info('Reconstruction Complete: ' + filename[x])
def reconstruct(filename,inputPath="", outputPath="", COR=COR, doOutliers=doOutliers, outlier_diff=outlier_diff, outlier_size=outlier_size, doFWringremoval=doFWringremoval, ringSigma=ringSigma,ringLevel=ringLevel, ringWavelet=ringWavelet,pad_sino=pad_sino, doPhaseRetrieval=doPhaseRetrieval, propagation_dist=propagation_dist, kev=kev,alphaReg=alphaReg, butterworthpars=butterworthpars, doPolarRing=doPolarRing,Rarc=Rarc, Rmaxwidth=Rmaxwidth, Rtmax=Rtmax, Rthr=Rthr, Rtmin=Rtmin, useAutoCOR=useAutoCOR, use360to180=use360to180, num_substacks=num_substacks,recon_slice=recon_slice): # Convert filename to list type if only one file name is given if type(filename) != list: filename=[filename] # If useAutoCor == true, a list of COR will be automatically calculated for all files # If a list of COR is given, only entries with boolean False will use automatic COR calculation if useAutoCOR==True or (len(COR) != len(filename)): logging.info('using auto COR for all input files') COR = [False]*len(filename) for x in range(len(filename)): logging.info('opening data set, checking metadata') fdata, gdata = read_als_832h5_metadata(inputPath[x]+filename[x]+'.h5') pxsize = float(gdata['pxsize'])/10.0 # convert from metadata (mm) to this script (cm) numslices = int(gdata['nslices']) # recon_slice == True, only center slice will be reconstructed # if integer is given, a specific if recon_slice != False: if (type(recon_slice) == int) and (recon_slice <= numslices): sinorange [recon_slice-1, recon_slice] else: sinorange = [numslices//2-1, numslices//2] else: sinorange = [0, numslices] # Calculate number of substacks (chunks) substacks = num_substacks #(sinorange[1]-sinorange[0]-1)//num_sino_per_substack+1 if (sinorange[1]-sinorange[0]) >= substacks: num_sino_per_substack = (sinorange[1]-sinorange[0])//num_substacks else: num_sino_per_substack = 1 firstcor, lastcor = 0, int(gdata['nangles'])-1 projs, flat, dark, floc = dxchange.read_als_832h5(inputPath[x]+filename[x]+'.h5', ind_tomo=(firstcor, lastcor)) projs = tomopy.normalize_nf(projs, flat, dark, floc) autocor = tomopy.find_center_pc(projs[0], projs[1], tol=0.25) if (type(COR[x]) == bool) or (COR[x]<0) or (COR[x]=='auto'): firstcor, lastcor = 0, int(gdata['nangles'])-1 projs, flat, dark, floc = dxchange.read_als_832h5(inputPath[x]+filename[x]+'.h5', ind_tomo=(firstcor, lastcor)) projs = tomopy.normalize_nf(projs, flat, dark, floc) cor = tomopy.find_center_pc(projs[0], projs[1], tol=0.25) else: cor = COR[x] logging.info('Dataset %s, has %d total slices, reconstructing slices %d through %d in %d substack(s), using COR: %f',filename[x], int(gdata['nslices']), sinorange[0], sinorange[1]-1, substacks, cor) for y in range(0, substacks): logging.info('Starting dataset %s (%d of %d), substack %d of %d',filename[x], x+1, len(filename), y+1, substacks) logging.info('Reading sinograms...') projs, flat, dark, floc = dxchange.read_als_832h5(inputPath[x]+filename[x]+'.h5', sino=(sinorange[0]+y*num_sino_per_substack, sinorange[0]+(y+1)*num_sino_per_substack, 1)) logging.info('Doing remove outliers, norm (nearest flats), and -log...') if doOutliers: projs = tomopy.remove_outlier(projs, outlier_diff, size=outlier_size, axis=0) flat = tomopy.remove_outlier(flat, outlier_diff, size=outlier_size, axis=0) tomo = tomopy.normalize_nf(projs, flat, dark, floc) tomo = tomopy.minus_log(tomo, out=tomo) # in place logarithm # Use padding to remove halo in reconstruction if present if pad_sino: npad = int(np.ceil(tomo.shape[2] * np.sqrt(2)) - tomo.shape[2])//2 tomo = tomopy.pad(tomo, 2, npad=npad, mode='edge') cor_rec = cor + npad # account for padding else: cor_rec = cor if doFWringremoval: logging.info('Doing ring (Fourier-wavelet) function...') tomo = tomopy.remove_stripe_fw(tomo, sigma=ringSigma, level=ringLevel, pad=True, wname=ringWavelet) if doPhaseRetrieval: logging.info('Doing Phase retrieval...') #tomo = tomopy.retrieve_phase(tomo, pixel_size=pxsize, dist=propagation_dist, energy=kev, alpha=alphaReg, pad=True) tomo = tomopy.retrieve_phase(tomo, pixel_size=pxsize, dist=propagation_dist, energy=kev, alpha=alphaReg, pad=True) logging.info('Doing recon (gridrec) function and scaling/masking, with cor %f...',cor_rec) rec = tomopy.recon(tomo, tomopy.angles(tomo.shape[0], 270, 90), center=cor_rec, algorithm='gridrec', filter_name='butterworth', filter_par=butterworthpars) #rec = tomopy.recon(tomo, tomopy.angles(tomo.shape[0], 180+angularrange/2, 180-angularrange/2), center=cor_rec, algorithm='gridrec', filter_name='butterworth', filter_par=butterworthpars) rec /= pxsize # intensity values in cm^-1 if pad_sino: rec = tomopy.circ_mask(rec[:, npad:-npad, npad:-npad], 0) else: rec = tomopy.circ_mask(rec, 0, ratio=1.0, val=0.0) if doPolarRing: logging.info('Doing ring (polar mean filter) function...') rec = tomopy.remove_ring(rec, theta_min=Rarc, rwidth=Rmaxwidth, thresh_max=Rtmax, thresh=Rthr, thresh_min=Rtmin) logging.info('Writing reconstruction slices to %s', filename[x]) #dxchange.write_tiff_stack(rec, fname=outputPath+'alpha'+str(alphaReg)+'/rec'+filename[x]+'/rec'+filename[x], start=sinorange[0]+y*num_sino_per_substack) dxchange.write_tiff_stack(rec, fname=outputPath + 'recon_'+filename[x]+'/recon_'+filename[x], start=sinorange[0]+y*num_sino_per_substack) logging.info('Reconstruction Complete: '+ filename[x])
def do_work(out_path, scan_id, center_offset, mpi_projs, flats, darks, mpi_thetas): logger.info("Flat Field Correction") mpi_projs = normalize_mpi(mpi_projs, flats, darks) #utils_mpi.write_stack_mpi(out_path/"flat", mpi_projs) del flats del darks logger.info("Outlier Removal") # TODO: base parameters on clean simulation data! - might need fill projs = mpi_projs.scatter(0) # TODO: put back in, I think it is causing issues right now... tomopy.remove_outlier(projs, 0.1, 5, ncore=ncore, out=projs) #tomopy.remove_outlier_cuda(projs, 0.1, 5, ncore, out=projs) np.clip(projs, 1E-6, 1 - 1E-6, projs) # TODO: distortion correction factor? # TODO: ring removal? # # flat field change correction # remove_low_frequency_mpi(mpi_projs) # utils_mpi.write_stack_mpi(out_path/"low_frequency_removed", mpi_projs) # bulk Si intensity correction # removes constant absorption contribution from bulk Si, and mounting material # TODO: base parameters on clean simulation data! - will need fill # TODO: alternatively, refine result after good recon - with theta offset target_transmission = 0.80 logger.info(f"Setting target transmission to {target_transmission}") set_target_transmission_mpi(mpi_projs, mpi_thetas, target_transmission) projs = mpi_projs.scatter(0) np.clip(projs, 1E-6, 1 - 1E-6, projs) utils_mpi.write_stack_mpi(out_path / "constant_transmission", mpi_projs) # center finding - manual for now? if center_offset is None: logger.info("Finding center") # algorithm = "SART" # pixel_size = 2 * 0.000016 #16nm bin 1 # options = {"PixelWidth": pixel_size, # "PixelHeight": pixel_size, # "windowFOV": False, # "archDir": out_path, # "_mpi_rank": mpi_rank, # } # alg_params = {"N_iter": 1, # "N_subsets": 20, # "nonnegativityConstraint": True, # "useFBPasSeedImage": False, # # "Preconditioner": "RAMP", # # "beta": 2e-7, # # "p": 1, # # "delta": 1/20, # delta sets edge strength (difference between regions divide by ten) # # "inverseVarianceExponent": 1.0, # set to 1 to include noise model # # "other": 3, #convergence of low frequencies # } # # load data into LTT, then find the center before recon # ltt_tomopy.initialize_recon(sinos, thetas, xcenter, True, algorithm, options, ncore=ncore) # center = align_tomo.find_center_ltt(lambda c: ltt_tomopy.preview(center=c, algorithm=algorithm, sinogram_order=True, close=False, options=options, alg_params=alg_params, ncore=ncore), xcenter, 0.1, ratio=0.8) # ltt_tomopy.recon_close() logger.info("Padding sinos for center finding") mpi_sinos = utils_mpi.create_sinos_mpi(mpi_projs, ncore) sinos = mpi_sinos.scatter(0) sinos = pad_sinos(sinos) mpi_sinos = MpiArray(sinos) gthetas = mpi_thetas.allgather() xcenter = sinos.shape[2] // 2 cen_range = (xcenter - 20, xcenter + 20, 0.5) if mpi_rank == mpi_size // 2: tomopy.write_center(sinos, gthetas, out_path / ("center"), cen_range, sinogram_order=True) del mpi_sinos, sinos import sys comm.Barrier() sys.exit() # center_offset = mpi_projs.shape[2]//2-center # mpi_projs.comm.Barrier() #for printing # print(f"{mpi_projs.mpi_rank}: center={center} offset={center_offset}") #center = xcenter + center_offset # Shift correction? # use MPI and binning for speed # use LTT for all recon # define recon extent with extra X and less Z # Quick recon - LTT with SART or other? (can't use FBP) algorithm = "gridrec" options = { "filter_name": "parzen", } logger.info(f"Finding layer alignment within volume using {algorithm}") mpi_rec = tomopy_recon_mpi(mpi_projs, mpi_thetas, center_offset, algorithm, ncore=ncore, **options) utils_mpi.write_stack_mpi(out_path / ("quick_" + algorithm), mpi_rec) theta_deg, start_z1, end_z1 = align_layers.find_angle_mpi(mpi_rec, 0) logger.info(f"Theta offset angle {theta_deg:0.2} deg") # find phi angle (axis 2) phi_deg, start_z2, end_z2 = align_layers.find_angle_mpi(mpi_rec, 2) logger.info(f"Phi offset angle {phi_deg:0.2} deg") start_z = min(start_z1, start_z2) end_z = max(end_z1, end_z2) # add buffer for start and end start_z = max(start_z - 20, 0) end_z = min(end_z + 20, mpi_rec.shape[1] - 1) #FIXME: override start and end start_z = 0 end_z = mpi_rec.shape[1] - 1 logger.info(f"Layer extent: {start_z} - {end_z}") # change theta with correction for next reconstruction thetas = mpi_thetas.scatter(0) thetas += np.deg2rad(theta_deg) # modify projections to remove phi angle # TODO: combine with stage shift code projs = mpi_projs.scatter(0) align_layers.apply_phi_correction(projs, thetas, phi_deg, projs) # Quick aligned recon algorithm = "gridrec" options = { "filter_name": "parzen", } logger.info("Quick Tomopy Recon") mpi_rec = tomopy_recon_mpi(mpi_projs, mpi_thetas, center_offset, algorithm, ncore=ncore, **options) rec = mpi_rec.scatter(0) rec = rec[:, start_z:end_z, :] mpi_rec = MpiArray(rec) utils_mpi.write_stack_mpi(out_path / algorithm, mpi_rec) del mpi_rec, rec # Aligned recon # iterative recon with extra recon space in X and a restricted Z axis algorithm = "SART" #"RDLS"#"DFM"#"ASD-POCS"#"SART"#"FBP" logger.info(f"Reconstructing aligned layers using {algorithm}") mpi_sinos = utils_mpi.create_sinos_mpi(mpi_projs, ncore) #utils_mpi.write_stack_mpi(out_path/"sinos", mpi_sinos) sinos = mpi_sinos.scatter(0) # add padding to recon - fixes cupping effect xrecpadding = sinos.shape[2] // 2 pixel_size = 2 * 0.000016 # 16nm bin 1 options = { "PixelWidth": pixel_size, "PixelHeight": pixel_size, "ryoffset": start_z, "ryelements": end_z - start_z, "windowFOV": False, "rxelements": sinos.shape[2] + 2 * xrecpadding, "rxoffset": -xrecpadding, "_mpi_rank": mpi_rank, } alg_params = { "N_iter": 50, "nonnegativityConstraint": False, "useFBPasSeedImage": False, #"Preconditioner": "RAMP", #"descentType": "CG",#"GD", #"beta": 2e-7, #"p": 1, #"delta": 20/20, # delta sets edge strength (difference between regions divide by ten) #"inverseVarianceExponent": 1.0, # set to 1 to include noise model #"other": 3, #convergence of low frequencies } #TODO: add support to add overlap in future with updates between iterations (see xray_trust6.py) gthetas = mpi_thetas.allgather() #global thetas center = sinos.shape[2] // 2 + center_offset if gthetas[1] < gthetas[0]: # decreasing angle, LTT doesn't support, switch data around # TODO: load in reversed order? gthetas = gthetas[::-1] sinos[:] = sinos[:, ::-1, :] rec = ltt_tomopy.recon(sinos, gthetas, center, True, algorithm, alg_params, options, ncore=ncore) rec = rec[:, :, xrecpadding:xrecpadding + sinos.shape[2]] mpi_rec = MpiArray(rec, distribution=mpi_sinos.distribution) utils_mpi.write_stack_mpi(out_path / algorithm, mpi_rec) # Neural network processing? # Extract layers # Use template if available logger.info("Extracting Layers") mpi_layers = align_layers.extract_layers_mpi(mpi_rec) align_layers.write_layers_to_file_mpi(mpi_layers, "layers") logger.info(f"Finished {scan_id}")
def recon( filename, inputPath='./', outputPath=None, outputFilename=None, doOutliers1D=False, # outlier removal in 1d (along sinogram columns) outlier_diff1D=750, # difference between good data and outlier data (outlier removal) outlier_size1D=3, # radius around each pixel to look for outliers (outlier removal) doOutliers2D=False, # outlier removal, standard 2d on each projection outlier_diff2D=750, # difference between good data and outlier data (outlier removal) outlier_size2D=3, # radius around each pixel to look for outliers (outlier removal) doFWringremoval=True, # Fourier-wavelet ring removal doTIringremoval=False, # Titarenko ring removal doSFringremoval=False, # Smoothing filter ring removal ringSigma=3, # damping parameter in Fourier space (Fourier-wavelet ring removal) ringLevel=8, # number of wavelet transform levels (Fourier-wavelet ring removal) ringWavelet='db5', # type of wavelet filter (Fourier-wavelet ring removal) ringNBlock=0, # used in Titarenko ring removal (doTIringremoval) ringAlpha=1.5, # used in Titarenko ring removal (doTIringremoval) ringSize=5, # used in smoothing filter ring removal (doSFringremoval) doPhaseRetrieval=False, # phase retrieval alphaReg=0.0002, # smaller = smoother (used for phase retrieval) propagation_dist=75, # sample-to-scintillator distance (phase retrieval) kev=24, # energy level (phase retrieval) butterworth_cutoff=0.25, #0.1 would be very smooth, 0.4 would be very grainy (reconstruction) butterworth_order=2, # for reconstruction doPolarRing=False, # ring removal Rarc=30, # min angle needed to be considered ring artifact (ring removal) Rmaxwidth=100, # max width of rings to be filtered (ring removal) Rtmax=3000.0, # max portion of image to filter (ring removal) Rthr=3000.0, # max value of offset due to ring artifact (ring removal) Rtmin=-3000.0, # min value of image to filter (ring removal) cor=None, # center of rotation (float). If not used then cor will be detected automatically corFunction='pc', # center of rotation function to use - can be 'pc', 'vo', or 'nm' voInd=None, # index of slice to use for cor search (vo) voSMin=-40, # min radius for searching in sinogram (vo) voSMax=40, # max radius for searching in sinogram (vo) voSRad=10, # search radius (vo) voStep=0.5, # search step (vo) voRatio=2.0, # ratio of field-of-view and object size (vo) voDrop=20, # drop lines around vertical center of mask (vo) nmInd=None, # index of slice to use for cor search (nm) nmInit=None, # initial guess for center (nm) nmTol=0.5, # desired sub-pixel accuracy (nm) nmMask=True, # if True, limits analysis to circular region (nm) nmRatio=1.0, # ratio of radius of circular mask to edge of reconstructed image (nm) nmSinoOrder=False, # if True, analyzes in sinogram space. If False, analyzes in radiograph space use360to180=False, # use 360 to 180 conversion doBilateralFilter=False, # if True, uses bilateral filter on image just before write step # NOTE: image will be converted to 8bit if it is not already bilateral_srad=3, # spatial radius for bilateral filter (image will be converted to 8bit if not already) bilateral_rrad=30, # range radius for bilateral filter (image will be converted to 8bit if not already) castTo8bit=False, # convert data to 8bit before writing cast8bit_min=-10, # min value if converting to 8bit cast8bit_max=30, # max value if converting to 8bit useNormalize_nf=False, # normalize based on background intensity (nf) chunk_proj=100, # chunk size in projection direction chunk_sino=100, # chunk size in sinogram direction npad=None, # amount to pad data before reconstruction projused=None, #should be slicing in projection dimension (start,end,step) sinoused=None, #should be sliceing in sinogram dimension (start,end,step). If first value is negative, it takes the number of slices from the second value in the middle of the stack. correcttilt=0, #tilt dataset tiltcenter_slice=None, # tilt center (x direction) tiltcenter_det=None, # tilt center (y direction) angle_offset=0, #this is the angle offset from our default (270) so that tomopy yields output in the same orientation as previous software (Octopus) anglelist=None, #if not set, will assume evenly spaced angles which will be calculated by the angular range and number of angles found in the file. if set to -1, will read individual angles from each image. alternatively, a list of angles can be passed. doBeamHardening=False, #turn on beam hardening correction, based on "Correction for beam hardening in computed tomography", Gabor Herman, 1979 Phys. Med. Biol. 24 81 BeamHardeningCoefficients=None, #6 values, tomo = a0 + a1*tomo + a2*tomo^2 + a3*tomo^3 + a4*tomo^4 + a5*tomo^5 projIgnoreList=None, #projections to be ignored in the reconstruction (for simplicity in the code, they will not be removed and will be processed as all other projections but will be set to zero absorption right before reconstruction. *args, **kwargs): start_time = time.time() print("Start {} at:".format(filename) + time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.localtime())) outputPath = inputPath if outputPath is None else outputPath outputFilename = filename if outputFilename is None else outputFilename tempfilenames = [outputPath + 'tmp0.h5', outputPath + 'tmp1.h5'] filenametowrite = outputPath + '/rec' + filename.strip( ".h5") + '/' + outputFilename #filenametowrite = outputPath+'/rec'+filename+'/'+outputFilename print("cleaning up previous temp files", end="") for tmpfile in tempfilenames: try: os.remove(tmpfile) except OSError: pass print(", reading metadata") datafile = h5py.File(inputPath + filename, 'r') gdata = dict(dxchange.reader._find_dataset_group(datafile).attrs) pxsize = float(gdata['pxsize']) / 10 # /10 to convert unites from mm to cm numslices = int(gdata['nslices']) numangles = int(gdata['nangles']) angularrange = float(gdata['arange']) numrays = int(gdata['nrays']) npad = int(np.ceil(numrays * np.sqrt(2)) - numrays) // 2 if npad is None else npad projused = (0, numangles - 1, 1) if projused is None else projused # ndark = int(gdata['num_dark_fields']) # ind_dark = list(range(0, ndark)) # group_dark = [numangles - 1] inter_bright = int(gdata['i0cycle']) nflat = int(gdata['num_bright_field']) ind_flat = list(range(0, nflat)) if inter_bright > 0: group_flat = list(range(0, numangles, inter_bright)) if group_flat[-1] != numangles - 1: group_flat.append(numangles - 1) elif inter_bright == 0: group_flat = [0, numangles - 1] else: group_flat = None ind_tomo = list(range(0, numangles)) floc_independent = dxchange.reader._map_loc(ind_tomo, group_flat) #figure out the angle list (a list of angles, one per projection image) dtemp = datafile[list(datafile.keys())[0]] fltemp = list(dtemp.keys()) firstangle = float(dtemp[fltemp[0]].attrs.get('rot_angle', 0)) if anglelist is None: #the offset angle should offset from the angle of the first image, which is usually 0, but in the case of timbir data may not be. #we add the 270 to be inte same orientation as previous software used at bl832 angle_offset = 270 + angle_offset - firstangle anglelist = tomopy.angles(numangles, angle_offset, angle_offset - angularrange) elif anglelist == -1: anglelist = np.zeros(shape=numangles) for icount in range(0, numangles): anglelist[icount] = np.pi / 180 * (270 + angle_offset - float( dtemp[fltemp[icount]].attrs['rot_angle'])) #if projused is different than default, need to chnage numangles and angularrange #can't do useNormalize_nf and doOutliers2D at the same time, or doOutliers2D and doOutliers1D at the same time, b/c of the way we chunk, for now just disable that if useNormalize_nf == True and doOutliers2D == True: useNormalize_nf = False print( "we cannot currently do useNormalize_nf and doOutliers2D at the same time, turning off useNormalize_nf" ) if doOutliers2D == True and doOutliers1D == True: doOutliers1D = False print( "we cannot currently do doOutliers1D and doOutliers2D at the same time, turning off doOutliers1D" ) #figure out how user can pass to do central x number of slices, or set of slices dispersed throughout (without knowing a priori the value of numslices) if sinoused is None: sinoused = (0, numslices, 1) elif sinoused[0] < 0: sinoused = ( int(np.floor(numslices / 2.0) - np.ceil(sinoused[1] / 2.0)), int(np.floor(numslices / 2.0) + np.floor(sinoused[1] / 2.0)), 1) num_proj_per_chunk = np.minimum(chunk_proj, projused[1] - projused[0]) numprojchunks = (projused[1] - projused[0] - 1) // num_proj_per_chunk + 1 num_sino_per_chunk = np.minimum(chunk_sino, sinoused[1] - sinoused[0]) numsinochunks = (sinoused[1] - sinoused[0] - 1) // num_sino_per_chunk + 1 numprojused = (projused[1] - projused[0]) // projused[2] numsinoused = (sinoused[1] - sinoused[0]) // sinoused[2] BeamHardeningCoefficients = ( 0, 1, 0, 0, 0, .1) if BeamHardeningCoefficients is None else BeamHardeningCoefficients if cor is None: print("Detecting center of rotation", end="") if angularrange > 300: lastcor = int(np.floor(numangles / 2) - 1) else: lastcor = numangles - 1 #I don't want to see the warnings about the reader using a deprecated variable in dxchange with warnings.catch_warnings(): warnings.simplefilter("ignore") tomo, flat, dark, floc = dxchange.read_als_832h5( inputPath + filename, ind_tomo=(0, lastcor)) tomo = tomo.astype(np.float32) if useNormalize_nf: tomopy.normalize_nf(tomo, flat, dark, floc, out=tomo) else: tomopy.normalize(tomo, flat, dark, out=tomo) if corFunction == 'vo': # same reason for catching warnings as above with warnings.catch_warnings(): warnings.simplefilter("ignore") cor = tomopy.find_center_vo(tomo, ind=voInd, smin=voSMin, smax=voSMax, srad=voSRad, step=voStep, ratio=voRatio, drop=voDrop) elif corFunction == 'nm': cor = tomopy.find_center( tomo, tomopy.angles(numangles, angle_offset, angle_offset - angularrange), ind=nmInd, init=nmInit, tol=nmTol, mask=nmMask, ratio=nmRatio, sinogram_order=nmSinoOrder) elif corFunction == 'pc': cor = tomopy.find_center_pc(tomo[0], tomo[1], tol=0.25) else: raise ValueError("\'corFunction\' must be one of: [ pc, vo, nm ].") print(", {}".format(cor)) else: print("using user input center of {}".format(cor)) function_list = [] if doOutliers1D: function_list.append('remove_outlier1d') if doOutliers2D: function_list.append('remove_outlier2d') if useNormalize_nf: function_list.append('normalize_nf') else: function_list.append('normalize') function_list.append('minus_log') if doBeamHardening: function_list.append('beam_hardening') if doFWringremoval: function_list.append('remove_stripe_fw') if doTIringremoval: function_list.append('remove_stripe_ti') if doSFringremoval: function_list.append('remove_stripe_sf') if correcttilt: function_list.append('correcttilt') if use360to180: function_list.append('do_360_to_180') if doPhaseRetrieval: function_list.append('phase_retrieval') function_list.append('recon_mask') if doPolarRing: function_list.append('polar_ring') if castTo8bit: function_list.append('castTo8bit') if doBilateralFilter: function_list.append('bilateral_filter') function_list.append('write_output') # Figure out first direction to slice for func in function_list: if slice_dir[func] != 'both': axis = slice_dir[func] break done = False curfunc = 0 curtemp = 0 while True: # Loop over reading data in certain chunking direction if axis == 'proj': niter = numprojchunks else: niter = numsinochunks for y in range(niter): # Loop over chunks print("{} chunk {} of {}".format(axis, y + 1, niter)) if curfunc == 0: with warnings.catch_warnings(): warnings.simplefilter("ignore") if axis == 'proj': tomo, flat, dark, floc = dxchange.read_als_832h5( inputPath + filename, ind_tomo=range( y * num_proj_per_chunk + projused[0], np.minimum( (y + 1) * num_proj_per_chunk + projused[0], numangles)), sino=(sinoused[0], sinoused[1], sinoused[2])) else: tomo, flat, dark, floc = dxchange.read_als_832h5( inputPath + filename, ind_tomo=range(projused[0], projused[1], projused[2]), sino=(y * num_sino_per_chunk + sinoused[0], np.minimum((y + 1) * num_sino_per_chunk + sinoused[0], numslices), 1)) else: if axis == 'proj': start, end = y * num_proj_per_chunk, np.minimum( (y + 1) * num_proj_per_chunk, numprojused) tomo = dxchange.reader.read_hdf5( tempfilenames[curtemp], '/tmp/tmp', slc=((start, end, 1), (0, numslices, 1), (0, numrays, 1))) #read in intermediate file else: start, end = y * num_sino_per_chunk, np.minimum( (y + 1) * num_sino_per_chunk, numsinoused) tomo = dxchange.reader.read_hdf5(tempfilenames[curtemp], '/tmp/tmp', slc=((0, numangles, 1), (start, end, 1), (0, numrays, 1))) dofunc = curfunc keepvalues = None while True: # Loop over operations to do in current chunking direction func_name = function_list[dofunc] newaxis = slice_dir[func_name] if newaxis != 'both' and newaxis != axis: # We have to switch axis, so flush to disk if y == 0: try: os.remove(tempfilenames[1 - curtemp]) except OSError: pass appendaxis = 1 if axis == 'sino' else 0 dxchange.writer.write_hdf5( tomo, fname=tempfilenames[1 - curtemp], gname='tmp', dname='tmp', overwrite=False, appendaxis=appendaxis) #writing intermediate file... break print(func_name, end=" ") curtime = time.time() if func_name == 'remove_outlier1d': tomo = tomo.astype(np.float32, copy=False) remove_outlier1d(tomo, outlier_diff1D, size=outlier_size1D, out=tomo) if func_name == 'remove_outlier2d': tomo = tomo.astype(np.float32, copy=False) tomopy.remove_outlier(tomo, outlier_diff2D, size=outlier_size2D, axis=0, out=tomo) elif func_name == 'normalize_nf': tomo = tomo.astype(np.float32, copy=False) tomopy.normalize_nf( tomo, flat, dark, floc_independent, out=tomo ) #use floc_independent b/c when you read file in proj chunks, you don't get the correct floc returned right now to use here. elif func_name == 'normalize': tomo = tomo.astype(np.float32, copy=False) tomopy.normalize(tomo, flat, dark, out=tomo) elif func_name == 'minus_log': mx = np.float32(0.00000000000000000001) ne.evaluate('where(tomo>mx, tomo, mx)', out=tomo) tomopy.minus_log(tomo, out=tomo) elif func_name == 'beam_hardening': loc_dict = { 'a{}'.format(i): np.float32(val) for i, val in enumerate(BeamHardeningCoefficients) } tomo = ne.evaluate( 'a0 + a1*tomo + a2*tomo**2 + a3*tomo**3 + a4*tomo**4 + a5*tomo**5', local_dict=loc_dict, out=tomo) elif func_name == 'remove_stripe_fw': tomo = tomopy.remove_stripe_fw(tomo, sigma=ringSigma, level=ringLevel, pad=True, wname=ringWavelet) elif func_name == 'remove_stripe_ti': tomo = tomopy.remove_stripe_ti(tomo, nblock=ringNBlock, alpha=ringAlpha) elif func_name == 'remove_stripe_sf': tomo = tomopy.remove_stripe_sf(tomo, size=ringSize) elif func_name == 'correcttilt': if tiltcenter_slice is None: tiltcenter_slice = numslices / 2. if tiltcenter_det is None: tiltcenter_det = tomo.shape[2] / 2 new_center = tiltcenter_slice - 0.5 - sinoused[0] center_det = tiltcenter_det - 0.5 #add padding of 10 pixels, to be unpadded right after tilt correction. This makes the tilted image not have zeros at certain edges, which matters in cases where sample is bigger than the field of view. For the small amounts we are generally tilting the images, 10 pixels is sufficient. # tomo = tomopy.pad(tomo, 2, npad=10, mode='edge') # center_det = center_det + 10 cntr = (center_det, new_center) for b in range(tomo.shape[0]): tomo[b] = st.rotate( tomo[b], correcttilt, center=cntr, preserve_range=True, order=1, mode='edge', clip=True ) #center=None means image is rotated around its center; order=1 is default, order of spline interpolation # tomo = tomo[:, :, 10:-10] elif func_name == 'do_360_to_180': # Keep values around for processing the next chunk in the list keepvalues = [ angularrange, numangles, projused, num_proj_per_chunk, numprojchunks, numprojused, numrays, anglelist ] #why -.5 on one and not on the other? if tomo.shape[0] % 2 > 0: tomo = sino_360_to_180( tomo[0:-1, :, :], overlap=int( np.round((tomo.shape[2] - cor - .5)) * 2), rotation='right') angularrange = angularrange / 2 - angularrange / ( tomo.shape[0] - 1) else: tomo = sino_360_to_180( tomo[:, :, :], overlap=int(np.round((tomo.shape[2] - cor)) * 2), rotation='right') angularrange = angularrange / 2 numangles = int(numangles / 2) projused = (0, numangles - 1, 1) num_proj_per_chunk = np.minimum(chunk_proj, projused[1] - projused[0]) numprojchunks = (projused[1] - projused[0] - 1) // num_proj_per_chunk + 1 numprojused = (projused[1] - projused[0]) // projused[2] numrays = tomo.shape[2] anglelist = anglelist[:numangles] elif func_name == 'phase_retrieval': tomo = tomopy.retrieve_phase(tomo, pixel_size=pxsize, dist=propagation_dist, energy=kev, alpha=alphaReg, pad=True) elif func_name == 'recon_mask': tomo = tomopy.pad(tomo, 2, npad=npad, mode='edge') if projIgnoreList is not None: for badproj in projIgnoreList: tomo[badproj] = 0 rec = tomopy.recon( tomo, anglelist, center=cor + npad, algorithm='gridrec', filter_name='butterworth', filter_par=[butterworth_cutoff, butterworth_order]) rec = rec[:, npad:-npad, npad:-npad] rec /= pxsize # convert reconstructed voxel values from 1/pixel to 1/cm rec = tomopy.circ_mask(rec, 0) elif func_name == 'polar_ring': rec = np.ascontiguousarray(rec, dtype=np.float32) rec = tomopy.remove_ring(rec, theta_min=Rarc, rwidth=Rmaxwidth, thresh_max=Rtmax, thresh=Rthr, thresh_min=Rtmin, out=rec) elif func_name == 'castTo8bit': rec = convert8bit(rec, cast8bit_min, cast8bit_max) elif func_name == 'bilateral_filter': rec = pyF3D.run_BilateralFilter( rec, spatialRadius=bilateral_srad, rangeRadius=bilateral_rrad) elif func_name == 'write_output': dxchange.write_tiff_stack(rec, fname=filenametowrite, start=y * num_sino_per_chunk + sinoused[0]) print('(took {:.2f} seconds)'.format(time.time() - curtime)) dofunc += 1 if dofunc == len(function_list): break if y < niter - 1 and keepvalues: # Reset original values for next chunk angularrange, numangles, projused, num_proj_per_chunk, numprojchunks, numprojused, numrays, anglelist = keepvalues curtemp = 1 - curtemp curfunc = dofunc if curfunc == len(function_list): break axis = slice_dir[function_list[curfunc]] print("cleaning up temp files") for tmpfile in tempfilenames: try: os.remove(tmpfile) except OSError: pass print("End Time: " + time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.localtime())) print('It took {:.3f} s to process {}'.format(time.time() - start_time, inputPath + filename))
last_sino_slice = sinorange[0]+(y+1)*num_sino_per_chunk else: first_sino_slice = np.maximum(sinorange[0]+y*num_sino_per_chunk-overlap_chunk, sinorange[0]) last_sino_slice = np.minimum(sinorange[0]+(y+1)*num_sino_per_chunk+overlap_chunk, sinorange[1]) projs, flat, dark, flocNotUsedSeeOtherFLOCvar = dxchange.read_als_832h5(idirectory+iname[x]+'.h5', sino=(first_sino_slice, last_sino_slice, 1)) if specialBakCrop[x]>0: num_per_flat = flat.shape[0]//original_floc_length #logging.info('length flats before: %d',flat.shape[0]) flat = flat[0:-specialBakCrop[x]*num_per_flat] #logging.info('length flats after: %d',flat.shape[0]) if doOutliers: projs = tomopy.remove_outlier(projs, outlier_diff, size=outlier_size, axis=0) flat = tomopy.remove_outlier(flat, outlier_diff, size=outlier_size, axis=0) if useNormalize_nf: logging.info('Doing normalize (nearest flats)') tomo = tomopy.normalize_nf(projs, flat, dark, floc) else: logging.info('Doing normalize') tomo = tomopy.normalize(projs, flat, dark) #sinofilenametowrite = odirectory+'/rec'+iname[x]+'/'+iname[x]+'sino' #dxchange.write_tiff_stack(tomo, fname=sinofilenametowrite, start=sinorange[0]+y*num_sino_per_chunk,axis=1) projs = None flat = None
logger.info("flat field correction..") flat = np.median(flats, axis=0) dark = np.median(darks, axis=0) del flats, darks # Only use middle of image if crop_center is not None: logger.info("Cropping to center of image") slc = center_slices(projs[0], [crop_center, crop_center]) projs = projs[:, slc[0], slc[1]] dark = dark[slc] flat = flat[slc] projs = tomopy.normalize(projs, flat[np.newaxis,:,:], dark[np.newaxis,:,:]) logger.info("Remove outliers") tomopy.remove_outlier(projs, 0.1, out=projs) # logger.info("shift correction using projections") # shifts = proj_shift_correction(projs) #print(shifts) #np.savetxt("shifts_proj.txt", shifts) #apply_shifts(projs, shifts, out=projs) #shifted_projs = projs #change name after shifts #del projs #write_stack("shifted_projs_using_elastix", shifted_projs) # initial shifts #shifts = np.zeros((projs.shape[0], 2), np.float32) #shifts = np.loadtxt("shifts_ltt.txt") # # align using image correlation
def recon( filename, inputPath = './', outputPath = None, outputFilename = None, doOutliers1D = False, # outlier removal in 1d (along sinogram columns) outlier_diff1D = 750, # difference between good data and outlier data (outlier removal) outlier_size1D = 3, # radius around each pixel to look for outliers (outlier removal) doOutliers2D = False, # outlier removal, standard 2d on each projection outlier_diff2D = 750, # difference between good data and outlier data (outlier removal) outlier_size2D = 3, # radius around each pixel to look for outliers (outlier removal) doFWringremoval = True, # Fourier-wavelet ring removal doTIringremoval = False, # Titarenko ring removal doSFringremoval = False, # Smoothing filter ring removal ringSigma = 3, # damping parameter in Fourier space (Fourier-wavelet ring removal) ringLevel = 8, # number of wavelet transform levels (Fourier-wavelet ring removal) ringWavelet = 'db5', # type of wavelet filter (Fourier-wavelet ring removal) ringNBlock = 0, # used in Titarenko ring removal (doTIringremoval) ringAlpha = 1.5, # used in Titarenko ring removal (doTIringremoval) ringSize = 5, # used in smoothing filter ring removal (doSFringremoval) doPhaseRetrieval = False, # phase retrieval alphaReg = 0.0002, # smaller = smoother (used for phase retrieval) propagation_dist = 75, # sample-to-scintillator distance (phase retrieval) kev = 24, # energy level (phase retrieval) butterworth_cutoff = 0.25, #0.1 would be very smooth, 0.4 would be very grainy (reconstruction) butterworth_order = 2, # for reconstruction doTranslationCorrection = False, # correct for linear drift during scan xshift = 0, # undesired dx transation correction (from 0 degree to 180 degree proj) yshift = 0, # undesired dy transation correction (from 0 degree to 180 degree proj) doPolarRing = False, # ring removal Rarc=30, # min angle needed to be considered ring artifact (ring removal) Rmaxwidth=100, # max width of rings to be filtered (ring removal) Rtmax=3000.0, # max portion of image to filter (ring removal) Rthr=3000.0, # max value of offset due to ring artifact (ring removal) Rtmin=-3000.0, # min value of image to filter (ring removal) cor=None, # center of rotation (float). If not used then cor will be detected automatically corFunction = 'pc', # center of rotation function to use - can be 'pc', 'vo', or 'nm' voInd = None, # index of slice to use for cor search (vo) voSMin = -40, # min radius for searching in sinogram (vo) voSMax = 40, # max radius for searching in sinogram (vo) voSRad = 10, # search radius (vo) voStep = 0.5, # search step (vo) voRatio = 2.0, # ratio of field-of-view and object size (vo) voDrop = 20, # drop lines around vertical center of mask (vo) nmInd = None, # index of slice to use for cor search (nm) nmInit = None, # initial guess for center (nm) nmTol = 0.5, # desired sub-pixel accuracy (nm) nmMask = True, # if True, limits analysis to circular region (nm) nmRatio = 1.0, # ratio of radius of circular mask to edge of reconstructed image (nm) nmSinoOrder = False, # if True, analyzes in sinogram space. If False, analyzes in radiograph space use360to180 = False, # use 360 to 180 conversion doBilateralFilter = False, # if True, uses bilateral filter on image just before write step # NOTE: image will be converted to 8bit if it is not already bilateral_srad = 3, # spatial radius for bilateral filter (image will be converted to 8bit if not already) bilateral_rrad = 30, # range radius for bilateral filter (image will be converted to 8bit if not already) castTo8bit = False, # convert data to 8bit before writing cast8bit_min=-10, # min value if converting to 8bit cast8bit_max=30, # max value if converting to 8bit useNormalize_nf = False, # normalize based on background intensity (nf) chunk_proj = 100, # chunk size in projection direction chunk_sino = 100, # chunk size in sinogram direction npad = None, # amount to pad data before reconstruction projused = None, #should be slicing in projection dimension (start,end,step) sinoused = None, #should be sliceing in sinogram dimension (start,end,step). If first value is negative, it takes the number of slices from the second value in the middle of the stack. correcttilt = 0, #tilt dataset tiltcenter_slice = None, # tilt center (x direction) tiltcenter_det = None, # tilt center (y direction) angle_offset = 0, #this is the angle offset from our default (270) so that tomopy yields output in the same orientation as previous software (Octopus) anglelist = None, #if not set, will assume evenly spaced angles which will be calculated by the angular range and number of angles found in the file. if set to -1, will read individual angles from each image. alternatively, a list of angles can be passed. doBeamHardening = False, #turn on beam hardening correction, based on "Correction for beam hardening in computed tomography", Gabor Herman, 1979 Phys. Med. Biol. 24 81 BeamHardeningCoefficients = None, #6 values, tomo = a0 + a1*tomo + a2*tomo^2 + a3*tomo^3 + a4*tomo^4 + a5*tomo^5 projIgnoreList = None, #projections to be ignored in the reconstruction (for simplicity in the code, they will not be removed and will be processed as all other projections but will be set to zero absorption right before reconstruction. *args, **kwargs): start_time = time.time() print("Start {} at:".format(filename)+time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.localtime())) outputPath = inputPath if outputPath is None else outputPath outputFilename = filename if outputFilename is None else outputFilename outputFilename = outputFilename.replace('.h5','') tempfilenames = [outputPath+'tmp0.h5',outputPath+'tmp1.h5'] filenametowrite = outputPath+'/rec'+filename.strip(".h5")+'/'+outputFilename #filenametowrite = outputPath+'/rec'+filename+'/'+outputFilename print("cleaning up previous temp files", end="") for tmpfile in tempfilenames: try: os.remove(tmpfile) except OSError: pass print(", reading metadata") datafile = h5py.File(inputPath+filename, 'r') gdata = dict(dxchange.reader._find_dataset_group(datafile).attrs) pxsize = float(gdata['pxsize'])/10 # /10 to convert units from mm to cm numslices = int(gdata['nslices']) numangles = int(gdata['nangles']) angularrange = float(gdata['arange']) numrays = int(gdata['nrays']) npad = int(np.ceil(numrays * np.sqrt(2)) - numrays)//2 if npad is None else npad projused = (0,numangles-1,1) if projused is None else projused # ndark = int(gdata['num_dark_fields']) # ind_dark = list(range(0, ndark)) # group_dark = [numangles - 1] inter_bright = int(gdata['i0cycle']) nflat = int(gdata['num_bright_field']) ind_flat = list(range(0, nflat)) if inter_bright > 0: group_flat = list(range(0, numangles, inter_bright)) if group_flat[-1] != numangles - 1: group_flat.append(numangles - 1) elif inter_bright == 0: group_flat = [0, numangles - 1] else: group_flat = None ind_tomo = list(range(0, numangles)) floc_independent = dxchange.reader._map_loc(ind_tomo, group_flat) #figure out the angle list (a list of angles, one per projection image) dtemp = datafile[list(datafile.keys())[0]] fltemp = list(dtemp.keys()) firstangle = float(dtemp[fltemp[0]].attrs.get('rot_angle',0)) if anglelist is None: #the offset angle should offset from the angle of the first image, which is usually 0, but in the case of timbir data may not be. #we add the 270 to be inte same orientation as previous software used at bl832 angle_offset = 270 + angle_offset - firstangle anglelist = tomopy.angles(numangles, angle_offset, angle_offset-angularrange) elif anglelist==-1: anglelist = np.zeros(shape=numangles) for icount in range(0,numangles): anglelist[icount] = np.pi/180*(270 + angle_offset - float(dtemp[fltemp[icount]].attrs['rot_angle'])) #if projused is different than default, need to chnage numangles and angularrange #can't do useNormalize_nf and doOutliers2D at the same time, or doOutliers2D and doOutliers1D at the same time, b/c of the way we chunk, for now just disable that if useNormalize_nf==True and doOutliers2D==True: useNormalize_nf = False print("we cannot currently do useNormalize_nf and doOutliers2D at the same time, turning off useNormalize_nf") if doOutliers2D==True and doOutliers1D==True: doOutliers1D = False print("we cannot currently do doOutliers1D and doOutliers2D at the same time, turning off doOutliers1D") #figure out how user can pass to do central x number of slices, or set of slices dispersed throughout (without knowing a priori the value of numslices) if sinoused is None: sinoused = (0,numslices,1) elif sinoused[0]<0: sinoused=(int(np.floor(numslices/2.0)-np.ceil(sinoused[1]/2.0)),int(np.floor(numslices/2.0)+np.floor(sinoused[1]/2.0)),1) num_proj_per_chunk = np.minimum(chunk_proj,projused[1]-projused[0]) numprojchunks = (projused[1]-projused[0]-1)//num_proj_per_chunk+1 num_sino_per_chunk = np.minimum(chunk_sino,sinoused[1]-sinoused[0]) numsinochunks = (sinoused[1]-sinoused[0]-1)//num_sino_per_chunk+1 numprojused = (projused[1]-projused[0])//projused[2] numsinoused = (sinoused[1]-sinoused[0])//sinoused[2] BeamHardeningCoefficients = (0, 1, 0, 0, 0, .1) if BeamHardeningCoefficients is None else BeamHardeningCoefficients if cor is None: print("Detecting center of rotation", end="") if angularrange>300: lastcor = int(np.floor(numangles/2)-1) else: lastcor = numangles-1 #I don't want to see the warnings about the reader using a deprecated variable in dxchange with warnings.catch_warnings(): warnings.simplefilter("ignore") tomo, flat, dark, floc = dxchange.read_als_832h5(inputPath+filename,ind_tomo=(0,lastcor)) tomo = tomo.astype(np.float32) if useNormalize_nf: tomopy.normalize_nf(tomo, flat, dark, floc, out=tomo) else: tomopy.normalize(tomo, flat, dark, out=tomo) if corFunction == 'vo': # same reason for catching warnings as above with warnings.catch_warnings(): warnings.simplefilter("ignore") cor = tomopy.find_center_vo(tomo, ind=voInd, smin=voSMin, smax=voSMax, srad=voSRad, step=voStep, ratio=voRatio, drop=voDrop) elif corFunction == 'nm': cor = tomopy.find_center(tomo, tomopy.angles(numangles, angle_offset, angle_offset-angularrange), ind=nmInd, init=nmInit, tol=nmTol, mask=nmMask, ratio=nmRatio, sinogram_order=nmSinoOrder) elif corFunction == 'pc': cor = tomopy.find_center_pc(tomo[0], tomo[1], tol=0.25) else: raise ValueError("\'corFunction\' must be one of: [ pc, vo, nm ].") print(", {}".format(cor)) else: print("using user input center of {}".format(cor)) function_list = [] if doOutliers1D: function_list.append('remove_outlier1d') if doOutliers2D: function_list.append('remove_outlier2d') if useNormalize_nf: function_list.append('normalize_nf') else: function_list.append('normalize') function_list.append('minus_log') if doBeamHardening: function_list.append('beam_hardening') if doFWringremoval: function_list.append('remove_stripe_fw') if doTIringremoval: function_list.append('remove_stripe_ti') if doSFringremoval: function_list.append('remove_stripe_sf') if correcttilt: function_list.append('correcttilt') if use360to180: function_list.append('do_360_to_180') if doPhaseRetrieval: function_list.append('phase_retrieval') function_list.append('recon_mask') if doPolarRing: function_list.append('polar_ring') if castTo8bit: function_list.append('castTo8bit') if doBilateralFilter: function_list.append('bilateral_filter') function_list.append('write_output') # Figure out first direction to slice for func in function_list: if slice_dir[func] != 'both': axis = slice_dir[func] break done = False curfunc = 0 curtemp = 0 while True: # Loop over reading data in certain chunking direction if axis=='proj': niter = numprojchunks else: niter = numsinochunks for y in range(niter): # Loop over chunks print("{} chunk {} of {}".format(axis, y+1, niter)) if curfunc==0: with warnings.catch_warnings(): warnings.simplefilter("ignore") if axis=='proj': tomo, flat, dark, floc = dxchange.read_als_832h5(inputPath+filename,ind_tomo=range(y*num_proj_per_chunk+projused[0],np.minimum((y + 1)*num_proj_per_chunk+projused[0],numangles)),sino=(sinoused[0],sinoused[1], sinoused[2]) ) else: tomo, flat, dark, floc = dxchange.read_als_832h5(inputPath+filename,ind_tomo=range(projused[0],projused[1],projused[2]),sino=(y*num_sino_per_chunk+sinoused[0],np.minimum((y + 1)*num_sino_per_chunk+sinoused[0],numslices),1) ) else: if axis=='proj': start, end = y * num_proj_per_chunk, np.minimum((y + 1) * num_proj_per_chunk,numprojused) tomo = dxchange.reader.read_hdf5(tempfilenames[curtemp],'/tmp/tmp',slc=((start,end,1),(0,numslices,1),(0,numrays,1))) #read in intermediate file else: start, end = y * num_sino_per_chunk, np.minimum((y + 1) * num_sino_per_chunk,numsinoused) tomo = dxchange.reader.read_hdf5(tempfilenames[curtemp],'/tmp/tmp',slc=((0,numangles,1),(start,end,1),(0,numrays,1))) dofunc = curfunc keepvalues = None while True: # Loop over operations to do in current chunking direction func_name = function_list[dofunc] newaxis = slice_dir[func_name] if newaxis != 'both' and newaxis != axis: # We have to switch axis, so flush to disk if y==0: try: os.remove(tempfilenames[1-curtemp]) except OSError: pass appendaxis = 1 if axis=='sino' else 0 dxchange.writer.write_hdf5(tomo,fname=tempfilenames[1-curtemp],gname='tmp',dname='tmp',overwrite=False,appendaxis=appendaxis) #writing intermediate file... break print(func_name, end=" ") curtime = time.time() if func_name == 'remove_outlier1d': tomo = tomo.astype(np.float32,copy=False) remove_outlier1d(tomo, outlier_diff1D, size=outlier_size1D, out=tomo) if func_name == 'remove_outlier2d': tomo = tomo.astype(np.float32,copy=False) tomopy.remove_outlier(tomo, outlier_diff2D, size=outlier_size2D, axis=0, out=tomo) elif func_name == 'normalize_nf': tomo = tomo.astype(np.float32,copy=False) tomopy.normalize_nf(tomo, flat, dark, floc_independent, out=tomo) #use floc_independent b/c when you read file in proj chunks, you don't get the correct floc returned right now to use here. elif func_name == 'normalize': tomo = tomo.astype(np.float32,copy=False) tomopy.normalize(tomo, flat, dark, out=tomo) elif func_name == 'minus_log': mx = np.float32(0.00000000000000000001) ne.evaluate('where(tomo>mx, tomo, mx)', out=tomo) tomopy.minus_log(tomo, out=tomo) elif func_name == 'beam_hardening': loc_dict = {'a{}'.format(i):np.float32(val) for i,val in enumerate(BeamHardeningCoefficients)} tomo = ne.evaluate('a0 + a1*tomo + a2*tomo**2 + a3*tomo**3 + a4*tomo**4 + a5*tomo**5', local_dict=loc_dict, out=tomo) elif func_name == 'remove_stripe_fw': tomo = tomopy.remove_stripe_fw(tomo, sigma=ringSigma, level=ringLevel, pad=True, wname=ringWavelet) elif func_name == 'remove_stripe_ti': tomo = tomopy.remove_stripe_ti(tomo, nblock=ringNBlock, alpha=ringAlpha) elif func_name == 'remove_stripe_sf': tomo = tomopy.remove_stripe_sf(tomo, size=ringSize) elif func_name == 'correcttilt': if tiltcenter_slice is None: tiltcenter_slice = numslices/2. if tiltcenter_det is None: tiltcenter_det = tomo.shape[2]/2 new_center = tiltcenter_slice - 0.5 - sinoused[0] center_det = tiltcenter_det - 0.5 #add padding of 10 pixels, to be unpadded right after tilt correction. This makes the tilted image not have zeros at certain edges, which matters in cases where sample is bigger than the field of view. For the small amounts we are generally tilting the images, 10 pixels is sufficient. # tomo = tomopy.pad(tomo, 2, npad=10, mode='edge') # center_det = center_det + 10 cntr = (center_det, new_center) for b in range(tomo.shape[0]): tomo[b] = st.rotate(tomo[b], correcttilt, center=cntr, preserve_range=True, order=1, mode='edge', clip=True) #center=None means image is rotated around its center; order=1 is default, order of spline interpolation # tomo = tomo[:, :, 10:-10] elif func_name == 'do_360_to_180': # Keep values around for processing the next chunk in the list keepvalues = [angularrange, numangles, projused, num_proj_per_chunk, numprojchunks, numprojused, numrays, anglelist] #why -.5 on one and not on the other? if tomo.shape[0]%2>0: tomo = sino_360_to_180(tomo[0:-1,:,:], overlap=int(np.round((tomo.shape[2]-cor-.5))*2), rotation='right') angularrange = angularrange/2 - angularrange/(tomo.shape[0]-1) else: tomo = sino_360_to_180(tomo[:,:,:], overlap=int(np.round((tomo.shape[2]-cor))*2), rotation='right') angularrange = angularrange/2 numangles = int(numangles/2) projused = (0,numangles-1,1) num_proj_per_chunk = np.minimum(chunk_proj,projused[1]-projused[0]) numprojchunks = (projused[1]-projused[0]-1)//num_proj_per_chunk+1 numprojused = (projused[1]-projused[0])//projused[2] numrays = tomo.shape[2] anglelist = anglelist[:numangles] elif func_name == 'phase_retrieval': tomo = tomopy.retrieve_phase(tomo, pixel_size=pxsize, dist=propagation_dist, energy=kev, alpha=alphaReg, pad=True) elif func_name == 'translation_correction': tomo = linear_translation_correction(tomo,dx=xshift,dy=yshift,interpolation=False): elif func_name == 'recon_mask': tomo = tomopy.pad(tomo, 2, npad=npad, mode='edge') if projIgnoreList is not None: for badproj in projIgnoreList: tomo[badproj] = 0 rec = tomopy.recon(tomo, anglelist, center=cor+npad, algorithm='gridrec', filter_name='butterworth', filter_par=[butterworth_cutoff, butterworth_order]) rec = rec[:, npad:-npad, npad:-npad] rec /= pxsize # convert reconstructed voxel values from 1/pixel to 1/cm rec = tomopy.circ_mask(rec, 0) elif func_name == 'polar_ring': rec = np.ascontiguousarray(rec, dtype=np.float32) rec = tomopy.remove_ring(rec, theta_min=Rarc, rwidth=Rmaxwidth, thresh_max=Rtmax, thresh=Rthr, thresh_min=Rtmin,out=rec) elif func_name == 'castTo8bit': rec = convert8bit(rec, cast8bit_min, cast8bit_max) elif func_name == 'bilateral_filter': rec = pyF3D.run_BilateralFilter(rec, spatialRadius=bilateral_srad, rangeRadius=bilateral_rrad) elif func_name == 'write_output': dxchange.write_tiff_stack(rec, fname=filenametowrite, start=y*num_sino_per_chunk + sinoused[0]) print('(took {:.2f} seconds)'.format(time.time()-curtime)) dofunc+=1 if dofunc==len(function_list): break if y<niter-1 and keepvalues: # Reset original values for next chunk angularrange, numangles, projused, num_proj_per_chunk, numprojchunks, numprojused, numrays, anglelist = keepvalues curtemp = 1 - curtemp curfunc = dofunc if curfunc==len(function_list): break axis = slice_dir[function_list[curfunc]] print("cleaning up temp files") for tmpfile in tempfilenames: try: os.remove(tmpfile) except OSError: pass print("End Time: "+time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.localtime())) print('It took {:.3f} s to process {}'.format(time.time()-start_time,inputPath+filename))
def recon_hdf5(src_fanme, dest_folder, sino_range, sino_step, shift_grid, center_vec=None, center_eq=None, dtype='float32', algorithm='gridrec', tolerance=1, chunk_size=20, save_sino=False, sino_blur=None, flattened_radius=120, mode='180', test_mode=False, phase_retrieval=None, ring_removal=True, crop=None, num_iter=None, pad_length=0, read_theta=True, **kwargs): """ center_eq: a and b parameters in fitted center position equation center = a*slice + b. """ if not os.path.exists(dest_folder): try: os.mkdir(dest_folder) except: pass sino_ini = int(sino_range[0]) sino_end = int(sino_range[1]) sino_ls_all = np.arange(sino_ini, sino_end, sino_step, dtype='int') alloc_set = allocate_mpi_subsets(sino_ls_all.size, size, task_list=sino_ls_all) sino_ls = alloc_set[rank] # prepare metadata f = h5py.File(src_fanme) dset = f['exchange/data'] full_shape = dset.shape if read_theta: _, _, _, theta = read_data_adaptive(src_fanme, proj=(0, 1)) else: theta = tomopy.angles(full_shape[0]) if center_eq is not None: a, b = center_eq center_ls = sino_ls_all * a + b center_ls = np.round(center_ls) for iblock in range(int(sino_ls.size / chunk_size) + 1): internal_print('Beginning block {:d}.'.format(iblock)) t0 = time.time() istart = iblock * chunk_size iend = np.min([(iblock + 1) * chunk_size, sino_ls.size]) sub_sino_ls = sino_ls[istart:iend - 1] center = np.take(center_ls, sub_sino_ls) data = np.zeros([dset.shape[0], len(sub_sino_ls), dset.shape[2]]) for ind, i in enumerate(sub_sino_ls): data[:, ind, :] = dset[:, i, :] data[np.isnan(data)] = 0 data = data.astype('float32') data = tomopy.remove_stripe_ti(data, alpha=4) if sino_blur is not None: for i in range(data.shape[1]): data[:, i, :] = gaussian_filter(data[:, i, :], sino_blur) if phase_retrieval: data = tomopy.retrieve_phase(data, kwargs['pixel_size'], kwargs['dist'], kwargs['energy'], kwargs['alpha']) if pad_length != 0: data = pad_sinogram(data, pad_length) data = tomopy.remove_stripe_ti(data, alpha=4) if ring_removal: rec0 = tomopy.recon(data, theta, center=center + pad_length, algorithm=algorithm, **kwargs) rec = tomopy.remove_ring(np.copy(rec0)) cent = int((rec.shape[1] - 1) / 2) xx, yy = np.meshgrid(np.arange(rec.shape[2]), np.arange(rec.shape[1])) mask0 = ((xx - cent)**2 + (yy - cent)**2 <= flattened_radius**2) mask = np.zeros(rec.shape, dtype='bool') for i in range(mask.shape[0]): mask[i, :, :] = mask0 rec[mask] = (rec[mask] + rec0[mask]) / 2 else: rec = tomopy.recon(data, theta, center=center + pad_length, algorithm=algorithm, **kwargs) if pad_length != 0: rec = rec[:, pad_length:pad_length + full_shape[2], pad_length:pad_length + full_shape[2]] rec = tomopy.remove_outlier(rec, tolerance) rec = tomopy.circ_mask(rec, axis=0, ratio=0.95) if crop is not None: crop = np.asarray(crop) rec = rec[:, crop[0, 0]:crop[1, 0], crop[0, 1]:crop[1, 1]] for i in range(rec.shape[0]): slice = sub_sino_ls[i] dxchange.write_tiff( rec[i, :, :], fname=os.path.join( dest_folder, 'recon/recon_{:05d}.tiff').format(slice)) if save_sino: dxchange.write_tiff( data[:, i, :], fname=os.path.join( dest_folder, 'sino/recon_{:05d}_{:d}.tiff').format( slice, int(center[i]))) iblock += 1 internal_print('Block {:d} finished in {:.2f} s.'.format( iblock, time.time() - t0)) else: # divide chunks grid_bins = np.append(np.ceil(shift_grid[:, 0, 0]), full_shape[1]) chunks = [] center_ls = [] istart = 0 counter = 0 # irow should be 0 for slice 0 irow = np.searchsorted(grid_bins, sino_ls[0], side='right') - 1 for i in range(sino_ls.size): counter += 1 sino_next = i + 1 if i != sino_ls.size - 1 else i if counter >= chunk_size or sino_ls[sino_next] >= grid_bins[ irow + 1] or sino_next == i: iend = i + 1 chunks.append((istart, iend)) istart = iend center_ls.append(center_vec[irow]) if sino_ls[sino_next] >= grid_bins[irow + 1]: irow += 1 counter = 0 # reconstruct chunks iblock = 1 for (istart, iend), center in zip(chunks, center_ls): internal_print('Beginning block {:d}.'.format(iblock)) t0 = time.time() internal_print('Reading data...') sub_sino_ls = sino_ls[istart:iend] data = np.zeros([dset.shape[0], len(sub_sino_ls), dset.shape[2]]) for ind, i in enumerate(sub_sino_ls): data[:, ind, :] = dset[:, i, :] if mode == '360': overlap = 2 * (dset.shape[2] - center) data = tomosaic.sino_360_to_180(data, overlap=overlap, rotation='right') theta = tomopy.angles(data.shape[0]) data[np.isnan(data)] = 0 data = data.astype('float32') if sino_blur is not None: for i in range(data.shape[1]): data[:, i, :] = gaussian_filter(data[:, i, :], sino_blur) if phase_retrieval: data = tomopy.retrieve_phase(data, kwargs['pixel_size'], kwargs['dist'], kwargs['energy'], kwargs['alpha']) if pad_length != 0: data = pad_sinogram(data, pad_length) data = tomopy.remove_stripe_ti(data, alpha=4) if ring_removal: rec0 = tomopy.recon(data, theta, center=center + pad_length, algorithm=algorithm, **kwargs) rec = tomopy.remove_ring(np.copy(rec0)) cent = int((rec.shape[1] - 1) / 2) xx, yy = np.meshgrid(np.arange(rec.shape[2]), np.arange(rec.shape[1])) mask0 = ((xx - cent)**2 + (yy - cent)**2 <= flattened_radius**2) mask = np.zeros(rec.shape, dtype='bool') for i in range(mask.shape[0]): mask[i, :, :] = mask0 rec[mask] = (rec[mask] + rec0[mask]) / 2 else: rec = tomopy.recon(data, theta, center=center + pad_length, algorithm=algorithm, **kwargs) if pad_length != 0: rec = rec[:, pad_length:pad_length + full_shape[2], pad_length:pad_length + full_shape[2]] rec = tomopy.remove_outlier(rec, tolerance) rec = tomopy.circ_mask(rec, axis=0, ratio=0.95) if crop is not None: crop = np.asarray(crop) rec = rec[:, crop[0, 0]:crop[1, 0], crop[0, 1]:crop[1, 1]] for i in range(rec.shape[0]): slice = sub_sino_ls[i] if test_mode: dxchange.write_tiff( rec[i, :, :], fname=os.path.join( dest_folder, 'recon/recon_{:05d}_{:d}.tiff').format( slice, center), dtype=dtype) else: dxchange.write_tiff( rec[i, :, :], fname=os.path.join( dest_folder, 'recon/recon_{:05d}.tiff').format(slice), dtype=dtype) if save_sino: dxchange.write_tiff( data[:, i, :], fname=os.path.join( dest_folder, 'sino/recon_{:05d}_{:d}.tiff').format( slice, center), dtype=dtype) internal_print('Block {:d} finished in {:.2f} s.'.format( iblock, time.time() - t0)) iblock += 1 return
def recon_hdf5_mpi(src_fanme, dest_folder, sino_range, sino_step, center_vec, shift_grid, dtype='float32', algorithm='gridrec', tolerance=1, save_sino=False, sino_blur=None, **kwargs): """ Reconstruct a single tile, or fused HDF5 created using util/total_fusion. MPI supported. """ raise DeprecationWarning if rank == 0: if not os.path.exists(dest_folder): os.mkdir(dest_folder) sino_ini = int(sino_range[0]) sino_end = int(sino_range[1]) f = h5py.File(src_fanme) dset = f['exchange/data'] full_shape = dset.shape theta = tomopy.angles(full_shape[0]) center_vec = np.asarray(center_vec) sino_ls = np.arange(sino_ini, sino_end, sino_step, dtype='int') grid_bins = np.ceil(shift_grid[:, 0, 0]) t0 = time.time() alloc_set = allocate_mpi_subsets(sino_ls.size, size, task_list=sino_ls) for slice in alloc_set[rank]: print(' Rank {:d}: reconstructing {:d}'.format(rank, slice)) grid_line = np.digitize(slice, grid_bins) grid_line = grid_line - 1 center = center_vec[grid_line] data = dset[:, slice, :] if sino_blur is not None: data = gaussian_filter(data, sino_blur) data = data.reshape([full_shape[0], 1, full_shape[2]]) data[np.isnan(data)] = 0 data = data.astype('float32') if save_sino: dxchange.write_tiff(data[:, slice, :], fname=os.path.join( dest_folder, 'sino/recon_{:05d}_{:d}.tiff').format( slice, center)) # data = tomopy.remove_stripe_ti(data) rec = tomopy.recon(data, theta, center=center, algorithm=algorithm, **kwargs) # rec = tomopy.remove_ring(rec) rec = tomopy.remove_outlier(rec, tolerance) rec = tomopy.circ_mask(rec, axis=0, ratio=0.95) dxchange.write_tiff(rec, fname='{:s}/recon/recon_{:05d}_{:d}'.format( dest_folder, slice, center), dtype=dtype) print('Rank {:d} finished in {:.2f} s.'.format(rank, time.time() - t0)) return