如何删除附加到另一个大轮廓的小轮廓
How to remove small contours attached to another big one
我正在做细胞分割,所以我正在尝试编写一个函数来删除主要轮廓周围的所有次要轮廓,以便做一个掩码。
发生这种情况是因为我加载了带有一些颜色标记的图像:
问题是当我做阈值时,它假定颜色标记之间的 "box" 作为主要轮廓的一部分。
正如您在我的代码中看到的那样,我不会直接将彩色图像传递给灰色,因为红色会变成黑色,但也有其他颜色,至少有 8 种,并且每张图像总是不同的。我有成千上万张这样的图像,其中只显示一个单元格,但在大多数图像中,总是附有局外人的轮廓。我的目标是得出一个函数,为每个图像输入提供单个细胞的二值图像,就像这样。所以我从这段代码开始:
import cv2 as cv
cell1 = cv.imread(image_cell, 0)
imgray = cv.cvtColor(cell1,cv.COLOR_BGR2HSV)
imgray = cv.cvtColor(imgray,cv.COLOR_BGR2GRAY)
ret,thresh_binary = cv.threshold(imgray,107,255,cv.THRESH_BINARY)
cnts= cv.findContours(image =cv.convertScaleAbs(thresh_binary) , mode =
cv.RETR_TREE,method = cv.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv.drawContours(thresh_binary,[c], 0, (255,255,255), -1)
kernel = cv.getStructuringElement(cv.MORPH_RECT, (3,3))
opening = cv.morphologyEx(thresh_binary, cv.MORPH_OPEN, kernel,
iterations=2) # erosion followed by dilation
总而言之,如何从图像 1 中获取红色轮廓?
实际上,在您的代码中,'box' 是合法的额外轮廓。然后在最终图像上绘制所有轮廓,因此包括 'box'。如果任何其他彩色单元格完全位于图像中,这可能会导致问题。
更好的方法是分离出你想要的颜色。下面的代码创建了一个二进制掩码,它只显示定义的红色范围内的像素。您可以将此掩码与 findContours
.
一起使用
结果:
代码:
import cv2
# load image
img = cv2.imread("PjMQR.png")
# Convert HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# define range of red color in HSV
lower_val = np.array([0,20,0])
upper_val = np.array([15,255,255])
# Threshold the HSV image to get only red colors
mask = cv2.inRange(hsv, lower_val, upper_val)
# display image
cv2.imshow("Mask", mask)
cv2.waitKey(0)
cv2.destroyAllWindows()
This code can help you understand how the different values in this process (HSV with inRange) works. inRange docs
所以另一种方法,没有颜色范围。
我认为您的代码中有几处不正确。首先,您在 thresh_binary
上绘制轮廓,但它也已经具有其他单元格的外线 - 您试图摆脱的线。我认为这就是您使用 opening
(?) 的原因,而在这种情况下您不应该使用。
要解决问题,请先了解一下 findContours 的工作原理。 findContours 开始在黑色背景上寻找白色形状,然后在白色轮廓内寻找黑色形状,依此类推。这意味着 thresh_binary
中单元格的白色轮廓被检测为轮廓。它里面是其他轮廓,包括你想要的。 docs with examples
你应该做的是首先只寻找内部没有轮廓的轮廓。 findContours 也是 returns 轮廓的层次结构。它表示一个轮廓是否有'childeren'。如果它具有 none(值:-1),那么您将查看轮廓的大小并忽略那些太小的轮廓。您也可以只寻找最大的,因为这可能就是您想要的。最后,您在黑色蒙版上绘制轮廓。
结果:
代码:
import cv2 as cv
import numpy as np
# load image as grayscale
cell1 = cv.imread("PjMQR.png",0)
# threshold image
ret,thresh_binary = cv.threshold(cell1,107,255,cv.THRESH_BINARY)
# findcontours
contours, hierarchy = cv.findContours(image =thresh_binary , mode = cv.RETR_TREE,method = cv.CHAIN_APPROX_SIMPLE)
# create an empty mask
mask = np.zeros(cell1.shape[:2],dtype=np.uint8)
# loop through the contours
for i,cnt in enumerate(contours):
# if the contour has no other contours inside of it
if hierarchy[0][i][2] == -1 :
# if the size of the contour is greater than a threshold
if cv2.contourArea(cnt) > 10000:
cv.drawContours(mask,[cnt], 0, (255), -1)
# display result
cv2.imshow("Mask", mask)
cv2.imshow("Img", cell1)
cv2.waitKey(0)
cv2.destroyAllWindows()
注意:我用的是你上传的图片,你的图片可能像素少很多,所以contourArea要小一些
注 2:enumerate
循环遍历轮廓,returns 每个循环的轮廓和索引
我正在做细胞分割,所以我正在尝试编写一个函数来删除主要轮廓周围的所有次要轮廓,以便做一个掩码。
发生这种情况是因为我加载了带有一些颜色标记的图像:
问题是当我做阈值时,它假定颜色标记之间的 "box" 作为主要轮廓的一部分。
正如您在我的代码中看到的那样,我不会直接将彩色图像传递给灰色,因为红色会变成黑色,但也有其他颜色,至少有 8 种,并且每张图像总是不同的。我有成千上万张这样的图像,其中只显示一个单元格,但在大多数图像中,总是附有局外人的轮廓。我的目标是得出一个函数,为每个图像输入提供单个细胞的二值图像,就像这样。所以我从这段代码开始:
import cv2 as cv
cell1 = cv.imread(image_cell, 0)
imgray = cv.cvtColor(cell1,cv.COLOR_BGR2HSV)
imgray = cv.cvtColor(imgray,cv.COLOR_BGR2GRAY)
ret,thresh_binary = cv.threshold(imgray,107,255,cv.THRESH_BINARY)
cnts= cv.findContours(image =cv.convertScaleAbs(thresh_binary) , mode =
cv.RETR_TREE,method = cv.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv.drawContours(thresh_binary,[c], 0, (255,255,255), -1)
kernel = cv.getStructuringElement(cv.MORPH_RECT, (3,3))
opening = cv.morphologyEx(thresh_binary, cv.MORPH_OPEN, kernel,
iterations=2) # erosion followed by dilation
总而言之,如何从图像 1 中获取红色轮廓?
实际上,在您的代码中,'box' 是合法的额外轮廓。然后在最终图像上绘制所有轮廓,因此包括 'box'。如果任何其他彩色单元格完全位于图像中,这可能会导致问题。
更好的方法是分离出你想要的颜色。下面的代码创建了一个二进制掩码,它只显示定义的红色范围内的像素。您可以将此掩码与 findContours
.
结果:
代码:
import cv2
# load image
img = cv2.imread("PjMQR.png")
# Convert HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# define range of red color in HSV
lower_val = np.array([0,20,0])
upper_val = np.array([15,255,255])
# Threshold the HSV image to get only red colors
mask = cv2.inRange(hsv, lower_val, upper_val)
# display image
cv2.imshow("Mask", mask)
cv2.waitKey(0)
cv2.destroyAllWindows()
This code can help you understand how the different values in this process (HSV with inRange) works. inRange docs
所以另一种方法,没有颜色范围。
我认为您的代码中有几处不正确。首先,您在 thresh_binary
上绘制轮廓,但它也已经具有其他单元格的外线 - 您试图摆脱的线。我认为这就是您使用 opening
(?) 的原因,而在这种情况下您不应该使用。
要解决问题,请先了解一下 findContours 的工作原理。 findContours 开始在黑色背景上寻找白色形状,然后在白色轮廓内寻找黑色形状,依此类推。这意味着 thresh_binary
中单元格的白色轮廓被检测为轮廓。它里面是其他轮廓,包括你想要的。 docs with examples
你应该做的是首先只寻找内部没有轮廓的轮廓。 findContours 也是 returns 轮廓的层次结构。它表示一个轮廓是否有'childeren'。如果它具有 none(值:-1),那么您将查看轮廓的大小并忽略那些太小的轮廓。您也可以只寻找最大的,因为这可能就是您想要的。最后,您在黑色蒙版上绘制轮廓。
结果:
代码:
import cv2 as cv
import numpy as np
# load image as grayscale
cell1 = cv.imread("PjMQR.png",0)
# threshold image
ret,thresh_binary = cv.threshold(cell1,107,255,cv.THRESH_BINARY)
# findcontours
contours, hierarchy = cv.findContours(image =thresh_binary , mode = cv.RETR_TREE,method = cv.CHAIN_APPROX_SIMPLE)
# create an empty mask
mask = np.zeros(cell1.shape[:2],dtype=np.uint8)
# loop through the contours
for i,cnt in enumerate(contours):
# if the contour has no other contours inside of it
if hierarchy[0][i][2] == -1 :
# if the size of the contour is greater than a threshold
if cv2.contourArea(cnt) > 10000:
cv.drawContours(mask,[cnt], 0, (255), -1)
# display result
cv2.imshow("Mask", mask)
cv2.imshow("Img", cell1)
cv2.waitKey(0)
cv2.destroyAllWindows()
注意:我用的是你上传的图片,你的图片可能像素少很多,所以contourArea要小一些
注 2:enumerate
循环遍历轮廓,returns 每个循环的轮廓和索引