从分割数据集说起

8位彩色图

下图是 NYU数据集 里的一张Mask图像, 显示彩图, 但实际是单通道位深为8的png图像, 也就是单通道图像, 并不是常见的RGB三通道.

而这种图像怎么读取呢? 用opencv读取显然是行不通的, 这点在另一篇博客有过介绍, 因为改变了图像的像素值和格式, 参考在深度学习中遇到的opencv坑.

推荐使用PIL.Image.open()读取. 可以看到像素值在 [0, 255] 之间, 矩阵仍然是二维单通道.

再看一下具体的像素值, 可以通过统计元素的方法得到: (参考在深度学习中遇到的opencv坑)
px = [255, 7, 0, 37, 39, 6, 25, 4, 28]
这些值也是实例类别的标签!!! 因此不能使用opencv读取, 不能被改变.

8位灰度图

下图是 GTEA 数据集中的一张Mask图像, 显示为灰度图, 实际也是单通道 位深为8的png图像

使用PIL.Image.open()读取. 可以看到像素值在 [0, 255] 之间, 矩阵仍然是二维单通道.

统计具体的像素值, 只有两个:
px = [0, 200]
对于二值Mask图像来说, 这种情况是需要处理的, 可以读取该数据集的其他图像, 能够看出像素值并不统一, 而分割只有两种: 手和背景. 因此需要将图像标签处理为 0/1, 本文讨论的重点就是如何处理这类不规则格式的Mask图像

1位二值图

下图是 HandOverFace数据集的一张图像, 显示为二值图, 实际是单通道 位深为1的png图像, 这个格式就比较统一, 但仍然存在麻烦.

使用PIL.Image.open()读取. 可以看到像素值是bool型, 矩阵是二维单通道.

这种数据集仍然需要处理.

什么是图像位深

图像位深, 又叫色深(Color Depth),指像素点可以有多少种色彩来描述,单位是“bit”(位), 如1-bit、8-bit、24-bit和32-bit. 深度数值越高,可以获得更多的色彩.

  • 1位图像, 位深度是1, 含有 2 1 2^1 21 种颜色或灰度级;
  • 8位图像,位深度是8,含有 2 8 = 256 2^8=256 28=256 种颜色或灰度级.;
  • 24位图像可称为真彩色图像,位深度是24,能组合成 2 24 = 1677 , 7216 2^{24}=1677,7216 224=1677,7216 种颜色,超过了人眼能够分辨的颜色数量. 实际上, 计算机是以RGB三通道的形式来存储的, 每一通道都是8位, 可指示256种颜色, 三基色相互组合就形成了 ( 2 8 ) 3 (2^8)^3 (28)3 种颜色;
  • 32位图像是在RGB图像基础上增加了 α \alpha α通道, 代表还存在256级透明度;
  • 还存在不规则的2位/4位/x位等图像, 即含有 2 x 2^x 2x 种颜色或灰度级.

所以, 图像的像素值即指示了所在像素是哪一种颜色/哪一级灰度, 这里存在一种"数值-颜色"的索引关系, 称为 调色板. 对于最常见的RGB图像, 我们知道像素值[0,0,0]是黑色, [255,255,255]是白色; 对于1位图像, 像素值[0]则代表黑色, [1]代表白色; 但是对于8位图像, 则需要看调色板索引的实际颜色, 其中像素值[0]代表了调色板定义的第0种颜色, [255]代表第255种颜色.

因此, 和上述数据集Mask图像的情形一致, 同样的单通道矩阵, 如果调色板配置不同, 所呈现的图像也是不一样的.

PNG图像格式简介

参考: PNG - 维基百科

  • PNG(Portable Network Graphics,便携式网络图形)是一种无损压缩的位图图形格式,支持索引、灰度、RGB三种颜色方案以及Alpha通道等特性.

  • PNG图像的文件结构包括一个8字节的PNG文件标识域和3个以上的后续数据块组成, 共有两种类型的数据块:一种是PNG文件必须包含、读写软件必须支持的关键块(critical chunk);另一种是辅助块(ancillary chunks). PNG允许软件忽略它不认识的附加块, 这种基于数据块的设计, 使得PNG格式在扩展时仍能保持与旧版本兼容.

  • 关键数据块中有4个部分:
    文件头数据块 IHDR(header chunk):包含有图像基本信息,作为第一个数据块出现并只出现一次;
    调色板数据块 PLTE(palette chunk):必须放在图像数据块之前;
    图像数据块 IDAT(image data chunk):存储实际图像数据。PNG数据允许包含多个连续的图像数据块;
    图像结束数据 IEND(image trailer chunk):放在文件尾部,表示PNG数据流结束.

如何处理Mask图像

在语义分割/实例分割中, Mask图像的像素值代表了像素点所属的类别标签. 对于单个类别的数据集来说, Mask像素值应该是[0/1], 显示为黑色/白色以区分背景/目标; 对于多个类别的数据集, Mask像素值应该是[0,1,2,…], 显示为彩色以区分不同目标. 这就需要用到调色板索引.

考虑到通用性, 不使用1位图像[False/True], 不使用不规则的位深, 推荐Mask图像都转为8-bits二值图或彩色图. 当然, 不额外处理数据集也是可行的, 只需要在读取时对像素值做转换即可.

处理代码如下:

import numpy as np
import PIL.Image as Image

src= Image.open("1.png")
mat = np.array(src)
mat = mat.astype(np.uint8)
dst = Image.fromarray(mat, 'P')
bin_colormap = [0,0,0] + [255,255,255]*254    # 二值调色板
dst.putpalette(bin_colormap)
dst.save('new.png')

cmap = np.load('../utils/cmap.npy')
dst.putpalette(cmap)

PIL库相关函数