如何用调色板信息写入图像?

How to write image with palette information?

我想使用 Pillow and/or pypng 在 Python 中创建一个包含调色板信息的 PNG 图像文件。

输入为:

  1. 调色板信息

    [[0, 128, 0],
     [0, 64, 128],
     [0, 128, 128],
     [0, 64, 0],
     [0, 64, 64],
     [128, 128, 0],
     ...
    ]
    
  2. 输入图像(numpy.ndarray

    img = cv2.imread("myimage.png")
    print(img)
    
    [[[0, 128, 0],
      [0, 128, 0],
      [0, 128, 0],
      ...
     ]
     [[0, 128, 0],
      [0, 64, 64],
      [0, 64, 0],
      ...
     ]
    ]
    

并且,输出是:

image = PIL.Image.open("output.png")
image = np.array(image)
print(image)

[[0, 0, 0, 0, ..... 5, 5, 5]
 [0, 4, 3, 3, ..... 4, 4, 4]
  ...
]

输入图像和输出图像在视觉上必须相同,

PIL.Image.open读取输出图像后,改成NumPy数组,应该是如上输出。

有办法做到吗?

下面是将现有 RGB 图像转换为某些 indexed color image. Please keep in mind, that Pillow only allows storing 256 different colors in some color palette, cf. Image.putpalette 的一些演示代码。因此,请确保您的输入图像不包含超过 256 种不同的颜色。

此外,我假设调色板是已知的,并且现有 RGB 图像中的所有颜色都完全来自该调色板。否则,您需要添加代码来提取所有颜色,并预先设置合适的调色板。

import cv2
import numpy as np
from PIL import Image

# Existing palette as nested list
palette = [
    [0, 128, 0],
    [0, 64, 128],
    [0, 128, 128],
    [0, 64, 0],
]

# Existing RGB image, read with OpenCV (Attention: Correct color ordering)
img = cv2.imread('myimage.png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
h, w = img.shape[:2]
print(img)
# [[[  0 128   0]
#   [  0 128   0]
#   [  0 128   0]
#   ...
#   [  0 128 128]
#   [  0 128 128]
#   [  0 128 128]]

# Generate grayscale output image with replaced values
img_pal = np.zeros((h, w), np.uint8)
for i_p, p in enumerate(palette):
    img_pal[np.all(img == p, axis=2)] = i_p
cv2.imwrite('output.png', img_pal)

# Read grayscale image with Pillow
img_pil = Image.open('output.png')
print(np.array(img_pil))
# [[0 0 0 ... 2 2 2]
#  [0 0 0 ... 2 2 2]
#  [0 0 0 ... 2 2 2]
#  ...
#  [1 1 1 ... 3 3 3]
#  [1 1 1 ... 3 3 3]
#  [1 1 1 ... 3 3 3]]

# Convert to mode 'P', and apply palette as flat list
img_pil = img_pil.convert('P')
palette = [value for color in palette for value in color]
img_pil.putpalette(palette)

# Save indexed image for comparison
img_pil.save('output_indexed.png')

这是现有的 RGB 图像 myimage.png:

那是中间色 output.png – 您很可能不会看到不同的深灰色、接近黑色的颜色:

为了比较,这是索引彩色图像,在转换为 mode P 并应用调色板后:

----------------------------------------
System information
----------------------------------------
Platform:      Windows-10-10.0.19041-SP0
Python:        3.9.1
PyCharm:       2021.1.1
NumPy:         1.19.5
OpenCV:        4.5.2
Pillow:        8.2.0
----------------------------------------