def Neel(dim, chirality = 'io', pad = True, ir=0,show=False): """Create a Neel magnetization structure. Unlike Lillihook, this function does not produce a rigorously calculated magnetization structure. Args: dim (int): Dimension of lattice. chirality (str): - 'cw': clockwise rotation - 'ccw': counter-clockwise rotation. pad (bool): Whether or not to leave some space between the edge of the magnetization and the edge of the image. ir (float): Inner radius of the vortex in pixels. show (bool): If True, will show the x, y, z components in plot form. Returns: tuple: (mag_x, mag_y, mag_z) - mag_x (``ndarray``) -- 2D Numpy array of shape (dim, dim). x-component of magnetization vector. - mag_y (``ndarray``) -- 2D Numpy array of shape (dim, dim). y-component of magnetization vector. - mag_z (``ndarray``) -- 2D Numpy array of shape (dim, dim). z-component of magnetization vector. """ cx, cy = [dim//2,dim//2] if pad: rad = 3*dim//8 else: rad = dim//2 x,y = np.array(np.ogrid[:dim, :dim]) - dim//2 circmask = circ4(dim, rad) circ_ir = circ4(dim, ir) zmask = -1*np.ones_like(circmask) + circmask + circ_ir circmask -= circ_ir dist = dist4(dim) mag_y = -x * np.sin(np.pi*dist/(rad-ir) - np.pi*(2*ir-rad)/(rad-ir)) * circmask mag_x = -y * np.sin(np.pi*dist/(rad-ir) - np.pi*(2*ir-rad)/(rad-ir)) * circmask mag_x /= np.max(mag_x) mag_y /= np.max(mag_y) # b = 1 # mag_z = (b - 2*b*dist/rad) * circmask mag_z = (-ir-rad + 2*dist)/(ir-rad) * circmask mag_z[np.where(zmask==1)] = 1 mag_z[np.where(zmask==-1)] = -1 mag = np.sqrt(mag_x**2 + mag_y**2 + mag_z**2) mag_x /= mag mag_y /= mag mag_z /= mag if chirality == 'oi': mag_x *= -1 mag_y *= -1 if show: show_im(np.sqrt(mag_x**2 + mag_y**2 + mag_z**2), 'mag') show_im(mag_x, 'mag x') show_im(mag_y, 'mag y') show_im(mag_z, 'mag z') x = np.arange(0,dim,1) fig,ax = plt.subplots() ax.plot(x,mag_x[dim//2],label='x') ax.plot(x,-mag_y[:,dim//2],label='y') ax.plot(x,mag_z[dim//2], label='z') plt.legend() plt.show() return (mag_x, mag_y, mag_z)
def Lillihook(dim, rad = None, Q = 1, gamma = 1.5708, P=1, show=False): """Define a skyrmion magnetization. This function makes a skyrmion magnetization as calculated and defined in [1]. It returns three 2D arrays of size (dim, dim) containing the x, y, and z magnetization components at each pixel. Args: dim (int): Dimension of lattice. rad (float): Radius parameter (see [1]). Default dim//16. Q (int): Topological charge. - 1: skyrmion - 2: biskyrmion - -1: antiskyrmion gamma (float): Helicity. - 0 or Pi: Neel - Pi/2 or 3Pi/2: Bloch P (int): Polarity (z direction in center), +/- 1. show (bool): (`optional`) If True, will plot the x, y, z components. Returns: tuple: (mag_x, mag_y, mag_z) - mag_x (``ndarray``) -- 2D Numpy array (dim, dim). x-component of magnetization vector. - mag_y (``ndarray``) -- 2D Numpy array (dim, dim). y-component of magnetization vector. - mag_z (``ndarray``) -- 2D Numpy array (dim, dim). z-component of magnetization vector. References: 1) Lilliehöök, D., Lejnell, K., Karlhede, A. & Sondhi, S. Quantum Hall Skyrmions with higher topological charge. Phys. Rev. B 56, 6805–6809 (1997). """ cx, cy = [dim//2,dim//2] cy = dim//2 cx = dim//2 if rad is None: rad = dim//16 print(f'Radius parameter set to {rad}.') a = np.arange(dim) b = np.arange(dim) x,y = np.meshgrid(a,b) x -= cx y -= cy dist = np.sqrt(x**2 + y**2) zeros = np.where(dist==0) dist[zeros] = 1 f = ((dist/rad)**(2*Q)-4) / ((dist/rad)**(2*Q)+4) re = np.real(np.exp(1j*gamma)) im = np.imag(np.exp(1j*gamma)) mag_x = -np.sqrt(1-f**2) * (re*np.cos(Q*np.arctan2(y,x)) + im*np.sin(Q*np.arctan2(y,x))) mag_y = -np.sqrt(1-f**2) * (-1*im*np.cos(Q*np.arctan2(y,x)) + re*np.sin(Q*np.arctan2(y,x))) mag_z = -P*f mag_x[zeros] = 0 mag_y[zeros] = 0 if show: show_im(mag_x, 'mag x') show_im(mag_y, 'mag y') show_im(mag_z, 'mag z') x = np.arange(0,dim,1) fig,ax = plt.subplots() ax.plot(x,mag_z[dim//2], label='mag_z profile along x-axis.') ax.set_xlabel("x-axis, y=0") ax.set_ylabel("mag_z") plt.legend() plt.show() return (mag_x, mag_y, mag_z)
def Bloch(dim, chirality = 'cw', pad = True, ir=0, show=False): """Create a Bloch vortex magnetization structure. Unlike Lillihook, this function does not produce a rigorously calculated magnetization structure. It is just a vortex that looks like some experimental observations. Args: dim (int): Dimension of lattice. chirality (str): - 'cw': clockwise rotation - 'ccw': counter-clockwise rotation. pad (bool): Whether or not to leave some space between the edge of the magnetization and the edge of the image. ir (float): Inner radius of the vortex in pixels. show (bool): If True, will show the x, y, z components in plot form. Returns: tuple: (mag_x, mag_y, mag_z) - mag_x (``ndarray``) -- 2D Numpy array of shape (dim, dim). x-component of magnetization vector. - mag_y (``ndarray``) -- 2D Numpy array of shape (dim, dim). y-component of magnetization vector. - mag_z (``ndarray``) -- 2D Numpy array of shape (dim, dim). z-component of magnetization vector. """ cx, cy = [dim//2,dim//2] if pad: rad = 3*dim//8 else: rad = dim//2 # mask x,y = np.ogrid[:dim, :dim] cy = dim//2 cx = dim//2 r2 = (x-cx)*(x-cx) + (y-cy)*(y-cy) circmask = r2 <= rad*rad circmask *= r2 >= ir*ir # making the magnetizations a = np.arange(dim) b = np.arange(dim) x,y = np.meshgrid(a,b) x -= cx y -= cy dist = np.sqrt(x**2 + y**2) mag_x = -np.sin(np.arctan2(y,x)) * np.sin(np.pi*dist/(rad-ir) - np.pi*(2*ir-rad)/(rad-ir)) * circmask mag_y = np.cos(np.arctan2(y,x)) * np.sin(np.pi*dist/(rad-ir) - np.pi*(2*ir-rad)/(rad-ir)) * circmask mag_x /= np.max(mag_x) mag_y /= np.max(mag_y) mag_z = (-ir-rad + 2*dist)/(ir-rad) * circmask mag_z[np.where(dist<ir)] = 1 mag_z[np.where(dist>rad)] = -1 mag = np.sqrt(mag_x**2 + mag_y**2 + mag_z**2) mag_x /= mag mag_y /= mag mag_z /= mag if chirality == 'ccw': mag_x *= -1 mag_y *= -1 if show: show_im(mag_x, 'mag x') show_im(mag_y, 'mag y') show_im(mag_z, 'mag z') x = np.arange(0,dim,1) fig,ax = plt.subplots() ax.plot(x,mag_z[dim//2], label='mag_z profile along x-axis.') plt.legend() plt.show() return (mag_x, mag_y, mag_z)
def SITIE(image=None, defval=None, scale=1, E=200e3, ptie=None, i=-1, flipstack=False, pscope=None, data_loc='', dataname='', sym=False, qc=None, save=False, v=1): """Uses a modified derivative to get the magnetic phase shift with TIE from a single image. This technique is only applicable to uniformly thin samples from which the only source of contrast is magnetic Fresnel contrast. All other sources of contrast including sample contamination, thickness variation, and diffraction contrast will give false magnetic inductions. For more information please refer to: Chess, J. J. et al. Ultramicroscopy 177, 78–83 (2018). This function has two ways of picking which image to use. First, if an image is given directly along with a defocus value, it will use that. You should also be sure to specify the scale of the image and accelerating voltage of the microscope (default 200kV). You can also choose to pass it an image from a ``TIE_params`` object, in which case you need specify only whether to choose from the imstack or flipstack and the index of the image to use. It's possible that in the future this method of selecting an image will be removed or moved to a separate function. Args: image (2D array): Input image to reconstruct. defval (float): Defocus value corresponding to ``image``. scale (float): Scale (nm/pixel) corresponding to ``image``. E (float): Accelerating voltage of microscope that produced ``image``. ptie (``TIE_params`` object): Object containing the image. From TIE_params.py i (int): Index of `the ptie.imstack` or `ptie.flipstack` to reconstruct. This is not the defocus index like in TIE. Default value is -1 which corresponds to the most overfocused image. flipstack (bool): (`optional`) Whether to pull the image from ptie.imstack[i] or ptie.flipstack[i]. Default is False, calls image from imstack. pscope (``Microscope`` object): Should have same accelerating voltage as the microscope that took the images. dataname (str): The output filename to be used for saving the images. sym (bool): (`optional`) Fourier edge effects are marginally improved by symmetrizing the images before reconstructing. Default False. qc (float/str): (`optional`) The Tikhonov frequency to use as filter. Default None. If you use a Tikhonov filter the resulting phase shift and induction is no longer quantitative. save (bool/str): Whether you want to save the output. =========== ============ input value saved images =========== ============ True All images 'b' bx, by, and color image 'color' Color image False None =========== ============ Files will be saved as ptie.data_loc/images/dataname_<defval>_<key>.tiff, where <key> is the key for the returned dictionary that corresponds to the image. v (int): (`optional`) Verbosity. === ======== v print output === ======== 0 No output 1 Default output 2 Extended output for debugging. === ======== Returns: dict: A dictionary of image arrays ========= ============== key value ========= ============== 'byt' y-component of integrated magnetic induction 'bxt' x-component of integrated magnetic induction 'bbt' Magnitude of integrated magnetic induction 'phase_m' Magnetic phase shift (radians) 'color_b' RGB image of magnetization ========= ============== """ results = { 'byt': None, 'bxt': None, 'bbt': None, 'phase_m': None, 'color_b': None } # turning off the print function if v=0 vprint = print if v >= 1 else lambda *a, **k: None if image is not None and defval is not None: vprint( f"Running with given image, defval = {defval:g}nm, and scale = {scale:.3g}nm/pixel" ) ptie = TIE_params(imstack=[image], defvals=[defval], data_loc=data_loc, v=0) ptie.set_scale(scale) dim_y, dim_x = image.shape if pscope is None: pscope = Microscope(E=E) else: # selecting the right defocus value for the image if i >= ptie.num_files: print("i given outside range.") sys.exit(1) else: if ptie.num_files > 1: unders = list(reversed([-1 * i for i in ptie.defvals])) defvals = unders + [0] + ptie.defvals defval = defvals[i] else: defval = ptie.defvals[0] vprint(f'SITIE defocus: {defval:g} nm') right, left = ptie.crop['right'], ptie.crop['left'] bottom, top = ptie.crop['bottom'], ptie.crop['top'] dim_y = bottom - top dim_x = right - left vprint(f"Reconstructing with ptie image {i} and defval {defval}") if flipstack: print("Reconstructing with single flipped image.") image = ptie.flipstack[i].data[top:bottom, left:right] else: image = ptie.imstack[i].data[top:bottom, left:right] if sym: print("Reconstructing with symmetrized image.") dim_y *= 2 dim_x *= 2 # setup the inverse frequency distribution q = dist(dim_y, dim_x) q[0, 0] = 1 if qc is not None and qc is not False: # add Tikhonov filter print("Reconstructing with Tikhonov value: {:}".format(qc)) qi = q**2 / (q**2 + qc**2)**2 else: # normal laplacian method print("Reconstructing with normal Laplacian method") qi = 1 / q**2 qi[0, 0] = 0 ptie.qi = qi # saves the freq dist # constructing "infocus" image infocus = np.ones(np.shape(image)) * np.mean(image) # calculate "derivative" and normalize dIdZ = 2 * (image - infocus) dIdZ -= np.sum(dIdZ) / np.size(infocus) ### Now calling the phase reconstruct in the normal way print('Calling SITIE solver\n') resultsB = phase_reconstruct(ptie, infocus, dIdZ, pscope, defval, sym=sym) results['byt'] = resultsB['ind_y'] results['bxt'] = resultsB['ind_x'] results['bbt'] = np.sqrt(resultsB['ind_x']**2 + resultsB['ind_y']**2) results['phase_m'] = resultsB['phase'] results['color_b'] = color_im(resultsB['ind_x'], resultsB['ind_y'], hsvwheel=True, background='black') if v >= 1: show_im(results['color_b'], "B field color, HSV colorhweel", cbar=False, scale=scale) # save the images if save: save_results(defval, results, ptie, dataname, sym, qc, save, v, directory="SITIE") print('Phase reconstruction completed.') return results
def TIE(i=-1, ptie=None, pscope=None, dataname='', sym=False, qc=None, save=False, hsv=True, long_deriv=False, v=1): """Sets up the TIE reconstruction and calls phase_reconstruct. This function calculates the necessary arrays, derivatives, etc. and then passes them to phase_reconstruct which solve the TIE. Args: i (int): Index of ptie.defvals to use for reconstruction. Default value is -1 which corresponds to the most defocused images for a central difference method derivative. i is ignored if using a longitudinal derivative. ptie (``TIE_params`` object): Object containing the images and other data parameters. From TIE_params.py pscope (``Microscope`` object): Should have same accelerating voltage as the microscope that took the images. dataname (str): The output filename to be used for saving the images. sym (bool): (`optional`) Fourier edge effects are marginally improved by symmetrizing the images before reconstructing. Default False. qc (float/str): (`optional`) The Tikhonov frequency to use as filter. Default None. If you use a Tikhonov filter the resulting phase shift and induction is no longer quantitative. save (bool/str): Whether you want to save the output. =========== ============ input value saved images =========== ============ True All images 'b' bx, by, and color image 'color' Color image False None =========== ============ Files will be saved as ptie.data_loc/images/dataname_<defval>_<key>.tiff, where <key> is the key for the results dictionary that corresponds to the image. hsv (bool): Whether to use the hsv colorwheel (True) or the 4-fold colorwheel (False). long_deriv (bool): Whether to use the longitudinal derivative (True) or central difference method (False). Default False. v (int): (`optional`) Verbosity. === ======== v print output === ======== 0 No output 1 Default output 2 Extended output for debugging. === ======== Returns: dict: A dictionary of image arrays ========= ============== key value ========= ============== 'byt' y-component of integrated magnetic induction 'bxt' x-component of integrated magnetic induction 'bbt' Magnitude of integrated magnetic induction 'phase_m' Magnetic phase shift (radians) 'phase_e' Electrostatic phase shift (if using flip stack) (radians) 'dIdZ_m' Intensity derivative for calculating phase_m 'dIdZ_e' Intensity derivative for calculating phase_e (if using flip stack) 'color_b' RGB image of magnetization 'inf_im' In-focus image ========= ============== """ results = { 'byt': None, 'bxt': None, 'bbt': None, 'phase_e': None, 'phase_m': None, 'dIdZ_m': None, 'dIdZ_e': None, 'color_b': None, 'inf_im': None } # turning off the print function if v=0 vprint = print if v >= 1 else lambda *a, **k: None if long_deriv: unders = list(reversed([-1 * ii for ii in ptie.defvals])) defval = unders + [0] + ptie.defvals if ptie.flip: vprint('Aligning with complete longitudinal derivatives:\n', defval, '\nwith both flip/unflip tfs.') else: vprint('Aligning with complete longitudinal derivatives:\n', defval, '\nwith only unflip tfs.') else: defval = ptie.defvals[i] if ptie.flip: vprint( f'Aligning for defocus value: {defval:g}, with both flip/unflip tfs.' ) else: vprint( f'Aligning for defocus value: {defval:g}, with only unflip tfs.' ) right, left = ptie.crop['right'], ptie.crop['left'] bottom, top = ptie.crop['bottom'], ptie.crop['top'] dim_y = bottom - top dim_x = right - left tifs = select_tifs(i, ptie, long_deriv) if sym: vprint("Reconstructing with symmetrized image.") dim_y *= 2 dim_x *= 2 # make the inverse laplacian, uses python implementation of IDL dist funct q = dist(dim_y, dim_x) q[0, 0] = 1 if qc is not None and qc is not False: vprint("Reconstructing with Tikhonov value: {:}".format(qc)) qi = q**2 / (q**2 + qc**2)**2 else: # normal Laplacian method vprint("Reconstructing with normal Laplacian method") qi = 1 / q**2 qi[0, 0] = 0 ptie.qi = qi # saves the freq dist # If rotation and translation to be applied if ptie.rotation != 0 or ptie.x_transl != 0 or ptie.y_transl != 0: rotate, x_shift, y_shift = ptie.rotation, ptie.x_transl, ptie.y_transl for ii in range(len(tifs)): tifs[ii] = scipy.ndimage.rotate(tifs[ii], rotate, reshape=False, order=0) tifs[ii] = scipy.ndimage.shift(tifs[ii], (-y_shift, x_shift), order=0) mask = scipy.ndimage.rotate(ptie.mask, rotate, reshape=False, order=0) mask = scipy.ndimage.shift(mask, (-y_shift, x_shift), order=0) # crop images and apply mask if ptie.rotation == 0 and ptie.x_transl == 0 and ptie.y_transl == 0: mask = ptie.mask[top:bottom, left:right] else: mask = mask[top:bottom, left:right] # crop images and apply mask # mask = ptie.mask[top:bottom, left:right] for ii in range(len(tifs)): tifs[ii] = tifs[ii][top:bottom, left:right] tifs[ii] *= mask # Normalizing, scaling the images scaled_tifs = scale_stack(tifs) scaled_tifs += 1e-9 # get the infocus image if long_deriv and ptie.flip: inf_unflip = scaled_tifs[len(tifs) // 4] inf_flip = scaled_tifs[3 * len(tifs) // 4] inf_im = (inf_unflip + inf_flip) / 2 else: inf_im = scaled_tifs[len(tifs) // 2] # Inverting masked areas on infocus image because we divide by it inf_im += 1 - mask # Make sure there are no zeros left: inf_im = np.where(scaled_tifs[len(tifs) // 2] == 0, 0.001, inf_im) results['inf_im'] = inf_im if v >= 2: print("""\nScaled images (+- = unflip/flip, +- = over/underfocus) in order [ +- , -- , 0 , ++ , -+ ]""") for im in scaled_tifs: print("max: {:.3f}, min: {:.2f}, total intensity: {:.4f}".format( np.max(im), np.min(im), np.sum(im))) print() # Calculate derivatives if long_deriv: # have to renormalize each stack unflip_stack = tifs[:ptie.num_files] unflip_stack = scale_stack(unflip_stack) + 1e-9 flip_stack = tifs[ptie.num_files:] flip_stack = scale_stack(flip_stack) + 1e-9 vprint('Computing the longitudinal derivative.') unflip_deriv = polyfit_deriv(unflip_stack, defval, v) if ptie.flip: vprint('Computing the flip stack longitudinal derivative.') flip_deriv = polyfit_deriv(flip_stack, defval, v) dIdZ_m = (unflip_deriv - flip_deriv) / 2 dIdZ_e = (unflip_deriv + flip_deriv) / 2 else: dIdZ_m = unflip_deriv else: # three point derivative, default. if ptie.flip: dIdZ_m = 1 / 2 * (scaled_tifs[3] - scaled_tifs[0] - (scaled_tifs[4] - scaled_tifs[1])) dIdZ_e = 1 / 2 * (scaled_tifs[3] - scaled_tifs[0] + (scaled_tifs[4] - scaled_tifs[1])) else: dIdZ_m = scaled_tifs[2] - scaled_tifs[0] # Set derivatives to have 0 total "energy" dIdZ_m *= mask totm = np.sum(dIdZ_m) / np.sum(mask) dIdZ_m -= totm dIdZ_m *= mask results['dIdZ_m'] = dIdZ_m if ptie.flip: dIdZ_e *= mask tote = np.sum(dIdZ_e) / np.sum(mask) dIdZ_e -= tote dIdZ_e *= mask results['dIdZ_e'] = dIdZ_e ### Now time to call phase_reconstruct, first for E if we have a flipped tfs vprint('Calling TIE solver\n') if ptie.flip: resultsE = phase_reconstruct(ptie, inf_im, dIdZ_e, pscope, defval, sym=sym, long_deriv=long_deriv) # We only care about the E phase. results['phase_e'] = resultsE['phase'] ### Now run for B, resultsB = phase_reconstruct(ptie, inf_im, dIdZ_m, pscope, defval, sym=sym, long_deriv=long_deriv) results['byt'] = resultsB['ind_y'] results['bxt'] = resultsB['ind_x'] results['bbt'] = np.sqrt(resultsB['ind_x']**2 + resultsB['ind_y']**2) results['phase_m'] = resultsB['phase'] results['color_b'] = color_im(resultsB['ind_x'], resultsB['ind_y'], hsvwheel=hsv, background='black') if v >= 1: show_im(results['color_b'], "B-field color HSV colorwheel", cbar=False, scale=ptie.scale) # save the images if save: save_results(defval, results, ptie, dataname, sym, qc, save, v, long_deriv=long_deriv) vprint('Phase reconstruction completed.') return results
def SITIE(ptie=None, pscope=None, dataname='', sym=False, qc=None, save=True, i=-1, flipstack=False, v=1): """Uses a modified derivative to get the magnetic phase shift with TIE from a single image. This technique is only appplicable to uniformly thin samples from which the only source of contrast is magnetic Fresnel contrast. All other sources of contrast including dirt on the sample, thickness variation, and diffraction contrast will give false magnetic inductions. For more information please refer to: Chess, J. J. et al. Ultramicroscopy 177, 78–83 (2018). Args: ptie: TIE_params object. Object containing the image from TIE_params.py pscope : microscope object. Should have correct accelerating voltage as the microscope that took the images. dataname : String. The output filename to be used for saving the images. Files will be saved ptie.data_loc/images/dataname_<defval>_<key>.tiff sym: Boolean. Fourier edge effects are marginally improved by symmetrizing the images before reconstructing (image reconstructed is 4x as large). Default False. qc: Float. The Tikhonov frequency to use as filter, or "percent" to use 15% of q, Default None. save: Bool or string. Whether you want to save the output. Default False. save = True -> saves all images. save = 'b' -> save just bx, by, and color_b save = 'color' -> saves just color_b save = False -> don't save. Saves the images to ptie.data_loc/images/ long_deriv: Bool. Whether to use the longitudinal derivative (True) or central difference method (False). Default False. i: Int. index of __the ptie.imstack or ptie.flipstack__ to reconstruct. This is not the defocus index like in TIE. Default value is -1 which corresponds to the most overfocused image. flipstack: Bool. Whether to pull the image from the ptie.dmrstack[i] or ptie.flipstack[i]. Default is False, calls image from imstack. v: Int. Verbosity. 0 : ##TODO no output 1 : Default output 2 : Extended output for debugging. Returns: A dictionary of arrays. results = { 'byt' : y-component of integrated magnetic induction, 'bxt' : x-copmonent of integrated magnetic induction, 'bbt' : magnitude of integrated magnetic induction, 'phase_m' : magnetic phase shift (radians), 'color_b' : RGB image of magnetization, } """ results = { 'byt' : None, 'bxt' : None, 'bbt' : None, 'phase_m' : None, 'color_b' : None} # turning off the print function if v=0 vprint = print if v>=1 else lambda *a, **k: None # selecting the right defocus value for the image if i >= ptie.num_files: print("i given outside range.") sys.exit(1) else: if ptie.num_files > 1: unders = list(reversed([-1*i for i in ptie.defvals])) defvals = unders + [0] + ptie.defvals defval = defvals[i] else: defval = ptie.defvals[0] vprint(f'SITIE defocus: {defval} nm') right, left = ptie.crop['right'] , ptie.crop['left'] bottom, top = ptie.crop['bottom'] , ptie.crop['top'] dim_y = bottom - top dim_x = right - left if flipstack: print("Reconstructing with single flipped image.") image = ptie.flipstack[i].data[top:bottom, left:right] else: image = ptie.imstack[i].data[top:bottom, left:right] if sym: print("Reconstructing with symmetrized image.") dim_y *= 2 dim_x *= 2 # setup the inverse frequency distribution q = dist(dim_y,dim_x) q[0, 0] = 1 if qc is not None and qc is not False: # add Tikhonov filter if qc == 'percent': print("Reconstructing with Tikhonov percentage: 15%") qc = 0.15 * q * ptie.scale**2 else: qc = qc print("Reconstructing with Tikhonov value: {:}".format(qc)) qi = q**2 / (q**2 + qc**2)**2 else: # normal laplacian method print("Reconstructing with normal Laplacian method") qi = 1 / q**2 qi[0, 0] = 0 ptie.qi = qi # saves the freq dist # constructing "infocus" image infocus = np.ones(np.shape(image))*np.mean(image) # calculate "derivative" and normalize dIdZ = 2 * (image - infocus) dIdZ -= np.sum(dIdZ)/np.size(infocus) ### Now calling the phase reconstruct in the normal way print('Calling SITIE solver\n') resultsB = phase_reconstruct(ptie, infocus, dIdZ, pscope, defval, sym = sym) results['byt'] = resultsB['ind_y'] results['bxt'] = resultsB['ind_x'] results['bbt'] = np.sqrt(resultsB['ind_x']**2 + resultsB['ind_y']**2) results['phase_m'] = resultsB['phase'] results['color_b'] = color_im(resultsB['ind_x'], resultsB['ind_y'], hsvwheel=True, background='black') if v >= 1: show_im(results['color_b'], "B field color, HSV colorhweel") # save the images if save: save_results(defval, results, ptie, dataname, sym, qc, save, v, directory = "SITIE") print('Phase reconstruction completed.') return results
def TIE(i=-1, ptie=None, pscope=None, dataname='', sym=False, qc=None, save=False, long_deriv=False, v=1): """Sets up the TIE reconstruction and calls phase_reconstruct. This function calculates the necessary arrays, derivatives, etc. and then calls passes them to phase_reconstruct which solve the TIE. Args: i: Int. index of ptie.defvals to use for reconstruction. Default value is -1 which corresponds to the most defocused images for a central difference method derivative. i is ignored if using a longitudinal derivative. ptie: TIE_params object. Object containing the image from TIE_params.py pscope : microscope object. Should have correct accelerating voltage as the microscope that took the images. dataname : String. The output filename to be used for saving the images. Files will be saved ptie.data_loc/images/dataname_<defval>_<key>.tiff sym: Boolean. Fourier edge effects are marginally improved by symmetrizing the images before reconstructing (image reconstructed is 4x as large). Default False. qc: Float. The Tikhonov frequency to use as filter, or "percent" to use 15% of q, Default None. If you use a Tikhonov filter the resulting magnetization is no longer quantitative! save: Bool or string. Whether you want to save the output. Default False. save = True -> saves all images. save = 'b' -> save just bx, by, and color_b save = 'color' -> saves just color_b save = False -> don't save. Saves the images to ptie.data_loc/images/ long_deriv: Bool. Whether to use the longitudinal derivative (True) or central difference method (False). Default False. __Currently has bugs__. Qualitatively looks alright but quantitatively is not accurate. v: Int. Verbosity. 0 : no output 1 : Default output 2 : Extended output for debugging. Returns: A dictionary of arrays. results = { 'byt' : y-component of integrated magnetic induction, 'bxt' : x-copmonent of integrated magnetic induction, 'bbt' : magnitude of integrated magnetic induction, 'phase_m' : magnetic phase shift (radians), 'phase_e' : electrostatic phase shift (if using flip stack) (radians), 'dIdZ_m' : intensity derivative for calculating phase_m, 'dIdZ_e' : intensity derivative for calculating phase_e (if using flip stack), 'color_b' : RGB image of magnetization, 'inf_im' : the in-focus image } """ results = { 'byt' : None, 'bxt' : None, 'bbt' : None, 'phase_e' : None, 'phase_m' : None, 'dIdZ_m' : None, 'dIdZ_e' : None, 'color_b' : None, 'inf_im' : None} # turning off the print function if v=0 vprint = print if v>=1 else lambda *a, **k: None if long_deriv: unders = list(reversed([-1*ii for ii in ptie.defvals])) defval = unders + [0] + ptie.defvals if ptie.flip: vprint('Aligning with complete longitudinal derivates:\n', defval, '\nwith both flip/unflip tfs.') else: vprint('Aligning with complete longitudinal derivates:\n', defval, '\nwith only unflip tfs.') else: defval = ptie.defvals[i] if ptie.flip: vprint('Aligning for defocus value: ', defval, ' with both flip/unflip tfs.') else: vprint('Aligning for defocus value: ', defval, ' with only unflip tfs.') right, left = ptie.crop['right'] , ptie.crop['left'] bottom, top = ptie.crop['bottom'] , ptie.crop['top'] dim_y = bottom - top dim_x = right - left tifs = select_tifs(i, ptie, long_deriv) if sym: vprint("Reconstructing with symmetrized image.") dim_y *= 2 dim_x *= 2 # make the inverse laplacian, uses python implementation of IDL dist funct q = dist(dim_y,dim_x) q[0, 0] = 1 if qc is not None and qc is not False: if qc == 'percent': vprint("Reconstructing with Tikhonov percentage: 15%") qc = 0.15 * q * ptie.scale**2 else: qc = qc vprint("Reconstructing with Tikhonov value: {:}".format(qc)) qi = q**2 / (q**2 + qc**2)**2 else: # normal laplacian method vprint("Reconstructing with normal Laplacian method") qi = 1 / q**2 qi[0, 0] = 0 ptie.qi = qi # saves the freq dist # crop images and apply mask mask = ptie.mask[top:bottom, left:right] for ii in range(len(tifs)): tifs[ii] = tifs[ii][top:bottom, left:right] tifs[ii] *= mask # Normalizing, scaling the images scaled_tifs = scale_stack(tifs) # very small offset from 0, affects uniform magnetizations # but can be compensated for (if 0) by symmetrizing the image. scaled_tifs += 1e-9 # get the infocus image if long_deriv and ptie.flip: inf_unflip = scaled_tifs[len(tifs)//4] inf_flip = scaled_tifs[3*len(tifs)//4] inf_im = (inf_unflip+inf_flip)/2 else: inf_im = scaled_tifs[len(tifs)//2] # Inverting masked areas on infocus image because we divide by it inf_im += 1 - mask # Make sure there are no zeros left: inf_im = np.where(scaled_tifs[len(tifs)//2] == 0, 0.001, inf_im) results['inf_im'] = inf_im if v >= 2: print("""\nScaled images (+- = unflip/flip, +- = over/underfocus) in order [ +- , -- , 0 , ++ , -+ ]""") for im in scaled_tifs: print("max: {:.3f}, min: {:.2f}, total intensity: {:.4f}".format( np.max(im), np.min(im), np.sum(im))) print() # Calculate derivatives if long_deriv: unflip_stack = tifs[:ptie.num_files] flip_stack = tifs[ptie.num_files:] if long_deriv == 'multi': vprint('Computing the longitudinal derivative with Multiprocessing.') unflip_deriv = polyfit_deriv_multiprocess(unflip_stack, defval) else: vprint('Computing the longitudinal derivative normally.') unflip_deriv = polyfit_deriv(unflip_stack, defval, v) if ptie.flip: if long_deriv == 'multi': vprint('Computing the flip stack longitudinal derivative with Multiprocessing.') flip_deriv = polyfit_deriv_multiprocess(flip_stack, defval) else: vprint('Computing the flip stack longitudinal derivative normally.') flip_deriv = polyfit_deriv(flip_stack, defval, v) dIdZ_m = (unflip_deriv - flip_deriv)/2 dIdZ_e = (unflip_deriv + flip_deriv)/2 else: dIdZ_m = unflip_deriv else: if ptie.flip: dIdZ_m = 1/2 * (scaled_tifs[3] - scaled_tifs[0] - (scaled_tifs[4] - scaled_tifs[1])) dIdZ_e = 1/2 * (scaled_tifs[3] - scaled_tifs[0] + (scaled_tifs[4] - scaled_tifs[1])) else: dIdZ_m = scaled_tifs[2] - scaled_tifs[0] # Set derivatives to have 0 total "energy" dIdZ_m *= mask totm = np.sum(dIdZ_m)/np.sum(mask) dIdZ_m -= totm dIdZ_m *= mask results['dIdZ_m'] = dIdZ_m if ptie.flip: dIdZ_e *= mask tote = np.sum(dIdZ_e)/np.sum(mask) dIdZ_e -= tote dIdZ_e *= mask results['dIdZ_e'] = dIdZ_e ### Now time to call phase_reconstruct, first for E if we have a flipped tfs vprint('Calling TIE solver\n') if ptie.flip: resultsE = phase_reconstruct(ptie, inf_im, dIdZ_e, pscope, defval, sym = sym, long_deriv = long_deriv) # We only care about the E phase. results['phase_e'] = resultsE['phase'] ### Now run for B, resultsB = phase_reconstruct(ptie, inf_im, dIdZ_m, pscope, defval, sym = sym, long_deriv = long_deriv) results['byt'] = resultsB['ind_y'] results['bxt'] = resultsB['ind_x'] results['bbt'] = np.sqrt(resultsB['ind_x']**2 + resultsB['ind_y']**2) results['phase_m'] = resultsB['phase'] results['color_b'] = color_im(resultsB['ind_x'], resultsB['ind_y'], hsvwheel=True, background='black') if v >= 1: show_im(results['color_b'], "B-field color HSV colorwheel") # save the images if save: save_results(defval, results, ptie, dataname, sym, qc, save, v, long_deriv = long_deriv) vprint('Phase reconstruction completed.') return results