Alaa Alwaisy
Alaa Alwaisy

Reputation: 105

How can I correctly count the number of the neighboring cells for each cell in the image?

Please, I'd like to compute the percentage of hexagonal cells (e.g., the number of cells having 6 neighbors cells/ total number of cells) in a binary image and produce a colored coded image as shown below.

I've tried the python code below but I didn't get the correct output. For example, as you can see that the red cells in the binary image have either, 4,5,6, or 7 neighbors cells, but it wrongly calculated as they have 3,4 and 6 neighbors.

An example of a binary image and its output is attached.

import sys
import json
import cv2
import os
import scipy.io
import numpy as np
from scipy.ndimage import measurements, morphology
from skimage import measure
import time


def cells_measurements(path, orig_image, color="yellow", size=3, 
    pixel_sz=0.00104167):
    size = int(size)
    im = cv2.imread(path, cv2.IMREAD_GRAYSCALE)
    _, im = cv2.threshold(im, 127, 255, cv2.THRESH_BINARY)
    labeled_image, num_of_cells = measurements.label(255 - im)
    props = measure.regionprops(labeled_image)
     number_of_cells = len(props)

  colored_image = np.pad(cv2.cvtColor(~im, cv2.COLOR_GRAY2BGR), ((1, 1),(1, 1), (0, 0,)), mode='constant',constant_values=0)

  colors = [[0, 0, 128], [0, 0, 255], [0, 128, 255], [0, 255, 255],[128,255, 128], [255, 255, 0], [255, 128, 0],[255, 0, 0]]

   count_hex = 0
   labels = np.unique(labeled_image)
   for l in labels[1:]:
       i_temp = (labeled_image == l).astype(float) * 255
       i_temp = cv2.dilate(i_temp, np.ones((3, 3)), iterations=2) - i_temp
       i_temp2 = np.copy(labeled_image)
       i_temp2[i_temp == 0.] = 0
       adjacent = len(np.unique(i_temp2)) - 1
       if adjacent == 6:
          count_hex += 1
     cv2.floodFill(colored_image, None, (int(cell_center_all[l - 1][1]), 
     int(cell_center_all[l - 1][0])), colors[min(adjacent, 7)])

       hexagonal_cells = (count_hex / num_of_cells) * 100

     colored_image = np.pad(colored_image, ((0, 0), (0, 75), (0, 0)), 
    'constant', constant_values=255)
       for i in range(8):
           step = colored_image.shape[0] // 8
           colored_image[i * step:+(i + 1) * step, -60:-35] = colors[7 - i]
           colored_image[i * step, -60:-35] = 0
           colored_image[(i + 1) * step, -60:-35] = 0
           colored_image[i * step:(i + 1) * step, -60] = 0
           colored_image[i * step:(i + 1) * step, -35] = 0
           cv2.putText(colored_image, str(7 - i), (colored_image.shape[1] - 
           30, 5 + i * step + step // 2), cv2.FONT_HERSHEY_DUPLEX, 0.5, 0)

   color_path = 'Labeled Images/' + fn + "_color.png"
   cv2.imwrite(color_path, colored_image)

  Auto_Cells_Pleomorphism = []

  Mask_Path = 'generated_samples_masks/'
  Original_img_path = 'TestingSet/enhanced_imgs/'
  # please note that the "mask" image is the result of the segmentation 
  algorithm which I will provide today

  # Loop over images
  for i in range(1, 640):
      filename = str(i) + '.png'
      print(filename)
      Masks_Path = os.path.join(Mask_Path, filename)
      Original_image = os.path.join(Original_img_path, filename)
      [hexagonal_cells] = cells_measurements(Masks_Path, Original_image, 
      color="yellow", size=3) 

  Auto_Cells_Pleomorphism.append(round(hexagonal_cells))

The binary image

As you can see that the binary image has some blurred edges. How can I improve their visibility?

Colored coded image

Upvotes: 0

Views: 499

Answers (1)

Joe
Joe

Reputation: 7131

One approach could be to use the Watershed algorithm for segmentation.

Read about it here:

https://scikit-image.org/docs/dev/auto_examples/segmentation/plot_watershed.html

https://www.pyimagesearch.com/2015/11/02/watershed-opencv/

There are many other segmentation algorithms that you could look at to see if anything works better (e.g. Felsenszwalb's algorithm). Here is a simple comparison of three algorithms, you could easily extend this example to see how they compare on your dataset.

Once you have the contours of all elements (polygons) you can use them to create a "complete map" (identify all cells and their neighbours, no holes, no unknown elements) to make sure you are not missing any cells. You could call this the topology of your cells.

One thing that might help is to create "clearer" polygons from the once returned by the watershed algorithm.

You might have to

  • merge nodes if they are very close
  • connect edges if the angles at their connection is about 0 °

This is called "fixing, repairing, simplifying a mesh". It is a topic on its own, and there are of course tools for that. You can read about Python-related projects here, here or here.

Once this is finished you just count the edges, create a list of all neighbouring cells and cross-check that all neighbours are detected.

This algorithm is not something that will work out of the box, you will have to improve and tweak it until it works.

Upvotes: 1

Related Questions