基于字典将3D数组转换为2D数组

时间:2014-11-17 22:28:39

标签: python numpy

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

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,:])]

例如,字典是:

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}

这样做的pythonic方法是什么?

2 个答案:

答案 0 :(得分:1)

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更灵活 - 它的键可以是任何可清洗对象。阵列 必须用整数索引。但是阵列在NumPy中可能更合适 设置,因为数组本身可以由整数数组索引:

In [8]: index = np.array([3,2,1,0])

In [10]: arr[index]
Out[10]: array([40, 30, 20, 10])

而使用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.unique' s return_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要快得多 而不是使用双重循环:

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)

现在我们对color2ind dict中的(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中的值与标签类似,每个标签都与特定颜色相关联。 color.size中的第一个index项是color中颜色的标签,len(color2ind)中的最后index项是{{1}中的键的标签}}

color2ind

现在我们只需要使用color_labels = index[:int_colors.size] key_labels = index[-len(color2ind):] 中的值创建一个数组colormap,以便键标签映射到值:

color2ind.values()

colormap[key_labels] = color2ind.values() 中的值放在索引位置等于 关联的键标签,我们创建一个可以实际行动的色彩映射数组 像一个字典。 color2ind将颜色标签映射到colormap[color_labels]值,这正是我们想要的:

color2ind

答案 1 :(得分:0)

通过使用逻辑索引,您将获得更快的结果。这是您实现这一目标的一种方式:

>>> 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映射中显示。

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