使用PIL和NumPy将图像转换为Lab数组,修改值后再转换回去

15 投票
2 回答
19142 浏览
提问于 2025-04-16 01:10

我正在尝试使用NumPy把一个PIL图像转换成数组。然后我想把这个数组转换成Lab值,修改这些值,再把数组转换回图像并保存。我的代码如下:

import Image, color, numpy

# Open the image file
src = Image.open("face-him.jpg")

# Attempt to ensure image is RGB
src = src.convert(mode="RGB")

# Create array of image using numpy
srcArray = numpy.asarray(src)

# Convert array from RGB into Lab
srcArray = color.rgb2lab(srcArray)

# Modify array here

# Convert array back into Lab
end = color.lab2rgb(srcArray)

# Create image from array
final = Image.fromarray(end, "RGB")

# Save
final.save("out.jpg")

这段代码依赖于PIL、NumPy和color库。你可以在SciPy的源代码中找到color,这里有链接。我下载了color.py文件以及一些colordata .txt文件。我修改了color.py,使它可以独立于SciPy源代码运行,运行起来似乎一切正常——当我进行转换时,数组中的值会改变。

我的问题是,当我运行上面的代码,简单地把图像转换成Lab,然后再转换回RGB并保存时,得到的图像是这样的:

alt text

这到底出了什么问题?是因为我在使用color.py中的函数吗?

供参考:
源图像 - face-him.jpg
测试所需的所有源文件 - colour-test.zip

2 个回答

7

正如Denis所提到的,lab2rgbrgb2lab这两个函数没有进行范围检查,而rgb2lab似乎是期待输入的值在[0,1]这个范围内。

>>> a = numpy.array([[1,2,3],[4,5,6],[7,8,9]])
>>> a
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])
>>> color.lab2rgb(color.rgb2lab(a))
array([[ -1.74361805e-01,   1.39592186e-03,   1.24595808e-01],
       [  1.18478213e+00,   1.15700655e+00,   1.13767806e+00],
       [  2.62956273e+00,   2.38687422e+00,   2.21535897e+00]])
>>> from __future__ import division
>>> b = a/10
>>> b
array([[ 0.1,  0.2,  0.3],
       [ 0.4,  0.5,  0.6],
       [ 0.7,  0.8,  0.9]])
>>> color.lab2rgb(color.rgb2lab(a))
array([[ 0.1,  0.2,  0.3],
       [ 0.4,  0.5,  0.6],
       [ 0.7,  0.8,  0.9]])

在color.py文件中,xyz2lablab2xyz这两个函数在做一些我一时看不懂的数学运算(我对numpy或者图像转换不太熟悉)。

编辑(这段代码解决了问题):

PIL给出的数字范围是[0,255],在传递给rgb2lab函数之前,试着把这些数字缩放到[0,1]的范围,然后在输出时再放大回来。例如:

#from __future__ import division # (if required)
[...]
# Create array of image using numpy
srcArray = numpy.asarray(src)/255

# Convert array from RGB into Lab
srcArray = color.rgb2lab(srcArray)

# Convert array back into Lab
end = color.lab2rgb(srcArray)*255
end = end.astype(numpy.uint8)
10

在没有尝试之前,转换颜色时常常会出现缩放错误:
RGB的范围是0到255,比如黄色是[255,255,0],而rgb2xyz()等函数处理的是浮点数的三元组,黄色则表示为[1.,1.,0]。
color.py没有范围检查:lab2rgb( rgb2lab([255,255,0]) )的结果是无效的。)

在IPython中,输入%run main.py,然后打印srcArray的角落和结束?

补充说明:为了记录和方便搜索,这里有一些NumPy的用法,可以用来打包、解包和转换RGB图像数组:

    # unpack image array, 10 x 5 x 3 -> r g b --
img = np.arange( 10*5*3 ).reshape(( 10,5,3 ))
print "img.shape:", img.shape
r,g,b = img.transpose( 2,0,1 )  # 3 10 5
print "r.shape:", r.shape

    # pack 10 x 5 r g b -> 10 x 5 x 3 again --
rgb = np.array(( r, g, b )).transpose( 1,2,0 )  # 10 5 3 again
print "rgb.shape:", rgb.shape
assert (rgb == img).all()

    # rgb 0 .. 255 <-> float 0 .. 1 --
imgfloat = img.astype(np.float32) / 255.
img8 = (imgfloat * 255).round().astype(np.uint8)  
assert (img == img8).all()

撰写回答