目录
如果不想知道一一对应,只是想知道有哪些颜色,可以使用如下代码
这篇的是为了说明PIL库中图像的mode参数。
我做的事情是:
- 在本地找了jpg的图,convert为不同mode,将不同的图截取做了个脑图,有个直观的感觉吧。
- 把不同mode的图通过np.array()转化为array, 打印出array的shape, 和array[0, 0]的值, 便于理解不同mode的通道和像素值的存储。
部分结果见下
部分代码和结果
# 将不同模式的图片打印出shape 和 [0, 0]像素点的值
from PIL import Image
import matplotlib.pyplot as plt
image = Image.open('images/tower.jpg') # 本地一个文件
mode_list = ['1', 'L', 'I', 'F', 'P', 'RGB', 'RGBA', 'CMYK', 'YCbCr' ]
for mode in mode_list:
img = image.convert(mode)
img_data = np.array(img)
print('img_{:>1}.shape: {}' .format(mode, img_data.shape))
print('img_{:>}_data[0, 0]: {}'.format(mode, img_data[0, 0]))
print('---')
# 以下为output
img_1.shape: (1276, 1920)
img_1_data[0, 0]: False
---
img_L.shape: (1276, 1920)
img_L_data[0, 0]: 88
---
img_I.shape: (1276, 1920)
img_I_data[0, 0]: 88
---
img_F.shape: (1276, 1920)
img_F_data[0, 0]: 88.94599914550781
---
img_P.shape: (1276, 1920)
img_P_data[0, 0]: 131
---
img_RGB.shape: (1276, 1920, 3)
img_RGB_data[0, 0]: [ 51 97 147]
---
img_RGBA.shape: (1276, 1920, 4)
img_RGBA_data[0, 0]: [ 51 97 147 255]
---
img_CMYK.shape: (1276, 1920, 4)
img_CMYK_data[0, 0]: [204 158 108 0]
---
img_YCbCr.shape: (1276, 1920, 3)
img_YCbCr_data[0, 0]: [ 88 160 100]
---
以上可对mode参数有所了解,第一篇拙劣,还望指正。
最后一点关于颜色模式的,供备注用
- RGB 为真色彩模式, 可组合为 256 x 256 x256 种, 打印需要更改为 CMYK模式, 需要注意数值溢出的问题。
- HSB 模式(本篇没有涉及),建立基于人类感觉颜色的方式,将颜色分为色相(Hue),饱和度(Saturation),明亮度(Brightness),这里不详细展开。
- CMYK模式,应用在印刷领域,4个字母意思是青、洋红、黄、黑,因为不能保证纯度,所以需要黑。
- 位图模式,见1, 颜色由黑和白表示(True, False)。
- 灰度模式,只有灰度, 所有颜色转化为灰度值,见L,I,F。
- 双色调模式(未有涉及),节约成本将可使用双色调。
- Lab模式(未涉及,ps内置),由3通道组成(亮度,a,b)组成,作为RGB到CMYK的过渡。
- 多通道模式,删除RGB,CMYK,Lab中某一个通道后,会转变为多通道,多通道用于处理特殊打印,它的每个通道都为256级灰度通道。
- 索引颜色模式,用在多媒体和网页,通过颜色表查取,没有则就近取,仅支持单通道,(8位/像素)。
重点关注P模式
如果你已经了解RGB模式的含义,那么你需要的只是一个利用“重映射”组织数据的思想。
以一个简单的3×3数组为例,比如这样的
A = [[0, 0, 0],
[0, 0, 0],
[0, 0, 0]]
它可以被我们视为一张极其简单的图片。假设其中每个元素都是6比特无符号整数类型(虽然实际没有6bit的数据类型,但作为理论分析还是有意义的,可表示0~63),用来表示颜色(至于表示什么颜色就随意了,我们这里只做一个假想的概念),那么数组A理论占用的空间就是9×6=54比特。具体地,考虑一个非全零的A,
A = [[18, 21, 0],
[ 0, 35, 35],
[ 0, 35, 18]]
这并不影响它占用的空间大小。但我们很容易发现,这个数组中实际出现的数字其实并不多,只有0、18、21、35这几个而已。如果我们建立一个映射关系
0 → 0
1 → 18
2 → 21
3 → 35
上面的数组就可以重写为
B = [[1, 2, 0],
[0, 3, 3],
[0, 3, 1]]
哎你会发现这样的表示紧凑了很多。而0123只需要两个比特就能表示,数组B占用的空间可以压缩到9×2=18比特,是原来的1/3。即使把这套映射关系也保存下来,即存成一个新的一维数组
c = [0, 18, 21, 35]
元素的位置即代表了它的映射值。向量c的占用为4×6=24比特,跟数组B的加起来也才不过42bit。而且你可以很容易想见,如果数组A的规模十分巨大,而实际出现的数据还是这么几个,这种重映射后的表示法优势就非常明显。
在Python中numpy库的体系下,通过语句c[B]
,就可以重建数组A
。
总结起来,对于元素数据长度特别大,但实际出现的数据去重后数量非常少的情况,我们完全可以把这些弥散在很大范围内的数据,重映射到一个更为紧凑的范围内(上面就是将弥散在几十范围内的数据映射到了小于4的几个值上),从而实现以小见大的效果。
具体到PIL图片的P模式,跟这个原理就非常类似了,它是把原来单像素占用24(32)个bit的RGB(A)真彩图片中的像素值,重映射到了8bit长,即0~255的数值范围内。而这套映射关系,就是属于这张图的所谓“调色板”(Pallete)。当然,一张图片里实际出现的颜色很可能远不止256种,这时只需要按照一种合适的取舍规则,把一些相近的颜色合并成一种就好了。不过这样就有可能带来一定的失真。如何根据具体的图片尽可能少失真地合并颜色,就是另一个话题了。
现在,你应该可以理解P模式的含义了。更重要的是,这种重映射的组织数据的思想,远不止在图像处理领域适用。
获取实际图片的调色板
from PIL import Image
import numpy as np
img = Image.open(r"F:\data\VOC\VOCdevkit\VOC2012\SegmentationClass\2007_000250.png")
img_arr = np.array(img)
unique_value = np.unique(img_arr)
palette = np.array(img.getpalette(),dtype=np.uint8).reshape((256,3))
print(unique_value)
print(palette)
其中unique_value为
array([ 0, 5, 11, 255], dtype=uint8)
其中palette的值为
array([[ 0, 0, 0],
[128, 0, 0],
[ 0, 128, 0],
[128, 128, 0],
[ 0, 0, 128],
[128, 0, 128],
[ 0, 128, 128],
[128, 128, 128],
[ 64, 0, 0],
[192, 0, 0],
[ 64, 128, 0],
[192, 128, 0],
[ 64, 0, 128],
[192, 0, 128],
[ 64, 128, 128],
[192, 128, 128],
[ 0, 64, 0],
[128, 64, 0],
[ 0, 192, 0],
[128, 192, 0],
[ 0, 64, 128],
[128, 64, 128],
[ 0, 192, 128],
[128, 192, 128],
[ 64, 64, 0],
[192, 64, 0],
[ 64, 192, 0],
[192, 192, 0],
[ 64, 64, 128],
[192, 64, 128],
[ 64, 192, 128],
[192, 192, 128],
[ 0, 0, 64],
[128, 0, 64],
[ 0, 128, 64],
[128, 128, 64],
[ 0, 0, 192],
[128, 0, 192],
[ 0, 128, 192],
[128, 128, 192],
[ 64, 0, 64],
[192, 0, 64],
[ 64, 128, 64],
[192, 128, 64],
[ 64, 0, 192],
[192, 0, 192],
[ 64, 128, 192],
[192, 128, 192],
[ 0, 64, 64],
[128, 64, 64],
[ 0, 192, 64],
[128, 192, 64],
[ 0, 64, 192],
[128, 64, 192],
[ 0, 192, 192],
[128, 192, 192],
[ 64, 64, 64],
[192, 64, 64],
[ 64, 192, 64],
[192, 192, 64],
[ 64, 64, 192],
[192, 64, 192],
[ 64, 192, 192],
[192, 192, 192],
[ 32, 0, 0],
[160, 0, 0],
[ 32, 128, 0],
[160, 128, 0],
[ 32, 0, 128],
[160, 0, 128],
[ 32, 128, 128],
[160, 128, 128],
[ 96, 0, 0],
[224, 0, 0],
[ 96, 128, 0],
[224, 128, 0],
[ 96, 0, 128],
[224, 0, 128],
[ 96, 128, 128],
[224, 128, 128],
[ 32, 64, 0],
[160, 64, 0],
[ 32, 192, 0],
[160, 192, 0],
[ 32, 64, 128],
[160, 64, 128],
[ 32, 192, 128],
[160, 192, 128],
[ 96, 64, 0],
[224, 64, 0],
[ 96, 192, 0],
[224, 192, 0],
[ 96, 64, 128],
[224, 64, 128],
[ 96, 192, 128],
[224, 192, 128],
[ 32, 0, 64],
[160, 0, 64],
[ 32, 128, 64],
[160, 128, 64],
[ 32, 0, 192],
[160, 0, 192],
[ 32, 128, 192],
[160, 128, 192],
[ 96, 0, 64],
[224, 0, 64],
[ 96, 128, 64],
[224, 128, 64],
[ 96, 0, 192],
[224, 0, 192],
[ 96, 128, 192],
[224, 128, 192],
[ 32, 64, 64],
[160, 64, 64],
[ 32, 192, 64],
[160, 192, 64],
[ 32, 64, 192],
[160, 64, 192],
[ 32, 192, 192],
[160, 192, 192],
[ 96, 64, 64],
[224, 64, 64],
[ 96, 192, 64],
[224, 192, 64],
[ 96, 64, 192],
[224, 64, 192],
[ 96, 192, 192],
[224, 192, 192],
[ 0, 32, 0],
[128, 32, 0],
[ 0, 160, 0],
[128, 160, 0],
[ 0, 32, 128],
[128, 32, 128],
[ 0, 160, 128],
[128, 160, 128],
[ 64, 32, 0],
[192, 32, 0],
[ 64, 160, 0],
[192, 160, 0],
[ 64, 32, 128],
[192, 32, 128],
[ 64, 160, 128],
[192, 160, 128],
[ 0, 96, 0],
[128, 96, 0],
[ 0, 224, 0],
[128, 224, 0],
[ 0, 96, 128],
[128, 96, 128],
[ 0, 224, 128],
[128, 224, 128],
[ 64, 96, 0],
[192, 96, 0],
[ 64, 224, 0],
[192, 224, 0],
[ 64, 96, 128],
[192, 96, 128],
[ 64, 224, 128],
[192, 224, 128],
[ 0, 32, 64],
[128, 32, 64],
[ 0, 160, 64],
[128, 160, 64],
[ 0, 32, 192],
[128, 32, 192],
[ 0, 160, 192],
[128, 160, 192],
[ 64, 32, 64],
[192, 32, 64],
[ 64, 160, 64],
[192, 160, 64],
[ 64, 32, 192],
[192, 32, 192],
[ 64, 160, 192],
[192, 160, 192],
[ 0, 96, 64],
[128, 96, 64],
[ 0, 224, 64],
[128, 224, 64],
[ 0, 96, 192],
[128, 96, 192],
[ 0, 224, 192],
[128, 224, 192],
[ 64, 96, 64],
[192, 96, 64],
[ 64, 224, 64],
[192, 224, 64],
[ 64, 96, 192],
[192, 96, 192],
[ 64, 224, 192],
[192, 224, 192],
[ 32, 32, 0],
[160, 32, 0],
[ 32, 160, 0],
[160, 160, 0],
[ 32, 32, 128],
[160, 32, 128],
[ 32, 160, 128],
[160, 160, 128],
[ 96, 32, 0],
[224, 32, 0],
[ 96, 160, 0],
[224, 160, 0],
[ 96, 32, 128],
[224, 32, 128],
[ 96, 160, 128],
[224, 160, 128],
[ 32, 96, 0],
[160, 96, 0],
[ 32, 224, 0],
[160, 224, 0],
[ 32, 96, 128],
[160, 96, 128],
[ 32, 224, 128],
[160, 224, 128],
[ 96, 96, 0],
[224, 96, 0],
[ 96, 224, 0],
[224, 224, 0],
[ 96, 96, 128],
[224, 96, 128],
[ 96, 224, 128],
[224, 224, 128],
[ 32, 32, 64],
[160, 32, 64],
[ 32, 160, 64],
[160, 160, 64],
[ 32, 32, 192],
[160, 32, 192],
[ 32, 160, 192],
[160, 160, 192],
[ 96, 32, 64],
[224, 32, 64],
[ 96, 160, 64],
[224, 160, 64],
[ 96, 32, 192],
[224, 32, 192],
[ 96, 160, 192],
[224, 160, 192],
[ 32, 96, 64],
[160, 96, 64],
[ 32, 224, 64],
[160, 224, 64],
[ 32, 96, 192],
[160, 96, 192],
[ 32, 224, 192],
[160, 224, 192],
[ 96, 96, 64],
[224, 96, 64],
[ 96, 224, 64],
[224, 224, 64],
[ 96, 96, 192],
[224, 96, 192],
[ 96, 224, 192],
[224, 224, 192]], dtype=uint8)
这几个加粗的值就是上面unique_value的值就是下面的索引。
所以0 --> [ 0, 0, 0]; 5-->[128, 0, 128]; 11-->[192, 128, 0]; 255->[224, 224, 192]
如果不想知道一一对应,只是想知道有哪些颜色,可以使用如下代码
# Convert Image to RGB and make into Numpy array
na = np.array(im.convert('RGB'))
# Get used colours and counts of each
colours, counts = np.unique(na.reshape(-1,3), axis=0, return_counts=1)
如何将灰度图像保存为P模式的图像呢?
from PIL import Image
import numpy as np
import os
def label_colormap(N=256):
def bitget(byteval, idx):
return ((byteval & (1 << idx)) != 0)
cmap = np.zeros((N, 3))
for i in range(0, N):
id = i
r, g, b = 0, 0, 0
for j in range(0, 8):
r = np.bitwise_or(r, (bitget(id, 0) << 7 - j))
g = np.bitwise_or(g, (bitget(id, 1) << 7 - j))
b = np.bitwise_or(b, (bitget(id, 2) << 7 - j))
id = (id >> 3)
cmap[i, 0] = r
cmap[i, 1] = g
cmap[i, 2] = b
cmap = cmap.astype(np.float32)
return cmap
colormap = label_colormap(255)
ori_mask_path = "/data/seg/mask_png"
target_mask_path = "/data/seg/mask_png_P"
for img_name in os.listdir(ori_mask_path):
img_path = os.path.join(ori_mask_path, img_name)
img = Image.open(img_path)
img_arr = np.array(img)
img_pil = Image.fromarray(img_arr.astype(np.uint8), mode='P')
img_pil.putpalette((colormap).astype(np.uint8).flatten())
print(img_pil.getpalette())
img_pil.save(os.path.join(target_mask_path, img_name))
print("end")
参考: