I need to segment the seeds in the image below and crop them.
https://i.sstatic.net/ndOkX.jpg
They can be pretty close to each other and sometimes overlap, so I chose to use the watershed algorithm for this task.
My results are in the image below, after drawing the contours of the markers that are returned, and as you can see I'm having problems defining good markers for applying it. The individual seeds are outlined but there are many inner lines that I do not want.
https://i.sstatic.net/BtOfj.jpg
How would I go about removing them or defining better markers?
The code I'm running:
from skimage.feature import peak_local_max
from skimage.segmentation import watershed
import matplotlib.pyplot as plt
from scipy import ndimage
import cv2 as cv
import imutils
import numpy as np
img = cv.imread("image.jpg");
blur = cv.GaussianBlur(img,(7,7),0)
#color space change
mSource_Hsv = cv.cvtColor(blur,cv.COLOR_BGR2HSV);
mMask = cv.inRange(mSource_Hsv,np.array([0,0,0]),np.array([80,255,255]));
output = cv.bitwise_and(img, img, mask=mMask)
#grayscale
img_grey = cv.cvtColor(output, cv.COLOR_BGR2GRAY)
#thresholding
ret,th1 = cv.threshold(img_grey,0,255,cv.THRESH_BINARY + cv.THRESH_OTSU)
#dist transform
D = ndimage.distance_transform_edt(th1)
#markers
localMax = peak_local_max(D, indices=False, min_distance=20, labels=th1)
markers = ndimage.label(localMax, structure=np.ones((3, 3)))[0]
#apply watershed
labels = watershed(-D, markers, mask=th1)
print("[INFO] {} unique segments found".format(len(np.unique(labels)) - 1))
# loop over the unique labels
for label in np.unique(labels):
if label == 0:
continue
# draw label on the mask
mask = np.zeros(img_grey.shape, dtype="uint8")
mask[labels == label] = 255
# detect contours in the mask and grab the largest one
cnts = cv.findContours(mask.copy(), cv.RETR_EXTERNAL,
cv.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
c = max(cnts, key=cv.contourArea)
cv.drawContours(img, cnts, -1, (0, 255, 0), 2)
cv.imshow("segmented",img)
cv.waitKey(0)
You can merge every two contours that applies the following condition:
The following solution uses a kind of "brute force" approach that tries merging every contour with all other contours (not very efficient).
Here is a working code sample (please read the comments):
from skimage.feature import peak_local_max
from skimage.segmentation import watershed
import matplotlib.pyplot as plt
from scipy import ndimage
import cv2 as cv
import imutils
import numpy as np
img = cv.imread("image.jpg");
blur = cv.GaussianBlur(img,(7,7),0)
#color space change
mSource_Hsv = cv.cvtColor(blur,cv.COLOR_BGR2HSV);
mMask = cv.inRange(mSource_Hsv,np.array([0,0,0]),np.array([80,255,255]));
output = cv.bitwise_and(img, img, mask=mMask)
#grayscale
img_grey = cv.cvtColor(output, cv.COLOR_BGR2GRAY)
#thresholding
ret,th1 = cv.threshold(img_grey,0,255,cv.THRESH_BINARY + cv.THRESH_OTSU)
#dist transform
D = ndimage.distance_transform_edt(th1)
#markers
localMax = peak_local_max(D, indices=False, min_distance=20, labels=th1)
markers = ndimage.label(localMax, structure=np.ones((3, 3)))[0]
#apply watershed
labels = watershed(-D, markers, mask=th1)
print("[INFO] {} unique segments found".format(len(np.unique(labels)) - 1))
contours = []
# loop over the unique labels, and append contours to all_cnts
for label in np.unique(labels):
if label == 0:
continue
# draw label on the mask
mask = np.zeros(img_grey.shape, dtype="uint8")
mask[labels == label] = 255
# detect contours in the mask and grab the largest one
cnts = cv.findContours(mask.copy(), cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
c = max(cnts, key=cv.contourArea)
## Ignore small contours
#if c.shape[0] < 20:
# continue
# Get convex hull of contour - it' going to help when merging contours
hull = cv.convexHull(c)
#cv.drawContours(img, c, -1, (0, 255, 0), 2)
cv.drawContours(img, [hull], -1, (0, 255, 0), 2, 1)
# Append hull to contours list
contours.append(hull)
# Merge the contours that does not increase the convex hull by much.
# Note: The solution is kind of "brute force" solution, and can be better.
################################################################################
for i in range(len(contours)):
c = contours[i]
area = cv.contourArea(c)
# Iterate all contours from i+1 to end of list
for j in range(i+1, len(contours)):
c2 = contours[j]
area2 = cv.contourArea(c2)
area_sum = area + area2
# Merge contours together
tmp = np.vstack((c, c2))
merged_c = cv.convexHull(tmp)
merged_area = cv.contourArea(merged_c)
# Replace contours c and c2 by the convex hull of merged c and c2, if total area is increased by no more then 10%
if merged_area < area_sum*1.1:
# Replace contour with merged one.
contours[i] = merged_c
contours[j] = merged_c
c = merged_c
area = merged_area
################################################################################
# Draw new contours in red color
for c in contours:
#Ignore small contours
if cv.contourArea(c) > 100:
cv.drawContours(img, [c], -1, (0, 0, 255), 2, 1)
cv.imshow("segmented",img)
cv.waitKey(0)
cv.destroyAllWindows()