用整数替换numpy数组中的RGB值非常慢

时间:2015-10-18 09:03:25

标签: python arrays numpy image-processing rgb

我想将numpy数组的rgb值替换为单个整数表示。我的代码有效,但速度太慢,我正在迭代每个元素。我可以加快速度吗?我是numpy的新手。

from skimage import io

# dictionary of color codes for my rgb values
_color_codes = {
    (255, 200, 100): 1,
    (223, 219, 212): 2,
    ...
}

# get the corresponding color code for the rgb vector supplied
def replace_rgb_val(rgb_v):
    rgb_triple = (rgb_v[0], rgb_v[1], rgb_v[2])
    if rgb_triple in _color_codes:
        return _color_codes[rgb_triple]
    else:
        return -1

# function to replace, this is where I iterate
def img_array_to_single_val(arr):
    return np.array([[replace_rgb_val(arr[i][j]) for j in range(arr.shape[1])] for i in range(arr.shape[0])])


# my images are square so the shape of the array is (n,n,3)
# I want to change the arrays to (n,n,1)
img_arr = io.imread(filename)
# this takes from ~5-10 seconds, too slow!
result = img_array_to_single_val(img_arr)

3 个答案:

答案 0 :(得分:5)

反过来替换颜色值。查找每个RGB三元组,并在新数组中设置相应的索引:

def img_array_to_single_val(arr, color_codes):
    result = numpy.ndarray(shape=arr.shape[:2], dtype=int)
    result[:,:] = -1
    for rgb, idx in color_codes.items():
        result[(arr==rgb).all(2)] = idx
    return result

让我们将颜色索引分配分开:首先arr==rgb将每个像素-rgb值与列表rgb进行比较,得到一个n x n x 3 - 布尔数组。只有当所有三个颜色部分都相同时,我们才会找到匹配项,因此.all(2)会减少最后一个轴,使用n {n - 布尔数组,每个匹配True的像素都会rgb。最后一步是,使用此掩码设置相应像素的索引。

更快,可能是,首先将RGB数组转换为int32,然后进行索引转换:

def img_array_to_single_val(image, color_codes):
    image = image.dot(numpy.array([65536, 256, 1], dtype='int32'))
    result = numpy.ndarray(shape=image.shape, dtype=int)
    result[:,:] = -1
    for rgb, idx in color_codes.items():
        rgb = rgb[0] * 65536 + rgb[1] * 256 + rgb[2]
        result[arr==rgb] = idx
    return result

对于非常大或很多图像,您应首先创建直接颜色映射:

color_map = numpy.ndarray(shape=(256*256*256), dtype='int32')
color_map[:] = -1
for rgb, idx in color_codes.items():
    rgb = rgb[0] * 65536 + rgb[1] * 256 + rgb[2]
    color_map[rgb] = idx

def img_array_to_single_val(image, color_map):
    image = image.dot(numpy.array([65536, 256, 1], dtype='int32'))
    return color_map[image]

答案 1 :(得分:2)

这里可以提出两个完全向量化的解决方案。

方法#1:使用NumPy's powerful broadcasting capability -

# Extract color codes and their IDs from input dict
colors = np.array(_color_codes.keys())
color_ids = np.array(_color_codes.values())

# Initialize output array
result = np.empty((img_arr.shape[0],img_arr.shape[1]),dtype=int)
result[:] = -1

# Finally get the matches and accordingly set result locations
# to their respective color IDs
R,C,D = np.where((img_arr == colors[:,None,None,:]).all(3))
result[C,D] = color_ids[R]

方法#2:使用cdist from scipy.spatial.distance可以替换approach #1的最终步骤,如此 -

from scipy.spatial.distance import cdist

R,C = np.where(cdist(img_arr.reshape(-1,3),colors)==0)
result.ravel()[R] = color_ids[C]

答案 2 :(得分:1)

手动浏览每个像素并创建一个256 ** 3项目的字典只是为了得到另一个调色板对我来说似乎很奇怪,如果你没有追求你想要创建的特定效果。如果您只想将图像展平为整数值,可以使用skimage rg2gray(img)函数。这将为您提供图片亮度。

您可以使用pylabs colormaps获取另一种表示形式:

import matplotlib.pylab as plt
import skimage 
import matplotlib.cm as cm

img = io.imread("Fox.jpg")
gray_img = skimage.color.rgb2gray(img)
plt.imshow(img, cmap=cm.Jet)
plt.show()