我最初的任务是从放大 40 倍的全玻片扫描的裁剪区域执行细胞核分割。许多原子核是接触的,有些可能是重叠的。我开始使用具有公平结果的分水岭算法。我现在想做的是完全消除与其他轮廓共享边界的任何轮廓。本质上,我感兴趣的只是完全隔离的轮廓。我知道这违背了分水岭算法的目的,但我很好奇这将如何执行。这是我正在使用的代码。其中大部分内容是从 pyimagesearch文章中借来的。
# USAGE
# python watershed.py --image images/coins_01.png
import argparse
import cv2
import numpy as np
import tifffile as tiff
from scipy import ndimage
# import the necessary packages
from skimage.feature import peak_local_max
from skimage.morphology import watershed
# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True,
help="path to input image")
ap.add_argument("-m", "--min_distance", required=False, default=10, nargs="?", type=int,
help="minimum distance used for distance transformation")
args = vars(ap.parse_args())
# load the image and perform pyramid mean shift filtering
# to aid the thresholding step
filename = args["image"]
minimum_distance = args["min_distance"]
print("minimum distance is " + str(minimum_distance))
if filename.lower().endswith('.tif'):
image = tiff.imread(filename)
image = np.uint8(image)
image *= 1 / 256
else:
image = cv2.imread(args["image"])
shifted = cv2.pyrMeanShiftFiltering(image, 21, 51)
cv2.imshow("Input", image)
# convert the mean shift image to grayscale, then apply
# Otsu's thresholding
gray = cv2.cvtColor(shifted, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255,
cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv2.imshow("Thresh", thresh)
# compute the exact Euclidean distance from every binary
# pixel to the nearest zero pixel, then find peaks in this
# distance map
D = ndimage.distance_transform_edt(thresh)
localMax = peak_local_max(D, indices=False, min_distance=minimum_distance,
labels=thresh)
# perform a connected component analysis on the local peaks,
# using 8-connectivity, then apply the Watershed algorithm
markers = ndimage.label(localMax, structure=np.ones((3, 3)))[0]
labels = watershed(-D, markers, mask=thresh)
print("[INFO] {} unique segments found".format(len(np.unique(labels)) - 1))
# loop over the unique labels returned by the Watershed
# algorithm
for label in np.unique(labels):
# if the label is zero, we are examining the 'background'
# so simply ignore it
if label == 0:
continue
# otherwise, allocate memory for the label region and draw
# it on the mask
mask = np.zeros(gray.shape, dtype="uint8")
mask[labels == label] = 255
# detect contours in the mask and grab the largest one
cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)[-2]
for idx, c in enumerate(cnts):
# compute the center of the contour
M = cv2.moments(c)
area = cv2.contourArea(c)
equi_diameter = np.sqrt(4 * area / np.pi)
arc_length = cv2.arcLength(c, closed=1)
perimeter_over_diameter = arc_length / equi_diameter
ellipse = cv2.fitEllipse(c)
center, axes, orientation = ellipse
major_axis_length = max(axes)
minor_axis_length = min(axes)
eccentricity = np.sqrt(1 - (minor_axis_length / major_axis_length) ** 2)
if 500 <= area <= 2000 and perimeter_over_diameter > 3.6 and eccentricity < 0.9:
if M["m00"] == 0:
M["m00"] = 1
cX = int(M["m10"] / M["m00"])
cY = int(M["m01"] / M["m00"])
# draw the contour and center of the shape on the image
cv2.drawContours(image, [c], -1, (255, 0, 0), 2)
cv2.circle(image, (cX, cY), 3, (255, 255, 255), 1)
# show the output image
cv2.imwrite("dapi_segmented.png", image)
cv2.namedWindow("Output", cv2.WINDOW_NORMAL)
cv2.imshow("Output", image)
cv2.waitKey(0)
换句话说,我只希望返回红色的轮廓圆圈: