Source code for brighteyes_ism.analysis.Graph_lib

import numpy as np
import matplotlib.pyplot as plt

from numbers import Number

from matplotlib_scalebar.scalebar import ScaleBar
from mpl_toolkits.axes_grid1 import make_axes_locatable
from matplotlib.colors import Normalize, hsv_to_rgb, LogNorm
import matplotlib.gridspec as gridspec

import numbers

from ..simulation.detector import det_coords, airy_to_hex

#%%

from matplotlib import cm
from matplotlib.colors import ListedColormap

_hot = cm.get_cmap('hot', 256)
_hot_array = _hot(np.linspace(0, 1, 256))

_bluehot = _hot_array.copy()
_bluehot[:, 0] = _hot_array[:, 2]
_bluehot[:, 2] = _hot_array[:, 0]

bluehot = ListedColormap(_bluehot)

_greenhot = _hot_array.copy()
_greenhot[:, 0] = _hot_array[:, 1]
_greenhot[:, 1] = _hot_array[:, 0]

greenhot = ListedColormap(_greenhot)

#%%
[docs] def ShowImg(image: np.ndarray, pxsize_x: float, clabel: str = None, vmin: float = None, vmax: float = None, fig: plt.Figure = None, ax: plt.axis = None, colorbar: bool = True, cmap: str = 'hot', integer_values = True, log_scale: bool = False): """ It shows the input image with a scalebar and a colorbar. It returns the corresponding figure and axis. Parameters ---------- image : np.ndarray Image (Nx x Ny). pxsize_x : float Pixel size in micrometers (um). clabel : str Label of the colorbar. vmin : float, optional Lower bound of the intensity axis. If None, is set to the minimum value of the image. The default is None. vmax : float, optional Upper bound of the intensity axis. If None, is set to the maximum value of the image. The default is None. fig : plt.Figure, optional Figure where to display the plot. If None, a new figure is created. The default is None. ax : plt.axis, optional Axis where to display the plot. If None, a new axis is created. The default is None. colormap : bool, optional If True, a colormap is shown. The default is True. cmap : str, optional Colormap, to be chosen within the matplotlib list. The default is 'hot'. Returns ------- fig : plt.Figure Matplotlib figure. ax : plt.axis Matplotlib axis. """ if fig is None or ax is None: fig, ax = plt.subplots() # Nx, Ny = image.shape # rangex = Nx * pxsize_x # rangey = Nx * pxsize_x # extent = (-rangex/2, rangex/2, -rangey/2, rangey/2) if log_scale == True: im = ax.imshow(image, vmin=vmin, vmax=vmax, cmap=cmap, norm = LogNorm()) else: im = ax.imshow(image, vmin=vmin, vmax=vmax, cmap=cmap)#, extent=extent) ax.axis('off') if vmax is None: vmax = np.nanmax(image) if vmin is None: vmin = np.nanmin(image) if colorbar==True: divider = make_axes_locatable(ax) cax = divider.append_axes("right", size="5%", pad=0.05) cbar = fig.colorbar(im, cax=cax, ticks=[]) if integer_values == True: vmax_text = int(np.floor(vmax)) vmin_text = int(np.floor(vmin)) else: vmax_text = np.round(vmax, 3) vmin_text = np.round(vmin, 3) if isinstance(clabel, Number): clabel_text = f'Counts / {clabel:.0f} ' + '$\mathregular{\mu s}$' else: clabel_text = clabel cbar.ax.text(0.6, 0.5, clabel_text, horizontalalignment='center', verticalalignment='center', rotation='vertical', transform=cax.transAxes) cbar.ax.text(0.6, 0.98, f'{vmax_text}', horizontalalignment='center', verticalalignment='top', rotation='vertical', transform=cax.transAxes) cbar.ax.text(0.6, 0.02, f'{vmin_text}', horizontalalignment='center', verticalalignment='bottom', rotation='vertical', transform=cax.transAxes, color='white') scalebar = ScaleBar( pxsize_x, "um", # default, extent is calibrated in meters box_alpha=0, color='w', length_fraction=0.25) ax.add_artist(scalebar) return fig, ax
[docs] def ShowStack(image: np.ndarray, pxsize_x: float, pxsize_z: float, clabel: str = None, planes: tuple = None, projection = None, vmin: float = None, vmax: float = None, cmap: str = 'hot', figsize: tuple = (10, 10)): """ It shows the input image with a scalebar and a colorbar. It returns the corresponding figure and axis. Parameters ---------- image : np.ndarray Image (Nz x Ny x Nx). pxsize_x : float Lateral ixel size in micrometers (um). pxsize_z : float Axial pixel size in micrometers (um). clabel : str Label of the colorbar. planes : tuple Coordinates (z0, x0, y0) of the slices. cmap : str, optional Colormap, to be chosen within the matplotlib list. The default is 'hot'. figsize : tuple Size of the figure. The default is (10, 10). Returns ------- fig : plt.Figure Matplotlib figure. """ Nz, Ny, Nx = image.shape # find slice coordinates if planes is None: x0 = Nx//2 y0 = Ny//2 z0 = Nz//2 else: z0, y0, x0 = planes rangez = Nz * pxsize_z rangex = Nx * pxsize_x rangey = Ny * pxsize_x # define extents extent_xy = ( -rangex/2, rangex/2, -rangey/2, rangey/2) extent_xz = ( -rangex/2, rangex/2, -rangez/2, rangez/2) extent_zy = ( -rangez/2, rangez/2, -rangey/2, rangey/2) # generate projection images if projection == 'mip': image_z = image.max(axis=0) image_y = image.max(axis=1) image_x = image.max(axis=2) else: image_z = image[z0, :, :] image_y = image[:, y0, :] image_x = image[:, :, x0] # find vmax if vmax is None: max3d = np.empty(image.ndim) max3d[0] = np.max(image_z) max3d[1] = np.max(image_y) max3d[2] = np.max(image_x) vmax = np.max(max3d) # find vmin if vmin is None: min3d = np.empty(image.ndim) min3d[0] = np.min(image_z) min3d[1] = np.min(image_y) min3d[2] = np.min(image_x) vmin = np.max(min3d) # plot figure fig = plt.figure(figsize = figsize) gs = gridspec.GridSpec(2, 2, width_ratios=[rangez, rangex], height_ratios=[rangey, rangez], wspace=0.02, hspace=0.02, left=0.05, right=0.95, bottom=0.05, top=0.95) ax = np.asarray([plt.subplot(gs[i]) for i in range(4)]).reshape((2,2)) ax[0, 0].imshow(image_x[::-1, :].T, cmap = cmap, vmin = vmin, vmax = vmax, extent = extent_zy) ax[0, 0].axis('off') im = ax[0, 1].imshow(image_z, cmap=cmap, vmin=vmin, vmax=vmax, extent=extent_xy) ax[0, 1].axis('off') ax[1, 1].imshow(image_y, cmap = cmap, vmin = vmin, vmax = vmax, extent = extent_xz) ax[1, 1].axis('off') ax[1, 0].axis('off') # share axes ax[0, 0].sharey(ax[0, 1]) ax[0, 1].sharex(ax[1, 1]) # add colorbar vmax_text = int(np.floor(vmax)) vmin_text = int(np.floor(vmin)) if isinstance(clabel, Number): clabel_text = f'Counts / {clabel:.0f} ' + '$\mathregular{\mu s}$' else: clabel_text = clabel y0 = ax[-1, -1].get_position().y0 y1 = ax[0, 0].get_position().y1 height = y1 - y0 cax = fig.add_axes([0.96, y0, 0.03, height]) cbar = fig.colorbar(im, cax=cax, ticks=[]) cbar.ax.text(0.6, 0.5, clabel_text, horizontalalignment='center', verticalalignment='center', rotation='vertical', transform=cax.transAxes) cbar.ax.text(0.6, 0.98, f'{vmax_text}', horizontalalignment='center', verticalalignment='top', rotation='vertical', transform=cax.transAxes) cbar.ax.text(0.6, 0.02, f'{vmin_text}', horizontalalignment='center', verticalalignment='bottom', rotation='vertical', transform=cax.transAxes, color='white') # add scalebar scalebar = ScaleBar( 1, "um", # default, extent is calibrated in meters box_alpha=0, color='w', length_fraction=0.25) ax[0, 1].add_artist(scalebar) return fig
[docs] def StackSlider(image: np.ndarray, pxsize_x: float, pxsize_z: float, clabel: str = None, cmap: str = 'hot', figsize: tuple = (10, 10)): from matplotlib.widgets import Slider Nz, Ny, Nx = image.shape rangez = Nz * pxsize_z rangex = Nx * pxsize_x rangey = Ny * pxsize_x # Find color limits vmax = np.max(image) vmin = np.min(image) # Make figure fig = ShowStack(image = image, pxsize_x = pxsize_x, pxsize_z = pxsize_z, clabel = clabel, cmap = cmap, vmin = vmin, vmax= vmax, figsize = figsize) ax = fig.axes line_y = ax[1].vlines(0, -rangey/2, rangey/2, colors='white', linestyles='dashed') line_x = ax[1].hlines(0, -rangex/2, rangex/2, colors='white', linestyles='dashed') line_zx = ax[0].vlines(0, -rangey/2, rangey/2, colors='white', linestyles='dashed') line_zy = ax[3].hlines(0, -rangex/2, rangex/2, colors='white', linestyles='dashed') y0 = ax[1].get_position().y0 y1 = ax[1].get_position().y1 height = y1 - y0 x0 = ax[1].get_position().x0 x1 = ax[1].get_position().x1 width = x1 - x0 ax_x = fig.add_axes([x0, 0.01, width, 0.03]) x_slider = Slider( ax=ax_x, label='X', valmin=0, valmax=Nx-1, valinit=Nx//2, valstep = 1, initcolor='none', facecolor='grey', track_color='grey' ) ax_y = fig.add_axes([0.01, y0, 0.0225, height]) y_slider = Slider( ax=ax_y, label="Y", valmin=-Ny+1, valmax=0, valinit=-Ny//2, valstep = 1, initcolor='none', orientation="vertical", facecolor='grey', track_color='grey' ) y_slider.valtext.set_text(str(-y_slider.valinit)) ax_z = fig.add_axes([x0, y1, width, 0.03]) z_slider = Slider( ax=ax_z, label="Z", valmin=0, valmax=Nz-1, valinit=Nz//2, valstep = 1, initcolor='none', facecolor = 'grey', track_color = 'grey' ) # The function to be called anytime a slider's value changes def update_y(val): y = int(-val) im = ax[3].get_images()[0] im.set_data(image[:, y, :]) y_units = - y*pxsize_x + rangey/2 seg_x = [np.array([[-rangex/2, y_units], [rangex/2, y_units]])] line_x.set_segments( seg_x ) y_slider.valtext.set_text(str(y)) # fig.canvas.draw_idle() def update_x(val): x = int(val) im = ax[0].get_images()[0] im.set_data(image[::-1, :, x].T) x_units = x * pxsize_x - rangex / 2 seg_y = [np.array([[x_units, -rangey/2], [x_units, rangey/2]])] line_y.set_segments( seg_y ) # fig.canvas.draw_idle() def update_z(val): z = int(val) im = ax[1].get_images()[0] im.set_data(image[z]) z_units = -z * pxsize_z + rangez / 2 seg_zx = [np.array([[z_units, -rangey/2], [z_units, rangey/2]])] line_zx.set_segments( seg_zx ) seg_zy = [np.array([[-rangex/2, z_units], [rangex/2, z_units]])] line_zy.set_segments( seg_zy ) # fig.canvas.draw_idle() # register the update function with each slider y_slider.on_changed(update_y) x_slider.on_changed(update_x) z_slider.on_changed(update_z) return x_slider, y_slider, z_slider
[docs] def ShowDataset(dset: np.ndarray, cmap: str = 'hot', pxsize: float = None, normalize: bool = False, colorbar: bool = False, xlims: list = [None, None], ylims: list = [None, None], extent = None, figsize: tuple = (6, 6), gridshape = None) -> plt.Figure: ''' It displays all the images of the ISM dataset in a squared grid. It returns the corresponding figure. Parameters ---------- dset : np.ndarray ISM dataset (Nx x Ny x Nch). cmap : str, optional Colormap, to be chosen within the matplotlib list. The default is 'hot'. pxsize : float, optional Pixel size in micrometers (um). The default is None. normalize : bool, optional If True, each image is normalized with respect to the whole dataset. If False, each image is normalized to itself. The default is False. colorbar : bool, optional If true, a colorbar is shown. The default is False. xlims : list, optional If given, only the region with the x-range is displayed. The default is [None, None]. ylims : list, optional If given, only the region with the y-range is displayed. The default is [None, None]. figsize : tuple, optional Size of the figure. The default is (6, 6). Returns ------- fig : plt.Figure Matplotlib figure. ''' if gridshape is None: nx = int(np.sqrt(dset.shape[-1])) ny = int(np.sqrt(dset.shape[-1])) else: nx = gridshape[0] ny = gridshape[1] if normalize == True: vmin = np.min(dset) vmax = np.max(dset) norm = Normalize(vmin=vmin, vmax=vmax) fig, ax = plt.subplots(nx, ny, sharex=True, sharey=True, figsize=figsize) for i in range(nx*ny): if np.min( [nx, ny] ) > 1: idx = np.unravel_index(i, [nx, ny]) else: idx = i if normalize == True: im = ax[idx].imshow(dset[:, :, i], norm=norm, cmap=cmap, extent=extent) else: im = ax[idx].imshow(dset[:, :, i], cmap=cmap, extent=extent) ax[idx].set_xlim(xlims) ax[idx].set_ylim(ylims) ax[idx].axis('off') if isinstance(pxsize, numbers.Number): scalebar = ScaleBar( pxsize, "um", # default, extent is calibrated in meters box_alpha=0, color='w', location='lower right', length_fraction=0.5) ax[-1, -1].add_artist(scalebar) fig.tight_layout() if colorbar == True and normalize == True: y0 = ax[-1, -1].get_position().y0 y1 = ax[0, 0].get_position().y1 height = y1 - y0 fig.subplots_adjust(right=0.92) cbar_ax = fig.add_axes([0.94, y0, 0.05, height]) cbar = fig.colorbar(im, cax=cbar_ax, ticks=[]) cbar.ax.text(0.3, 0.95, f'{int(np.floor(vmax))}', rotation=90, transform=cbar_ax.transAxes) cbar.ax.text(0.3, 0.02, f'{int(np.floor(vmin))}', rotation=90, transform=cbar_ax.transAxes, color='white') return fig
[docs] def PlotShiftVectors(shift_vectors: np.ndarray, pxsize: float = 1, labels: bool = True, color: np.ndarray = None, cmap: str = 'summer_r', fig: plt.Figure = None, ax: plt.axis = None): """ It plots the shift vectors in a scatter plot. It returns the corresponding figure and axis. Parameters ---------- shift_vectors : np.ndarray Array of the coordinates of the shift vectors (Nch x 2). pxsize : float, optional Pixel size in micrometers (um). The default is 1. labels : bool, optional If true, the channel number is printed close to each point. The default is True. color : np.ndarray, optional Array defining the color value (Nch). The default is None. cmap : str, optional Colormap, to be chosen within the matplotlib list. The default is 'summer_r'. fig : plt.Figure, optional Figure where to display the plot. If None, a new figure is created. The default is None. ax : plt.axis, optional Axis where to display the plot. If None, a new axis is created. The default is None. Returns ------- fig : plt.Figure Matplotlib figure. ax : plt.axis Matplotlib axis. """ if fig == None or ax == None: fig, ax = plt.subplots() shift = shift_vectors * pxsize Nch = shift.shape[0] if isinstance(color, str) and color == 'auto': N = int(np.sqrt(Nch)) x = np.arange(-(N // 2), N // 2 + 1) X, Y = np.meshgrid(x, x) R = np.sqrt(X ** 2 + Y ** 2) color = R ax.scatter(shift[:, 0], shift[:, 1], s=80, c=color, edgecolors='black', cmap=cmap) ax.set_aspect('equal', 'box') if labels == True: for n in range(Nch): ax.annotate(str(n), shift[n], xytext=(3, 3), textcoords='offset points') ax.set_xlabel(r'Shift$_x$ (nm)') ax.set_ylabel(r'Shift$_y$ (nm)') ax.set_title('Shift vectors') ax.set_aspect('equal') return fig, ax
[docs] def ShowFingerprint(dset: np.ndarray, cmap: str = 'hot', colorbar: bool = False, clabel: str = None, normalize: bool = False, fig: plt.Figure = None, ax: plt.axis = None, hex_grid = False, name = None): """ It calculates and shows the fingerprint of an ISM dataset. It returns the corresponding figure and axis. Parameters ---------- dset : np.ndarray ISM dataset (Nx x Ny x Nch). cmap : str, optional Colormap, to be chosen within the matplotlib list. The default is 'hot'. colorbar : bool, optional If true, a colorbar is shown. The default is False clabel : str, optional Label of the colorbar. The default is None normalize : bool, optional If true, the fingerprint values are normalized between 0 and 1. The default is False fig : plt.Figure, optional Figure where to display the plot. If None, a new figure is created. The default is None. ax : plt.axis, optional Axis where to display the plot. If None, a new axis is created. The default is None. Returns ------- fig : plt.Figure Matplotlib figure. ax : plt.axis Matplotlib axis. """ if fig is None or ax is None: fig, ax = plt.subplots() fingerprint = dset.sum(axis=(0, 1)) if name == 'airyscan': N = 7 hex_grid = True fingerprint = airy_to_hex(fingerprint) else: N = int(np.ceil(np.sqrt(dset.shape[-1]))) if normalize is True: max_counts = np.nanmax(fingerprint) fingerprint = fingerprint / max_counts if hex_grid is True: k = (N-1)/2 gridsize = np.asarray([N - (1+(-1)**k)/2, N//2]).astype(int) if name == 'airyscan': gridsize[0] -= 1 s = -det_coords(N, 'hex') s = np.flip(s, axis=0) im = ax.hexbin(s[0], s[1], fingerprint, gridsize=gridsize, cmap=cmap) w = N//2 + 1 ax.set_xlim([-w,w]) ax.set_ylim([-w,w]) ax.set_aspect('equal') else: fingerprint = fingerprint.reshape(N, N) im = ax.imshow(fingerprint, cmap=cmap) ax.set_aspect('equal') ax.axis('off') fig.tight_layout() if colorbar is True: vmax = int(np.floor(np.max(fingerprint))) vmin = int(np.floor(np.min(fingerprint))) divider = make_axes_locatable(ax) cax = divider.append_axes("right", size="5%", pad=0.05) cbar = fig.colorbar(im, cax=cax, ticks=[]) cbar.ax.text(0.6, 0.5, clabel, horizontalalignment='center', verticalalignment='center', rotation='vertical', transform=cax.transAxes) cbar.ax.text(0.6, 0.98, f'{vmax}', horizontalalignment='center', verticalalignment='top', rotation='vertical', transform=cax.transAxes) cbar.ax.text(0.6, 0.02, f'{vmin}', horizontalalignment='center', verticalalignment='bottom', rotation='vertical', transform=cax.transAxes, color='white') return fig, ax
[docs] class ColorMap2D: def __init__(self): self.var_bounds = [0, 1] # minimum and maximum variable value of the colorbar self.int_bounds = [0, 1] # minimum and maximum intensity value of the colorbar self.equalize = True self.colormap = 'rainbow'
[docs] def image(self, intensity_map, variable_map): output = np.zeros(intensity_map.shape + (3,)) intensity = np.clip(intensity_map, self.int_bounds[0], self.int_bounds[1]) intensity = (intensity - self.int_bounds[0]) / (self.int_bounds[1] - self.int_bounds[0]) cmap = plt.get_cmap(self.colormap) N = cmap.N cmapArray = np.zeros((N, 3)) for i in range(N): cmapArray[i, :] = cmap(i)[0:3] # equalize luminescence if self.equalize: lmax = np.max((299 * cmapArray[:, 0] + 587 * cmapArray[:, 1] + 114 * cmapArray[:, 2]) / 1000) for i in range(N): lum = (299 * cmapArray[i, 0] + 587 * cmapArray[i, 1] + 114 * cmapArray[i, 2]) / 1000 lumfactor = np.min((lmax / lum, 1 / np.max(cmapArray[i, :]))) cmapArray[i, :] *= lumfactor # get index of color map for lifetime variable = np.clip(variable_map, self.var_bounds[0], self.var_bounds[1]) idx = (np.floor((variable - self.var_bounds[0]) / (self.var_bounds[1] - self.var_bounds[0]) * N)).astype(int) idx = np.clip(idx, 0, N - 1) for k in range(3): output[:, :, k] = np.take(cmapArray[:, k], idx) * intensity return output
[docs] def colorbar(self, n): LG = np.linspace(self.var_bounds[0], self.var_bounds[1], num=n) VarGradient = np.tile(LG, (n, 1)) IG = np.linspace(self.int_bounds[0], self.int_bounds[1], num=n) IntensityGradient = np.transpose(np.tile(IG, (n, 1))) RGB_colormap = self.image(IntensityGradient, VarGradient) RGB_colormap = np.moveaxis(RGB_colormap, 0, 1) return RGB_colormap
[docs] def show_flim(image: np.ndarray, lifetime: np.ndarray, pxsize: list, pxdwelltime: float, lifetime_bounds: list = None, intensity_bounds: list = None, colormap='gist_rainbow', fig=None, ax=None): """ Display the flim image, where intensity and lifetime image are represented with a 2D colormap. Referring to the HSV color model: Intensity values are mapped in Value Lifetime values are mapped in Hue Parameters ---------- image : np.ndarray 2D intensity image (Ny x Nx). lifetime : np.ndarray 2D lifetime map (Ny x Nx). pxsize : list Lateral pixel size, in um. pxdwelltime : float Pixel dwell time, in us. lifetime_bounds : list, optional Lifetime bounds of the colormap intensity_bounds : list, optional Intensity bounds of the colormap colormap : str, optional Matplotlib colormap used to represent lifetime values. The default is 'rainbow' fig : plt.Figure, optional Figure where to display the plot. If None, a new figure is created. The default is None. ax : plt.axis, optional Axis where to display the plot. If None, a new axis is created. The default is None. Returns ------- fig : plt.Figure Figure that contains the image and the colormap. ax : plt.axis Axis array that contain the image and the colormap.. """ # params settings cmap = ColorMap2D() cmap.colormap = colormap if intensity_bounds is None: cmap.int_bounds = [np.min(image), np.max(image)] else: cmap.int_bounds = intensity_bounds if lifetime_bounds is None: cmap.var_bounds = [np.min(lifetime), np.max(lifetime)] else: cmap.var_bounds = lifetime_bounds # Image RGB = cmap.image(image, lifetime) # Colorbar N = 256 RGB_colormap = cmap.colorbar(N) # Define extents sz = image.shape img_extent = (0, sz[1] * pxsize, 0, sz[0] * pxsize) cmap_extent = (cmap.int_bounds[0], cmap.int_bounds[1], cmap.var_bounds[0], cmap.var_bounds[1]) # Show image with colorbar if fig is None or ax is None: fig, ax = plt.subplots() ax.imshow(RGB, extent=img_extent) ax.axis('off') divider = make_axes_locatable(ax) cax = divider.append_axes("right", size="5%", pad=0.05) cax.imshow(RGB_colormap, origin='lower', aspect='auto', extent=cmap_extent) cax.set_xticks([int(cmap.int_bounds[0]), int(cmap.int_bounds[1])]) cax.set_xlabel(f'Counts/{pxdwelltime} ' + '$\mathregular{\mu s}$') cax.set_ylabel(r'Lifetime (ns)') cax.yaxis.tick_right() cax.yaxis.set_label_position("right") # Add scalebar scalebar = ScaleBar( 1, "um", # default, extent is calibrated in meters box_alpha=0, color='w', length_fraction=0.25) ax.add_artist(scalebar) plt.tight_layout() return fig, ax
[docs] def depth_stack(stack: np.ndarray, pxsize: list, pxdwelltime: float, axis: int = 0, colormap='rainbow', fig=None, ax=None): """ Display the maximum intensity projection of stack, where intensity and depth image are represented with a 2D colormap. Referring to the HSV color model: Intensity values are mapped in Value Depth values are mapped in Hue Parameters ---------- stack : np.ndarray 3D image (Nz x Ny x Nx). pxsize : list List of pixel size of each dimension, in um [px_z, px_y, px_x]. pxdwelltime : float Pixel dwell time, in us. axis : int, optional Projection axis. The default is 0 (zeta). colormap : str, optional Matplotlib colormap used to represent lifetime values. The default is 'rainbow' fig : plt.Figure, optional Figure where to display the plot. If None, a new figure is created. The default is None. ax : plt.axis, optional Axis where to display the plot. If None, a new axis is created. The default is None. Returns ------- fig : plt.Figure Figure that contains the image and the colormap. ax : plt.axis Axis array that contain the image and the colormap.. """ image_mip = stack.max(axis=axis) depth_mip = stack.argmax(axis=axis) * pxsize[axis] # params settings cmap = ColorMap2D() cmap.colormap = colormap cmap.int_bounds = [np.min(image_mip), np.max(image_mip)] cmap.var_bounds = [np.min(depth_mip), np.max(depth_mip)] # Image RGB = cmap.image(image_mip, depth_mip) # Colorbar N = stack.shape[axis] RGB_colormap = cmap.colorbar(N) # Define extents sz = image_mip.shape px = np.delete(pxsize, axis) img_extent = (0, sz[1] * px[1], 0, sz[0] * px[0]) cmap_extent = (cmap.int_bounds[0], cmap.int_bounds[1], cmap.var_bounds[0], cmap.var_bounds[1]) # Show image with colorbar if fig is None or ax is None: fig, ax = plt.subplots() ax.imshow(RGB, extent=img_extent) ax.axis('off') divider = make_axes_locatable(ax) cax = divider.append_axes("right", size="5%", pad=0.05) cax.imshow(RGB_colormap, origin='lower', aspect='auto', extent=cmap_extent) cax.set_xticks([int(cmap.int_bounds[0]), int(cmap.int_bounds[1])]) cax.set_xlabel(f'Counts/{pxdwelltime} ' + '$\mathregular{\mu s}$') cax.set_ylabel(r'Depth ($\mathregular{\mu m}$)') cax.yaxis.tick_right() cax.yaxis.set_label_position("right") # Add scalebar scalebar = ScaleBar( 1, "um", # default, extent is calibrated in meters box_alpha=0, color='w', length_fraction=0.25) ax[0].add_artist(scalebar) plt.tight_layout() return fig, ax