scipy 使用python查找图像中的接触标签/对象/遮罩的身份

euoag5mw  于 2022-11-10  发布在  Python
关注(0)|答案(2)|浏览(123)

不幸的是,我找不到任何关于这个主题,所以这里去:
我有一个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,segmentation和scipy,但没有找到合适的函数。
我想知道
1.已经有一个预先存在的函数可以做到这一点(我盲目地忽略了它)
1.可以仅保留argwhere函数中作为掩码的边界像素的位置,从而减少用于检查周围8个像素的输入像素的数量,条件是这些边界像素总是保留其原始值作为标识符的形式。
任何形式的建议都非常感谢!
关于我为什么要这么做的更多背景信息:
我目前正在获取多个细胞在不同时间内的时间流逝。有时细胞分裂后,两个子细胞核粘在一起,可能会被错误分割成一个细胞核或两个细胞核。这种情况很少发生,但我想过滤掉这些细胞在一个或两个掩模之间交替的时间轨迹。我还计算了这些细胞的面积。但是对掩蔽区域中的不合理变化的过滤遇到两个问题:
1.移入(或移出)图像的细胞也会显示这种大小变化,
1.显微镜的失焦也会导致较小的掩膜(当再次获得适当的聚焦时,会导致较大的掩膜)。不幸的是,在时间推移期间,我们的显微镜也会不时发生这种情况。我的想法是在整个时间推移期间获得接触掩膜的身份,以便在过滤此类细胞时有一个更多的标准需要考虑。

ttcibm8c

ttcibm8c1#

skimage.graph.pixel_graph函数会告诉你图像中哪些像素连接到其他像素,你可以用这个图来回答你的问题--我想非常快。
(Note您共享的图像不是分割遮罩,而是值为[0,255]的RGBA灰度图像,因此我无法在下面的分析中使用它。)
第一步:我们建立像素图,为此,我们只想保留两个标签不同的边缘,所以我们传入一个边缘函数,当值不同时返回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数组,其中包含从矩阵索引到像素索引的Map。
该矩阵还包含我们不关心的像素的所有零元素,因此使用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=',')

或者制作将每个细胞核Map到相邻细胞核列表的字典:

from collections import defaultdict
pairs = defaultdict(list)

for i, j in zip(center_values, neighbor_values):
    pairs[i].append(j)

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

piok6c0g

piok6c0g2#

你可以通过skimage.segmentation中的find_boundaries()函数找到遮罩的共享边界。遗憾的是,这也会包括背景边界,但我们可以通过对所有前景像素的遮罩进行xor来过滤掉背景边界。

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

在我的电脑上,这需要大约0.05秒的1000x1000图像和500口罩。
如果需要掩码的值,只需

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

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

相关问题