Python中与label2idx MATLAB函数等效的函数

3

是否有 Python 中 label2idx() 函数的库实现?

我希望将标签表示中的超像素提取到与 label2idx() 函数返回的格式完全相同的格式中。

label2idx 函数:


你可能正在寻找 np.where(label_arr == label_value) - norok2
@norok2 不完全是这样。我必须循环遍历所有标签值,并基本上实现label2idx。我对库的实现感兴趣,以获得更清晰和更快的代码。 - Ansh Khurana
你是对的,你必须先使用.ravel()函数对label_arr进行展平操作,然后循环遍历标签值。 - norok2
2个回答

4

假设有一个标签数组label_arr,其中包含从1max(label_arr)的所有标签,你可以这样操作:

def label2idx(label_arr):
    return [
        np.where(label_arr.ravel() == i)[0]
        for i in range(1, np.max(label_arr) + 1)]

如果您希望放宽对所有标签的要求,可以添加一个简单的 if,如下所示:

如果您想放宽所有标签的要求,可以添加一个简单的 if 条件语句,例如:

def label2idx(label_arr):
    return [
        np.where(label_arr.ravel() == i)[0]
            if i in label_arr else np.array([], dtype=int)
        for i in range(1, np.max(label_arr) + 1)]

仅为了复制MATLAB文档中的示例:

import numpy as np
import scipy as sp
import scipy.ndimage

struct_arr = np.array(
    [[1, 1, 1, 0, 0, 0, 0, 0],
     [1, 1, 1, 0, 1, 1, 0, 0],
     [1, 1, 1, 0, 1, 1, 0, 0],
     [1, 1, 1, 0, 0, 0, 0, 0],
     [1, 1, 1, 0, 0, 0, 1, 0],
     [1, 1, 1, 0, 0, 0, 1, 0],
     [1, 1, 1, 0, 0, 1, 1, 0],
     [1, 1, 1, 0, 0, 0, 0, 0]])

label_arr, num_labels = sp.ndimage.label(struct_arr)
# label_arr:
# [[1 1 1 0 0 0 0 0]
#  [1 1 1 0 2 2 0 0]
#  [1 1 1 0 2 2 0 0]
#  [1 1 1 0 0 0 0 0]
#  [1 1 1 0 0 0 3 0]
#  [1 1 1 0 0 0 3 0]
#  [1 1 1 0 0 3 3 0]
#  [1 1 1 0 0 0 0 0]]

def label2idx(label_arr):
    return [
        np.where(label_arr.ravel() == i)[0]
        for i in range(1, np.max(label_arr) + 1)]

pixel_idxs = label2idx(label_arr)

for pixel_idx in pixel_idxs:
    print(pixel_idx)

# [ 0  1  2  8  9 10 16 17 18 24 25 26 32 33 34 40 41 42 48 49 50 56 57 58]
# [12 13 20 21]
# [38 46 53 54]

请注意,由于MATLAB和NumPy之间的差异,您可能无法获得完全相同的结果,其中包括:
  • MATLAB使用Fortran样式矩阵索引和1为基础的索引
  • Python+NumPy使用C样式矩阵索引和0为基础的索引
如果您想获得与MATLAB相同的数字,您可以使用以下方法(请注意额外的.T和+1):
def label2idx_MATLAB(label_arr):
    return [
        np.where(label_arr.T.ravel() == i)[0] + 1
        for i in range(1, np.max(label_arr) + 1)]

2
MATLAB的label2idx函数会输出给定标记图像的扁平化索引(按列主序排序)。
我们可以使用scikit-image内置的regionprops函数从标记图像中获取这些索引。 Scikit-image还为我们提供了一个内置函数来获取标记图像,因此所有操作都可以在同一个软件包中完成。实现代码如下 -
from skimage.measure import label,regionprops

def label2idx(L):
    # Get region-properties for all labels
    props = regionprops(L)

    # Get XY coordinates/indices for each label
    indices = [p.coords for p in props]

    # Get flattened-indices for each label, similar to MATLAB version
    # Note that this is row-major ordered.
    flattened_indices = [np.ravel_multi_index(idx.T,L.shape) for idx in indices]
    return indices, flattened_indices

示例运行 -

# Input array
In [62]: a
Out[62]: 
array([[1, 1, 1, 0, 0, 0, 0, 0],
       [1, 1, 1, 0, 1, 1, 0, 0],
       [1, 1, 1, 0, 1, 1, 0, 0],
       [1, 1, 1, 0, 0, 0, 0, 0],
       [1, 1, 1, 0, 0, 0, 1, 0],
       [1, 1, 1, 0, 0, 0, 1, 0],
       [1, 1, 1, 0, 0, 1, 1, 0],
       [1, 1, 1, 0, 0, 0, 0, 0]])

# Get labeled image
In [63]: L = label(a)

In [64]: idx,flat_idx = label2idx(L)

In [65]: flat_idx
Out[65]: 
[array([ 0,  1,  2,  8,  9, 10, 16, 17, 18, 24, 25, 26, 32, 33, 34, 40, 41,
        42, 48, 49, 50, 56, 57, 58]),
 array([12, 13, 20, 21]),
 array([38, 46, 53, 54])]

如果您需要按列主顺序的索引,就像在MATLAB中一样,只需转置图像,然后输入即可。
In [5]: idx,flat_idx = label2idx(L.T)

In [6]: flat_idx
Out[6]: 
[array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
        17, 18, 19, 20, 21, 22, 23]),
 array([33, 34, 41, 42]),
 array([46, 52, 53, 54])]

请注意,索引仍然从0开始,与MATLAB不同,MATLAB的索引从1开始。
使用SciPy获取标记图像的替代方法
SciPy还内置了一个用于获取标记图像的函数:scipy.ndimage.label
from scipy.ndimage import label

L = label(a)[0]

我认为可以使用np.where()以更简单(可能更快)的方式解决这个问题。 - norok2
这不是MATLAB文档所期望的行为。MATLAB函数返回一个单元格数组——这是像素ID(按列主序的线性索引)的集合。 - Ansh Khurana
@AnshKhurana Python或NumPy没有任何单元数组的概念。最接近的是基于NumPy的索引数组列表,这就是我们在这里得到的。您是否期望其他类型的输出格式? - Divakar
如果您需要按列主序的方式,请先转置标记图像,然后将其提供给所提出的解决方案。在帖子中添加了相应的示例代码。 - Divakar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接