Aenaon
Aenaon

Reputation: 3573

watershed segmentation: Cannot split some cells

I am trying to segment an image of cell nuclei but I end-up with an under-segmented result. Some big blobs appear that ideally should be broken down to smaller objects, like those at the right edge, see below.

enter image description here

Is there anything I could do to handle that please. I would like to split these big cells like the blue one near the middle of the right edge using watershed segmentation (either skimage or opencv)

My code so far looks like this:

def segment_dapi(img_in):
    img = cv2.cvtColor(img_in, cv2.COLOR_BGR2GRAY)
    kernel = np.ones((3, 3), np.uint8)

    # set the parameters
    thresh = 90
    min_size = 5


    # Adjust brightness
    lims = stretchlim(img)
    img_adj = imadjust(img, lims)

    # Threshold the image
    thres_val = np.percentile(img, thresh)

    _, bw_img = cv2.threshold(img_adj, thres_val, 255, cv2.THRESH_BINARY)

    # Apply morphology opening to remove small objects
    img_obj = cv2.morphologyEx(bw_img, cv2.MORPH_OPEN, kernel, iterations=1)

    bg = cv2.dilate(img_obj, kernel, iterations=1)  # black points belong to the background

    # white points (value = 255) belong to the foreground
    dist_transform = cv2.distanceTransform(img_obj, cv2.DIST_L2, 3)
    _, fg = cv2.threshold(dist_transform, min_size, 255, cv2.THRESH_BINARY)
    fg = np.uint8(fg)
    fg_temp = 255/fg.max() * fg

    x = cv2.subtract(bg, fg)

    _, markers = cv2.connectedComponents(fg)
    markers = markers + 1  # prevent the markers from having values = 0
    markers[x == 255] = 0

    '''
    markers:
    > 1: absolute foreground
    = 1: absolute background
    = 0: unknown area (TBD by watershed)
    '''
    markers = cv2.watershed(img_in, markers)
    img_in[markers == -1] = [0, 255, 255]
    cv2.imwrite('watershed_borders.tif', img_in);
    small_img = cv2.resize(img_in, None, fx=1/2, fy=1/2)
    # cv2.imshow('Overlay', small_img)
    # cv2.waitKey(0)
    ''' 
    markers after watershed:
    = 0: background (set by watershed)
    = 1: background (because the markers have been shifted by 1)
    > 1: object labels 
    - 1: borders between object
    '''

    markers[markers>0] = markers[markers>0]-1
    markers[markers == -1] = 0

    print(markers.max())
    overlay = color.label2rgb(markers, bg_label=0)

    my_dpi = 72
    fig, ax = plt.subplots(figsize=(6000 / my_dpi, 6000 / my_dpi), dpi=my_dpi)
    plt.imshow(overlay)
    ax.set_axis_off()
    plt.tight_layout()
    plt.show()



def stretchlim(img):
    nbins = 255
    tol_low = 0.01
    tol_high = 0.99
    sz = np.shape(img)
    if len(sz) == 2:
        img = img[:, :, None]
        sz = np.shape(img)

    p = sz[2]
    ilowhigh = np.zeros([2, p])
    for i in range(0,p):
        hist,bins = np.histogram(img[:, :, i].ravel(), nbins+1, [0, nbins])
        cdf = np.cumsum(hist) / sum(hist)
        ilow = np.argmax(cdf > tol_low)
        ihigh = np.argmax(cdf >= tol_high)
        if ilow == ihigh:
            ilowhigh[:, i] = np.array([1, nbins])
        else:
            ilowhigh[:, i] = np.array([ilow, ihigh])

    lims = ilowhigh / nbins
    return lims


def imadjust(img, lims):
    lims = lims.flatten()
    lowIn = lims[0]
    highIn = lims[1]
    lowOut = 0
    highOut = 1
    gamma = 1
    lut = adjustWithLUT(img, lowIn, highIn, lowOut, highOut, gamma)
    out = lut[img].astype(np.uint8)
    return out


def adjustWithLUT(img,lowIn,highIn,lowOut,highOut,gamma):
    lutLength = 256 # assumes uint8
    lut = np.linspace(0, 1, lutLength)
    lut = adjustArray(lut, lowIn, highIn, lowOut, highOut, gamma)
    lut = img_as_ubyte(lut)
    return lut


def adjustArray(img, lIn, hIn, lOut, hOut, g):
    # %make sure img is in the range [lIn;hIn]
    img = np.maximum(lIn, np.minimum(hIn, img))
    out = ((img - lIn) / (hIn - lIn)) ** g
    out = out ** (hOut - lOut) + lOut
    return out

Upvotes: 0

Views: 591

Answers (1)

Juan
Juan

Reputation: 5738

You could spend a long time finding the exact parameters to segment this nicely, but in my experience it is finicky and then doesn't work for the next image you want to segment. These days I would actually recommend you use a pre-trained deep learning network such as cellpose.

Upvotes: 2

Related Questions