def bp(self, prj: np.ndarray, angle_ind: int = None): """Back-projection of a single sinogram line to the volume. Parameters ---------- prj : numpy.array_like The sinogram to back-project or a single line. angle_ind : int, optional The angle index to foward project. The default is None. Returns ------- numpy.array_like The back-projected volume. """ filter_name = _set_filter_name(None) if angle_ind is None: vol = np.empty([self.prj_shape[-1], *self.vol_shape], dtype=prj.dtype) for ii_a, a in enumerate(self.angles_rot_deg): vol[ii_a, ...] = skt.iradon(prj[ii_a, :, np.newaxis], [a], **filter_name) return vol.sum(axis=0) else: return skt.iradon(prj[:, np.newaxis], self.angles_rot_deg[angle_ind:angle_ind + 1:], **filter_name)
def baseline(sinogram, noisy_sinogram, sino_scale, theta): r""" Use filtered backprojection for baseline recon. Invert the radon transform using filtered backprojection for both the noise-free and noisy sinograms. Since the sinogram was scaled using sino_scale as described above, we need to apply the inverse scaling to the reconstructions. Args: sinogram: sinogram from get_scaled_sinogram noisy_sinogram: noisy sinogram from add_noise sino_scale: scale from get_scaled_sinogram theta: angles of the views in the sinogram Returns: Filtered backprojection for sinogram and noisy_sinogram """ # Invert the radon transform in the noise-free and noisy cases fbp_recon = iradon(sinogram, theta=theta, circle=True, filter_name='ramp') fbp_noisy_recon = iradon(noisy_sinogram, theta=theta, circle=True, filter_name='ramp') # Scale to recover the appropriate units fbp_recon = fbp_recon / sino_scale fbp_noisy_recon = fbp_noisy_recon / sino_scale return fbp_recon, fbp_noisy_recon
def check_iradon_center(size, theta, circle): debug = False # Create a test sinogram corresponding to a single projection # with a single non-zero pixel at the rotation center if circle: sinogram = np.zeros((size, 1), dtype=np.float) sinogram[size // 2, 0] = 1.0 else: diagonal = int(np.ceil(np.sqrt(2) * size)) sinogram = np.zeros((diagonal, 1), dtype=np.float) sinogram[sinogram.shape[0] // 2, 0] = 1.0 maxpoint = np.unravel_index(np.argmax(sinogram), sinogram.shape) print("shape of generated sinogram", sinogram.shape) print("maximum in generated sinogram", maxpoint) # Compare reconstructions for theta=angle and theta=angle + 180; # these should be exactly equal reconstruction = iradon(sinogram, theta=[theta], circle=circle) reconstruction_opposite = iradon(sinogram, theta=[theta + 180], circle=circle) print("rms deviance:", np.sqrt(np.mean((reconstruction_opposite - reconstruction) ** 2))) if debug: import matplotlib.pyplot as plt imkwargs = dict(cmap="gray", interpolation="nearest") plt.figure() plt.subplot(221) plt.imshow(sinogram, **imkwargs) plt.subplot(222) plt.imshow(reconstruction_opposite - reconstruction, **imkwargs) plt.subplot(223) plt.imshow(reconstruction, **imkwargs) plt.subplot(224) plt.imshow(reconstruction_opposite, **imkwargs) plt.show() assert np.allclose(reconstruction, reconstruction_opposite)
def check_radon_iradon_circle(interpolation, shape, output_size): # Forward and inverse radon on synthetic data image = _random_circle(shape) radius = min(shape) // 2 sinogram_rectangle = radon(image, circle=False) reconstruction_rectangle = iradon(sinogram_rectangle, output_size=output_size, interpolation=interpolation, circle=False) sinogram_circle = radon(image, circle=True) reconstruction_circle = iradon(sinogram_circle, output_size=output_size, interpolation=interpolation, circle=True) # Crop rectangular reconstruction to match circle=True reconstruction width = reconstruction_circle.shape[0] excess = int(np.ceil((reconstruction_rectangle.shape[0] - width) / 2)) s = np.s_[excess:width + excess, excess:width + excess] reconstruction_rectangle = reconstruction_rectangle[s] # Find the reconstruction circle, set reconstruction to zero outside c0, c1 = np.ogrid[0:width, 0:width] r = np.sqrt((c0 - width // 2)**2 + (c1 - width // 2)**2) reconstruction_rectangle[r > radius] = 0. print(reconstruction_circle.shape) print(reconstruction_rectangle.shape) np.allclose(reconstruction_rectangle, reconstruction_circle)
def calc_sirt(R, theta, Rmin, Rmax, nonnegconst, maxiter, nrows): R = (R - Rmin) / (Rmax - Rmin) S1 = np.sum(R) At = iradon(R, theta=theta, output_size=nrows) S2 = np.sum(At) At = (At / S2) * S1 xk = At for k in range(maxiter): t = iradon(radon(xk, theta), theta) #normalize St = np.sum(t) t = (t / St) * S1 #update using (At g - At A x_k) #new xk = xk + difference between reconstruction At_starting - t_previuous_step xk = xk + At - t if nonnegconst == 1: #delete values <0 aka not real! xk = xk.clip(min=0) return xk
def test_iradon_angles(): """ Test with different number of projections """ size = 100 # Synthetic data image = np.tri(size) + np.tri(size)[::-1] # Large number of projections: a good quality is expected nb_angles = 200 theta = np.linspace(0, 180, nb_angles, endpoint=False) radon_image_200 = radon(image, theta=theta, circle=False) reconstructed = iradon(radon_image_200, circle=False) delta_200 = np.mean( abs(_rescale_intensity(image) - _rescale_intensity(reconstructed))) assert delta_200 < 0.03 # Lower number of projections nb_angles = 80 radon_image_80 = radon(image, theta=theta, circle=False) # Test whether the sum of all projections is approximately the same s = radon_image_80.sum(axis=0) assert np.allclose(s, s[0], rtol=0.01) reconstructed = iradon(radon_image_80, circle=False) delta_80 = np.mean( abs(image / np.max(image) - reconstructed / np.max(reconstructed))) # Loss of quality when the number of projections is reduced assert delta_80 > delta_200
def my_iradon(radon_image, theta=None, output_size=None, filter="ramp", interpolation="linear", circle=True): if not (type(output_size) is tuple): return iradon( radon_image, theta=theta, output_size=output_size, filter=filter, interpolation=interpolation, circle=circle ) elif (output_size[0] - output_size[1]) % 2 > 0: raise Exception('margin size is not an integer') elif output_size[0] < output_size[1]: margin = int((output_size[1] - output_size[0]) / 2) image = iradon( radon_image, theta=theta, output_size=output_size[1], filter=filter, interpolation=interpolation, circle=circle ) return image[margin:-margin, :] else: margin = int((output_size[0] - output_size[1]) / 2) image = iradon( radon_image, theta=theta, output_size=output_size[0], filter=filter, interpolation=interpolation, circle=circle ) return image[:, margin:-margin]
def test_iradon_angles(): """ Test with different number of projections """ size = 100 # Synthetic data image = np.tri(size) + np.tri(size)[::-1] # Large number of projections: a good quality is expected nb_angles = 200 radon_image_200 = radon(image, theta=np.linspace(0, 180, nb_angles, endpoint=False)) reconstructed = iradon(radon_image_200) delta_200 = np.mean(abs(_rescale_intensity(image) - _rescale_intensity(reconstructed))) assert delta_200 < 0.03 # Lower number of projections nb_angles = 80 radon_image_80 = radon(image, theta=np.linspace(0, 180, nb_angles, endpoint=False)) # Test whether the sum of all projections is approximately the same s = radon_image_80.sum(axis=0) assert np.allclose(s, s[0], rtol=0.01) reconstructed = iradon(radon_image_80) delta_80 = np.mean(abs(image / np.max(image) - reconstructed / np.max(reconstructed))) # Loss of quality when the number of projections is reduced assert delta_80 > delta_200
def fbp(self, prj: np.ndarray, fbp_filter: str): """Apply filtered back-projection of a sinogram or stack of sinograms. Parameters ---------- prj : numpy.array_like The sinogram or stack of sinograms. fbp_filter : str The filter to use in the filtered back-projection. Returns ------- vol : numpy.array_like The reconstructed volume. """ filter_name = _set_filter_name(fbp_filter.lower()) if len(prj.shape) > 2: num_lines = prj.shape[1] vol = np.empty([num_lines, *self.vol_shape], dtype=prj.dtype) for ii_v in range(num_lines): vol[ii_v, ...] = skt.iradon(prj[ii_v, ...].transpose(), self.angles_rot_deg, **filter_name) return vol else: return skt.iradon(prj.transpose(), self.angles_rot_deg, **filter_name)
def main(): image = imread(data_dir + "/phantom.png", as_gray=True) image = rescale(image, scale=0.2, mode='reflect', multichannel=False) fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(4.5, 12)) ax1.set_title("Original") ax1.imshow(image, cmap=plt.cm.Greys_r) theta = np.linspace(0., 180., max(image.shape), endpoint=False) tic = time.time() sinogram = radon(image, theta=theta, circle=True) toc = time.time() ax2.set_title("Scikit Radon transform\n(t={0:.3f}s)".format(toc - tic)) ax2.set_xlabel("Projection angle (deg)") ax2.set_ylabel("Projection position (pixels)") ax2.imshow(sinogram, cmap=plt.cm.Greys_r, extent=(0, 180, 0, sinogram.shape[0]), aspect='auto') pr = cProfile.Profile() pr.enable() tic = time.time() my_sinogram = my_radon(image, theta=theta, circle=True) toc = time.time() pr.disable() pr.print_stats(sort='cumtime') ax3.set_title("My Radon transform\n(t={0:.3f}s)".format(toc - tic)) ax3.set_xlabel("Projection angle (deg)") ax3.set_ylabel("Projection position (pixels)") ax3.imshow(my_sinogram, cmap=plt.cm.Greys_r, extent=(0, 180, 0, my_sinogram.shape[0]), aspect='auto') fig.tight_layout() plt.show() reconstruction_fbp = iradon(sinogram, theta=theta, circle=True) my_reconstruction_fbp = iradon(my_sinogram, theta=theta, circle=True) error = reconstruction_fbp - image my_error = my_reconstruction_fbp - image imkwargs = dict(vmin=-0.2, vmax=0.2) fig, axes = plt.subplots(2, 2, figsize=(8, 4.5), sharex=True, sharey=True) axes[0, 0].set_title("Reconstruction on\nScikit sinogram") axes[0, 0].imshow(reconstruction_fbp, cmap=plt.cm.Greys_r) axes[0, 1].set_title(f"Reconstruction error\n(error={np.sqrt(np.mean(error**2)):.3g})") axes[0, 1].imshow(reconstruction_fbp - image, cmap=plt.cm.Greys_r, **imkwargs) axes[1, 0].set_title("Reconstruction on\nmy sinogram") axes[1, 0].imshow(my_reconstruction_fbp, cmap=plt.cm.Greys_r) axes[1, 1].set_title(f"Reconstruction error\n(error={np.sqrt(np.mean(my_error**2)):.3g})") axes[1, 1].imshow(my_reconstruction_fbp - image, cmap=plt.cm.Greys_r, **imkwargs) plt.show()
def tiltaxisalign(im_series,tilt_angles,shift_and_tilt=('hold','hold')): series_shape = np.shape(im_series) new_series = im_series.copy() final_series = im_series.copy() #deg0_int = input('Which image is the 0 degree image? ') midy = int(series_shape[1]/2) axis_shift = shift_and_tilt[0] axis_tilt = shift_and_tilt[1] if axis_shift == 'hold': shift_continue = 1 while shift_continue == 1: plt.imshow(iradon(np.rot90(new_series[:,midy,:]), # rot theta = tilt_angles,output_size = series_shape[2]))# anti-clokwise plt.show() axis_shift = float(input('By how many pixels from the original mid-point should the tilt axis be shifted? ')) for i in range(series_shape[0]): new_series[i,:,:] = interpolation.shift(im_series.copy()[i,:,:],(0,axis_shift)) # shift along np x-axis shift_continue = int(input('Would you like to apply further image shifts (1 for yes, 0 for no)? ')) for i in range(series_shape[0]): final_series[i,:,:] = interpolation.shift(final_series[i,:,:],(0,axis_shift)) topy = int(3*series_shape[1]/8) bottomy = int(5*series_shape[1]/8) if axis_tilt == 'hold': tilt_series = final_series.copy() tilt_continue = 1 while tilt_continue == 1: plt.imshow(iradon(np.rot90(new_series[:,topy,:]), theta = tilt_angles,output_size = series_shape[2])) plt.show() plt.imshow(iradon(np.rot90(new_series[:,bottomy,:]), theta = tilt_angles,output_size = series_shape[2])) plt.show() axis_tilt = float(input('By what angle from the original y axis (in degrees) should the tilt axis be rotated? ')) for i in range(series_shape[0]): new_series[i,:,:] = interpolation.rotate(tilt_series.copy()[i,:,:],axis_tilt,reshape=False) tilt_continue = int(input('Would you like to try another tilt angle (1 for yes, 0 for no)? ')) for i in range(series_shape[0]): final_series[i,:,:] = interpolation.rotate(final_series[i,:,:],axis_tilt,reshape=False) shift_and_tilt = (axis_shift,axis_tilt) return(final_series, shift_and_tilt)
def test_iradon_dtype(preserve_range): sinogram = np.zeros((16, 1), dtype=int) sinogram[8, 0] = 1. sinogram64 = sinogram.astype('float64') sinogram32 = sinogram.astype('float32') assert iradon(sinogram, theta=[0], preserve_range=preserve_range).dtype == 'float64' assert iradon(sinogram64, theta=[0], preserve_range=preserve_range).dtype == sinogram64.dtype assert iradon(sinogram32, theta=[0], preserve_range=preserve_range).dtype == sinogram32.dtype
def reconstruction(sinogram, theta, method='FBP', output_size='None', filter='ramp'): """Reconstructing with Filtered Back Projection by deflaut. Needs theta as 1D matrix, output_size as a number or default and filter could be ramp, cosine...""" if method == 'FBP': if output_size == 'default': rec = iradon(sinogram.T, theta, filter=filter) else: rec = iradon(sinogram.T, theta, output_size=output_size, filter=filter) return rec
def tomo_reconstruct_layer(rad_stack,cross_sectional_dim,layer_row=1024,start_tomo_ang=0., end_tomo_ang=360.,tomo_num_imgs=360, center=0.,pixel_size=0.00148): sinogram=np.squeeze(rad_stack[:,layer_row,:]) rotation_axis_pos=-int(np.round(center/pixel_size)) #rotation_axis_pos=13 theta = np.linspace(start_tomo_ang, end_tomo_ang, tomo_num_imgs, endpoint=False) max_rad=int(cross_sectional_dim/pixel_size/2.*np.sqrt(2.)) if rotation_axis_pos>=0: sinogram_cut=sinogram[:,2*rotation_axis_pos:] else: sinogram_cut=sinogram[:,:(2*rotation_axis_pos)] dist_from_edge=np.round(sinogram_cut.shape[1]/2.).astype(int)-max_rad sinogram_cut=sinogram_cut[:,dist_from_edge:-dist_from_edge] print('Inverting Sinogram....') reconstruction_fbp = iradon(sinogram_cut.T, theta=theta, circle=True) reconstruction_fbp=np.rot90(reconstruction_fbp,3)#Rotation to get the result consistent with hexrd, needs to be checked return reconstruction_fbp
def recon(dtheta, ax=None, axf1=None, axf2=None, filter="ramp"): dtheta = int(dtheta) theta = range(0, 180, dtheta) sinogram = st.radon(phantom, theta, circle=False) rc_phantom = st.iradon(sinogram, theta, circle=False, filter=filter) error = rc_phantom - phantom rms = np.sqrt(np.mean(error**2)) ft = np.fft.fft2(rc_phantom, norm="ortho") ft[0, 0] = 0 # ft = higt_pass_filter(ft, radius=0) spec = abs(np.fft.fftshift(ft))**2 # maximum = np.max(spec) # spec /= maximum if ax: ax.imshow(rc_phantom, cmap=plt.cm.Greys_r) ax.set_title("{}˚, {}".format(dtheta, filter if filter else "None")) ax.set_xlabel("rms = {:.4f}".format(rms)) ax.set_xticks([]) ax.set_yticks([]) if axf1: axf1.imshow(spec, cmap=plt.cm.Greys_r) axf1.axis("off") if axf2: slicef = spec[spec.shape[0] // 2] axf2.plot(slicef) axf2.axis([0, 160, 0, 1]) axf2.set_xticks([]) axf2.set_xlabel("frequency") return rms
def fbp_reconstruction(self, theta=np.array([None]), backproject=False): """ Reconstruction from the low view CT sinograms. Inputs : `theta` : `backproject` : Do a backprojection instead of fbp """ # (expect artefacts even when reconstructing from pure sinusoids if image size is small) S_measured = self.measurement N = S_measured.shape[0] image_shape = self.shape[1::] assert self.shape[ 0] == N, 'Number of images not equal to number of sinograms' if (theta == None).all(): num_thetas = S_measured.shape[2] theta = np.linspace(0., 179., num_thetas) if backproject == False: filt = 'ramp' else: filt = None recon = [ iradon(S_measured[i], theta=theta, circle=False, filter=filt) for i in range(N) ] self.recon = np.array(recon) return self.recon
def core_denoise(sinogram,image_res,outerloops,innerloops,CGloops, convtestnorm,lambdapen,thetas,jpegflag, i,output,SVD_Flag): """ Core run through preprocessor and denoiser algorithm""" rec_FBP = iradon(sinogram[:,:,i],thetas,output_size = image_res, circle = True, filter = 'hann') if (low_l2_meth == 0): TVrec,Err_diff = Radon_TV_Split_Bregman_LBFGS(sinogram[:,:,i],rec_FBP, thetas, outerloops,innerloops,image_res, convtestnorm,lambdapen,i) elif (low_l2_meth == 1): TVrec,Err_diff = PD_denoise(sinogram[:,:,i],rec_FBP,thetas,outerloops, innerloops,CGloops,image_res,convtestnorm, lambdapen,i) else: TVrec = rec_FBP , Err_diff = 0. print("[Slice %d] finished Split-Breg on Radon" ) print("[Slice %d] is finished" % (i)) output[:,:,i] = TVrec if jpegflag>0: #Want jpeg outputs filename = 'Slice'+str(i)+'.jpg' misc.imsave(filename,output[:,:,i]) np.savetxt('Slice'+str(i)+'resid.txt',Err_diff)
def backproj(sinogram, FOV, ANGLES, symmetric, filter): theta = np.linspace(-int(FOV / 2), int(FOV / 2), ANGLES, endpoint=symmetric) fbp_recon = iradon(sinogram, theta, circle=False, filter=filter) return fbp_recon
def test_torch_iradon(): obj = imread( '/scratch0/ilya/locDoc/data/siim-medical-images/337/ID_0004_AGE_0056_CONTRAST_1_CT.png', as_grey=True) #obj = np.random.rand(256,256) ang = np.linspace(0., 180., 50, endpoint=False) proj = radon(obj, theta=ang, circle=False) # b 262 # projG = rec = torch_iradon(proj, theta=ang, circle=False) rec2 = iradon(proj, theta=ang, circle=False) reconstructed = ( rec.data).cpu().numpy() # and get rid of first two dimensions rec3 = reconstructed[0, 0, :, :] im = Image.fromarray(rec3).convert('RGB') im.save('/cfarhomes/ilyak/Desktop/time.png') # plt.imshow(reconstructed[0,0,:,:], cmap='gray') # plt.show() # plt.imshow(rec2, cmap='gray') # plt.show() rec2n = (rec2 - np.min(rec2)) / (np.max(rec2) - np.min(rec2)) * 255.0 rec3n = (rec3 - np.min(rec3)) / (np.max(rec3) - np.min(rec3)) * 255.0 showme = plt.imshow(rec2n - rec3n) plt.colorbar(showme) plt.show() pdb.set_trace()
def __init__(self, domain_dim, center=np.array([0])): self.domain_dim = domain_dim # attention: self doppelt... self.center = center if not any(self.center): if len(self.domain_dim) == 2 and self.domain_dim[1] == 1: self.center = int((self.domain_dim[0] + 1) / 2) else: self.cemter = int((self.domain_dim + 1) / 2) # ToDO: input check if len(center) == 1: self.onevec = 1 else: self.onevec = np.ones(len(self.domain_dim)) roll_val = self.center - self.onevec self.theta = np.linspace(0., 180., 100, endpoint=False) # max(self.domain_dim) fwfcthdl = lambda u: radon(u, theta=self.theta, circle=False) bwfcthdl = lambda f: iradon(f, theta=self.theta, circle=False) image_dim = (int(np.ceil(np.sqrt(2) * max(domain_dim))), len(self.theta)) super(CtRt, self).__init__(domain_dim, image_dim, fwfcthdl, bwfcthdl)
def myAtA(self, img): img = img.cpu().detach().numpy() Aimg = radon(img, theta=self.theta, circle=True) AtAimg = iradon(Aimg, theta=self.theta) AtA = AtAimg + self.lam * img AtA = torch.from_numpy(AtA).cuda() return AtA
def SIRT(image, sinogram, theta, circle=True, alpha=0.1, n_iter=1e2, init=None): """ image 再構成画像 sinogram 投影データ theta 投影角度 n_iter イテレーション """ # initial if init is None: if circle == True: recon = np.zeros(image.shape) rr, cc = cir(image.shape[0] / 2, image.shape[1] / 2, image.shape[0] / 2 - 1) recon[rr, cc] = 1 else: recon = np.ones(image.shape) else: recon = init mse = [] for iter in tqdm(range(n_iter), desc="sirt iter", leave=False): predict_image = radon(recon, theta, circle=circle) recon = recon + alpha * iradon( sinogram - predict_image, theta, circle=circle) recon[recon < 0] = 0 recon[recon > 1] = 1 if iter > 0: mse.append(mean_squared_error(recon, image)) # make dir make_dir("result") make_dir("result/recon") make_dir("result/recon/sirt") make_dir("result/mse") # plot plt.imshow(recon) plt.colorbar() plt.title("sirt recon, iteration: %d" % (iter + 1)) # save filename = "./result/recon/sirt/recon_sirt_iter" + str(iter + 1).zfill( 3) + ".png" plt.savefig(filename) plt.pause(1) plt.close() np.save("./result/mse/mse_sirt.npy", mse) return recon
def reconstruct(self, sinogram, centre_of_rotations, angles, vol_shape): in_pData = self.get_plugin_in_datasets()[0] sinogram = np.swapaxes(sinogram, 0, 1) sinogram = self._shift(sinogram, centre_of_rotations) sino = sinogram.astype(np.float64) theta = np.linspace(0, 180, sinogram.shape[1]) result = transform.iradon( sino, theta=theta, output_size=(in_pData.get_shape()[1]), # self.parameters['output_size'], filter="ramp", # self.parameters['filter'], interpolation="linear", # self.parameters['linear'], circle=False, ) # self.parameters[False]) for i in range(self.parameters["iterations"]): print "Iteration %i" % i result = transform.iradon_sart( sino, theta=theta, image=result, # self.parameters['result'], projection_shifts=None, # self.parameters['None'], clip=None, # self.parameters[None], relaxation=0.15 # self.parameters[0.15]) ) return result
def reconstruct(self, sinogram, centre_of_rotation, angles, shape, center): print sinogram.shape sinogram = np.swapaxes(sinogram, 0, 1) sinogram = self._shift(sinogram, centre_of_rotation) sino = np.nan_to_num(sinogram) theta = np.linspace(0, 180, sinogram.shape[1]) result = \ transform.iradon(sino, theta=theta, output_size=(sinogram.shape[0]), # self.parameters['output_size'], filter='ramp', # self.parameters['filter'], interpolation='linear', # self.parameters['linear'], circle=False) # self.parameters[False]) for i in range(self.parameters["iterations"]): print "Iteration %i" % i result = transform.iradon_sart(sino, theta=theta, image=result, # self.parameters['result'], projection_shifts=None, # self.parameters['None'], clip=None, # self.parameters[None], relaxation=0.15 # self.parameters[0.15]) ) return result
def iradon_and_save(sinogram_np, angles, wbp_dir, sart_dir, denoise_dir, sart_denoise_dir): sinogram_np = sinogram_np * 0.6 recon_np = iradon(sinogram_np, angles)[20:276, 20:276] Image.fromarray(recon_np * 255).convert("L").save(wbp_dir) recon_np = recon_np[np.newaxis, np.newaxis, :] tensor = torch.Tensor(recon_np).float() tensor = tensor.cuda(device) with torch.no_grad(): out = model(tensor).detach() recon_np = out.cpu().numpy()[0][0] * 255 Image.fromarray(recon_np).convert("L").save(denoise_dir) # --------------------------- recon_np = None for _ in range(10): recon_np = iradon_sart(sinogram_np.astype(np.float), angles, image=recon_np, relaxation=0.25) recon_np = recon_np[20:276, 20:276] Image.fromarray(recon_np * 255).convert("L").save(sart_dir) recon_np = recon_np[np.newaxis, np.newaxis, :] tensor = torch.Tensor(recon_np).float() tensor = tensor.cuda(device) with torch.no_grad(): out = model(tensor).detach() recon_np = out.cpu().numpy()[0][0] * 255 Image.fromarray(recon_np).convert("L").save(sart_denoise_dir)
def get_fbp(ct_img, theta, circle=False, sart=False): """ CT Img -> radon -> sinogram -> iradon -> FBP Img Using skimage transform method Parameters ---------- theta : projection angle in degree, dtype=np linspace circle : boolean, param of radon, default False sart : not implemented use only true, boolean, use iradon_sart, default False Returns ------- sinogram, normalized fbp [0 1] ndarray Examples -------- >>> sinogram, fbp = get_fbp(ct, theta) >>> plt.imshow(sinogram), plt.imshow(fbp) """ if ct_img.min() < 0 or ct_img.max() > 1: raise ValueError("CT Img Range : [%d %d]" % (ct_img.min(), ct_img.max())) sinogram = radon(ct_img, theta=theta, circle=circle) if sart is False: fbp = iradon(sinogram, theta=theta, circle=circle) else: # TODO: Check iradon_sart params raise NotImplementedError("not implemented sart") fbp = iradon_sart(sinogram, theta) if fbp.shape[0] != 512 or fbp.shape[1] != 512: raise ValueError("FBP Shape : [%d %d]" % (fbp.shape[0], fbp.shape[1])) np.clip(fbp, 0, 1, out=fbp) return sinogram, fbp
def reconstructImage(sinogram, theta, filter='ramp', inter='linear'): reconstructed_fbp = iradon(sinogram, theta=theta, circle=False, filter=filter, interpolation=inter) return reconstructed_fbp
def test(self): self.load_model(self.test_epoch) self.model.eval() with torch.no_grad(): # Must use the sample test dataset!!! X_fbp = torch.zeros_like(self.test_images) batch_size = self.test_images.shape[0] for i in range(batch_size): sino = self.test_data[i].squeeze() X0 = iradon(sino, theta=self.theta) X_fbp[i] = torch.from_numpy(X0) if self.model_name == "denoise_net": test_res = self.model(X_fbp.cuda().float()) if self.model_name == "fista_net": [test_res, _] = self.model(X_fbp.cuda().float(), self.test_data.cuda().float()) # torch.save(test_res, 'iteration_result.pt') # iteration result # test_res = test_res[5] # iteration result if self.model_name == "MoDL": test_res = self.model(X_fbp.cuda().float(), self.test_data.cuda().float()) return test_res
def process_frames(self, data): sino = data[0] centre_of_rotations, angles, vol_shape, init = self.get_frame_params() in_pData = self.get_plugin_in_datasets()[0] sinogram = np.swapaxes(sino, 0, 1) sinogram = self._shift(sinogram, centre_of_rotations) sino = sinogram.astype(np.float64) theta = np.linspace(0, 180, sinogram.shape[1]) result = \ transform.iradon(sino, theta=theta, output_size=(in_pData.get_shape()[1]), # self.parameters['output_size'], filter='ramp', # self.parameters['filter'], interpolation='linear', # self.parameters['linear'], circle=False) # self.parameters[False]) for i in range(self.parameters["iterations"]): print "Iteration %i" % i result = transform.iradon_sart( sino, theta=theta, image=result, # self.parameters['result'], projection_shifts=None, # self.parameters['None'], clip=None, # self.parameters[None], relaxation=0.15 # self.parameters[0.15]) ) return result
def reconstruct(self, sinogram, centre_of_rotation, angles, shape, center): print sinogram.shape sinogram = np.swapaxes(sinogram, 0, 1) sinogram = self._shift(sinogram, centre_of_rotation) sino = np.nan_to_num(sinogram) theta = np.linspace(0, 180, sinogram.shape[1]) result = \ transform.iradon(sino, theta=theta, output_size=(sinogram.shape[0]), # self.parameters['output_size'], filter='ramp', # self.parameters['filter'], interpolation='linear', # self.parameters['linear'], circle=False) # self.parameters[False]) for i in range(self.parameters["iterations"]): print "Iteration %i" % i result = transform.iradon_sart( sino, theta=theta, image=result, # self.parameters['result'], projection_shifts=None, # self.parameters['None'], clip=None, # self.parameters[None], relaxation=0.15 # self.parameters[0.15]) ) return result
def slice_wise_iradon(sinograms, theta): recon_obj = np.zeros((Nx, ) + (min(Ny, Nz), ) * 2) for s in range(Nx): recon_obj[s, :, :] = st.iradon(sinograms[:, :, s], theta=theta, circle=True) return recon_obj
def reconstructRad(self): self.reconstructedImage = abs( iradon(self.spectrum.T, theta=self.theta, circle=True)) self.reconstructedImage = self.reconstructedImage - np.amin( self.reconstructedImage) self.reconstructedImage = self.reconstructedImage / np.amax( self.reconstructedImage)
def read_data(path, number_of_angles): training_images = [] inverted_images = [] th = np.linspace(0., 180., number_of_angles, endpoint=False) for file_name in glob.glob(path): #+'/**/*.jpg', recursive=True): image = Image.open(file_name).resize((128, 128)) image = np.asarray(image) max, min = image.max(), image.min() image = (image - min) / (max - min) training_images.append(image) inverted_images.append( iradon(radon(image, theta=th, circle=False, preserve_range=True), circle=False)) training_images = np.asarray(training_images) inverted_images = np.asarray(inverted_images) return np.expand_dims( inverted_images[:32256], axis=-1).astype('float32'), np.expand_dims( training_images[:32256], axis=-1).astype('float32'), np.expand_dims( inverted_images[32256:32768], axis=-1).astype('float32'), np.expand_dims( training_images[32256:32768], axis=-1).astype('float32'), np.expand_dims( inverted_images[32768:], axis=-1).astype('float32'), np.expand_dims( training_images[32768:], axis=-1).astype('float32')
def _load_whole_data(current_version, file, dump_folder): hashstr = hashlib.sha256( ("".join(file) + current_version).encode()).hexdigest() dump_file = os.path.join(dump_folder, hashstr + ".h5") print(dump_file) rebuild_data = True if os.path.exists(dump_file): print("dump file existed") with h5py.File(dump_file, "r") as h5file: if "version" in list(h5file.keys()): if h5file["version"].value == current_version: rebuild_data = False print("rebuild_data", rebuild_data) if rebuild_data: data = [] mat_contents = sio.loadmat(file) gt = np.squeeze(mat_contents['data_gt']) sparse = np.zeros_like(gt) full = np.zeros_like(gt) for ind in range(gt.shape[2]): img = np.squeeze(gt[:, :, ind]) theta = np.linspace(0., 180., 1e3, endpoint=False) sinogram = radon(img, theta=theta, circle=False) theta_down = theta[0:1000:20] sparse[:, :, ind] = iradon(sinogram[:, 0:1000:20], theta=theta_down, circle=False) full[:, :, ind] = iradon(sinogram, theta=theta, circle=False) print("iteration : ", ind, "/", gt.shape[2]) norm_val = np.amax(sparse) print("norm_val", norm_val) print("finished rebuild") saveh5( { "label": full / norm_val * 255., "sparse": sparse / norm_val * 255., "version": current_version }, dump_file) f_handle = h5py.File(dump_file, "r") label = np.array(f_handle["label"]) sparse = np.array(f_handle["sparse"]) print("size of label, ", label.shape) print("size of sparse, ", sparse.shape)
def sirt_xin(sinogram_np, angles, numIter=10): # SIRT # print("--start sirt_xin---") recon_SIRT = iradon(sinogram_np, theta=angles, filter=None, circle=True) for i in range(0, numIter): reprojs = radon(recon_SIRT, theta=angles, circle=True) ratio = sinogram_np / reprojs ratio[np.isinf(ratio)] = 1e8 ratio[np.isnan(ratio)] = 1 timet = iradon(ratio, theta=angles, filter=None, circle=True) timet[np.isinf(timet)] = 1 timet[np.isnan(timet)] = 1 recon_SIRT = recon_SIRT * timet # print('SIRT %.3g' % i) # plt.imshow(recon_SIRT, cmap=plt.cm.Greys_r) # plt.show() return recon_SIRT
def radon_then_back_proj(img, N, ramp): angles = np.linspace(0, 180, N, endpoint=False) img_radon = transform.radon(img, theta=angles, circle=False) img_proj = transform.iradon(img_radon, theta=angles, circle=False, filter_name='ramp' if ramp else None) return img_proj
def centreshift(data,angles,tiltrange,increment): series_shape = np.shape(data) new_series = data.copy() midy = int(series_shape[1]/2) for i in range(series_shape[0]): new_series[i,:,:] = interpolation.shift(data.copy()[i,:,:],(0,axis_shift)) iradon(np.rot90(data[:,midy,:]), theta = angles,output_size = series_shape[2]) #if __name__ == "__main__": #x = hspy.load('C:/Users/Tom/Documents/TEM data/20150521_SS316needle/EDX Tomo/Cr/Signed_zero/cr_sub_hdr0_2.ali') '''x = h5py.File('C:/Users/Tom/Documents/TEM data/20150521_SS316needle/EDX Tomo/Cr/Signed_zero/cr_sub_hdr0_2_bin8.h5', "r") tilt_angles = np.linspace(-90.,90.,37) x_data = x['/0'] reconstruction = reconstruct(x_data,tilt_angles)''' '''
def PD_denoise(sinogram,rec_FBP,thetas,outerloops,innerloops, CGloops,image_res,convtestnorm,lambdapen,slicenum): """ Solves the problem via the following formulation. We minimize F(Ku)+G(u), where K:= (grad,R)^T and F(x):= ||x_1||_1+lambda/2||x_2-g||^2, and G(u):=0. Then, F^*(y) = \delta_p(y_1)+1/(2lambda)||y_2||^2+<g,y_2>. We then perform the primal dual algorithm of Chambolle-Pock '11. We assume it is better to have repeated G evaluations and fewer thing stored. Do not use; needs significant tuning/possible debugging. """ uk = (TV_Split_utilities.generateSheppLogan(Phant_size)) radius = min(uk.shape) // 2 c0, c1 = np.ogrid[0:uk.shape[0], 0:uk.shape[1]] Circle_Mask = ((c0 - uk.shape[0] // 2) ** 2+ (c1 - uk.shape[1] // 2) ** 2) Circle_Mask = Circle_Mask <= 0.95*(radius ** 2) Circle_Mask = np.matrix(Circle_Mask) uk[0==Circle_Mask]=0. y_onek = np.gradient(0.*uk) y_twok = 0.*sinogram sigma = 1.e-2 tau = 1.e-2 theta = 1. ubark = uk Err_diff = np.zeros((int(.5*rec_FBP.size))) for i in range(int(.5*rec_FBP.size)): y_onek[0],y_onek[1], y_twok = resolvent_Fstar(y_onek[0]+sigma* grad_one(ubark),y_onek[1]+sigma*grad_two(ubark), y_twok+sigma*radon(ubark,thetas,circle=True),sigma,lambdapen, sinogram) ubark = (1+theta)*resolvent_G(uk+tau*(-1.*Divu(y_onek)+iradon(y_twok, thetas,output_size=image_res,circle=True,filter = 'ramp' )))-theta*uk ubark[0==Circle_Mask]=0. uk = resolvent_G(uk-tau*(-1.*Divu(y_onek)+iradon(y_twok, thetas,output_size=image_res,circle=True,filter = 'ramp' ))) uk[0==Circle_Mask]=0. Err_diff[i] = np.linalg.norm((ubark-uk)/theta) plt.imshow(uk);plt.pause(.0001) print('Slice %d]' %(slicenum)) print('Outer Iterate = ' ,i) print('Update = ' ,Err_diff[i]) if Err_diff[i]<1.e-5: break return uk,Err_diff
def tiff_sinos_pad(sinogram,flag,thetas): """ Pads Octopus sinograms (after shape has been extracted elsewhere) so that applying radon to other data won't need a mask and will still be the right shape. NOTE: Very Very hacked together. """ from skimage.transform import radon, iradon if flag=='FBP':#apply Radon transform to FBP of sinogram to use as data imres=sinogram.shape[0] sinogram = radon(iradon(sinogram,thetas,output_size=imres,circle=True), thetas,circle=False) elif flag=='pad':#Insert 0's into sinogram on either side imres=sinogram.shape[0] temp = radon(iradon(0.*sinogram,thetas,output_size=imres,circle=True), thetas,circle=False) sizediff = abs(sinogram.shape[0]-temp.shape[0]) if sizediff>0: #padding is needed sinogram = np.concatenate((temp[0:np.ceil(sizediff/2.),:],sinogram, temp[0:np.floor(sizediff/2.),:])) return sinogram
def low_L2_objgrad(u,sinogram, lambdapen,dkx,dky,bkx,bky,thetas,image_res, Circle_Mask,TV,TVT,TVTTV): u = np.reshape(u,(image_res,image_res)) u[0==Circle_Mask] = 0. dtheta = (thetas[1]-thetas[0])/180. dx = 1./image_res grad = (iradon(radon(u,thetas,circle = True)-sinogram,thetas, output_size = image_res, circle = True,filter = None ))*dtheta*dx*(2.*len(thetas))/np.pi grad -= dx*dx*((lambdapen*TVTTV*u+lambdapen*u*TVTTV) +lambdapen*(TVT*(dkx- bkx)+(dky-bky)*TV)) return np.ravel(grad)
def check_radon_iradon_minimal(shape, slices): debug = False theta = np.arange(180) image = np.zeros(shape, dtype=np.float) image[slices] = 1. sinogram = radon(image, theta) reconstructed = iradon(sinogram, theta) print('\n\tMaximum deviation:', np.max(np.abs(image - reconstructed))) if debug: _debug_plot(image, reconstructed, sinogram) if image.sum() == 1: assert (np.unravel_index(np.argmax(reconstructed), image.shape) == np.unravel_index(np.argmax(image), image.shape))
def reconstruct(tilt_data,tilt_angles,algorithm='ASTRA_SIRT',iterations=20,geometry='parallel3d'): '''Function to reconstruct a tilt series. Data should be in the format (Angles,X,Y), where the tilt axis is situated about the mid column of the data.''' t0 = time.time() data_shape = np.shape(tilt_data) y_size = data_shape[1] recon = np.zeros((data_shape[2],y_size,data_shape[2])) #sino = np.zeros(data_shape) #Reconstruction using Filtered/Weighted Backprojection from skimage (check how filtering is done) if algorithm == 'SKI_FBP' or 'SKI_WBP': for y in range(0,y_size-1): recon[:,y,:] = iradon(np.rot90(tilt_data[:,y,:]), theta = tilt_angles,output_size = data_shape[2]) #This is supposed to reorder axis to orientation for projection but not sure the x and y are correct for y in range(0,y_size-1): recon[:,y,:] = np.rot90(recon[:,y,:]) recon[:,y,:] = np.rot90(recon[:,y,:]) #recon[:,y,:] = np.rot90(recon[:,y,:]) if algorithm == 'SKI_SART': for y in range(0,y_size-1): recon[:,y,:] = iradon_sart(np.rot90(tilt_data[:,y,:]), theta = tilt_angles,clip=(0,np.max(tilt_data))) if iterations > 1: for it in range(iterations-1): print("Iteration number "+str(it+2)+" in progress.") for y in range(0,y_size-1): recon[:,y,:] = iradon_sart(np.rot90(tilt_data[:,y,:]), theta = tilt_angles,image=recon[:,y,:],clip=(0,np.max(tilt_data))) #This is supposed to reorder axis to orientation for projection but not sure the x and y are correct for y in range(0,y_size-1): recon[:,y,:] = np.rot90(recon[:,y,:]) recon[:,y,:] = np.rot90(recon[:,y,:]) #recon[:,y,:] = np.rot90(recon[:,y,:]) if algorithm == 'ASTRA_SIRT': recon = astrarecon(tilt_data,tilt_angles,iterations,geometry) '''for z in xrange(0,data_shape[2]): recon[:,:,z] = np.rot90(recon[:,:,z]) recon[:,:,z] = np.rot90(recon[:,:,z]) recon[:,:,z] = np.rot90(recon[:,:,z])''' print("Reconstruction completed in {} seconds.".format(time.time() - t0)) return(recon)
def inverAbel(x,y): x = numpy.array(x) y = list(y) dx = abs(x[1]-x[0]) length = len(y) #~ print len(numpy.array(y*100)) sino = numpy.array(y*100).reshape(100,length).transpose() reconstruction = iradon(sino) iry = reconstruction[:,math.ceil(len(reconstruction)*0.5)]/dx cropxs = int((length - len(iry))*0.5)-1 cropxe = cropxs + len(iry) irx = x[cropxs:cropxe] #~ print len(irx),len(iry) return irx,iry
def reconstruct(self, sino, centre_of_rotations, angles, vol_shape, init): in_pData = self.get_plugin_in_datasets()[0] in_meta_data = self.get_in_meta_data()[0] sinogram = np.swapaxes(sino, 0, 1) sinogram = self._shift(sinogram, centre_of_rotations) theta = in_meta_data.get_meta_data('rotation_angle') result = \ transform.iradon(sinogram, theta=theta, output_size=(in_pData.get_shape()[1]), # self.parameters['output_size'], filter='ramp', # self.parameters['filter'], interpolation='linear', # self.parameters['linear'], circle=False) # self.parameters[False]) return result
def iridgelet(ridge): lvl, nr,nt = np.shape(ridge) # for l in np.linspace(0,lvl-1,lvl): # x = ski.radon(ridge[l,:,:],theta = np.linspace(0,180-1,nt)) # if l ==0: # nr,nt = np.shape(x) # rad_ridgelet = np.zeros((lvl,nr,nt)) # rad_ridgelet[l,:,:] = x rad_ridgelet = ridge rad = np.zeros((nr,nt)) for i in np.linspace(0,nt-1,nt): rad[:,i] = iuwt_1D(rad_ridgelet[:,:,i]) img = ski.iradon(rad) return img
def check_radon_iradon(interpolation_type, filter_type): debug = False image = PHANTOM reconstructed = iradon(radon(image), filter=filter_type, interpolation=interpolation_type) delta = np.mean(np.abs(image - reconstructed)) print("\n\tmean error:", delta) if debug: _debug_plot(image, reconstructed) if filter_type in ("ramp", "shepp-logan"): if interpolation_type == "nearest": allowed_delta = 0.03 else: allowed_delta = 0.025 else: allowed_delta = 0.05 assert delta < allowed_delta
def reconstruct(self, sinogram, centre_of_rotation, angles, shape, center): print sinogram.shape sinogram = np.swapaxes(sinogram, 0, 1) sinogram = self._shift(sinogram, centre_of_rotation) sino = np.nan_to_num(sinogram) theta = np.linspace(0, 180, sinogram.shape[1]) result = \ transform.iradon(sino, theta=theta, output_size=(sinogram.shape[0]), # self.parameters['output_size'], filter='ramp', # self.parameters['filter'], interpolation='linear', # self.parameters['linear'], circle=False) # self.parameters[False]) return result
def check_radon_iradon(interpolation_type, filter_type): debug = False image = PHANTOM reconstructed = iradon(radon(image, circle=False), filter=filter_type, interpolation=interpolation_type) delta = np.mean(np.abs(image - reconstructed)) print('\n\tmean error:', delta) if debug: _debug_plot(image, reconstructed) if filter_type in ('ramp', 'shepp-logan'): if interpolation_type == 'nearest': allowed_delta = 0.03 else: allowed_delta = 0.025 else: allowed_delta = 0.05 assert delta < allowed_delta
def test_iradon_bias_circular_phantom(): """ test that a uniform circular phantom has a small reconstruction bias """ pixels = 128 xy = np.arange(-pixels / 2, pixels / 2) + 0.5 x, y = np.meshgrid(xy, xy) image = x**2 + y**2 <= (pixels/4)**2 theta = np.linspace(0., 180., max(image.shape), endpoint=False) sinogram = radon(image, theta=theta) reconstruction_fbp = iradon(sinogram, theta=theta) error = reconstruction_fbp - image tol = 5e-5 roi_err = np.abs(np.mean(error)) assert( roi_err < tol )
def recon_scikit_fbp(im, angles, filter_type): """Reconstruct a sinogram with the FBP algorithm of the Scikit-Image Python package Parameters ---------- im : array_like Sinogram image data as numpy array. angles : double Value in radians representing the number of angles of the input sinogram. filter_type : string A string with e.g. "ramp", "shepp-logan", "cosine", "hamming", "hann". """ # Filters: ramp, shepp-logan, cosine, hamming, hann # Interp: ‘linear’, ‘nearest’, and ‘cubic’ rec = iradon(im.T, linspace(0, angles / pi * 180.0, im.shape[0], False), im.shape[1] , filter_type, 'nearest', True) return rec
def process_frames(self, data): sino = data[0] centre_of_rotations, angles, vol_shape, init = self.get_frame_params() in_pData = self.get_plugin_in_datasets()[0] in_meta_data = self.get_in_meta_data()[0] sinogram = np.swapaxes(sino, 0, 1) sinogram = self._shift(sinogram, centre_of_rotations) theta = in_meta_data.get('rotation_angle') dim_detX = in_pData.get_data_dimension_by_axis_label('detector_x') size = self.parameters['output_size'] size = in_pData.get_shape()[dim_detX] if size == 'auto' or \ size is None else size result = \ transform.iradon(sinogram, theta=theta, output_size=(size), filter=self.parameters['filter'], interpolation=self.parameters['interpolation'], circle=self.parameters['circle']) return result
def scikit_radon_back_projector(sinogram, geometry, range, out=None): """Calculate forward projection using scikit Parameters ---------- sinogram : `DiscreteLpElement` Sinogram (projections) to backproject. geometry : `Geometry` The projection geometry to use. range : `DiscreteLp` range of this projection (volume space). out : ``range`` element, optional An element in range that the result should be written to. Returns ------- sinogram : ``range`` element Sinogram given by the projection. """ theta = scikit_theta(geometry) scikit_range = scikit_sinogram_space(geometry, range, sinogram.space) scikit_sinogram = scikit_range.element() scikit_sinogram.sampling(clamped_interpolation(range, sinogram)) if out is None: out = range.element() else: # Only do asserts here since these are backend functions assert out in range out[:] = iradon(scikit_sinogram.asarray().T, theta, output_size=range.shape[0], filter=None) # Empirically determined value, gives correct scaling scale = 4.0 * float(geometry.motion_params.length) / (2 * np.pi) out *= scale return out
def _call(self, x): return self.range.element(iradon(x.asarray(), self.theta, self.npoint, filter=None))
phantom = np.zeros((phantomSize, phantomSize)) offset = (phantomSize - squareSize)/2 phantom[offset:offset+squareSize, offset:offset+squareSize] = squareAtt nrProjections = 300 angleRange = 360.0 angleInc = nrProjections/angleRange angles = np.linspace(0, angleRange, nrProjections) sinogram = radon(phantom, theta=angles) simple_backprojection = iradon( sinogram, theta=angles, filter=None, output_size=phantomSize ) filtered_backprojection = iradon( sinogram, theta=angles, filter="ramp", output_size=phantomSize ) fig, axes = plt.subplots(1, 3) images = [phantom, simple_backprojection, filtered_backprojection] names = ["phantom", "sbp", "fbp"]
image = imread(data_dir + "/phantom.png", as_grey=True) image = zoom(image, 0.4) plt.figure(figsize=(8, 8.5)) plt.subplot(221) plt.title("Original"); plt.imshow(image, cmap=plt.cm.Greys_r) plt.subplot(222) projections = radon(image, theta=[0, 45, 90]) plt.plot(projections); plt.title("Projections at\n0, 45 and 90 degrees") plt.xlabel("Projection axis"); plt.ylabel("Intensity"); projections = radon(image) plt.subplot(223) plt.title("Radon transform\n(Sinogram)"); plt.xlabel("Projection axis"); plt.ylabel("Intensity"); plt.imshow(projections) reconstruction = iradon(projections) plt.subplot(224) plt.title("Reconstruction\nfrom sinogram") plt.imshow(reconstruction, cmap=plt.cm.Greys_r) plt.subplots_adjust(hspace=0.4, wspace=0.5) plt.show()
The mathematical foundation of the filtered back projection is the Fourier slice theorem [2]_. It uses Fourier transform of the projection and interpolation in Fourier space to obtain the 2D Fourier transform of the image, which is then inverted to form the reconstructed image. The filtered back projection is among the fastest methods of performing the inverse Radon transform. The only tunable parameter for the FBP is the filter, which is applied to the Fourier transformed projections. It may be used to suppress high frequency noise in the reconstruction. ``skimage`` provides a few different options for the filter. """ from skimage.transform import iradon reconstruction_fbp = iradon(sinogram, theta=theta, circle=True) error = reconstruction_fbp - image print('FBP rms reconstruction error: %.3g' % np.sqrt(np.mean(error**2))) imkwargs = dict(vmin=-0.2, vmax=0.2) fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 4.5), sharex=True, sharey=True, subplot_kw={'adjustable':'box-forced'}) ax1.set_title("Reconstruction\nFiltered back projection") ax1.imshow(reconstruction_fbp, cmap=plt.cm.Greys_r) ax2.set_title("Reconstruction error\nFiltered back projection") ax2.imshow(reconstruction_fbp - image, cmap=plt.cm.Greys_r, **imkwargs) plt.show() """ .. image:: PLOT2RST.current_figure Reconstruction with the Simultaneous Algebraic Reconstruction Technique
def reconstruct(self, downsample=(4, 4), crop=True, median_filter=False, kernel=9, recon_alg='fbp', sart_iters=1, crop_circle=False, save=True, fancy_out=True): """ Reconstruct the data using a radon transform. Reconstructed slices saved in folder specified upon class creation. # downsample: Downsample (local mean) data before reconstructing. Specify mean kernel size (height, width). # pre_filter: If True apply median filter to data before reconstructing # kernel: Kernel size to use for median filter """ if self.cor_offset >= 0: images = self.im_stack[:, self.cor_offset:] else: images = self.im_stack[:, :self.cor_offset] images = images[:, :, self.p0:self.num_images + self.p0] if crop: left, right, top, bottom = self.crop images = images[top:bottom, left:right] images = downscale_local_mean(images, downsample + (1, )) recon_height, recon_width = images.shape[:2] self.recon_data = np.zeros((recon_width, recon_width, recon_height)) if median_filter: print('Applying median filter...') for i in range(images.shape[-1]): sys.stdout.write('\rProgress: [{0:20s}] {1:.0f}%'.format('#' * int(20 * (i + 1) / images.shape[-1]), 100 * ((i + 1) / images.shape[-1]))) sys.stdout.flush() images[:, :, i] = medfilt(images[:, :, i], kernel_size=kernel) print('\nReconstructing...') if save: save_folder = os.path.join(self.folder, 'reconstruction') if not os.path.exists(save_folder): os.makedirs(save_folder) for the_file in os.listdir(save_folder): file_path = os.path.join(save_folder, the_file) if os.path.isfile(file_path): os.unlink(file_path) if fancy_out: fig, ax = plt.subplots(figsize=(4, 4)) fig.canvas.set_window_title('Reconstruction') patch = Wedge((.5, .5), .375, 90, 90, width=0.1) ax.add_patch(patch) ax.axis('equal') ax.set_xlim([0, 1]) ax.set_ylim([0, 1]) ax.axis('off') t = ax.text(0.5, 0.5, '0%%', fontsize=15, ha='center', va='center') for j in range(recon_height): # Update figure every other slice if fancy_out and j % 2 == 0: patch.set_theta1(90 - 360 * (j + 1) / recon_height) progress = 100 * (j + 1) / recon_height t.set_text('%02d%%' % progress) plt.pause(0.001) else: sys.stdout.write('\rProgress: [{0:20s}] {1:.0f}%'.format('#' * int(20 * (j + 1) / recon_height), 100 * ((j + 1) / recon_height))) sys.stdout.flush() sino_tmp = np.squeeze(images[j, :, :]) if recon_alg is 'sart': image_tmp = iradon_sart(sino_tmp, theta=self.angles) for i in range(sart_iters - 1): image_tmp = iradon_sart(sino_tmp, theta=self.angles, image=image_tmp) else: image_tmp = iradon(sino_tmp, theta=self.angles, filter=None, circle=True) # if crop_circle: # image_tmp = image_tmp[w0:wf, w0:wf] self.recon_data[:, :, j] = image_tmp if crop_circle: w = int((recon_width**2 / 2)**0.5) w = w if (w - recon_width) % 2 == 0 else w - 1 w0 = int((recon_width - w) / 2 ) wf = int(w0 + w) self.recon_data = self.recon_data[w0:wf, w0:wf] if save: for j in range(recon_height): image_tmp = self.recon_data[:, :, j] imsave(os.path.join(save_folder, '%04d.tif' % j), image_tmp) if fancy_out: plt.close()
def test_reconstruct_with_wrong_angles(): a = np.zeros((3, 3)) p = radon(a, theta=[0, 1, 2]) iradon(p, theta=[0, 1, 2]) assert_raises(ValueError, iradon, p, theta=[0, 1, 2, 3])
theta = np.linspace(0., 180., max(image.shape), endpoint=False) sinogram = radon(image, theta=theta, circle=True) # add noise if count > 0: val = sinogram.sum() sinogram = np.random.poisson(sinogram / val * count).astype(np.float) sinogram *= val / count # normalization matrix nview = len(theta) norm = np.ones(shape) wgts = [] for sub in xrange(nsub): views = range(sub, nview, nsub) wgt = iradon(norm[:, views], theta=theta[views], filter=None, circle=True) wgts.append(wgt) # comparison OSEM, IRS recons = [] for b, rw, m in [(0, False, False), (beta, True, False), (beta, True, median)]: print 'beta', b, 'rw', rw # initial recon = np.zeros(shape) rr, cc = circle(shape[0] / 2, shape[1] / 2, shape[0] / 2 - 1) recon[rr, cc] = 1 if rw: weight = np.ones(shape)