Reputation: 11
I have an image of cells which I have thresholded and also detected the cells (using cv2).
I would like to create an array with values True or False to show whether each component touches the boundaries of the image (True) or not (False).
import cv2 as cv
# Read the image you want connected components of, IN BLACK AND WHITE
img = cv.imread('../images/37983_ERSyto/cellpaintingfollowup-reimage_a01_s1_w26ae36209-938b-45ef-b166-3aba3af125df.tif', cv.IMREAD_GRAYSCALE)
seed_pt = (100, 800) #point in the background
fill_color = 0
mask = np.zeros_like(img)
kernel = cv.getStructuringElement(cv.MORPH_RECT, (6, 5))
for th in range(7,70):
#creates a copy of the mask:
prev_mask = mask.copy()
#thresholded image:
mask = cv.threshold(img, th, 22331, cv.THRESH_BINARY)[1]
#FloodFill: fill a connected component starting from the seed point with the specified color.
mask = cv.floodFill(mask, None, seed_pt, fill_color)[1]
#cv.bitwise: calculates the per-element bit-wise disjunction of two arrays or an array and a scalar. Superposition of thresholded images
mask = cv.bitwise_or(mask, prev_mask)
#clean speckles
mask = cv.morphologyEx(mask, cv.MORPH_OPEN, kernel)
#compute the connected components labeled image of boolean image and also produce a statistics output for each label
connectivity = 8 #You need to choose 4 or 8 for connectivity type.
#OBTAIN FEATURE OF THE AREA IN PIXELS OF THE CELLS
stats = cv.connectedComponentsWithStats(mask, connectivity, cv.CV_32S)[2]
label_area = stats[1:, cv.CC_STAT_AREA] #we dont include the first element because it represents the area of the background
#OBTAIN FEATURES OF THE CENTROID POSITION
centroids = cv.connectedComponentsWithStats(mask, connectivity, cv.CV_32S)[3]
label_centroids_x = centroids[1:, 0] #dont include the first element because it represents the background
label_centroids_y = centroids[1:,1]
#HIGHT: The vertical size of the bounding box.
label_hight = stats[1:, cv.CC_STAT_HEIGHT]
#WIDTH: The horizontal size of the bounding box.
label_width = stats[1:, cv.CC_STAT_WIDTH]
#TOUCHING IMAGE BOUNDARIES: is the component touching the boundaries of the matrix/image?--> True/False
label_bounary = #boolean array
I first thought about searching for the contour of every component and defining some restriction, but I have troubles understanding how the labels of every component are stored and therefore, I could not select the desired components.
Thank you very much in advance.
Upvotes: 0
Views: 1755
Reputation: 4090
If you're open to using scikit-image
, you can try clear_border
:
>>> import numpy as np
>>> from skimage.segmentation import clear_border
>>> labels = np.array([[0, 0, 0, 0, 0, 0, 0, 1, 0],
... [1, 1, 0, 0, 1, 0, 0, 1, 0],
... [1, 1, 0, 1, 0, 1, 0, 0, 0],
... [0, 0, 0, 1, 1, 1, 1, 0, 0],
... [0, 1, 1, 1, 1, 1, 1, 1, 0],
... [0, 0, 0, 0, 0, 0, 0, 0, 0]])
>>> clear_border(labels)
array([[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 1, 0, 0, 0],
[0, 0, 0, 1, 1, 1, 1, 0, 0],
[0, 1, 1, 1, 1, 1, 1, 1, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0]])
Upvotes: 2
Reputation: 3143
Using your code (thanks for commenting), I got this mask. It's possible it's not the same since .jpg compression can mess with an image (it's not a lossless compression scheme)
@fmw42 is exactly right, he commented before I could finish my code
import cv2 as cv
import numpy as np
# Read the image you want connected components of, IN BLACK AND WHITE
img = cv.imread('cells.jpg', cv.IMREAD_GRAYSCALE)
seed_pt = (100, 800) #point in the background
fill_color = 0
mask = np.zeros_like(img)
kernel = cv.getStructuringElement(cv.MORPH_RECT, (6, 5))
for th in range(7,70):
#creates a copy of the mask:
prev_mask = mask.copy()
#thresholded image:
mask = cv.threshold(img, th, 22331, cv.THRESH_BINARY)[1]
#FloodFill: fill a connected component starting from the seed point with the specified color.
mask = cv.floodFill(mask, None, seed_pt, fill_color)[1]
#cv.bitwise: calculates the per-element bit-wise disjunction of two arrays or an array and a scalar. Superposition of thresholded images
mask = cv.bitwise_or(mask, prev_mask)
#clean speckles
mask = cv.morphologyEx(mask, cv.MORPH_OPEN, kernel)
# show mask
cv.imshow("Mask", mask);
cv.waitKey(0);
# contours OpenCV 3.4, if you're using OpenCV 2 or 4, it returns (contours, _)
_, contours, _ = cv.findContours(mask, cv.RETR_TREE, cv.CHAIN_APPROX_NONE);
# get bounds and check if they're touching edge
height, width = mask.shape[:2];
touching_edge = []; # boolean array, index matches the contours list
for con in contours:
# get bounds
x, y, w, h = cv.boundingRect(con);
# check if touching edge
on_edge = False;
if x <= 0 or (x + w) >= (width - 1):
on_edge = True;
if y <= 0 or (y + h) >= (height - 1):
on_edge = True;
# add to list
touching_edge.append(on_edge);
# mark the contours on the edge
colored = cv.cvtColor(mask, cv.COLOR_GRAY2BGR);
for index in range(len(contours)):
if touching_edge[index]:
# drawContours(image, contour_list, index, color, thickness) # -1 is filled
cv.drawContours(colored, contours, index, (50,50,200), -1);
# show
cv.imshow("Colored", colored);
cv.waitKey(0);
Upvotes: 1