Dense cell segmentation


I’m transferring a cell segmentation pipeline from python/skimage to Julia/Images.jl, and ran into some function calls that weren’t straightforward to translate. In particular:

  • autolevel_percentile
  • skimage.morphology.disk
  • skimage.feature.match_template
  • peak_local_max

The first two I can write if need be. match_template and peak_local_max, with its footprint option, would seem to be much more involved, so wanted to ask if anyone was aware of pertinent Julia options?

Also, if anyone has other comments, feedback or code to point me to that may be relevant, even if taking a different approach, that would be great! I did see Celltracker possibilities?, which is helpful albeit in a lower density regime.

import os
import numpy as np
import scipy
from scipy import ndimage
import skimage
from skimage.morphology import disk, watershed
from skimage.filter import rank
from skimage.filter.rank import otsu, autolevel_percentile, enhance_contrast_percentile
from skimage.filter import threshold_otsu
from skimage.feature import peak_local_max
from skimage.exposure import adjust_gamma
def segment_nuclei(img):
    maxval = np.max(img)
    img_gamma = np.power(img,0.45) * maxval / np.power(maxval,0.45)
    img_gamma = img_gamma.astype('uint16')

    # use image histogram to filter out background
    nbins = 200
    hist, bin_edges = np.histogram(np.ravel(img_gamma), bins=nbins)
    hthres = bin_edges[np.argmax(hist) + 6]
    #print "Histogram threshold: " + str(float(hthres) / np.max(img_gamma))
    fg_mask = (img_gamma >= hthres)

    # autolevel_percentile
    selem = disk(4)
    img_level = autolevel_percentile(img_gamma, selem=selem, p0=.1, p1=.90)
    img_level *= fg_mask  # apply otsu mask
    # otsu threshold 2 to binary image
    gthres = threshold_otsu(img_level)
    img_gthres = img_level >= gthres
    # find cell centers
    kern = disk(2)
    distance = ndimage.distance_transform_edt(img_gthres)
    feat_img = skimage.feature.match_template(img_level, kern, pad_input=True, mode='constant', constant_values=0)
    local_maxi = peak_local_max(feat_img, indices=False, footprint=disk(4), exclude_border=False, threshold_rel=.4)
    # apply watershed
    markers = ndimage.label(local_maxi)[0]
    labels = watershed(-feat_img, markers, mask=img_gthres)
    # exclude regions that are too small, too large
    roi_props = skimage.measure.regionprops(labels, intensity_image=None, cache=True)
    bKeep = np.array([(rp['area']>5) & (rp['area']<70) & (rp['eccentricity']<.9) for rp in roi_props]).astype(bool)
    labels = np.zeros_like(labels)
    from itertools import compress
    for nLabel, rp in enumerate(compress(roi_props,bKeep)):
        labels[rp['coords'][:,0], rp['coords'][:,1]] = nLabel
    roi_props = skimage.measure.regionprops(labels, intensity_image=None, cache=True)

    # filter rois according to properities
    label_img = skimage.color.label2rgb(labels, image=None, colors=None, alpha=1, bg_label=0, bg_color=[0,0,0], image_alpha=.5)
    intermediates = {}
    intermediates['img_gamma'] = img_gamma
    intermediates['img_level'] = img_level
    intermediates['img_gthres'] = img_gthres
    intermediates['img_gthres'] = img_gthres
    rois = labels
    roi_image = label_img
    return roi_props, rois, roi_image, intermediates

GOAL: to segment all of these cells :slight_smile:

Edit: some tips here for peak_local_max, but 1D case only. Maybe findlocalmaxima does what I need, but it’s not clear from documentation if something like min_distance can be achieved.

1 Like

match_template: add HolyLabRegistry and then Pkg.add("RegisterMismatch"). See the mismatch function. I don’t have time to go into details but this algorithm handles boundary conditions better than most normalized correlation approaches I’ve seen out there, so you may find it gives you more robust results than you are used to.

Pretty please in exchange for the help: this package was started before Julia had internal documentation and because it’s been mostly internal it never got a README or docs. I would :heart: anyone for adding some.

Footprints, sorry, can’t help with that.

And I recognize that fish…are these anatomical or GCaMP images?

1 Like

Thanks so much Tim! I will check it out…and certainly would be happy to contribute documentation if/when I get thinks working.

This is a elavl3:H2B-GCamP6s zebrafish line, so nuclear-localized fluorescence. I imaged on a 2P microscope near the isosbestic point (820nm) to aid with cell segmentation.

1 Like