Exemple #1
0
    def _get_img2d(self, i, img, max_intens=0):
        """Get the 2D image from the given 3D image, scaling and downsampling
        as necessary.

        Args:
            i (int): Index of 3D image in sequence of 3D images, assuming
                order of ``(main_image, labels_img, borders_img)``.
            img (:obj:`np.ndarray`): 3D image from which to extract a 2D plane.
            max_intens (int): Number of planes to incorporate for maximum
                intensity projection; defaults to 0 to not perform this
                projection.

        Returns:
            :obj:`np.ndarray`: 2D plane, downsampled if necessary.

        """
        z = self.coord[0]
        z_scale = 1
        if self._img3d_scales[i] is not None:
            # rescale z-coordinate based on image scaling to the main image
            z_scale = self._img3d_scales[i][0]
            z = int(z * z_scale)
        # downsample to reduce access time; use same factor for both x and y
        # to retain aspect ratio
        downsample = self._downsample[i]
        img = img[:, ::downsample, ::downsample]
        if max_intens:
            # max intensity projection (MIP) across the given number of
            # planes available
            z_stop = z + int(max_intens * z_scale)
            num_z = len(img)
            if z_stop > num_z:
                z_stop = num_z
            z_range = np.arange(z, z_stop)
            img2d = plot_support.extract_planes(img[None],
                                                z_range,
                                                max_intens_proj=True)[0]
        else:
            img2d = img[z]
        return img2d
def setup_stack(
        image5d: np.ndarray,
        path: Optional[str] = None,
        offset: Optional[Sequence[int]] = None,
        roi_size: Optional[Sequence[int]] = None,
        slice_vals: Optional[Sequence[int]] = None,
        rescale: Optional[float] = None,
        labels_imgs: Optional[Sequence[np.ndarray]] = None) -> StackPlaneIO:
    """Set up a stack of images for export to file.
     
    Supports a stack of image files in a directory or a single volumetric image
    and associated labels images.
    
    Args:
        image5d: Images as a 4/5D Numpy array (t,z,y,x[c]). Can be None if 
            ``path`` is set.
        path: Path to an image directory from which all files will be imported 
            in Python sorted order, taking precedence over ``imaged5d``; 
            defaults to None.
        offset: Tuple of offset given in user order (x, y, z); defaults to 
            None. Requires ``roi_size`` to not be None.
        roi_size: Size of the region of interest in user order (x, y, z); 
            defaults to None. Requires ``offset`` to not be None.
        slice_vals: List from which to construct a slice object to 
            extract only a portion of the image. Defaults to None, which 
            will give the whole image. If ``offset`` and ``roi_size`` are 
            also given, ``slice_vals`` will only be used for its interval term.
        rescale: Rescaling factor for each image, performed on a plane-by-plane 
            basis; defaults to None, in which case 1.0 will be used.
        labels_imgs: Sequence of labels-based images as a Numpy z,y,x arrays, 
            typically including labels and borders images; defaults to None.
    
    Returns:
        Stack builder instance.
    
    """
    print("Starting image stack setup")

    # build "z" slice, which will be applied to the transposed image
    interval = 1  # default to export each plane
    if offset is not None and roi_size is not None:
        # extract planes based on ROI settings

        # transpose coordinates to given plane
        _, arrs_1d = plot_support.transpose_images(
            config.plane, arrs_1d=[offset[::-1], roi_size[::-1]])
        offset = arrs_1d[0][::-1]
        roi_size = arrs_1d[1][::-1]

        # ROI offset and size take precedence over slice vals except
        # for use of the interval term
        if slice_vals is not None and len(slice_vals) > 2:
            interval = slice_vals[2]
        size = roi_size[2]
        img_sl = slice(offset[2], offset[2] + size, interval)
        if interval is not None and interval < 0:
            # reverse start/stop order to iterate backward
            img_sl = slice(img_sl.stop, img_sl.start, interval)
        print("using ROI offset {}, size {}, {}".format(offset, size, img_sl))

    elif slice_vals:
        # build directly from slice vals, replacing start and step if None
        sl = slice(*slice_vals)
        sl = [sl.start, sl.stop, sl.step]
        if sl[0] is None:
            # default to start at beginning of stack
            sl[0] = 0
        if sl[2] is None:
            # default to interval/step of 1
            sl[2] = 1
        img_sl = slice(*sl)

    else:
        # default to take the whole image stack
        img_sl = slice(0, None, 1)

    if rescale is None:
        rescale = 1.0
    aspect = None
    origin = None
    cmaps_labels = []
    extracted_planes = []
    start_planei = 0
    if path and os.path.isdir(path):
        # builds animations from all files in a directory
        planes = sorted(glob.glob(os.path.join(path, "*")))[::interval]
        _logger.info("Importing images from %s: %s", path, planes)
        fnc = StackPlaneIO.import_img
        extracted_planes.append(planes)
    else:
        # load images from path and extract ROI based on slice parameters,
        # assuming 1st image is atlas, 2nd and beyond are labels-based
        imgs = [image5d]
        if labels_imgs is not None:
            for img in labels_imgs:
                if img is not None: imgs.append(img[None])
            _setup_labels_cmaps(imgs, cmaps_labels)
        main_shape = None  # z,y,x shape of 1st image
        for img in imgs:
            sl = img_sl
            img_shape = img.shape[1:4]
            if main_shape:
                if main_shape != img_shape:
                    # scale slice bounds to the first image's shape
                    scaling = np.divide(img_shape, main_shape)
                    axis = plot_support.get_plane_axis(config.plane, True)
                    sl = libmag.scale_slice(sl, scaling[axis], img_shape[axis])
            else:
                main_shape = img_shape
            planes, aspect, origin = plot_support.extract_planes(
                img, sl, plane=config.plane)
            if offset is not None and roi_size is not None:
                # get ROI using transposed coordinates on transposed planes;
                # returns list
                planes = planes[:, offset[1]:offset[1] + roi_size[1],
                                offset[0]:offset[0] + roi_size[0]]
            extracted_planes.append(planes)
        fnc = StackPlaneIO.process_plane
        if img_sl.start:
            start_planei = img_sl.start

    # store in stack worker
    stacker = StackPlaneIO()
    StackPlaneIO.interp_order = config.transform[
        config.Transforms.INTERPOLATION]
    stacker.images = extracted_planes
    stacker.fn_process = fnc
    stacker.rescale = rescale
    stacker.start_planei = start_planei
    stacker.origin = origin
    stacker.aspect = aspect
    stacker.cmaps_labels = cmaps_labels
    stacker.img_slice = img_sl

    return stacker
Exemple #3
0
def stack_to_ax_imgs(ax,
                     image5d,
                     path=None,
                     offset=None,
                     roi_size=None,
                     slice_vals=None,
                     rescale=None,
                     labels_imgs=None,
                     multiplane=False,
                     fit=False):
    """Export a stack of images in a directory or a single volumetric image
    and associated labels images to :obj:`matplotlib.image.AxesImage`
    objects for export.
    
    Args:
        ax (:obj:`plt.Axes`): Matplotlib axes on which to plot images.
        image5d: Images as a 4/5D Numpy array (t,z,y,x[c]). Can be None if 
            ``path`` is set.
        path: Path to an image directory from which all files will be imported 
            in Python sorted order, taking precedence over ``imaged5d``; 
            defaults to None.
        offset: Tuple of offset given in user order (x, y, z); defaults to 
            None. Requires ``roi_size`` to not be None.
        roi_size: Size of the region of interest in user order (x, y, z); 
            defaults to None. Requires ``offset`` to not be None.
        slice_vals: List from which to construct a slice object to 
            extract only a portion of the image. Defaults to None, which 
            will give the whole image. If ``offset`` and ``roi_size`` are 
            also given, ``slice_vals`` will only be used for its interval term.
        rescale: Rescaling factor for each image, performed on a plane-by-plane 
            basis; defaults to None, in which case 1.0 will be used.
        labels_imgs: Sequence of labels-based images as a Numpy z,y,x arrays, 
            typically including labels and borders images; defaults to None.
        multiplane: True to extract the images as an animated GIF or movie 
            file; False to extract a single plane only. Defaults to False.
        fit (bool): True to fit the figure frame to the resulting image.
    
    Returns:
        List[:obj:`matplotlib.image.AxesImage`]: List of image objects.
    
    """
    print("Starting image stack export")

    # build "z" slice, which will be applied to the transposed image;
    # reduce image to 1 plane if in single mode
    interval = 1
    if offset is not None and roi_size is not None:
        # transpose coordinates to given plane
        _, arrs_1d = plot_support.transpose_images(
            config.plane, arrs_1d=[offset[::-1], roi_size[::-1]])
        offset = arrs_1d[0][::-1]
        roi_size = arrs_1d[1][::-1]

        # ROI offset and size take precedence over slice vals except
        # for use of the interval term
        interval = None
        if slice_vals is not None and len(slice_vals) > 2:
            interval = slice_vals[2]
        size = roi_size[2] if multiplane else 1
        img_sl = slice(offset[2], offset[2] + size, interval)
        if interval is not None and interval < 0:
            # reverse start/stop order to iterate backward
            img_sl = slice(img_sl.stop, img_sl.start, interval)
        print("using ROI offset {}, size {}, {}".format(offset, size, img_sl))
    elif slice_vals is not None:
        # build directly from slice vals unless not an animation
        if multiplane:
            img_sl = slice(*slice_vals)
        else:
            # single plane only for non-animation
            img_sl = slice(slice_vals[0], slice_vals[0] + 1)
    else:
        # default to take the whole image stack
        img_sl = slice(None, None)
    if rescale is None:
        rescale = 1.0
    aspect = None
    origin = None
    cmaps_labels = []
    extracted_planes = []
    start_planei = 0
    if path and os.path.isdir(path):
        # builds animations from all files in a directory
        planes = sorted(glob.glob(os.path.join(path, "*")))[::interval]
        print(planes)
        fnc = StackPlaneIO.import_img
        extracted_planes.append(planes)
    else:
        # load images from path and extract ROI based on slice parameters,
        # assuming 1st image is atlas, 2nd and beyond are labels-based
        imgs = [image5d]
        if labels_imgs is not None:
            for img in labels_imgs:
                if img is not None: imgs.append(img[None])
            _setup_labels_cmaps(imgs, cmaps_labels)
        main_shape = None  # z,y,x shape of 1st image
        for img in imgs:
            sl = img_sl
            img_shape = img.shape[1:4]
            if main_shape:
                if main_shape != img_shape:
                    # scale slice bounds to the first image's shape
                    scaling = np.divide(img_shape, main_shape)
                    axis = plot_support.get_plane_axis(config.plane, True)
                    sl = libmag.scale_slice(sl, scaling[axis], img_shape[axis])
            else:
                main_shape = img_shape
            planes, aspect, origin = plot_support.extract_planes(
                img, sl, plane=config.plane)
            if offset is not None and roi_size is not None:
                # get ROI using transposed coordinates on transposed planes;
                # returns list
                planes = planes[:, offset[1]:offset[1] + roi_size[1],
                                offset[0]:offset[0] + roi_size[0]]
            extracted_planes.append(planes)
        fnc = StackPlaneIO.process_plane
        if img_sl.start:
            start_planei = img_sl.start

    # export planes
    plotted_imgs = _build_stack(
        ax,
        extracted_planes,
        fnc,
        rescale,
        aspect=aspect,
        origin=origin,
        cmaps_labels=cmaps_labels,
        scale_bar=config.plot_labels[config.PlotLabels.SCALE_BAR],
        start_planei=start_planei)

    if fit and plotted_imgs:
        # fit frame to first plane's first available image
        ax_img = None
        for ax_img in plotted_imgs[0]:
            # images may be None if alpha set to 0
            if ax_img is not None: break
        if ax_img is not None:
            plot_support.fit_frame_to_image(ax_img.figure,
                                            ax_img.get_array().shape, aspect)

    return plotted_imgs