Python:获取数组所有掩码的最快方法

Python: Fastest way to get all masks for an array

有没有比循环遍历二维数组的所有组件以获取特定范围内所有可能的掩码更快的方法,例如:

import numpy as np
numOfLabels = 80
array2D = np.random.choice(255,(512,512))
for i in range(1,numOfLabels):
    mask = array2D==i

可能与广播和创建包含所有掩码的 3d 阵列有关

编辑:

感谢您已经写好的答案。 为了更好的解释。我想做什么:

我有一个带有组件的二维标签矩阵。组件标有数字,比方说 1 到 80。我有两张图片。现在我想计算所有 80 个组件的这两个图像的平均值、最大值、最小值。也许我当时的想法完全错误。

编辑2:

根据评论我找到了一个方法,代码如下:

from scipy import ndimage
import numpy as np

def calculateMeanMaxMin(val):
    return np.array([np.mean(val),np.max(val),np.min(val)])

def getTheStatsForComponents(array1,array2):
    ret, thresholded= cv2.threshold(array2, 50, 255, cv2.THRESH_BINARY)
    thresholded= thresholded.astype(np.uint8)
    numLabels, labels, stats, centroids = cv2.connectedComponentsWithStats(thresholded, 8, cv2.CV_8UC1)
    allComponentStats=[]
    meanmaxminArray2 = ndimage.labeled_comprehension(array2, labels, np.arange(1, numLabels+1), calculateMeanMaxMin, np.ndarray, 0)
    meanmaxminArray1 = ndimage.labeled_comprehension(array1, labels, np.arange(1, numLabels+1), calculateMeanMaxMin, np.ndarray, 0)
    for position, label in enumerate(range(1, numLabels)):
        currentLabel = np.uint8(labels== label)
        _, contour, _ = cv2.findContours(currentLabel, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
        (side1,side2)=cv2.minAreaRect(contour[0])[1]
        componentStat = stats[label]
        allstats = {'position':centroids[label,:],'area':componentStat[4],'height':componentStat[3],
                              'width':componentStat[2],'meanArray1':meanmaxminArray1[position][0],'maxArray1':meanmaxminArray1[position][1],
                              'minArray1':meanmaxminArray1[position][2],'meanArray2':meanmaxminArray2[position][0],'maxArray2':meanmaxminArray2[position][1],
                              'minArray2':meanmaxminArray2[position][2]}
        allComponentStats.append(allstats)
    return allComponentStats

但我想知道是否有更快的方法将所有组件的所有统计信息写入字典,因为我不知道 ndimage.measurements.labeled_comprehension 是否快,以及我是否可以通过某种方式避免这个循环?

array2D==i给你array2D == i的位置,虽然我不明白你在做什么,你可以通过

修改代码
import numpy as np
numOfLabels = 80
array2D = np.random.choice(255,(512,512))
mask = np.zeros((512,512,numOfLabels))
for i in range(numOfLabels):
    mask[array2D==i, i] = 1

您可以使用np.put_along_axis一次创建所有蒙版。所有未标记的点都进入零平面:

all_masks = np.zeros((numOfLabels, *array2D.shape), bool)
np.put_along_axis(all_masks, array2D*(array2D<numOfLabels)[None], True, axis=0)

# check
for i in range(1,numOfLabels):
    assert (all_masks[i] == (array2D==i)).all()

这将使用广播创建 3-D 掩码数组:

mask = array2D == np.arange(1, numOfLabels)[:, None, None]

例如

In [26]: numOfLabels = 80                                                                                          

In [27]: array2D = np.random.choice(255, (512, 512))                                                               

In [28]: mask = array2D == np.arange(1, numOfLabels)[:, None, None]                                                

In [29]: mask.shape                                                                                                
Out[29]: (79, 512, 512)

In [30]: array2D[:10, :13]                                                                                         
Out[30]: 
array([[111, 161, 109, 235, 193, 224,  63,  78, 106, 245, 140,  64,  28],
       [245, 239, 225,  31, 239, 212, 137,  17, 130, 185, 110,  70,  55],
       [251,  65, 114, 199, 229, 241,  97,  84,  13,  10,   4, 114, 185],
       [217, 252, 205,  94,  93, 202,  99,  91,  65,  34,  86,  84, 233],
       [115,  51, 217, 105, 187,  61, 203,  30, 178, 178, 183, 193, 231],
       [ 75, 195,  21, 143, 180,  32,  38, 224, 188,  85,  80, 250,   4],
       [163, 174,  35,  49, 202, 110, 223, 209,  80,  38, 127,  31, 208],
       [137, 133,  41,  30, 193, 187, 182, 171,  72, 151, 180, 152,  14],
       [145, 108, 112, 254,  92,  87,  45, 173,  45,  28, 189, 248,  48],
       [147, 222,  37,  31, 198,  69,   8, 136,  85, 162, 225, 203,  68]])

注意:我从您的代码中复制了标签范围,您在此处以 1 开始范围。这意味着标签 4 的掩码将位于索引 3:

In [31]: mask[3, :10, :13].astype(int)  # Display bools as ints for compact output                                                                               
Out[31]: 
array([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])