Exemple #1
0
    def plot_3d_surface(self,
                        roi,
                        channel,
                        segment=False,
                        flipz=False,
                        offset=None):
        """Plots areas with greater intensity as 3D surfaces.

        The scene will be cleared before display.
        
        Args:
            roi (:class:`numpy.ndarray`): Region of interest either as a 3D
                ``z,y,x`` or 4D ``z,y,x,c`` array.
            channel (int): Channel to select, which can be None to indicate all
                channels.
            segment (bool): True to denoise and segment ``roi`` before
                displaying, which may remove artifacts that might otherwise
                lead to spurious surfaces. Defaults to False.
            flipz: True to invert ``roi`` along z-axis to match handedness
                of Matplotlib with z progressing upward; defaults to False.
            offset (Sequence[int]): Origin coordinates in ``z,y,x``; defaults
                to None.

        Returns:
            list: List of Mayavi surfaces for each displayed channel, which
            are also stored in :attr:`surfaces`.

        """
        # Plot in Mayavi
        print("viewing 3D surface")
        pipeline = self.scene.mlab.pipeline
        settings = config.roi_profile
        if flipz:
            # invert along z-axis to match handedness of Matplotlib with z up
            roi = roi[::-1]
            if offset is not None:
                # invert z-offset and translate by ROI z-size so ROI is
                # mirrored across the xy-plane
                offset = np.copy(offset)
                offset[0] = -offset[0] - roi.shape[0]
        isotropic = plot_3d.get_isotropic_vis(settings)

        # saturate to remove noise and normalize values
        roi = plot_3d.saturate_roi(roi, channel=channel)

        # turn off segmentation if ROI too big (arbitrarily set here as
        # > 10 million pixels) to avoid performance hit and since likely showing
        # large region of downsampled image anyway, where don't need hi res
        num_pixels = np.prod(roi.shape)
        to_segment = num_pixels < 10000000

        time_start = time()
        multichannel, channels = plot_3d.setup_channels(roi, channel, 3)
        surfaces = []
        for chl in channels:
            roi_show = roi[..., chl] if multichannel else roi

            # clip to minimize sub-nuclear variation
            roi_show = np.clip(roi_show, 0.2, 0.8)

            if segment:
                # denoising makes for much cleaner images but also seems to
                # allow structures to blend together
                # TODO: consider segmenting individual structures and rendering
                # as separate surfaces to avoid blending
                roi_show = restoration.denoise_tv_chambolle(roi_show,
                                                            weight=0.1)

                # build surface from segmented ROI
                if to_segment:
                    vmin, vmax = np.percentile(roi_show, (40, 70))
                    walker = segmenter.segment_rw(roi_show,
                                                  chl,
                                                  vmin=vmin,
                                                  vmax=vmax)
                    roi_show *= np.subtract(walker[0], 1)
                else:
                    print("deferring segmentation as {} px is above threshold".
                          format(num_pixels))

            # ROI is in (z, y, x) order, so need to transpose or swap x,z axes
            roi_show = np.transpose(roi_show)
            surface = pipeline.scalar_field(roi_show)

            # Contour -> Surface pipeline

            # create the surface
            surface = pipeline.contour(surface)
            # remove many more extraneous points
            surface = pipeline.user_defined(surface,
                                            filter="SmoothPolyDataFilter")
            surface.filter.number_of_iterations = 400
            surface.filter.relaxation_factor = 0.015
            # distinguishing pos vs neg curvatures?
            surface = pipeline.user_defined(surface, filter="Curvatures")
            surface = self.scene.mlab.pipeline.surface(surface)
            module_manager = surface.module_manager
            module_manager.scalar_lut_manager.data_range = np.array([-2, 0])
            module_manager.scalar_lut_manager.lut_mode = "gray"
            '''
            # Surface pipleline with contours enabled (similar to above?)
            surface = pipeline.contour_surface(
                surface, color=(0.7, 1, 0.7), line_width=6.0)
            surface.actor.property.representation = 'wireframe'
            #surface.actor.property.line_width = 6.0
            surface.actor.mapper.scalar_visibility = False
            '''
            '''
            # IsoSurface pipeline

            # uses unique IsoSurface module but appears to have 
            # similar output to contour_surface
            surface = pipeline.iso_surface(surface)

            # limit contours for simpler surfaces including smaller file sizes; 
            # TODO: consider making settable as arg or through profile
            surface.contour.number_of_contours = 1
            try:
                # increase min to further reduce complexity
                surface.contour.minimum_contour = 0.5
                surface.contour.maximum_contour = 0.8
            except Exception as e:
                print(e)
                print("ignoring min/max contour for now")
            '''

            if offset is not None:
                # translate to offset scaled by isotropic factor
                surface.actor.actor.position = np.multiply(offset,
                                                           isotropic)[::-1]
            # scale surfaces, which expands/contracts but does not appear
            # to translate the surface position
            surface.actor.actor.scale = isotropic[::-1]
            surfaces.append(surface)

        # keep visual ordering of surfaces when opacity is reduced
        self.scene.renderer.use_depth_peeling = True
        print("time to render 3D surface: {}".format(time() - time_start))
        self.surfaces = surfaces
        return surfaces
Exemple #2
0
def threshold(roi):
    """Thresholds the ROI, with options for various techniques as well as
    post-thresholding morphological filtering.
    
    Args:
        roi: Region of interest, given as [z, y, x].
    
    Returns:
        The thresholded region.
    """
    settings = config.roi_profile
    thresh_type = settings["thresholding"]
    size = settings["thresholding_size"]
    thresholded = roi
    roi_thresh = 0

    # various thresholding model
    if thresh_type == "otsu":
        try:
            roi_thresh = filters.threshold_otsu(roi, size)
            thresholded = roi > roi_thresh
        except ValueError as e:
            # np.histogram may give an error apparently if any NaN, so
            # workaround is set all elements in ROI to False
            print(e)
            thresholded = roi > np.max(roi)
    elif thresh_type == "local":
        roi_thresh = np.copy(roi)
        for i in range(roi_thresh.shape[0]):
            roi_thresh[i] = filters.threshold_local(roi_thresh[i],
                                                    size,
                                                    mode="wrap")
        thresholded = roi > roi_thresh
    elif thresh_type == "local-otsu":
        # TODO: not working yet
        selem = morphology.disk(15)
        print(np.min(roi), np.max(roi))
        roi_thresh = np.copy(roi)
        roi_thresh = libmag.normalize(roi_thresh, -1.0, 1.0)
        print(roi_thresh)
        print(np.min(roi_thresh), np.max(roi_thresh))
        for i in range(roi.shape[0]):
            roi_thresh[i] = filters.rank.otsu(roi_thresh[i], selem)
        thresholded = roi > roi_thresh
    elif thresh_type == "random_walker":
        thresholded = segmenter.segment_rw(roi, size)

    # dilation/erosion, adjusted based on overall intensity
    thresh_mean = np.mean(thresholded)
    print("thresh_mean: {}".format(thresh_mean))
    selem_dil = None
    selem_eros = None
    if thresh_mean > 0.45:
        thresholded = morphology.erosion(thresholded, morphology.cube(1))
        selem_dil = morphology.ball(1)
        selem_eros = morphology.octahedron(1)
    elif thresh_mean > 0.35:
        thresholded = morphology.erosion(thresholded, morphology.cube(2))
        selem_dil = morphology.ball(2)
        selem_eros = morphology.octahedron(1)
    elif thresh_mean > 0.3:
        selem_dil = morphology.ball(1)
        selem_eros = morphology.cube(5)
    elif thresh_mean > 0.1:
        selem_dil = morphology.ball(1)
        selem_eros = morphology.cube(4)
    elif thresh_mean > 0.05:
        selem_dil = morphology.octahedron(2)
        selem_eros = morphology.octahedron(2)
    else:
        selem_dil = morphology.octahedron(1)
        selem_eros = morphology.octahedron(2)
    if selem_dil is not None:
        thresholded = morphology.dilation(thresholded, selem_dil)
    if selem_eros is not None:
        thresholded = morphology.erosion(thresholded, selem_eros)
    return thresholded
Exemple #3
0
def plot_3d_surface(roi, scene_mlab, channel, segment=False, flipz=False):
    """Plots areas with greater intensity as 3D surfaces.
    
    Args:
        roi (:obj:`np.ndarray`): Region of interest either as a 3D (z, y, x) or
            4D (z, y, x, channel) ndarray.
        scene_mlab (:mod:``mayavi.mlab``): Mayavi mlab module. Any
            current image will be cleared first.
        channel (int): Channel to select, which can be None to indicate all
            channels.
        segment (bool): True to denoise and segment ``roi`` before displaying,
            which may remove artifacts that might otherwise lead to 
            spurious surfaces. Defaults to False.
        flipz: True to invert ``roi`` along z-axis to match handedness
            of Matplotlib with z progressing upward; defaults to False.
    """
    # Plot in Mayavi
    #mlab.figure()
    print("viewing 3D surface")
    pipeline = scene_mlab.pipeline
    scene_mlab.clf()
    settings = config.roi_profile
    if flipz:
        # invert along z-axis to match handedness of Matplotlib with z up
        roi = roi[::-1]

    # saturate to remove noise and normalize values
    roi = saturate_roi(roi, channel=channel)

    # turn off segmentation if ROI too big (arbitrarily set here as
    # > 10 million pixels) to avoid performance hit and since likely showing
    # large region of downsampled image anyway, where don't need hi res
    num_pixels = np.prod(roi.shape)
    to_segment = num_pixels < 10000000

    time_start = time()
    multichannel, channels = setup_channels(roi, channel, 3)
    for chl in channels:
        roi_show = roi[..., chl] if multichannel else roi

        # clip to minimize sub-nuclear variation
        roi_show = np.clip(roi_show, 0.2, 0.8)

        if segment:
            # denoising makes for much cleaner images but also seems to allow
            # structures to blend together. TODO: consider segmented individual
            # structures and rendering as separate surfaces to avoid blending
            roi_show = restoration.denoise_tv_chambolle(roi_show, weight=0.1)

            # build surface from segmented ROI
            if to_segment:
                vmin, vmax = np.percentile(roi_show, (40, 70))
                walker = segmenter.segment_rw(roi_show,
                                              chl,
                                              vmin=vmin,
                                              vmax=vmax)
                roi_show *= np.subtract(walker[0], 1)
            else:
                print("deferring segmentation as {} px is above threshold".
                      format(num_pixels))

        # ROI is in (z, y, x) order, so need to transpose or swap x,z axes
        roi_show = np.transpose(roi_show)
        surface = pipeline.scalar_field(roi_show)

        # Contour -> Surface pipeline

        # create the surface
        surface = pipeline.contour(surface)
        # remove many more extraneous points
        surface = pipeline.user_defined(surface, filter="SmoothPolyDataFilter")
        surface.filter.number_of_iterations = 400
        surface.filter.relaxation_factor = 0.015
        # distinguishing pos vs neg curvatures?
        surface = pipeline.user_defined(surface, filter="Curvatures")
        surface = scene_mlab.pipeline.surface(surface)
        module_manager = surface.module_manager
        module_manager.scalar_lut_manager.data_range = np.array([-2, 0])
        module_manager.scalar_lut_manager.lut_mode = "gray"
        '''
        # Surface pipleline with contours enabled (similar to above?)
        surface = pipeline.contour_surface(
            surface, color=(0.7, 1, 0.7), line_width=6.0)
        surface.actor.property.representation = 'wireframe'
        #surface.actor.property.line_width = 6.0
        surface.actor.mapper.scalar_visibility = False
        '''
        '''
        # IsoSurface pipeline
        
        # uses unique IsoSurface module but appears to have 
        # similar output to contour_surface
        surface = pipeline.iso_surface(surface)
        
        # limit contours for simpler surfaces including smaller file sizes; 
        # TODO: consider making settable as arg or through profile
        surface.contour.number_of_contours = 1
        try:
            # increase min to further reduce complexity
            surface.contour.minimum_contour = 0.5
            surface.contour.maximum_contour = 0.8
        except Exception as e:
            print(e)
            print("ignoring min/max contour for now")
        '''

        _resize_glyphs_isotropic(settings, surface)

    print("time to render 3D surface: {}".format(time() - time_start))