如何用Pillow将仅含25色的RGB图像无损转为P模式(索引色)
问题描述
我有一张仅包含25种颜色的RGB图像,想要转换为P模式(索引色)来节省存储空间,但转换后图像虽为P模式,颜色数量却从25变成了70:
>>> from PIL import Image >>> img = Image.open(r"test_rgb2p.png") >>> img.mode 'RGB' >>> len(img.getcolors()) 25 >>> img = img.convert(mode='P') >>> img.mode 'P' >>> len(img.getcolors()) 70
我猜测convert方法做了不必要的重采样,毕竟颜色数量不该增加。用GIMP转换同一张图为索引色时,颜色数没变化。请问有没有办法实现RGB到P模式转换且不改变颜色数量?
解决方案
直接调用convert('P')时,PIL默认会使用自适应调色板生成算法,这会额外生成新颜色。要严格保留原图像的25种颜色,需要手动提取现有颜色并创建自定义调色板:
步骤说明
- 提取原图像的全部颜色集合
- 基于该集合生成符合P模式要求的调色板
- 用自定义调色板完成模式转换
实现代码
from PIL import Image # 打开原RGB图像 img = Image.open(r"test_rgb2p.png") # 获取图像中所有颜色,maxcolors设为足够大以覆盖全部颜色 color_counts = img.getcolors(maxcolors=1000000) # 提取纯颜色值,忽略计数 original_colors = [color for _, color in color_counts] # 构建P模式调色板:将RGB颜色展开为一维列表,不足256色的部分用0填充 palette = [] for r, g, b in original_colors: palette.extend([r, g, b]) # 调色板必须是768个元素(256*3),补充缺失部分 palette += [0] * (768 - len(palette)) # 创建P模式图像并设置调色板 indexed_img = Image.new("P", img.size) indexed_img.putpalette(palette) # 将原RGB图像内容粘贴到索引图上,自动匹配调色板颜色 indexed_img.paste(img, (0, 0)) # 验证结果 print(indexed_img.mode) # 输出: P print(len(indexed_img.getcolors())) # 输出: 25
关键细节
getcolors的maxcolors参数必须设为大于图像实际颜色数的值,否则会返回None- P模式的调色板要求是长度为768的一维列表,对应256种RGB颜色,不足部分用0填充即可
- 通过粘贴方式转换,会严格匹配原图像的颜色到自定义调色板,不会新增额外颜色
内容的提问来源于stack exchange,提问作者mmj
相关产品推荐
相关产品推荐

