使用Numpy数组操作将RGB格式转换为十六进制

3

我的目标是尽可能快地将RGB像素列表转换为十六进制。数据格式为Numpy的多维数组(RGB色彩空间),理想情况下应该将其从RGB转换为Hex并保持其形状。

我的尝试使用列表推导式来解决这个问题,但除了性能之外,它已经成功了。在性能方面,添加ravel和列表推导式真的会使速度变慢。不幸的是,我不知道足够的数学知识来加速此过程:

编辑:更新代码以反映最新更改。当前在35,000像素图像上运行约24ms。

def np_array_to_hex(array):
    array = np.asarray(array, dtype='uint32')
    array = (1 << 24) + ((array[:, :, 0]<<16) + (array[:, :, 1]<<8) + array[:, :, 2])
    return [hex(x)[-6:] for x in array.ravel()]

>>> np_array_to_hex(img)
['afb3bc', 'abaeb5', 'b3b4b9', ..., '8b9dab', '92a4b2', '9caebc']

https://docs.python.org/3/library/functions.html#hex 是 Python 的 hex 函数。它返回以 0x 开头的字符串。int(..., 16) 可以将其转换回整数。至今没有相应的 numpy 功能(据我所知)。您可以将此函数应用于数组的每个元素。 - hpaulj
@hpaulj 是的,正如您在问题中所看到的那样,我已经在使用它了。我的问题更多地是关于将该函数应用于numpy数组操作。 - stwhite
np.frompyfunc(hex,1,1)(arr) . Another is np.frompyfunc('0x{:07X}'.format,1,1)(arr) - hpaulj
@stwhite 你可以在这里浏览答案,看看它们是否能帮助你找到解决方案:https://dev59.com/N3A75IYBdhLWcg3wSm64#43572620 - user11563547
请问您为什么想这样做呢?看起来您想将其转换为十六进制以供人类使用/分析,因此人们不太可能注意到某些东西是否在23ms或48ms内准备好... 顺便问一下,您的数组有多大,最快的方法需要多长时间? - Mark Setchell
@MarkSetchell 这段代码用于在离线过程中从大约一百万张图像中提取颜色。因此,尽管它是离线的,时间仍然很重要。最新的代码使用此代码运行时间为24毫秒(使用最新的工作代码更新了问题)。这些图像被缩小到大约200像素,留下大约40k个像素(根据图像大小而变化)。您说得很对,我正在将其转换为可读的6位十六进制格式,但我可能不需要对完整的35k列表执行此操作,因为在此之后会进行去重处理。 - stwhite
1个回答

0

我试过使用一种称为 LUT(“查找表”)的方法 - 它需要几秒钟来初始化,且使用了 100MB (0.1GB)的RAM,但这是一个很小的代价,可以在处理百万张图片时摊销开来:

#!/usr/bin/env python3

import numpy as np

def np_array_to_hex1(array):
    array = np.asarray(array, dtype='uint32')
    array = ((array[:, :, 0]<<16) + (array[:, :, 1]<<8) + array[:, :, 2])
    return array

def np_array_to_hex2(array):
    array = np.asarray(array, dtype='uint32')
    array = (1 << 24) + ((array[:, :, 0]<<16) + (array[:, :, 1]<<8) + array[:, :, 2])
    return [hex(x)[-6:] for x in array.ravel()]

def me(array, LUT):
    h, w, d = array.shape
    # Reshape to a color vector
    z = np.reshape(array,(-1,3))
    # Make array and fill with 32-bit colour number
    y = np.zeros((h*w),dtype=np.uint32) 
    y = z[:,0]*65536 + z[:,1]*256 + z[:,2] 
    return LUT[y] 

# Define dummy image of 35,000 RGB pixels
w,h = 175, 200
im = np.random.randint(0,256,(h,w,3),dtype=np.uint8)

# %timeit np_array_to_hex1(im)
# 112 µs ± 1.1 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

# %timeit np_array_to_hex2(im)
# 8.42 ms ± 136 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

# This may take time to set up, but amortize that over a million images...
LUT = np.zeros((256*256*256),dtype='a6') 
for i in range(256*256*256): 
    h = hex(i)[2:].zfill(6)
    LUT[i] = h

# %timeit me(im,LUT)
# 499 µs ± 8.15 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

看起来这比你最快的慢了4倍,而那个并不起作用,比你最慢的快了17倍。

我的下一个建议是使用多线程或多进程,以便所有CPU核心可以并行工作,并通过4倍或更多的因素减少您的总时间,假设您有一个相当现代的4+核心CPU。


谢谢你的回答,马克!不过我有一个问题...在我的问题中,最后一个例子有什么问题吗?第一个例子(np_array_to_hex1)缺少(1 << 24),所以它仍然是一个整数而不是十六进制,尽管第二个例子修复了这个问题。 - stwhite
我不理解你的意思。你的第一个方法在我的机器上需要0.12毫秒,但是却不能正常工作。你的第二个方法虽然能工作,但是需要8毫秒,而你说这太慢了。因此,我提出了一种需要0.5毫秒的方法。我以为你的问题是关于如何优化那8毫秒,而不是调试它... - Mark Setchell
忽略方法1。本来只应该有一个方法——我已经从问题中消除了混淆。我需要在我的设置中测试你的代码,因为它不仅是针对图像的一个大循环。感谢你的回答! - stwhite

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