天天看点

自制VOC数据集的踩坑记录和解决方案:使用PIL生成8位深的RGB图像

自制VOC数据集的踩坑记录和解决方案:使用PIL生成8位深的RGB图像

最近做图像分割任务的时候,需要制作VOC格式的数据集。在制作的时候发现VOC数据集中的Ground Truth Mask(蒙板)尽管是彩色的,但是竟然是8位深的。

自制VOC数据集的踩坑记录和解决方案:使用PIL生成8位深的RGB图像

而一般情况下,图像的每个通道都有8位,因此RGB图像的位深度一般为24位。8位深的图像一般都是灰度图像。起初,我并没有发现VOC2012中mask的特殊性,直接用24位的Mask作为自制VOC数据集的mask,在pytorch上跑程序报了张量维度错误1only batches of spatial targets supported (non-empty 3D tensors) but got targets of size: : [16, 256, 256, 3]

尝试了网上很多解决方法,比如使用torch.squeeze()函数进行维度压缩,和将nn.CrossEntropyLoss换成nn.BCELoss都失败了。torch.squeeze()会压缩掉数值为1的维度,可是我们是一个三通道的RGB图像,因此torch.squeeze()不会对这里的target的张量造成任何影响。

换成 nn.BCELoss报的错误是Target and input must have the same number of elements. target nelement (3145728) != input nelement (2097152)。

这里输入张量的size为(16,2,256,256),目标张量的size为(16,256,256,3),3145728 = 256 * 256 * 16 * 3(通道数),2097152 = 256 * 256 * 16 * 2(类别数)。

那现在又产生了新的问题:既然VOC2012的ground truth也是彩色的。为什么模型在VOC2012上可以正常运行,在自制的VOC数据集上就会报错呢?

经过仔细的观察,终于发现原来VOC数据集的彩色ground truth竟然是8位的,其大小相当于单通道的灰度图!

那现在摆在我们面前的有两个解决方案:

(一)直接将mask转化8位深的灰度图

(二)将mask转化为8位深的彩色图

方案一后面我实验证实也是可行的,只要在代码里对target的描述做相应的修改即可,但是这样的话网络最终的输出就也是灰度的mask了。

方案二可以利用PIL的模式“P”来实现。PIL有“1”,“L”,“P”,“RGBA”等各种模式,其中“P”是调色板模式,模式“P”可以将图像转化为8位彩色图像,每个像素用8个Bit表示。

转化demo

>>> p24 = Image.open('G:/Desktop/test_24.png')  # 打开24位深的RGB图像
>>> p8 = p24.convert("P")  # 将24位深的RGB图像转化为8位深的模式“P”图像
>>> p8.save("G:/Desktop/test_8.png")  #保存图像
           
自制VOC数据集的踩坑记录和解决方案:使用PIL生成8位深的RGB图像

这里要注意一点是,p24.convert(“P”)返回的图像是模式”P“的,但是其自身并未发生改变

测试代码

>>> p24.convert("P")
>>> p24.save('G:/Desktop/test_24_1.png')
           
自制VOC数据集的踩坑记录和解决方案:使用PIL生成8位深的RGB图像

另外强调一点就是,当我们生成了8位的模式“P”图像,就不要用opencv对它进行修改了。因为我暂时没发现让opencv支持模式“P”的方法。当我们用cv2.imread()函数读取一张模式“P”的图像时,即使我们选择了参数cv2.IMREAD_UNCHANGED,读进来的图像也自动会转成W * H * 3的矩阵,用imwrite保存之后就变成一般的3通道24位深的RGB图像了。

测试代码

>>> p = cv2.imread('G:/Desktop/test_8.png',cv2.IMREAD_UNCHANGED)
>>> cv2.imwrite('G:/Desktop/test_cv2.png',p)
True
           
自制VOC数据集的踩坑记录和解决方案:使用PIL生成8位深的RGB图像

知识点总结:

  1. VOC数据集的ground truth 是8位深的,即每个像素占8个bit
  2. 用24位深的ground truth制作VOC数据集会报张量错误(除非修改网络结构)
  3. PIL的模式“P”可以生成(转化)8位深的彩色图像
  4. Image.open(“Imagepath”).convert(“mode”)不会改变自身的模式,但是会返回指定模式的图像
  5. cv2不支持模式“P”的图像,想生成8位深的彩色图得用PIL。
  6. 生成了8位深的彩色图像之后就不要用cv2处理了

继续阅读