这是我的代码,我根据字典映射将彩色图像转换为灰度
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方法是什么?
答案 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_colors
和int_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数组更适合进一步的图像处理。不过,如果这真的是你想要的,这不是一个糟糕的方法。