使用python查找图像中触摸标签/对象/掩码的身份



不幸的是,我找不到任何关于这个话题的东西,所以这里是:

我有一个图像作为numpy数组,包含不同细胞核的掩码作为整数,看起来像这样:

https://i.stack.imgur.com/nn8hG.png

单个蒙版有不同的值,背景为0。现在,对于该图像中的每个遮罩,我想获得其他触摸遮罩的身份(如果有的话)。到目前为止,我所拥有的是获得每个掩码值的像素位置的代码(通过argwhere函数),并检查8个周围像素中的任何像素是否不为0或其自己的值。

for i in range(1, np.max(mask_image+1)):
coordinates = np.argwhere(mask_image==i)
touching_masks = []
for pixel in coordinates:

if mask_image[pixel[0] + 1, pixel[1]] != 0 and mask_image[pixel[0] + 1, pixel[1]] != i:
touching_masks.append(mask_image[pixel[0] + 1, pixel[1]]) #bottom neighbour

elif mask_image[pixel[0] -1, pixel[1]] != 0 and mask_image[pixel[0] -1, pixel[1]] != i:
touching_masks.append(mask_image[pixel[0] -1, pixel[1]]) #top neighbour

elif mask_image[pixel[0], pixel[1]-1] != 0 and mask_image[pixel[0], pixel[1]-1] != i:
touching_masks.append(mask_image[pixel[0], pixel[1]-1]) #left neighbour

elif mask_image[pixel[0], pixel[1] + 1] != 0 and mask_image[pixel[0], pixel[1] + 1] != i:
touching_masks.append(mask_image[pixel[0], pixel[1] + 1]) #right neighbour

elif mask_image[pixel[0] + 1, pixel[1] + 1] != 0 and mask_image[pixel[0] + 1, pixel[1] + 1] != i:
touching_masks.append(mask_image[pixel[0] + 1, pixel[1] + 1]) #bottom-right neighbour

elif mask_image[pixel[0] - 1, pixel[1] - 1] != 0 and mask_image[pixel[0] - 1, pixel[1] - 1] != i:
touching_masks.append(mask_image[pixel[0] - 1, pixel[1] - 1]) #top-left neighbour

elif mask_image[pixel[0] + 1, pixel[1] - 1] != 0 and mask_image[pixel[0] + 1, pixel[1] - 1] != i:
touching_masks.append(mask_image[pixel[0] + 1, pixel[1] - 1]) #bottom-left neighbour

elif mask_image[pixel[0] - 1, pixel[1] + 1] != 0 and mask_image[pixel[0] - 1, pixel[1] + 1] != i:
touching_masks.append(mask_image[pixel[0] - 1, pixel[1] + 1]) #top-right neighbour

由于我每张图像大约有500个遮罩和大约200个图像的时间序列,这是非常慢的,我想改进它。我尝试了一些regionprops和skimage。分割和scipy,但没有找到合适的功能。

我想知道是否

  1. 已经有一个预先存在的功能可以做到这一点(我盲目地忽略了)
  2. 只能保留argwhere函数的位置,这些位置是掩码的边界像素,从而减少用于检查周围8个像素的输入像素的数量。条件是这些边界像素始终保留其原始值作为标识符的形式。

任何建议都是非常感谢的!

关于我为什么要这样做的更多背景信息:

我目前正在获取多个细胞在不同小时内的时间周期。有时,在细胞分裂后,两个子核粘在一起,可以分裂成一个核,也可以分裂成两个核。这种情况很少发生,但我想过滤掉这种在一个或两个掩模之间交替的细胞的时间轨迹。我也计算了这些单元格的面积,但是过滤不合理的掩模面积变化会遇到两个问题:

  1. 徘徊在图像内(或外)的单元格也可以显示这样的大小变化和
  2. 显微镜的失焦也会导致更小的掩模(当再次达到适当的焦点时,会导致更大的掩模)。不幸的是,在这段时间内,我们的显微镜也会时不时地发生这种情况。我的想法是在整个时间间隔中获得触摸口罩的身份,以便在过滤掉此类细胞时考虑更多标准。

skimage.graph.pixel_graph函数将告诉您图像中的哪些像素连接到其他像素。你可以用这个图表来回答你的问题——我想得很快。

(请注意,您共享的图像不是分割蒙版,而是值为[0,255]的RGBA灰度图像,所以我不能在下面的分析中使用它。)

步骤1:我们构建像素图。为此,我们只想保留两个标签不同的边,所以我们传入一个边函数,当值不同时返回1.0,否则返回0.0。

import numpy as np
from skimage.graph import pixel_graph

def different_labels(center_value, neighbor_value, *args):
return (center_value != neighbor_value).astype(float)

label_mask = ... # load your image here
g, nodes = pixel_graph(
label_mask,
mask=label_mask.astype(bool),
connectivity=2,  # count diagonals in 2D
)

现在,您需要知道g是scipy.sparse.csr_matrix格式,并且行索引对应于图像中的所有非零像素。为了回到实际的图像位置,您需要nodes数组,它包含从矩阵索引到像素索引的映射。

矩阵还包含我们不关心的像素的所有零条目,因此使用scipy.sparse.csr_matrix.eliminate_zeros()方法去除它们。

为了获得不同像素对,我们将矩阵转换为COO矩阵,然后我们获取相应的图像坐标并获取值:

g.eliminate_zeros()
coo = g.tocoo()
center_coords = nodes[coo.row]
neighbor_coords = nodes[coo.col]
center_values = label_mask.ravel()[center_coords]
neighbor_values = label_mask.ravel()[neighbor_coords]

现在我们有(i, j)对相互接触的原子核。(它们有些随意地排列成中心/邻居。同样,这对同时以(i, j)和(j, i)的形式出现。你可以对这些数组做任何你想做的事情,例如,将它们保存到一个文本文件中:

touching_masks = np.column_stack((center_values, neighbor_values))
np.savetxt('touching_masks.txt', touching_masks, delimiter=',')

或创建一个字典,将每个原子核映射到相邻的原子核列表:

from collections import defaultdict
pairs = defaultdict(list)
for i, j in zip(center_values, neighbor_values):
pairs[i].append(j)

一般来说,最好尽量避免遍历像素,而使用NumPy矢量化操作。pixel_graph函数的源代码可以为如何考虑这类问题提供进一步的启发!

您可以通过例如使用skimage.segmentation中的find_boundaries()函数来查找掩码的共享边界。遗憾的是,这也将包括背景边界,但我们可以过滤掉,通过xor与所有前景像素的蒙版。

a = find_boundaries(mask_image)
b = find_boundaries(mask_image != 0)
touching_masks = np.logical_xor(a, b)

在我的电脑上,1000x1000的图像和500个蒙版大约需要0.05秒。

如果你想要遮罩的值,你可以取

mask_values = mask_image.copy()
mask_values[~touching_masks] = 0

并使用您的代码查找相邻的值。

最新更新