PIL的Image.convert()函数如何与模式“ P”一起使用

时间:2019-07-10 03:07:03

标签: python image image-processing python-imaging-library

我有一组24位png文件,我想将它们转换为8位png文件。我使用PIL的Image.convert()方法来解决此问题。但是,在使用模式“ P”作为参数之后,我发现具有相同RGB值的像素可以进行不同的转换。

我将示例图像转移到numpy数组中,原始的24位png文件具有如下值:

  

RGB阵列

   ..., 
   [204, 102, 119],
   [204, 102, 119],
   [204, 102, 119],
   [204, 102, 119],
   [204, 102, 119],
   [204, 102, 119],
   [204, 102, 119],
   [204, 102, 119],
   [204, 102, 119],
   [204, 102, 119],
   [204, 102, 119], 
   ...

在模式“ P”下使用转换功能后,图像值变为:

  

8位数组

   ..., 98, 98, 134, 98, 98, 98, 134, 98, 98, 98, 134, ...

代码示例:

from PIL import Image
import numpy as np
img = Image.open("path-to-file.png")
p_img = img.convert("P")

我希望具有相同RGB值的像素以相同的方式转换。我知道像素会转换为调色板索引,但这对我来说仍然没有意义。我对PIL库不熟悉。有人可以解释为什么会这样吗?预先感谢。


按照Mark的示例进行了一些修改

import numpy as np
from PIL import Image
#from improc import GenerateNColourImage

# Set image height and width
N    = 6
h, w = 100, 100

# Generate repeatable random Numpy image with N^3 unique colours at most
n = np.random.randint(N, size=(h, w, 3), dtype=np.uint8)
# Work out indices of diagonal elements
diags = np.diag_indices(h)

# Intentionally set all diagonal elements same shade of blue
n[diags] = [10,20,200]
# Make Numpy image into PIL Image, palettise, convert back to Numpy array and check diagonals
a0 = Image.fromarray(n)

unique_colors = np.unique(n.reshape(-1, n.shape[2]), axis=0).shape
print(unique_colors)   #e.g. print (217, 3)
a1 = a0.convert('P')
a2 = np.array(a1)

# Look at diagonals - should all be the same
print(a2[diags])
print(' %s %d' % ("Number of unique colors:  ", np.unique(a2).shape[0]))
  

打印对角像素的值

... 154 154 154 154 154 154 124 154 160 160 160 154 160 ...

“ P”模式下的8位图像包含125个唯一的调色板索引。看来PIL无论如何都会执行抖动处理。

2 个答案:

答案 0 :(得分:1)

当我们将图像转换为P色彩模式时,这是正常现象。调色板模式的工作方式是创建一个映射表,该表将索引(在0-255范围内)对应于较大颜色空间(如RGB)中的离散颜色。例如,图像中的RGB颜色值(0、0、255)(纯蓝色)的索引为1(只是一个假设的例子)。原始图像中的每个唯一像素值都会经过相同的过程(但在映射过程中表大小不应超过256)。因此,numpy数组(或常规列表)的值如下:-

   ..., 98, 98, 134, 98, 98, 98, 134, 98, 98, 98, 134, ...

对应于映射表中的索引,而不是实际的颜色值本身。因此,您可以将它们解释为一个索引,该索引在读取图像后会转换为存储在该索引中的实际颜色值。

但是这些像素值不一定总是意味着图像处于彩色模式P。例如,如果您查看灰度图像(L)的像素数据,则这些值在调色板模式下看起来相同,但实际上与真实的颜色值(或灰色阴影)相对应,而不是索引。

答案 1 :(得分:1)

问题在于PIL /枕头是否为“抖动” 。基本上,如果图像中的颜色超过256种(调色板最多可容纳),则图像中必然存在调色板中不会出现的颜色。因此,PIL会立即累积误差(原始颜色和浅色之间的差异),然后插入颜色稍有不同的像素,以便从远处看图像或多或少是正确的。基本上是“错误扩散” 。因此,如果您的颜色陷入其中,有时会以不同的方式出现。

可以避免这种情况的一种方法是将图像量化到少于256色,这样就不会有任何误差扩散。

# Quantise to 256 colours
im256c = = image.quantize(colors=256, method=2)

请注意,这并不意味着您的蓝色阴影将始终映射到每个图像中的相同调色板索引,而只是意味着在任何给定图像中具有蓝色阴影的所有像素都将具有相同的调色板索引。

这里是一个例子:

#!/usr/bin/env python3

import numpy as np
from PIL import Image
from improc import GenerateNColourImage

# Set image height and width
h, w = 200, 200
N    = 1000

# Generate repeatable random Numpy image with N unique colours
np.random.seed(42)
n = GenerateNColourImage(h,w,N) 

# Work out indices of diagonal elements
diags = np.diag_indices(h)

# Intentionally set all diagonal elements same shade of blue
n[diags] = [10,20,200]

# Make Numpy image into PIL Image, palettise, convert back to Numpy array and check diagonals
a0 = Image.fromarray(n)
a1 = a0.convert('P')
a2 = np.array(a1)

# Look at diagonals - should all be the same
print(a2[diags])

输出

[154 154 154 154 154 154 154 154 160 154 160 154 154 154 154 154 160 154
 154 154 160 154 154 154 160 160 154 154 154 160 154 154 154 154 154 154
 154 154 160 154 154 154 154 154 154 154 154 160 160 154 154 154 154 154
 154 154 154 154 154 154 154 154 154 160 154 154 154 154 154 154 154 160
 154 160 154 154 154 154 154 154 154 154 154 154 154 160 154 160 160 154
 154 160 154 154 154 160 154 154 154 154 154 160 154 154 154 154 155 154
 154 160 154 154 154 154 154 154 154 154 154 160 154 154 154 160 154 154
 154 154 160 154 154 154 154 154 154 154 154 154 154 154 154 154 154 160
 154 160 154 160 154 160 154 160 160 154 154 154 154 154 154 154 154 154
 154 154 154 154 161 154 154 154 154 154 154 154 154 154 154 160 154 160
 118 154 160 154 154 154 154 154 154 154 154 154 160 154 154 160 154 154
 154 154]

糟糕,其中的值分别为154、118和160 ...


现在使用相同的Numpy数组再次执行最后4行,但使用quantise()

# Make Numpy image into PIL Image, quantise, convert back to Numpy array and check diagonals
b0 = Image.fromarray(n)
b1 = b0.quantize(colors=256,method=2)
b2 = np.array(b1)

# Look at diagonals - should all be the same
print(b2[diags])

输出

[64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64
 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64
 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64
 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64
 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64
 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64
 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64
 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64
 64 64 64 64 64 64 64 64]

那更好-都一样!

我要补充一点,如果您将图像另存为256色或更少的PNG,则PIL / Pillow将自动保存调色板图像。