查找多个连接对象的每个质心
Finding each centroid of multiple connected objects
我是 python 编码的超级新手,需要一些帮助。我能够分割生物组织内的每个细胞轮廓(超级酷!),现在我正在尝试使用以下方法找到组织内每个细胞的质心:
我正在使用此代码:
img = cv2.imread('/Users/kate/Desktop/SegmenterTest/SegmentedCells/Seg1.png')
image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(image, 60, 255, cv2.THRESH_BINARY)[1]
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
# loop over the contours
for c in cnts:
# compute the center of the contour
M = cv2.moments(c)
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, (0, 255, 0), 2)
cv2.circle(image, (cX, cY), 7, (255, 255, 255), -1)
cv2.putText(image, "center", (cX - 20, cY - 20),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)
# show the image
cv2.imshow("Image", image)
cv2.waitKey(0)
但是,当我使用这段代码时,它给出了整个对象的质心,而不是每个单独的对象。
我不知道从这里到哪里去,所以非常感谢向正确的方向轻推!
问题
cv2.findContours
使用的算法有一些不同 'retrieval modes'。这些会影响 returned 的轮廓以及 returned 的方式。这已记录在案 here。这些作为 findContours
的第二个参数给出。您的代码使用 cv2.RETR_EXTERNAL
,这意味着 findContours
只会 return 单独对象的最外层边框。
解决方案
将此参数更改为 cv2.RETR_LIST
将为您提供图像中的所有轮廓(包括最外层的边框)。这是最简单的解决方案。
例如
import cv2
import imutils
img = cv2.imread('/Users/kate/Desktop/SegmenterTest/SegmentedCells/Seg1.png')
image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(image, 60, 255, cv2.THRESH_BINARY)[1]
cnts = cv2.findContours(thresh.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
# loop over the contours
for c in cnts:
# compute the center of the contour
M = cv2.moments(c)
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, (0, 255, 0), 2)
cv2.circle(image, (cX, cY), 7, (255, 255, 255), -1)
cv2.putText(image, "center", (cX - 20, cY - 20),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)
# show the image
cv2.imshow("Image", image)
cv2.waitKey(0)
只选择最里面的对象
要可靠地忽略外部轮廓,您可以利用 findContours
的功能来 return 它检测到的轮廓的层次结构。为此,您可以再次将检索模式参数更改为 RETR_TREE
,这将生成完整的层次结构。
层次结构是一个数组,其中包含图像中每个轮廓的 4 个值的数组。每个值都是轮廓数组中轮廓的索引。来自 docs:
For each i-th contour contours[i]
, the elements hierarchy[i][0]
,
hierarchy[i][1]
, hierarchy[i][2]
, and hierarchy[i][3]
are set to
0-based indices in contours of the next and previous contours at the
same hierarchical level, the first child contour and the parent
contour, respectively. If for the contour i there are no next,
previous, parent, or nested contours, the corresponding elements of
hierarchy[i]
will be negative.
当我们说 'innermost' 时,我们的意思是没有子级的轮廓(它们内部的轮廓)。所以我们想要那些在层次结构中的条目具有负的第三值的轮廓。即 contours[i]
,使得 hierarchy[i][2] < 0
一个小问题是,尽管 findContours
return 是一个包含层次结构的元组,但 imutils.grabContours
丢弃了层次结构,而 return 只是轮廓数组。这意味着如果我们打算使用不同版本的 OpenCV,我们必须自己完成 grabContours
的工作。这只是一个简单的 if else
语句。
res = cv2.findContours(thresh.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# switch for different versions of OpenCV
if len(cnts) == 3:
_, cnts, hierarchy = res
else:
cnts, hierarchy = res
一旦你有 hierarchy
,检查轮廓是否 cnts[i]
是 'innermost' 可以用 hierarchy[0][i][2] < 0
完成,对于轮廓应该是 False
包含其他轮廓。
基于您问题代码的完整示例:
import cv2
import imutils
img = cv2.imread('/Users/kate/Desktop/SegmenterTest/SegmentedCells/Seg1.png')
image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(image, 60, 255, cv2.THRESH_BINARY)[1]
cnts = cv2.findContours(thresh.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# switch for different versions of OpenCV
if len(cnts) == 3:
_, cnts, hierarchy = cnts
else:
cnts, hierarchy = cnts
# loop over the contours
for i, c in enumerate(cnts):
# check that it is 'innermost'
if hierarchy[0][i][2] < 0:
# compute the center of the contour
M = cv2.moments(c)
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, (0, 255, 0), 2)
cv2.circle(image, (cX, cY), 7, (255, 255, 255), -1)
cv2.putText(image, "center", (cX - 20, cY - 20),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)
# show the image
cv2.imshow("Image", image)
cv2.waitKey(0)
您可以在您的案例中使用模块 scikit-image 中的函数 regionprops。这是我得到的。
这是我使用的代码。
import cv2
import matplotlib.pyplot as plt
from skimage import measure
import numpy as np
cells = cv2.imread('cells.png',0)
ret,thresh = cv2.threshold(cells,20,255,cv2.THRESH_BINARY_INV)
labels= measure.label(thresh, background=0)
bg_label = labels[0,0]
labels[labels==bg_label] = 0 # Assign background label to 0
props = measure.regionprops(labels)
fig,ax = plt.subplots(1,1)
plt.axis('off')
ax.imshow(cells,cmap='gray')
centroids = np.zeros(shape=(len(np.unique(labels)),2)) # Access the coordinates of centroids
for i,prop in enumerate(props):
my_centroid = prop.centroid
centroids[i,:]= my_centroid
ax.plot(my_centroid[1],my_centroid[0],'r.')
# print(centroids)
# fig.savefig('out.png', bbox_inches='tight', pad_inches=0)
plt.show()
祝你研究顺利!
我是 python 编码的超级新手,需要一些帮助。我能够分割生物组织内的每个细胞轮廓(超级酷!),现在我正在尝试使用以下方法找到组织内每个细胞的质心:
我正在使用此代码:
img = cv2.imread('/Users/kate/Desktop/SegmenterTest/SegmentedCells/Seg1.png')
image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(image, 60, 255, cv2.THRESH_BINARY)[1]
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
# loop over the contours
for c in cnts:
# compute the center of the contour
M = cv2.moments(c)
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, (0, 255, 0), 2)
cv2.circle(image, (cX, cY), 7, (255, 255, 255), -1)
cv2.putText(image, "center", (cX - 20, cY - 20),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)
# show the image
cv2.imshow("Image", image)
cv2.waitKey(0)
但是,当我使用这段代码时,它给出了整个对象的质心,而不是每个单独的对象。
我不知道从这里到哪里去,所以非常感谢向正确的方向轻推!
问题
cv2.findContours
使用的算法有一些不同 'retrieval modes'。这些会影响 returned 的轮廓以及 returned 的方式。这已记录在案 here。这些作为 findContours
的第二个参数给出。您的代码使用 cv2.RETR_EXTERNAL
,这意味着 findContours
只会 return 单独对象的最外层边框。
解决方案
将此参数更改为 cv2.RETR_LIST
将为您提供图像中的所有轮廓(包括最外层的边框)。这是最简单的解决方案。
例如
import cv2
import imutils
img = cv2.imread('/Users/kate/Desktop/SegmenterTest/SegmentedCells/Seg1.png')
image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(image, 60, 255, cv2.THRESH_BINARY)[1]
cnts = cv2.findContours(thresh.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
# loop over the contours
for c in cnts:
# compute the center of the contour
M = cv2.moments(c)
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, (0, 255, 0), 2)
cv2.circle(image, (cX, cY), 7, (255, 255, 255), -1)
cv2.putText(image, "center", (cX - 20, cY - 20),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)
# show the image
cv2.imshow("Image", image)
cv2.waitKey(0)
只选择最里面的对象
要可靠地忽略外部轮廓,您可以利用 findContours
的功能来 return 它检测到的轮廓的层次结构。为此,您可以再次将检索模式参数更改为 RETR_TREE
,这将生成完整的层次结构。
层次结构是一个数组,其中包含图像中每个轮廓的 4 个值的数组。每个值都是轮廓数组中轮廓的索引。来自 docs:
For each i-th contour
contours[i]
, the elementshierarchy[i][0]
,hierarchy[i][1]
,hierarchy[i][2]
, andhierarchy[i][3]
are set to 0-based indices in contours of the next and previous contours at the same hierarchical level, the first child contour and the parent contour, respectively. If for the contour i there are no next, previous, parent, or nested contours, the corresponding elements ofhierarchy[i]
will be negative.
当我们说 'innermost' 时,我们的意思是没有子级的轮廓(它们内部的轮廓)。所以我们想要那些在层次结构中的条目具有负的第三值的轮廓。即 contours[i]
,使得 hierarchy[i][2] < 0
一个小问题是,尽管 findContours
return 是一个包含层次结构的元组,但 imutils.grabContours
丢弃了层次结构,而 return 只是轮廓数组。这意味着如果我们打算使用不同版本的 OpenCV,我们必须自己完成 grabContours
的工作。这只是一个简单的 if else
语句。
res = cv2.findContours(thresh.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# switch for different versions of OpenCV
if len(cnts) == 3:
_, cnts, hierarchy = res
else:
cnts, hierarchy = res
一旦你有 hierarchy
,检查轮廓是否 cnts[i]
是 'innermost' 可以用 hierarchy[0][i][2] < 0
完成,对于轮廓应该是 False
包含其他轮廓。
基于您问题代码的完整示例:
import cv2
import imutils
img = cv2.imread('/Users/kate/Desktop/SegmenterTest/SegmentedCells/Seg1.png')
image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(image, 60, 255, cv2.THRESH_BINARY)[1]
cnts = cv2.findContours(thresh.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# switch for different versions of OpenCV
if len(cnts) == 3:
_, cnts, hierarchy = cnts
else:
cnts, hierarchy = cnts
# loop over the contours
for i, c in enumerate(cnts):
# check that it is 'innermost'
if hierarchy[0][i][2] < 0:
# compute the center of the contour
M = cv2.moments(c)
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, (0, 255, 0), 2)
cv2.circle(image, (cX, cY), 7, (255, 255, 255), -1)
cv2.putText(image, "center", (cX - 20, cY - 20),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)
# show the image
cv2.imshow("Image", image)
cv2.waitKey(0)
您可以在您的案例中使用模块 scikit-image 中的函数 regionprops。这是我得到的。
这是我使用的代码。
import cv2
import matplotlib.pyplot as plt
from skimage import measure
import numpy as np
cells = cv2.imread('cells.png',0)
ret,thresh = cv2.threshold(cells,20,255,cv2.THRESH_BINARY_INV)
labels= measure.label(thresh, background=0)
bg_label = labels[0,0]
labels[labels==bg_label] = 0 # Assign background label to 0
props = measure.regionprops(labels)
fig,ax = plt.subplots(1,1)
plt.axis('off')
ax.imshow(cells,cmap='gray')
centroids = np.zeros(shape=(len(np.unique(labels)),2)) # Access the coordinates of centroids
for i,prop in enumerate(props):
my_centroid = prop.centroid
centroids[i,:]= my_centroid
ax.plot(my_centroid[1],my_centroid[0],'r.')
# print(centroids)
# fig.savefig('out.png', bbox_inches='tight', pad_inches=0)
plt.show()
祝你研究顺利!