Exemple #1
0
def _detect_blobs(image, min_size=50, max_size=200, threshold=0.2, show=True):
    """
    Thin wrapper around skimage.morphology.blob_log.

    Finds blobs in the given grayscale image.
    Blobs are found using the Laplacian of Gaussian (LoG) method [1]_.
    For each blob found, the method returns its coordinates and the standard
    deviation of the Gaussian kernel that detected the blob.

    Parameters
    ----------
    image : path or 2D ndarray
        Path to grayscale image or the image itself.
        Blobs are assumed to be light on dark background (white on black).
    min_size : float
        Minimum blob area in pixels.
    max_size : float
        Maximum blob area in pixels.
    threshold : float, optional.
        The absolute lower bound for scale space maxima. Local maxima smaller
        than thresh are ignored. Reduce this to detect blobs with less
        intensities.
    show: bool

    Returns
    -------
    blobs : iterable of (x, y, radius) tuples
        The (x, y) positions and radii of the detected blobs.
    """

    image = utils.handle_grayscale_image_input(image)

    # convert area to an estimate of the STD of a corresponding Gaussian kernel
    # \sigma = radius / \sqrt(2)
    min_sigma = np.sqrt(min_size / np.pi) / np.sqrt(2)
    max_sigma = np.sqrt(max_size / np.pi) / np.sqrt(2)

    # detect blobs
    blobs = blob_log(image,
                     min_sigma=min_sigma,
                     max_sigma=max_sigma,
                     threshold=threshold)

    # convert sigma estimates back to radii
    blobs[:, 2] = blobs[:, 2] * np.sqrt(2)

    if show:
        fig, ax = plt.subplots(1, 1)
        ax.imshow(image, cmap='gray')
        for blob in blobs:
            y, x, r = blob
            c = plt.Circle((x, y), r, color='yellow', linewidth=1., fill=False)
            ax.add_patch(c)

    return blobs
Exemple #2
0
def get_count(soma_mask,
              soma_marker=None,
              threshold=0.1,
              return_soma_parameters=False,
              show=True,
              *args,
              **kwargs):
    """
    Count the number of somata.
    1) Estimate the lower and upper bound on soma size based on the separable
       objects in the soma mask.
    2) Detect blobs of the right sizes in the image.

    Arguments:
    ----------
    soma_mask: ndarray
        Grayscale image indicating the presence of somata.
        Pixel intensity scales approximately with the certainty that the pixel
        is part of a soma.

    soma_marker : ndarray or None (default None)
        Fluorescence image used to create the mask.
        If given, this image is used to find the soma rather than the soma mask.
        This is a more sensitive approach that will pick up fainter and/or less
        circular soma.

    threshold : float, optional (default 0.1).
        The absolute lower bound for scale space maxima. Local maxima smaller
        than the threshold are ignored. Reduce this to detect blobs with less
        relative intensity w.r.t. the background.

    return_soma_parameters : bool
        If true, also return the parameters of the detected somata

    show: bool
        Plotting flag.

    Returns:
    --------
    count : int
        Number of soma in the image.

    soma_parameters: (n, 3) ndarray
        List of (x, y, radius) tuples indicating soma position and radius.

    """
    # estimate bounds on soma area in pixels
    mode, (lower, upper) = _get_soma_size(soma_mask > 0, show=show)

    # fit blobs; constrain fit using bounds on soma size
    if soma_marker is None:
        blobs = _detect_blobs(soma_mask, lower, upper, threshold, show=show)
    else:
        soma_marker = utils.handle_grayscale_image_input(soma_marker)
        blobs = _detect_blobs(soma_marker,
                              lower,
                              upper,
                              show=show,
                              *args,
                              **kwargs)

    if return_soma_parameters:
        return len(blobs), blobs
    else:
        return len(blobs)
Exemple #3
0
def get_mask(soma_marker,
             intensity_threshold=50.,
             size_threshold=50,
             show=True):
    """Get a mask indicating the presence or absence of soma in a
    (immuno-)fluorescence image of a somatic marker.

    Arguments:
    ----------
    soma_marker: string or numpy.uint8 array
        Path to image of soma marker OR
        corresponding grayscale image with intensities in the range (0-255).

    intensity_threshold: float (default 50.)
        Minimum pixel intensity in percent which are considered for the soma mask.

    size_threshold : int (default 50)
        Minimum object size in pixels above which objects are included in the soma mask.

    show: bool (default True)
        If True, plots intermediate steps of image analysis.

    Returns:
    --------
    soma_mask: ndarray
        Grayscale image indicating the presence of somata.
        Pixel intensity scales approximately with the certainty that the pixel
        is part of a soma.

    """
    # handle input
    raw = utils.handle_grayscale_image_input(soma_marker)
    equalised = rank.equalize(raw, selem=disk(50))
    intensity_threshold = np.percentile(raw, intensity_threshold)
    equalised[raw < intensity_threshold] = intensity_threshold

    # determine local phase-symmetry -> maxima correspond to soma
    phase, _, _, _ = phasesym(equalised,
                              nscale=10,
                              norient=2,
                              minWaveLength=0.5,
                              mult=3.,
                              sigmaOnf=0.55,
                              k=2.,
                              polarity=1,
                              noiseMethod=-1)
    phase = utils.rescale_0_255(phase)

    # morphological cleaning
    clean = cleaning.morphological_cleaning(phase, disk(3))

    # remove thin and small objects
    mask = binary_opening(clean > 0, disk(2))
    mask = cleaning.remove_small_objects(mask, size_threshold)
    clean = mask * clean

    if show:
        images = [raw, equalised, phase, clean]
        titles = [
            'Soma marker', 'Local histogram equalisation', 'Phase symmetry',
            'Morphological cleaning'
        ]
        utils.plot_image_collection(images, titles, nrows=2, ncols=2)

    return clean
Exemple #4
0
def get_mask(neurite_marker,
             sigmas=range(1, 5),
             intensity_threshold=75.,
             size_threshold=1000,
             show=True):
    """Given a neurite stain image, returm a boolean mask that evaluates
    to true where there is a neurite. We carry out the following steps:

    1) Apply rank equalisation to obtain an evenly illuminated image.
    2) Apply the filter by Meijering et al. to isolate tubular structures.
    3) Obtain a mask by applying an intensity threshold.
    4) Remove small objects to clean up the mask.

    Arguments:
    ----------
    neurite_marker: string or numpy.uint8 array
        path to image of neurite marker OR
        corresponding grayscale image with intensities in the range (0-255)

    sigmas: iterable of floats (default range(1,5))
        Meijering filter scales.

    intensity_threshold: float (default 75.)
        Intensity threshold in percent.
        Applied after the Meijering filter to obtain a mask from the grayscale
        image.

    size_threshold: int (default 1000)
        Object size threshold in pixels.
        Smaller objects are removed from the mask.

    show: bool (default True)
        if True, plots intermediate steps of image analysis

    Returns:
    --------
    neurite_mask: ndarray
        Grayscale image indicating the presence of neurites.
        Pixel intensity scales approximately with the certainty that the pixel
        is part of a neurites.

    References:
    -----------
    Meijering, E., Jacob, M., Sarria, J. C., Steiner, P., Hirling, H.,
    Unser, M. (2004). Design and validation of a tool for neurite
    tracing and analysis in fluorescence microscopy images. Cytometry
    Part A, 58(2), 167-176. DOI:10.1002/cyto.a.20022

    """
    # handle input
    raw = utils.handle_grayscale_image_input(neurite_marker)

    # equalise
    equalised = rank.equalize(raw, selem=disk(50))

    # apply meijering filter
    filtered = _apply_meijering_filter(equalised, sigmas)

    # threshold
    thresholded = filtered > np.percentile(filtered, intensity_threshold)

    # remove small objects in the image
    clean = _remove_small_objects(thresholded, size_threshold)

    if show:
        combined = raw.astype(np.float)
        combined[clean] *= 1.15

        titles = [
            'Neurite marker',
            'Local histogram equalisation',
            'Meijering ridge filter',
            'Thresholded',
            'Removed small objects',
            'Combined',
        ]

        images = [
            raw,
            equalised,
            filtered,
            thresholded,
            clean,
            combined,
        ]

        fig1 = utils.plot_image_collection(images, titles, nrows=2, ncols=3)
        fig2 = utils.plot_image_mask_comparison(raw, clean)

    return clean
Exemple #5
0
def get_mask(synaptic_marker,
             neurite_mask,
             min_synapse_size=25,
             max_synapse_size=400,
             min_synapse_brightness=97.5,
             show=False):
    """Given a grayscale image of a synaptic stain, and a boolean mask for
    neurites, determine the pixels corresponding to synapses in the following steps:

    1) Only bright pixels can contain a synapse. Hence threshold the
    synaptic stain based on brightness.

    2) Synapses have a certain size. Hence remove objects that are
    either too small or too large.

    3) Synapses are on neurites. Hence remove all synapse candidates
    that are not on -- or the immediate vicinity of -- a neurite.

    Arguments:
    ----------
    synaptic_marker: string or numpy.uint8 array
        path to grayscale image of synaptic marker, OR
        corresponding numpy array with values in the range (0-255)

    neurite_mask: string or numpy.bool array
        path to binary image indicating the presence of neurites, OR
        corresponding boolean numpy.ndarray

    min_synapse_size: int (default 25)
        minimum acceptable synapse sizes in pixels

    max_synapse_size: int (default 400)
        maximum acceptable synapse sizes in pixels

    min_synapse_brightness: float in the range 0.-100., (default 95.)
        image intensity threshold in percent above which objects
        in synaptic marker images are labelled as putative synapses

    show: bool (default True)
        if True, plots intermediate steps of image analysis

    Returns:
    --------
    synapse_mask: numpy.bool array
        binary image indicating the presence of a synapse

    """

    # handle input
    synapses_raw = utils.handle_grayscale_image_input(synaptic_marker)
    neurite_mask = utils.handle_binary_image_input(neurite_mask)

    # threshold
    thresholded = synapses_raw > np.percentile(synapses_raw,
                                               min_synapse_brightness)

    # remove too large objects, remove too small objects
    cleaned = cleaning.remove_large_objects(thresholded, max_synapse_size + 1)
    cleaned = cleaning.remove_small_objects(cleaned, min_synapse_size - 1)

    # restrict synapse candidates to puncta within or juxtaposed to the neurite mask;
    # dilate mask to catch synapses that are next to the neurite but not directly on it
    neurite_mask = binary_dilation(neurite_mask, disk(2))
    synapse_mask = np.logical_and(cleaned, neurite_mask)

    if show:
        images = [
            synapses_raw, thresholded, cleaned + 0.3 * thresholded,
            synapse_mask + 0.3 * neurite_mask
        ]
        titles = [
            'Synaptic marker', 'Thresholded', 'Within size range',
            'Within neurite mask'
        ]
        fig = utils.plot_image_collection(images, titles, nrows=2, ncols=2)

    return synapse_mask
Exemple #6
0
    binary_dilation,
    binary_opening,
)
from neuronab import (
    utils,
    cleaning,
    somata,
)

if __name__ == '__main__':

    neun_path = 'test_images/neun.tif'
    pv_path   = 'test_images/pv.tif'
    kdel_path = 'test_images/kdel.tif'

    neun = utils.handle_grayscale_image_input(neun_path)
    kdel = utils.handle_grayscale_image_input(kdel_path)
    pv   = utils.handle_grayscale_image_input(pv_path)

    neun_mask = somata.get_mask(neun, intensity_threshold=50, show=True)
    # counts = somata.get_count(neun_mask, neun_path, threshold=0.05, show=True)
    counts = somata.get_count(neun_mask, threshold=0.05, show=True)
    print(f'Number of NeuN positive cells: {counts}')

    images = [kdel, neun, neun_mask, kdel * (neun_mask > 0)]
    titles = ['KDEL', 'NeuN', 'Soma mask', 'Masked KDEL']
    utils.plot_image_collection(images, titles, nrows=2, ncols=2)

    pv_mask = somata.get_mask(pv, intensity_threshold=95, show=True)
    # counts = somata.get_count(pv_mask, pv, threshold=0.1, show=True)
    counts = somata.get_count(pv_mask, threshold=0.1, show=True)