神坑警告!图像分割数据集SYNTHIA之标签读取

神坑警告!图像分割数据集SYNTHIA之标签读取

本篇博客主要阐述了如何读取SYNTHIA RAND-CITYSCAPES数据集1的图像分割标签,笔者找到并解决这个bug用了大半天,因此在本篇博客中进行记录。文末有正确的标签读取方法,衷心希望能对各位读者朋友的科研与项目有帮助,有任何疏漏欢迎在评论区指出,笔者不胜感激。

笔者在这里诚挚道歉,2018年12月21日分析的读取方式有误,于2019年3月1日进行改正,对大家造成的误导笔者感到非常惭愧。

结论在前:使cv2库进行SYNTHIA数据集的标签读取。

图像分割与SYNTHIA数据集简介

图像分割算法指对于一张输入图像,输出图中每个像素所属的类别。在训练基于深度学习全卷积神经网络2的图像分割算法中,对每一张输入图像,需要搭配一张与输入图像尺寸相同的单通道的图像分割标签,标签中每一个位置的数字记录了输入图像中该位置像素的类别。

SYNTHIA RAND-CITYSCAPES数据集1是通过三维仿真建模构造的主要包含城市场景的图像分割数据集,图像与标注效果如下图所示,对于每一张图像,提供了一张图像分割标签:
SYNTHIA images-color annotations

SYNTHIA数据集图像分割标签读取

在本篇博文中,在进行SYNTHIA数据集的图像分割标签读取时,笔者最开始使用了常用的OpenCV库进行标签读取,可是发现,读取出来的竟然是三通道的标签,代码与执行结果如下所示:

import numpy as np
import cv2

def main():
    raw_label = cv2.imread('.\\label\\0000000.png',-1)#用OpenCV读取一张标签
    label = np.array(raw_label, dtype=np.float32)#将其转化成numpy数组
    print(label.shape)#打印标签形状


if __name__ == '__main__':
    main()

执行结果1
在这里笔者感到挺奇怪,为什么读取的标签是三通道的而不是单通道的呢?笔者不由得去翻看了一下SYNTHIA RAN-CITYSCAPES提供的README.txt,重要部分如下所示:

GT/LABELS: folder containing png files (one per image). Annotations are given in two channels. The first
channel contains the class of that pixel (see the table below).
The second channel contains
the unique ID of the instance for those objects that are dynamic (cars, pedestrians, etc.).

里面提到,对于每一张图像分割标签,第一个通道记录的像素类别。而第二个通道,记录的是图像中的非静止目标,比如行人,车辆等。也就是说,对标签提取第一个通道就行。

可是,在笔者对标签的第一个通道进行提取后,发现里面的数值竟然为全零,代码与执行结果如下所示:

import numpy as np
import cv2

#get_label_set函数将标签中的值放入集合,目的是去除重复出现的值
def get_label_set(input):
    reshape_list = list(np.reshape(input,(-1,)))#先将二维标签转化成一维列表
    label_set = set(reshape_list)#将列表转化为集合
    return label_set

def main():
    raw_label = cv2.imread('.\\label\\0000000.png',-1)#用OpenCV读取一张标签
    label = np.array(raw_label, dtype=np.float32)#将其转化成numpy数组
    print(get_label_set(label[:,:,0]))#打印标签转化成的集合


if __name__ == '__main__':
    main()

执行结果2
对于一张图像分割标签的第一个通道,将值放入集合中,返回仅为0,这不禁让笔者陷入沉思。

为了追溯问题根源,笔者使用MATLAB对同样的标签进行了读取,代码如下所示:

raw_label = imread('D:\Pycharm\MyProjects\FileIOTest\label\0000000.png');%读取一张标签
raw_label_resize = imresize(raw_label,[360,640],'nearest');%按照最临近插值方式缩小标签,方便显示
label = raw_label(:,:,1);%取第一个通道(MATLAB语言下标从1开始)
label_set=unique(label)%取标签中的值集合,与上下文代码中get_label_set函数功能相同

为了更方便地展示标签,将标签进行了最临近插值缩小,raw_label_resize的可视化结果如下图所示:
可视化结果
可以看到,MATLAB读取的标签文件中是有值的。MATLAB代码执行结果如下如所示:
执行结果
到这里,能够完全证明,图像分割标签是正确的,肯定是笔者的读取方式有问题。于是,笔者在Python程序中换了一些方式(库)进行标签读取,代码与执行结果如下所示:

import numpy as np
import cv2
import imageio
from skimage import io
from PIL import Image
import matplotlib.pyplot as plt

#get_label_set函数将标签中的值放入集合,目的是去除重复出现的值
def get_label_set(input):
    reshape_list = list(np.reshape(input,(-1,)))#先将二维标签转化成一维列表
    label_set = set(reshape_list)#将列表转化为集合
    return label_set

def main():
    #cv2:用OpenCV读取
    raw_label_cv2 = cv2.imread('.\\label\\0000000.png',-1)
    label_cv2 = np.array(raw_label_cv2, dtype=np.float32)
    print("cv2:", get_label_set(label_cv2[:,:,0]))
    print("\n")

    #imageio:用imageio读取
    raw_label_imageio = imageio.imread('.\\label\\0000000.png')
    label_imageio = np.array(raw_label_imageio, dtype=np.float32)
    print("imageio:", get_label_set(label_imageio[:, :, 0]))
    print("\n")

    #skimage:用skimage读取
    raw_label_skimage = io.imread('.\\label\\0000000.png')
    label_skimage = np.array(raw_label_skimage, dtype=np.float32)
    print("skimage:", get_label_set(label_skimage[:, :, 0]))
    print("\n")

    #PIL:用PIL读取
    raw_label_PIL = Image.open('.\\label\\0000000.png')
    label_PIL = np.array(raw_label_PIL, dtype=np.float32)
    print("PIL:", get_label_set(label_PIL[:, :, 0]))
    print("\n")

    #matplotlib:用matplotlib读取
    raw_label_matplotlib = plt.imread('.\\label\\0000000.png')
    label_matplotlib = np.array(raw_label_matplotlib, dtype=np.float32)
    print("matplotlib:", get_label_set(label_matplotlib[:, :, 0]))


if __name__ == '__main__':
    main()

执行结果
如上图所示,只有matplotlib库读入的标签有数字,可是数字非常小(10-5e~10-4e),笔者最开始认为他们是正确的标签,只是进行了归一化操作。那么,只需要对其进行反归一化操作,就能得到正确的图像分割标签。而反归一化操作,即将标签中每一个数除以除0外最小的数。

于是,笔者对标签集合中的元素进行排序,选择除0外最小的数作为反归一化除数:

import numpy as np
import matplotlib.pyplot as plt

#get_label_set函数将标签中的值放入集合,目的是去除重复出现的值
def get_label_set(input):
    reshape_list = list(np.reshape(input,(-1,)))#先将二维标签转化成一维列表
    label_set = set(reshape_list)#将列表转化为集合
    return label_set

#read_SYNTHIA_label函数读取SYNTHIA数据集标签,传入标签路径即可
def read_SYNTHIA_label(path):
    raw_label = plt.imread(path)#用matplotlib读取一张标签
    label = raw_label[:, :, 0]#取标签的第一个通道,并将其转化成numpy数组
    label_array = np.array(list(get_label_set(label)))#使用get_label_set函数去除标签中重复值,转化成numpy数组
    label_array.sort()#将得到的数组从小到大排序
    if (label_array.shape)[0]==1 and label_array[0]==0.0:#如果标签中的数为全零
        return label#直接返回
	#在标签不为全零时,反归一化除数div为除零外最小的值
    if label_array[0]!=0.0:
        div= label_array[0]
    else:
        div = label_array[1]
    return label/div#对标签进行反归一化操作

def main():
    label = read_SYNTHIA_label('.\\label\\0000000.png')#读取一张标签
    print("label:", get_label_set(label))#打印标签转化成的集合


if __name__ == '__main__':
    main()

代码执行结果如下图所示:
执行结果
这样貌似得到了正确的图像分割标签呢,然后在实际实验中的低指标惨痛地告诉我自己,这样的读取方式也是大错特错的!!!

最后,笔者重新思考了一下,到底怎样才是对的呢?经过仔细思考后,笔者重新捡起了cv2。因为cv2.imread读取数据,读取的是BGR
顺序的图像,因此,我们应该读取最后一个通道,而不应该读取第一个通道。

正确的程序如下(在程序中给cv2读取的和matplotlib读取的做了个比较):

import numpy as np
import cv2
import json
import imageio
import glob
from skimage import io
from PIL import Image
import matplotlib.pyplot as plt
import scipy.misc as misc
import matplotlib.image as gImage

SYNTHIA_label_map = {3: 0, 4: 1, 2: 2, 21: 3, 5: 4, 7: 5, 15: 6, 9: 7, 6: 8, 1: 9, 10: 10, 17: 11, 8: 12, 19: 13, 12: 14, 11: 15}
image_size = (640, 360)

def get_label_set(input):
    reshape_list = list(np.reshape(input,(-1,)))
    label_set = set(reshape_list)
    return label_set

def read_SYNTHIA_label(label_path, kv_map, image_size):
    raw_label = cv2.imread(label_path,-1)
    raw_label_p = raw_label[:, :, -1]
    label = cv2.resize(raw_label_p, image_size, interpolation=cv2.INTER_NEAREST)
    label_copy = 255 * np.ones(label.shape, dtype=np.float32)
    for k, v in kv_map.items():
        label_copy[label == k] = v #others are turned to 255
    return label_copy

def read_SYNTHIA_label_ori(label_path, kv_map, image_size):
    raw_label = plt.imread(label_path)
    raw_label_p = raw_label[:, :, 0]
    label = cv2.resize(raw_label_p, image_size, interpolation=cv2.INTER_NEAREST)
    label_copy = 255 * np.ones(label.shape, dtype=np.float32)
    label_array = np.array(list(get_label_set(label)))
    label_array.sort()
    if (label_array.shape)[0] == 1 and label_array[0] == 0.0:
        return label
    if label_array[0] != 0.0:
        div = label_array[0]
    else:
        div = label_array[1]
    div_label = label/div
    for k, v in kv_map.items():
        label_copy[div_label == k] = v #others are turned to 255
    return label_copy

def get_palette(json_path):
    json_file = open(json_path, encoding='utf-8')
    palette = json.load(json_file)["palette"]
    print(palette)
    return palette

def get_coloured_pred(pred, palette, cls_nums):
    palette = np.array(palette,dtype = np.float32)
    pred = np.array(pred).astype(np.int32)
    rgb_pred = np.zeros(shape=[pred.shape[0], pred.shape[1], 3],dtype=np.float32)
    for i in range(pred.shape[0]):
        for j in range(pred.shape[1]):
            k =pred[i][j]
            if  k< cls_nums:
                rgb_pred[i][j] = palette[k][::-1]
    return rgb_pred

def main():
    palette = get_palette('.\\SYNTHIA_info.json')
    label = read_SYNTHIA_label('.\\SYNTHIA_label\\0009395.png', SYNTHIA_label_map, image_size)
    label_ori = read_SYNTHIA_label_ori('.\\SYNTHIA_label\\0009395.png', SYNTHIA_label_map, image_size)
    print(label)
    print(label_ori)
    rgb_pred = get_coloured_pred(label,palette,16)
    rgb_pred_ori = get_coloured_pred(label_ori, palette, 16)
    cv2.imwrite("a.png",rgb_pred)
    cv2.imwrite("b.png",rgb_pred_ori)
    print("done!")

if __name__ == '__main__':
    main()

然后,生成的a.png和b.png分别如下所示:

首先是a.png,这是正确的可视化结果,使用cv2读取的。
在这里插入图片描述
然后是b.png,这是错误的可视化结果,使用matplotlib读取的。
在这里插入图片描述
所以结论是:使用cv2进行读取。

到这里,SYNTHIA RAND-CITYSCAPES数据集的图像分割标签读取介绍就接近尾声了。通过分享解决bug的经历,笔者也衷心希望能对大家有帮助。如果各位读者朋友觉得博文有任何错误与疏漏,欢迎在评论区指出与讨论,笔者不胜感激。

欢迎阅读笔者后续博客,各位读者朋友的支持与鼓励是我最大的动力!

written by jiong
管他天下千万事,闲来轻笑二三声。


  1. SYNTHIA Dataset ↩︎ ↩︎

  2. Fully Convolutional Networks for Semantic Segmentation ↩︎

  • 13
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值