def handle_extracted_plane(): # get sub-plot and hide x/y axes ax = axs if libmag.is_seq(ax): ax = axs[imgi] plot_support.hide_axes(ax) # multiple artists can be shown at each frame by collecting # each group of artists in a list; overlay_images returns # a nested list containing a list for each image, which in turn # contains a list of artists for each channel ax_imgs = plot_support.overlay_images(ax, self.aspect, self.origin, imgs, None, cmaps_all, ignore_invis=True, check_single=True) if (colorbar is not None and len(ax_imgs) > 0 and len(ax_imgs[0]) > 0 and imgi == 0): # add colorbar with scientific notation if outside limits cbar = ax.figure.colorbar(ax_imgs[0][0], ax=ax, **colorbar) plot_support.set_scinot(cbar.ax, lbls=None, units=None) plotted_imgs[imgi] = np.array(ax_imgs).flatten() if libmag.is_seq(text_pos) and len(text_pos) > 1: # write plane index in axes rather than data coordinates text = ax.text(*text_pos[:2], "{}-plane: {}".format( plot_support.get_plane_axis(config.plane), self.start_planei + imgi), transform=ax.transAxes, color="w") plotted_imgs[imgi] = [*plotted_imgs[imgi], text] if scale_bar: plot_support.add_scale_bar(ax, 1 / self.rescale, config.plane)
def _build_stack(ax, images, process_fnc, rescale=1, aspect=None, origin=None, cmaps_labels=None, scale_bar=True, start_planei=0): """Builds a stack of Matploblit 2D images. Uses multiprocessing to load or resize each image. Args: images: Sequence of images. For import, each "image" is a path to and image file. For export, each "image" is a sequence of planes, with the first sequence assumed to an atlas, followed by labels-based images, each consisting of corresponding planes. process_fnc: Function to process each image through multiprocessing, where the function should take an index and image and return the index and processed plane. rescale (float): Rescale factor; defaults to 1. cmaps_labels: Sequence of colormaps for labels-based images; defaults to None. Length should be equal to that of ``images`` - 1. scale_bar: True to include scale bar; defaults to True. start_planei (int): Index of start plane, used for labeling the plane; defaults to 0. The plane is only annotated when :attr:`config.plot_labels[config.PlotLabels.TEXT_POS]` is given to specify the position of the text in ``x,y`` relative to the axes. Returns: :List[List[:obj:`matplotlib.image.AxesImage`]]: Nested list of axes image objects. The first list level contains planes, and the second level are channels within each plane. """ # number of image types (eg atlas, labels) and corresponding planes num_image_types = len(images) if num_image_types < 1: return None num_images = len(images[0]) if num_images < 1: return None # Matplotlib figure for building the animation plot_support.hide_axes(ax) # import the images as Matplotlib artists via multiprocessing plotted_imgs = [None] * num_images img_shape = images[0][0].shape target_size = np.multiply(img_shape, rescale).astype(int) multichannel = images[0][0].ndim >= 3 if multichannel: print("building stack for channel: {}".format(config.channel)) target_size = target_size[:-1] StackPlaneIO.set_data(images) pool = chunking.get_mp_pool() pool_results = [] for i in range(num_images): # add rotation argument if necessary pool_results.append( pool.apply_async(process_fnc, args=(i, target_size))) # setup imshow parameters colorbar = config.roi_profile["colorbar"] cmaps_all = [config.cmaps, *cmaps_labels] img_size = None text_pos = config.plot_labels[config.PlotLabels.TEXT_POS] for result in pool_results: i, imgs = result.get() if img_size is None: img_size = imgs[0].shape # multiple artists can be shown at each frame by collecting # each group of artists in a list; overlay_images returns # a nested list containing a list for each image, which in turn # contains a list of artists for each channel ax_imgs = plot_support.overlay_images(ax, aspect, origin, imgs, None, cmaps_all, ignore_invis=True, check_single=True) if colorbar and len(ax_imgs) > 0 and len(ax_imgs[0]) > 0: # add colorbar with scientific notation if outside limits cbar = ax.figure.colorbar(ax_imgs[0][0], ax=ax, shrink=0.7) plot_support.set_scinot(cbar.ax, lbls=None, units=None) plotted_imgs[i] = np.array(ax_imgs).flatten() if libmag.is_seq(text_pos) and len(text_pos) > 1: # write plane index in axes rather than data coordinates text = ax.text(*text_pos[:2], "{}-plane: {}".format( plot_support.get_plane_axis(config.plane), start_planei + i), transform=ax.transAxes, color="w") plotted_imgs[i] = [*plotted_imgs[i], text] pool.close() pool.join() if scale_bar: plot_support.add_scale_bar(ax, 1 / rescale, config.plane) return plotted_imgs
def show_overview(self): """Show the main 2D plane, taken as a z-plane.""" # assume colorbar already shown if set and image previously displayed colorbar = (config.roi_profile["colorbar"] and len(self.axes.images) < 1) self.axes.clear() self.hline = None self.vline = None # prep 2D image from main image, assumed to be an intensity image imgs2d = [self._get_img2d(0, self.img3d, self.max_intens_proj)] self._channels = [config.channel] cmaps = [config.cmaps] alphas = [config.alphas[0]] shapes = [self._img3d_shapes[0][1:3]] vmaxs = [None] vmins = [None] if self._plot_ax_imgs: # use settings from previously displayed images if available vmaxs[0] = [a.ax_img.norm.vmax for a in self._plot_ax_imgs[0]] vmins[0] = [a.ax_img.norm.vmin for a in self._plot_ax_imgs[0]] if self.img3d_labels is not None: # prep labels with discrete colormap and prior alpha if available imgs2d.append(self._get_img2d(1, self.img3d_labels)) self._channels.append([0]) cmaps.append(self.cmap_labels) alphas.append(self._ax_img_labels.get_alpha() if self. _ax_img_labels else self.alpha) shapes.append(self._img3d_shapes[1][1:3]) vmaxs.append(None) vmins.append(None) if self.img3d_borders is not None: # prep borders image, which may have an extra channels # dimension for multiple sets of borders img2d = self._get_img2d(2, self.img3d_borders) channels = img2d.ndim if img2d.ndim >= 3 else 1 for i, channel in enumerate(range(channels - 1, -1, -1)): # show first (original) borders image last so that its # colormap values take precedence to highlight original bounds img_add = img2d[..., channel] if channels > 1 else img2d imgs2d.append(img_add) self._channels.append([0]) cmaps.append(self.cmap_borders[channel]) alphas.append(libmag.get_if_within(config.alphas, 2 + i, 1)) shapes.append(self._img3d_shapes[2][1:3]) vmaxs.append(None) vmins.append(None) if self.img3d_extras is not None: for i, img in enumerate(self.img3d_extras): # prep additional intensity image imgi = 3 + i imgs2d.append(self._get_img2d(imgi, img)) self._channels.append([0]) cmaps.append(("Greys", )) alphas.append(0.4) shapes.append(self._img3d_shapes[imgi][1:3]) vmaxs.append(None) vmins.append(None) # overlay all images and set labels for footer value on mouseover; # if first time showing image, need to check for images with single # value since they fail to update on subsequent updates for unclear # reasons ax_imgs = plot_support.overlay_images( self.axes, self.aspect, self.origin, imgs2d, self._channels, cmaps, alphas, vmins, vmaxs, check_single=(self._ax_img_labels is None)) if colorbar: self.axes.figure.colorbar(ax_imgs[0][0], ax=self.axes) self.axes.format_coord = pixel_display.PixelDisplay( imgs2d, ax_imgs, shapes, cmap_labels=self.cmap_labels) # trigger actual display through slider or call update directly if self.plane_slider: self.plane_slider.set_val(self.coord[0]) else: self._update_overview(self.coord[0]) self.show_roi() if self.scale_bar: plot_support.add_scale_bar(self.axes, self._downsample[0]) # store displayed images if len(ax_imgs) > 1: self._ax_img_labels = ax_imgs[1][0] self._plot_ax_imgs = [[PlotAxImg(img) for img in imgs] for imgs in ax_imgs] if self.xlim is not None and self.ylim is not None: # restore pan/zoom view self.axes.set_xlim(self.xlim) self.axes.set_ylim(self.ylim) if not self.connected: # connect once get AxesImage self.connect() # text label with color for visibility on axes plus fig background self.region_label = self.axes.text(0, 0, "", color="k", bbox=dict(facecolor="xkcd:silver", alpha=0.5)) self.circle = None
def show_overview(self): """Show the main 2D plane, taken as a z-plane.""" self.axes.clear() self.hline = None self.vline = None # prep 2D image from main image, assumed to be an intensity image, # with settings for each channel within this main image imgs2d = [self._get_img2d(0, self.img3d, self.max_intens_proj)] self._channels = [config.channel] cmaps = [config.cmaps] alphas = [config.alphas[0]] alpha_blends = [None] shapes = [self._img3d_shapes[0][1:3]] vmaxs = [None] vmins = [None] brightnesses = [None] contrasts = [None] if self._plot_ax_imgs: # use vmin/vmax from norm values in previously displayed images # if available; None specifies auto-scaling vmaxs[0] = [ p.vmax if p.vmax is None else p.ax_img.norm.vmax for p in self._plot_ax_imgs[0] ] vmins[0] = [ p.vmin if p.vmin is None else p.ax_img.norm.vmin for p in self._plot_ax_imgs[0] ] # use opacity, brightness, anc contrast from prior images alphas[0] = [p.alpha for p in self._plot_ax_imgs[0]] alpha_blends[0] = [p.alpha_blend for p in self._plot_ax_imgs[0]] brightnesses[0] = [p.brightness for p in self._plot_ax_imgs[0]] contrasts[0] = [p.contrast for p in self._plot_ax_imgs[0]] if self.img3d_labels is not None: # prep labels with discrete colormap and prior alpha if available imgs2d.append(self._get_img2d(1, self.img3d_labels)) self._channels.append([0]) cmaps.append(self.cmap_labels) alphas.append(self._ax_img_labels.get_alpha() if self. _ax_img_labels else self.alpha) alpha_blends.append(None) shapes.append(self._img3d_shapes[1][1:3]) vmaxs.append(None) vmins.append(None) if self.img3d_borders is not None: # prep borders image, which may have an extra channels # dimension for multiple sets of borders img2d = self._get_img2d(2, self.img3d_borders) channels = img2d.ndim if img2d.ndim >= 3 else 1 for i, channel in enumerate(range(channels - 1, -1, -1)): # show first (original) borders image last so that its # colormap values take precedence to highlight original bounds img_add = img2d[..., channel] if channels > 1 else img2d imgs2d.append(img_add) self._channels.append([0]) cmaps.append(self.cmap_borders[channel]) # get alpha for last corresponding borders plane if available ax_img = libmag.get_if_within(self._plot_ax_imgs, 2 + i, None) alpha = (ax_img[i].alpha if ax_img else libmag.get_if_within( config.alphas, 2 + i, 1)) alphas.append(alpha) alpha_blends.append(None) shapes.append(self._img3d_shapes[2][1:3]) vmaxs.append(None) vmins.append(None) if self.img3d_extras is not None: for i, img in enumerate(self.img3d_extras): # prep additional intensity image imgi = 3 + i imgs2d.append(self._get_img2d(imgi, img)) self._channels.append([0]) cmaps.append(("Greys", )) alphas.append(0.4) alpha_blends.append(None) shapes.append(self._img3d_shapes[imgi][1:3]) vmaxs.append(None) vmins.append(None) # overlay all images and set labels for footer value on mouseover; # if first time showing image, need to check for images with single # value since they fail to update on subsequent updates for unclear # reasons ax_imgs = plot_support.overlay_images( self.axes, self.aspect, self.origin, imgs2d, self._channels, cmaps, alphas, vmins, vmaxs, check_single=(self._ax_img_labels is None), alpha_blends=alpha_blends) # add or update colorbar colobar_prof = config.roi_profile["colorbar"] if self._colorbar: self._colorbar.update_normal(ax_imgs[0][0]) elif colobar_prof: # store colorbar since it's tied to the artist, which will be # replaced with the next display and cannot be further accessed self._colorbar = self.axes.figure.colorbar(ax_imgs[0][0], ax=self.axes, **colobar_prof) # display coordinates and label values for each image self.axes.format_coord = pixel_display.PixelDisplay( imgs2d, ax_imgs, shapes, cmap_labels=self.cmap_labels) # trigger actual display through slider or call update directly if self.plane_slider: self.plane_slider.set_val(self.coord[0]) else: self._update_overview(self.coord[0]) self.show_roi() if self.scale_bar: plot_support.add_scale_bar(self.axes, self._downsample[0]) # store displayed images in the PlotAxImg container class and update # displayed brightness/contrast if len(ax_imgs) > 1: self._ax_img_labels = ax_imgs[1][0] self._plot_ax_imgs = [] for i, imgs in enumerate(ax_imgs): plot_ax_imgs = [] for j, img in enumerate(imgs): plot_ax_img = PlotAxImg(img) if i == 0: # specified vmin/vmax, in contrast to the AxesImages's # norm, which holds the values used for the displayed image plot_ax_img.vmin = libmag.get_if_within(vmins[i], j) plot_ax_img.vmax = libmag.get_if_within(vmaxs[i], j) # set brightness/contrast self.change_brightness_contrast( plot_ax_img, libmag.get_if_within(brightnesses[i], j), libmag.get_if_within(contrasts[i], j)) plot_ax_img.alpha = libmag.get_if_within(alphas[i], j) plot_ax_img.alpha_blend = libmag.get_if_within( alpha_blends[i], j) plot_ax_imgs.append(plot_ax_img) self._plot_ax_imgs.append(plot_ax_imgs) if self.xlim is not None and self.ylim is not None: # restore pan/zoom view self.axes.set_xlim(self.xlim) self.axes.set_ylim(self.ylim) if not self.connected: # connect once get AxesImage self.connect() # text label with color for visibility on axes plus fig background self.region_label = self.axes.text(0, 0, "", color="k", bbox=dict(facecolor="xkcd:silver", alpha=0.5)) self.circle = None