Currently I'm working on object detection for counting how many object presented on the frame. I already successfully separate some of them. There's still some object which is very close together which it turns into one blob I still don't know how to separate it properly since other touching object which just the tip can be separated. Also there are objects which in my distance transformation visualization looks pretty clear but some how the peak_local_max() function not recognized the object so its not giving a peak coordinates correctly and then on the watershed section that object is gone. Are there something I did wrong? Here is my debugging image.
Here is my sample_image if you wanted to try.
My watershed code is simple, I use a helper scipy's ndimage
import cv2
import numpy as np
from skimage.feature import peak_local_max
from skimage.segmentation import watershed
from scipy import ndimage
gray_for_mask = cv2.cvtColor(working, cv2.COLOR_BGR2GRAY)
mask_glare = cv2.threshold(gray_for_mask, 200, 255, cv2.THRESH_BINARY)[1]
if np.count_nonzero(mask_glare) > 0:
inpaint_mask = cv2.dilate(mask_glare, np.ones((3,3), np.uint8), iterations=1)
working = cv2.inpaint(working, inpaint_mask, inpaintRadius=3, flags=cv2.INPAINT_TELEA)
hsv = cv2.cvtColor(working, cv2.COLOR_BGR2HSV)
lower_gold = np.array([10, 40, 100]) # H,S,V
upper_gold = np.array([45, 255, 255])
mask_color = cv2.inRange(hsv, lower_gold, upper_gold)
gray = cv2.cvtColor(working, cv2.COLOR_BGR2GRAY)
gray_blur = cv2.medianBlur(gray, 5)
adaptive_thresh = cv2.adaptiveThreshold(gray_blur, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV , 31 , 3)
close_kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
adaptive_thresh = cv2.morphologyEx(adaptive_thresh, cv2.MORPH_CLOSE, close_kernel, iterations=1)
combined = cv2.bitwise_or(mask_color, adaptive_thresh)
k_open = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
k_close = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
mask = cv2.morphologyEx(combined, cv2.MORPH_CLOSE, k_close, iterations=1)
mask = remove_small_components(mask, min_area=150)
k_close = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (2,2))
mask = cv2.morphologyEx(mask, cv2.MORPH_DILATE, k_close, iterations=1)
contours, hierarchy = cv2.findContours(
mask, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE
)
mask_filled = mask.copy()
for i, cnt in enumerate(contours):
if hierarchy[0][i][3] == -1:
cv2.drawContours(mask_filled, [cnt], 0, 255, thickness=cv2.FILLED)
mask_filled = cv2.erode(mask_filled, kernel=np.ones((2,2), np.uint8), iterations=1)
dist = ndimage.distance_transform_edt(mask_filled.copy())
peaks = peak_local_max(dist, min_distance=40,
labels=mask_filled.copy())
dist_visual = cv2.normalize(dist, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)
peak_map = cv2.cvtColor(dist_visual.copy() * 255, cv2.COLOR_GRAY2BGR)
for (py, px) in peaks:
cv2.circle(peak_map, (px, py), 3, (0, 0, 255), -1)
local_max = np.zeros_like(dist, dtype=bool)
local_max[tuple(peaks.T)] = True
markers = ndimage.label(local_max)[0]
labels = watershed(-dist, markers, mask=mask_filled.copy())
watershed_vis = np.zeros((mask_filled.copy().shape[0], mask_filled.copy().shape[1], 3), dtype=np.uint8)
for label in np.unique(labels):
if label == 0:
continue
color = np.random.randint(15, 255, size=(3,), dtype=np.uint8)
watershed_vis[labels == label] = color







mask_filledfrom your input image? What is the signature ofwatershed? Most likelyskimage.segmentation.watershed, but we should not have to guess, etc. Your code should read the input and generate the bottom right output, so that we are able to reproduce.