Rec tomoΒΆ

[1]:
import numpy as np
import cupy as cp
import matplotlib.pyplot as plt
from holotomocupy.proc import linear, dai_yuan
from holotomocupy.tomo import R,RT
from holotomocupy.chunking import gpu_batch
from holotomocupy.utils import *
import holotomocupy.chunking as chunking

%matplotlib inline
chunking.global_chunk = 64 # chunk for GPU processing
[2]:
n = 256  # object size in each dimension
ntheta = 180  # number of angles (rotations)

center = n/2  # rotation axis
theta = cp.linspace(0, np.pi, ntheta).astype('float32')  # projection angles


npos = 1  # number of code positions

detector_pixelsize = 3e-6*0.5
energy = 33.35  # [keV] xray energy
wavelength = 1.2398419840550367e-09/energy  # [m] wave length

focusToDetectorDistance = 1.28  # [m]
sx0 = 3.7e-4
z1 = 4.584e-3-sx0#np.array([4.584e-3, 4.765e-3, 5.488e-3, 6.9895e-3])[:npos]-sx0
z1 = np.tile(z1,[npos])
z2 = focusToDetectorDistance-z1
distances = (z1*z2)/focusToDetectorDistance
magnifications = focusToDetectorDistance/z1
voxelsize = detector_pixelsize/magnifications[0]*2048/n  # object voxel size
norm_magnifications = magnifications/magnifications[0]
# scaled propagation distances due to magnified probes
distances = distances*norm_magnifications**2

z1p = 12e-3  # positions of the code and the probe for reconstruction

z2p = z1-np.tile(z1p, len(z1))
# magnification when propagating from the probe plane to the detector
magnifications2 = z1/z1p
# propagation distances after switching from the point source wave to plane wave,
distances2 = (z1p*z2p)/(z1p+z2p)
norm_magnifications2 = magnifications2/(z1p/z1[0])  # normalized magnifications
# scaled propagation distances due to magnified probes
distances2 = distances2*norm_magnifications2**2
distances2 = distances2*(z1p/z1)**2

# allow padding if there are shifts of the probe
pad = n//16*0
# sample size after demagnification
ne = n+2*pad
[3]:
psi_abs = read_tiff('data/rec_abs.tiff')
psi_angle = read_tiff('data/rec_angle.tiff')
psi = psi_abs*np.exp(1j*psi_angle)
mshow_polar(psi[0])
../../../_images/notebook_coded_apertures_3d_ald_rec_tomo_3_0.png
[ ]:
### Computing the logarithm
[4]:

def _mlog(psi): res = psi.copy() res[np.abs(psi) < 1e-32] = 1e-32 res = np.log(res) return res def logtomo(psi): """Log representation of projections, -i/\nu log(psi)""" return -1j * wavelength / (2*cp.pi) * _mlog(psi) / voxelsize
[5]:
def line_search(minf, gamma, fu, fd):
    """ Line search for the step sizes gamma"""
    while (minf(fu)-minf(fu+gamma*fd) < 0 and gamma > 1e-12):
        gamma *= 0.5
    if (gamma <= 1e-12):  # direction not found
        # print('no direction')
        gamma = 0
    return gamma


def cg_tomo(data, init, pars):
    """Conjugate gradients method for tomogarphy"""
    # minimization functional
    @gpu_batch
    def _minf(Ru, data):
        res = cp.empty(data.shape[0], dtype='float32')
        for k in range(data.shape[0]):
            res[k] = np.linalg.norm(Ru[k]-data[k])**2
        return res

    def minf(Ru):
        res = np.sum(_minf(Ru, data))
        return res

    u = init.copy()
    center_pad = center+ne//4
    for i in range(pars['niter']):
        # Take the gradient
        fu = R(u, theta, center_pad)
        grad = RT(fu-data, theta, center_pad)/np.float32(ne*ntheta)
        # Dai-Yuan direction
        if i == 0:
            d = -grad
        else:
            d = dai_yuan(d, grad, grad0)
        grad0 = grad
        fd = R(d, theta, center_pad)
        gamma = line_search(minf, pars['gamma'], fu, fd)
        # update u
        u = linear(u, d, 1, gamma)

        if i % pars['err_step'] == 0:
            fu = R(u, theta, center_pad)
            err = minf(fu)
            print(f'{i}) {gamma=}, {err=:1.5e}')

        if i % pars['vis_step'] == 0:
            mshow_complex(u[u.shape[0]//2, ne//4:-ne//4, ne//4:-ne//4])
            mshow_complex(u[:, ne//2+ne//4+2, ne//4:-ne//4])

    return u

# compute logarithm
data = logtomo(psi)

# if by chunk on gpu
# rec = np.zeros([ne, 3*ne//2, 3*ne//2], dtype='complex64')
# data_rec = np.pad(np.array(data.swapaxes(0, 1)),
#                   ((0, 0), (0, 0), (ne//4, ne//4)), 'edge')

# if fully on gpu
rec = cp.zeros([ne,3*ne//2,3*ne//2],dtype='complex64')
data_rec = cp.pad(cp.array(data.swapaxes(0,1)),((0,0),(0,0),(ne//4,ne//4)),'edge')

pars = {'niter': 129, 'err_step': 4, 'vis_step': 16, 'gamma': 1}
rec = cg_tomo(data_rec, rec, pars)
0) gamma=1, err=4.26340e-03
../../../_images/notebook_coded_apertures_3d_ald_rec_tomo_6_1.png
../../../_images/notebook_coded_apertures_3d_ald_rec_tomo_6_2.png
4) gamma=1, err=4.47588e-04
8) gamma=1, err=2.22505e-04
12) gamma=1, err=1.37048e-04
16) gamma=1, err=9.69631e-05
../../../_images/notebook_coded_apertures_3d_ald_rec_tomo_6_4.png
../../../_images/notebook_coded_apertures_3d_ald_rec_tomo_6_5.png
20) gamma=1, err=8.26368e-05
24) gamma=1, err=7.77001e-05
28) gamma=1, err=7.60277e-05
32) gamma=1, err=7.52831e-05
../../../_images/notebook_coded_apertures_3d_ald_rec_tomo_6_7.png
../../../_images/notebook_coded_apertures_3d_ald_rec_tomo_6_8.png
36) gamma=1, err=7.49357e-05
40) gamma=1, err=7.47128e-05
44) gamma=1, err=7.46110e-05
48) gamma=1, err=7.45450e-05
../../../_images/notebook_coded_apertures_3d_ald_rec_tomo_6_10.png
../../../_images/notebook_coded_apertures_3d_ald_rec_tomo_6_11.png
---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
Cell In[5], line 66
     63 data_rec = cp.pad(cp.array(data.swapaxes(0,1)),((0,0),(0,0),(ne//4,ne//4)),'edge')
     65 pars = {'niter': 129, 'err_step': 4, 'vis_step': 16, 'gamma': 1}
---> 66 rec = cg_tomo(data_rec, rec, pars)

Cell In[5], line 30, in cg_tomo(data, init, pars)
     27 for i in range(pars['niter']):
     28     # Take the gradient
     29     fu = R(u, theta, center_pad)
---> 30     grad = RT(fu-data, theta, center_pad)/np.float32(ne*ntheta)
     31     # Dai-Yuan direction
     32     if i == 0:

File ~/conda/miniforge3/envs/holotomo/lib/python3.10/site-packages/holotomocupy/chunking.py:38, in gpu_batch.<locals>.inner(*args, **kwargs)
     36 # if array is on gpu then just run the function
     37 if isinstance(args[0], cp.ndarray):
---> 38     out = func(*args, **kwargs)
     39     return out
     41 #else do processing by chunks

File ~/conda/miniforge3/envs/holotomo/lib/python3.10/site-packages/holotomocupy/tomo.py:116, in RT(sino, theta, rotation_axis)
    113 mua = cp.array([mu], dtype='float32')
    114 gather_kernel((int(cp.ceil(n/32)), int(cp.ceil(ntheta/32)), nz),
    115               (32, 32, 1), (sino, fde, theta, m, mua, n, ntheta, nz, 1))
--> 116 wrapadj_kernel((int(cp.ceil((2 * n + 2 * m)/32)),
    117                 int(cp.ceil((2 * n + 2 * m)/32)), nz), (32, 32, 1), (fde, n, nz, m))
    119 # STEP3: ifft 2d
    120 fde = fde[:, m:-m, m:-m]

KeyboardInterrupt: