基于字典的三维数组到二维数组的转换

2024-03-28 11:40:20 发布

您现在位置:Python中文网/ 问答频道 /正文

这是我根据字典映射将彩色图像转换为灰度的代码

M, N = color.shape[:2]
out = np.zeros((M, N))
for i in range(M):
    for j in range(N):
        out[i][j] = color2ind[tuple(color[i,j,:])]

例如字典是:

^{pr2}$

什么是更像Python的方式来做这件事?在


Tags: 代码infor字典npzerosrangeout
2条回答

dict是从键到值的映射。NumPy数组也可以用作 价值观的关键。例如

In [11]: dct = {3:40, 2:30, 1:20, 0:10}

In [9]: arr = np.array([10,20,30,40])

In [12]: arr[3]
Out[12]: 40

In [13]: dct[3]
Out[13]: 40

dict更灵活,它的键可以是任何散列对象。阵列 必须用整数索引。但阵列可能更适合于纽姆比 设置,因为数组本身可以由整数数组索引:

^{pr2}$

而使用dict的等价物需要一个循环:

In [17]: [dct[i] for i in index]
Out[17]: [40, 30, 20, 10]

整数索引比循环中的dict查找快得多:

In [19]: %timeit arr[index]
1000000 loops, best of 3: 201 ns per loop

In [20]: %timeit [dct[i] for i in index]
1000000 loops, best of 3: 1.63 µs per loop

dicts和NumPy数组之间的这种粗略的等价性是 激发下面的方法。剩下的代码就是要克服的 例如没有整数键(您将看到这是通过使用 np.uniquereturn_inverse=True以获得唯一的整数标签。)


假设您有这样的设置:

import numpy as np

color = np.array([
    [  0,   0,   0],
    [128,   0, 128],
    [  0, 128, 128],
    [  0,   0, 128],
    [  0, 128,   0],
    [128, 128,   0],
    [128, 128, 128],
    [128,   0,   0],], dtype='uint8').reshape(-1,2,3)

color2ind = {(128, 128, 128): 6, 
             (0, 128, 128): 2, 
             (128, 0, 128): 1, 
             (128, 0, 0): 7, 
             (128, 128, 0): 5, 
             (0, 0, 128): 3, 
             (0, 128, 0): 4, 
             (0, 0, 0): 0}

然后:

def rgb2int(arr):
    """
    Convert (N,...M,3)-array of dtype uint8 to a (N,...,M)-array of dtype int32
    """
    return arr[...,0]*(256**2)+arr[...,1]*256+arr[...,2]

def rgb2vals(color, color2ind):
    int_colors = rgb2int(color)
    int_keys = rgb2int(np.array(color2ind.keys(), dtype='uint8'))
    int_array = np.r_[int_colors.ravel(), int_keys]
    uniq, index = np.unique(int_array, return_inverse=True)
    color_labels = index[:int_colors.size]
    key_labels = index[-len(color2ind):]

    colormap = np.empty_like(int_keys, dtype='uint32')
    colormap[key_labels] = color2ind.values()
    out = colormap[color_labels].reshape(color.shape[:2])
    return out

print(rgb2vals(color, color2ind))

收益率

[[0 1]
 [2 3]
 [4 5]
 [6 7]]

(数字是按顺序排列的;color被选中,所以答案很容易检查。)


这是一个基准测试,显示使用NumPy索引的rgb2vals要快得多 而不是使用双for循环:

def using_loops(color, color2ind):
    M, N = color.shape[:2]
    out = np.zeros((M, N))
    for i in range(M):
        for j in range(N):
            out[i][j] = color2ind[tuple(color[i,j,:])]
    return out

In [295]: color = np.tile(color, (100,100,1))

In [296]: (rgb2vals(color, color2ind) == using_loops(color, color2ind)).all()
Out[296]: True

In [297]: %timeit rgb2vals(color, color2ind)
100 loops, best of 3: 6.74 ms per loop

In [298]: %timeit using_loops(color, color2ind)
1 loops, best of 3: 751 ms per loop

第一步是通过将每个(r,g,b)三元组转换为单个int,将color缩减为二维数组:

In [270]: int_colors = rgb2int(color)
In [270]: int_colors
Out[270]: 
array([[      0, 8388736],
       [  32896,     128],
       [  32768, 8421376],
       [8421504, 8388608]], dtype=uint32)

现在我们对color2inddict中的(r,g,b)三元组键执行相同的操作:

In [271]: int_keys = rgb2int(np.array(color2ind.keys(), dtype='uint8'))
In [271]: int_keys
Out[271]: 
array([8388608, 8421504, 8388736, 8421376,     128,       0,   32768,
         32896], dtype=uint32)

将这两个数组串联起来,然后使用np.unique来查找反向索引:

In [283]: int_array = np.r_[int_colors.ravel(), int_keys]

In [284]: uniq, index = np.unique(int_array, return_inverse=True)

In [285]: index
Out[285]: array([0, 5, 3, 1, 2, 6, 7, 4, 4, 7, 5, 6, 1, 0, 2, 3])

In [286]: uniq
Out[286]: 
array([      0,     128,   32768,   32896, 8388608, 8388736, 8421376,
       8421504], dtype=uint32)

uniq保存int_colorsint_keys中的唯一值。 index保存索引值,这样uniq[index] = int_array

In [265]: (uniq[index] == int_array).all()
Out[265]: True

一旦我们有了index,我们就是黄金。index中的值类似于标签,每个标签都与特定的颜色相关联。index中的第一个color.size项是color中颜色的标签,index中最后一个len(color2ind)项是{}中键的标签。在

color_labels = index[:int_colors.size]
key_labels = index[-len(color2ind):]

现在我们只需要用color2ind.values()中的值创建一个数组,colormap,这样键标签就映射到这些值:

colormap[key_labels] = color2ind.values()

通过将color2ind中的值放在与 关联键标签,我们创建一个colormap数组,它可以有效地执行 就像dict一样,colormap[color_labels]将颜色标签映射到color2ind值,这正是我们想要的:

out = colormap[color_labels].reshape(color.shape[:2])

In [267]: out
Out[267]: 
array([[7, 6],
       [1, 5],
       [3, 0],
       [4, 2]], dtype=uint32)

通过使用逻辑索引,您将获得更快的结果。这里有一种方法可以实现:

>>> A  # display test array that has all possibilities
array([[[   0,    0,    0],
        [   0,    0,  128],
        [   0,  128,    0],
        [   0,  128,  128]],

       [[ 128,    0,    0],
        [ 128,    0,  128],
        [ 128,  128,    0],
        [ 128,  128,  128]]])

>>> B = (A[:,:,2]*4 + A[:,:,1]*2 + A[:,:,0])/128 # Notice the typical binary tricks
>>> B
array([[ 0,  4,  2,  6],
       [ 1,  5,  3,  7]])
>>> color2ind = {7: 6, 3:2, 5:1, 4:7, 6:5, 1:3, 2:4, 0:0} # reinterpretation as binary coded of your map
>>> C = np.empty_like(B)
>>> for k,v in color2ind.items():
...    C[B == k] = v
...
>>> C
array([[ 0,  7,  4,  5],
       [ 3,  1,  2,  6]])

注意:它假设数组中的值只有0和128,这两个值会出现在您自己的代码和color2ind映射中。在

编辑:阿巴内特的评论是正确的。索引m x n x 3数组仍然非常快速,并且可能比这种彩色编码的2D数组更适合进一步的图像处理。不过,如果这真的是你想要的,这不是一个糟糕的方法。在

相关问题 更多 >