如何用PIL正确读取16位PNG?为何与OpenCV读取值存在差异?
问题:PIL与OpenCV读取16位PNG结果不一致,PIL无法正确读取掩码通道
现象重现
使用PIL读取的代码与输出
from PIL import Image import numpy as np value = np.asarray(Image.open(open(filename, "rb"))) print(value)
输出:
[[[128 128 0] [128 128 0] [128 128 0] ... [128 128 0] [128 128 0] [128 128 0]]]
使用OpenCV读取的代码与输出
import cv2 im = cv2.imread(filename, cv2.IMREAD_UNCHANGED) print(im)
输出:
[[[ 0 32768 32768] [ 0 32768 32768] [ 0 32768 32768] ... [ 1 32768 32768] [ 0 32768 32768] [ 0 32768 32768]]]
原因分析
- 位深转换差异:PIL的
Image.open()默认会将16位图像(像素值范围0-65535)线性压缩到8位(0-255),16位的中间值32768会被映射为8位的128;而OpenCV的cv2.IMREAD_UNCHANGED会保留原始16位位深,不做压缩。 - 掩码通道丢失:你的掩码通道是16位的0/1值,被PIL压缩到8位时,1对应的8位值约为0(
1/65535*255≈0),所以显示全0,丢失了原始掩码信息。 - 通道顺序差异:PIL默认采用RGB通道顺序,OpenCV则是BGR顺序,这也是两者输出通道数值顺序相反的原因。
解决方法:用PIL正确读取16位PNG
要让PIL保留原始16位位深读取图像,可按以下步骤操作:
方法1:转换为16位格式读取
from PIL import Image import numpy as np # 打开图像并转换为16位RGB模式 img_16bit = Image.open(filename).convert("RGB;16") # 转换为numpy数组,指定数据类型为uint16 value = np.asarray(img_16bit, dtype=np.uint16) # 如果需要和OpenCV的BGR顺序一致,反转通道 value_bgr = value[..., ::-1] print(value_bgr)
方法2:直接保留原始模式读取
如果图像本身是16位格式,可直接读取后转numpy:
from PIL import Image import numpy as np img = Image.open(filename) # 检查当前图像模式,确认是否为16位格式(如"RGB;16"或"I;16") print("图像模式:", img.mode) # 转换为numpy数组,保留16位数据类型 value = np.array(img, dtype=np.uint16)
这样读取后就能得到和OpenCV一致的16位原始像素值,包括掩码通道的0/1信息。
内容的提问来源于stack exchange,提问作者ssuraj
相关产品推荐
相关产品推荐

