在Numpy中正确地将sRGB矢量化为线性转换

4

在我的图像编辑应用程序中,我有一个将32位浮点图像从sRGB转换为线性颜色空间的功能。公式如下:

if value <= 0.04045: (value / 12.92)
if value > 0.04045: ((value + 0.055) / 1.055)^2.4)

我的图像是一个三维的numpy.ndarray,名字叫做img32

目前为止,我的实现如下:

boolarray = img32 <= 0.04045
lower = (img32 / 12.92) * boolarray.astype(np.int)
upper = np.power(((img32 + 0.055) / 1.055), 2.4) * np.invert(boolarray).astype(np.int)
img32 = lower + upper

所以,我正在创建一个新的数组boolarray,其中包含小于等于0.04045的真值,并乘以它。

有更好的解决方案吗?

我尝试了类似以下的内容:

img32[img32 < 0.04045] = img32 / 12.92

这个在第一步上运作正常,但在第二步失败了:

img32[img32 >= 0.04045] = np.power(((img32 + 0.055) / 1.055), 2.4)

可能是因为在 np.power 函数中使用时无法工作。

感谢任何帮助。


更好的解决方案 - 更好在哪里?是内存方面还是性能方面? - Divakar
这个似乎有点像 hack,我希望能找到标准的方法来做这件事情,让代码更加简洁。虽然我还没有测试速度(但应该还算快),但是我猜这肯定会浪费内存吧?抱歉表达不够清晰。 - tde
3个回答

3
一种简洁的方法是使用np.where函数,它可以根据掩码(mask)在两个值之间进行选择。在我们的情况下,掩码可以是img32 >= 0.04045,当条件为True时,我们将选择((img32 + 0.055) / 1.055)**2.4,否则选择img32/12.92

因此,我们可以像这样实现 -

np.where( img32 >= 0.04045,((img32 + 0.055) / 1.055)**2.4, img32/12.92 )

如果您非常关心内存并且想将结果写回输入数组,可以通过创建并选择性设置与这两个条件相对应的 elems 来完成三个步骤,如下所示 -

mask = img32 >= 0.04045
img32[mask] = ((img32[mask] + 0.055) / 1.055)**2.4
img32[~mask] = img32[~mask] / 12.92

示例案例 -

In [143]: img32 = np.random.rand(4,5).astype(np.float32)

In [144]: img32.nbytes
Out[144]: 80

In [145]: mask.nbytes
Out[145]: 20

所以,我们避免了创建一个会耗费 80 字节的输出数组,而是使用 20 字节的掩码。因此,在内存上节省了输入数组大小的 75%。请注意,这可能会导致性能略微降低。

1
相反的操作是(线性到sRGB):img = np.where( img < 0.0031308, img * 12.92, 1.055 * (pow(img, (1.0 / 2.4))) - 0.055) - João Cartucho

1
您可以使用numpy.piecewise
In [11]: img32 = np.random.rand(800, 600).astype(np.float32)

In [12]: img_linear = np.piecewise(img32, 
           [img32  <= 0.04045, img32 > 0.04045], 
           [lambda v: v/12.92, lambda v: ((v + 0.055)/1.055)**2.4] )

In [13]: img_linear.shape
Out[13]: (800, 600)

In [14]: img_linear.dtype
Out[14]: dtype('float32')

1
谢谢!我现在正在使用np.where,但稍后我会比较执行速度,看看哪种方法最快、最有效。 - tde

0
b = (img32 < 0.04045)
img32[b] /= 12.92

not_b = numpy.logical_not(b)
img32[not_b] += 0.05
img32[not_b] /= 1.055
img32[not_b] **= 2.4

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