如何使用OpenCV重建PIL调色板图像?(与P模式的混淆)
我正在尝试用OpenCV替换掉原本用PIL写的部分代码。理想情况下,我希望能完全去掉PIL,或者至少让输入(first_frame)变成OpenCV的数组。
原始的(PIL)代码:
from PIL import Image
import numpy as np
import cv2
first_frame_path = "00000.png"
image2 = Image.open(first_frame_path)
print(image2.mode) # out: P <---
image2_p = image2.convert("P")
image2_pil = np.array(image2_p)
print(image2_pil.mean()) # out: 0.039107 <---
OpenCV代码:
from PIL import Image
import numpy as np
import cv2
first_frame_path = "00000.png"
image = cv2.imread(first_frame_path, cv2.IMREAD_COLOR)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
image_from_array = Image.fromarray(image)
print(image_from_array.mode) # out: RGB <---
image_p = image_from_array.convert("P")
image_pil = np.array(image_p)
print(image_pil.mean()) # out: 0.48973 <---
我该如何调整我的OpenCV代码,使得image_pil和image2_pil的值相同呢?
我计算了mean(),只是为了展示这两个数组是不同的,而我的目标是得到相同的结果。
我正在使用一个简单的掩码:

我明白,差异的原因在于原始代码中掩码是直接以P格式加载的,而在OpenCV代码中是RGB格式。不幸的是,我不知道该如何解决这个问题。我尝试指定:
Image.fromarray(image, mode="P")
但是,我得到了一个错误:
发生异常:ValueError 维度过多:3 > 2。
你能告诉我该怎么做才能得到和原始代码一样的NumPy数组吗?
1 个回答
3
这个方法可以实现你想要的效果。它依赖于让PIL(Python Imaging Library)将图像转换为RGB格式,然后再转换回调色板模式,每次都会以自己的方式(希望是可预测的)创建(或重新创建)调色板。
总的来说,你现在做的事情其实不是特别推荐……
#!/usr/bin/env python3
import cv2 as cv
import numpy as np
from PIL import Image
# Load original image
im = Image.open('26dQH.png')
# Convert to RGB, then back to palette so that **PIL decides the palette**
im = im.convert('RGB').convert('P', palette=Image.Palette.ADAPTIVE)
# Get its colours
print(f'im.getcolors(): {im.getcolors()}')
# prints im.getcolors(): [(5367, 0), (5297, 1), (399256, 2)]
# Print first 3 palette entries
print(np.array(im.getpalette()).reshape((-1,3))[:3])
# Prints:
# [[ 0 128 0]
# [128 0 0]
# [ 0 0 0]]
# Read same image using OpenCV and convert to palette-mode PIL Image
na = cv.imread('26dQH.png', cv.IMREAD_COLOR)
pi = Image.fromarray(na).convert('P', palette=Image.Palette.ADAPTIVE)
# Get its colours
print(f'pi.getcolors(): {pi.getcolors()}')
# prints pi.getcolors(): [(5367, 0), (5297, 1), (399256, 2)]
# Print first 3 palette entries
print(np.array(im.getpalette()).reshape((-1,3))[:3])
# Prints:
# [[ 0 128 0]
# [128 0 0]
# [ 0 0 0]]
注意,你可以用pngcheck
来检查PNG的调色板,方法如下:
pngcheck -p 26dQH.png
zlib warning: different version (expected 1.2.11, using 1.2.12)
File: 26dQH.png (2063 bytes)
PLTE chunk: 256 palette entries
0: ( 0, 0, 0) = (0x00,0x00,0x00)
1: (128, 0, 0) = (0x80,0x00,0x00)
2: ( 0,128, 0) = (0x00,0x80,0x00)
3: (128,128, 0) = (0x80,0x80,0x00)
4: ( 0, 0,128) = (0x00,0x00,0x80)
5: (128, 0,128) = (0x80,0x00,0x80)
...
...
...
253: (224, 96,192) = (0xe0,0x60,0xc0)
254: ( 96,224,192) = (0x60,0xe0,0xc0)
255: (224,224,192) = (0xe0,0xe0,0xc0)
OK: 26dQH.png (854x480, 8-bit palette, non-interlaced, 99.5%).
或者你可以使用ImageMagick,这个工具非常有用,它可以让你看到每个调色板条目的频率/数量(在第一列):
magick 26dQH.png -format %c histogram:info:
399256: (0,0,0) #000000 black
5367: (0,128,0) #008000 green
5297: (128,0,0) #800000 maroon
或者用exiftool
,方法如下:
exiftool -b -palette 26dQH.png | xxd -g 1 -c 3
00000000: 00 00 00 ... # entry 0
00000003: 80 00 00 ... # entry 1
00000006: 00 80 00 ... # entry 2
00000009: 80 80 00 ...
...
...