点击小眼睛开启蜘蛛网特效

深度学习图像分割(二)——如何制作自己的PASCAL-VOC2012数据集

前言

在之前的那篇文章中:深度学习图像分割(一)——PASCAL-VOC2012数据集(vocdevkit、Vocbenchmark_release)详细介绍 我们大概了解了VOC2012图像分割数据集的基本格式,现在我们来讨论一下我们具体需要什么样的数据格式和我们如何去制作自己的数据集。

数据格式

实际我们在使用FCN算法进行深度学习训练过程中,对于图像的分割我们只需要两种数据:

一种是原始图像,就是我们要进行训练的图像:

《深度学习图像分割(二)——如何制作自己的PASCAL-VOC2012数据集》

而另一种是可以携带图像分割信息的图像或者标记语言文件,相当于分类中的label,不论是图像还是标记语言文件,我们都可以通过程序来得到我们需要的图像格式,一般来说我们最终需要的结果是一维的图像(这里的一维是指像灰度图一样只有一个通道的图像,图像中像素点只有固定的几个类型像素点,比如背景是0,分割物分别是1、2、3…):

  • 携带图像分割信息的图像:

《深度学习图像分割(二)——如何制作自己的PASCAL-VOC2012数据集》

  • 或者之前文章中提到的携带分割信息的.mat格式的文件。

《深度学习图像分割(二)——如何制作自己的PASCAL-VOC2012数据集》

  • 也或者是携带分割信息的json图像,当然json提供的是边缘点而不是具体的分割信息,相比上面那两个需要的处理过程稍微多一些。
  "shapes": [
    {
      "label": "plane",
      "line_color": null,
      "fill_color": red,
      "points": [
        [
          54,
          120
        ],
        [
          85,
          115
        ],
        [
          118,
          116
        ],
        [
          164,
          127
        ],
        [
          212,
          145
        ],
        [
          259,
          161
        ],
        [
          272,
          165
        ],
        [
          280,
          157
        ],
        [
          283,
          142
        ],
        [
          288,
          122
        ],
        [
          292,
          109
        ],
        [
          298,
          110
        ],
        [
          292,
          167
        ],
        [
          297,
          173
        ],
        [
          299,
          178
        ],
        [
          356,
          175
        ],
        [
          363,
          177
        ],
        [
          363,
          180
        ],
        [
          497,
          174
        ],
        [
          498,
          180
        ],
        [
          452,
          184
        ],
        [
          397,
          189
        ],
      .... 
      ]
    }
  ],
  "lineColor": [
    0,
    255,
    0,
    128
  ],
  "fillColor": [
    255,
    0,
    0,
    128
  ],
  "imagePath": "2007_000033.jpg",
  "imageData": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAIBAQEBAQIBAQECAgICAgQDAgICAgUEBAMEBgUGBgYFBgYGBwkIBgcJBwYGCAsICQoKCgoKBggLDAsKDAkKCgr/2wBDAQICAgICAgUDAwUKBwYHCgoKCgoK
  ...
}

我们需要的数据格式

刚才说到在训练过程中,我们投入原图和携带分割信息的图片。

这里有个地方需要注意一下,VOC数据集中的png标记图是8-bit彩色图像:

《深度学习图像分割(二)——如何制作自己的PASCAL-VOC2012数据集》

我们平时使用的彩色图是24-bit真彩色图,也就是RGB三通道都是8bit,值的范围分别是0-255,。而8-bit彩色图则是假彩色图片,这8位中是这样分配的:

Bit    7  6  5  4  3  2  1  0
Data   R  R  R  G  G  G  B  B

R、G各占3位,B占2位,组合起来能表示256种颜色。

我们通过程序来读取一下:

In[4]: import PIL.Image
# 通过PIL库读取8bit的png标记图
In[5]: img = PIL.Image.open('/home/prototype/Desktop/PycharmProjects/pytorch-fcn-master/examples/voc/VOC/VOCdevkit/VOC2012/SegmentationClass/2007_000039.png')  
In[6]: import numpy as np
In[7]: img_32 = np.array(img, dtype=np.int32)  # 将读取的png标记图转化为numpy数组
In[8]: import scipy
In[9]: import scipy.io
# 通过scipy库读取携带图像分割信息的.mat文件
In[10]: mat = scipy.io.loadmat('/home/prototype/Desktop/PycharmProjects/pytorch-fcn-master/examples/voc/VOC/benchmark_RELEASE/dataset/cls/2008_000008.mat')
# 取出.mat文件中有关标记信息的部分
In[11]: lbl = mat['GTcls'][0]['Segmentation'][0].astype(np.int32)
In[12]: lbl.shape
Out[12]: (442, 500) # .mat文件中的标记信息格式
In[15]: lbl_r[lbl_r > 0]  
Out[15]: array([15, 15, 15, ..., 13, 13, 13], dtype=int32)   
In[16]: img_32[img_32>0]
Out[16]: array([255, 255, 255, ..., 255, 255, 255], dtype=int32)
In[19]: img_32[img_32==255] = -1
In[20]: img_32[img_32>0]
Out[20]: array([20, 20, 20, ..., 20, 20, 20], dtype=int32)

我们在通过PIL读取的时候已经将8-bit的图像数据格式进行了转化,将8-bit彩色转化为8-bit灰度图,灰度的值就是这个假彩色的值。而且要注意上面我们把通过PIL读取然后转化为numpy数组的图像进行了这个img_32[img_32==255] = -1操作,这个操作会作用是什么,我们发现了在png标记图中,每个要分割的内容颜色填充都有一层白色轮廓:

《深度学习图像分割(二)——如何制作自己的PASCAL-VOC2012数据集》

这个白色边框在训练中这白色边框所占的像素点并不参与训练,边框只是为了能够更加清晰地显示要分割的目标,以及更好地和背景进行区分而设置的,实际操作中我们其实是可以忽略的。所以上面的执行代码就是将这些白色边框抹掉,赋予-1值在之后的训练中可以通过操作过滤掉。

制作自己的数据集

制作数据集有很多工具,matlab上面自带工具但是比较繁琐,这里我们使用wkentaro编写的labelme,这个软件是使用pyqt编写的轻量级软件,github地址:https://github.com/wkentaro/labelme。

这是软件界面。

《深度学习图像分割(二)——如何制作自己的PASCAL-VOC2012数据集》

至于软件怎么使用github项目页面上都有详细的介绍,我这也就不多赘述了。

唯一需要注意的是这个软件标记出来的文件是json文件,然后通过python代码将json文件转化为我们需要的png标记图,这个标记图的读取方式和我之前写的类似,作者也是建议使用PIL去读取然后转化为numpy格式,最后转化为上面说到的我们需要的格式:

# see load_label_png.py also.
>>> import numpy as np
>>> import PIL.Image

>>> label_png = 'apc2016_obj3_json/label.png'
>>> lbl = np.asarray(PIL.Image.open(label_png))
>>> print(lbl.dtype)
dtype('int32')
>>> np.unique(lbl)
array([0, 1, 2, 3], dtype=int32)
>>> lbl.shape
(907, 1210)

大概就说这么些,下篇文章介绍使用FCN做图像分割训练的原理。

《深度学习图像分割(二)——如何制作自己的PASCAL-VOC2012数据集》

  点赞
本篇文章采用 署名-非商业性使用-禁止演绎 4.0 国际 进行许可
转载请务必注明来源: https://oldpan.me/archives/image-segment-make-voc-datasets

   关注Oldpan博客微信公众号,你最需要的及时推送给你。


  1. w
    wpz说道:

    您好,我也有些问题关于图像大小的,已经加您微信了。
    您上面说,采集的数据集大小无所谓,只要训练的时候crop到一致大小就可以了。不知是否可以这样理解:我采集到了大小不一的数据集(原图),然后直接在原图上进行标记(labelme或者labelmg),标记完成后算是数据集制作完成。在进行网络训练时,同时对原图和标记图像进行crop到统一尺寸。那么在推理的时候如何处理?
    我看到一些代码有的是使用flow_from_directory进行增强,同时处理原图和标签,比如这里https://github.com/zhixuhao/unet,然后在推理的时候直接使用cv2进行resize,这样直接缩放可以么?

    1. O
      Oldpan说道:

      对,只要训练时候的输入图像大小一直就行。标记后图像和label同时会transform成一样大小的输入图像。推理的时候同样需要处理输入图像的大小,不过在模型推理完之后再将输入图像复原到原始大小。直接缩放可以。

      1. w
        wpz说道:

        非常感谢您的回复!!!
        也就是说,在推理的时候,如果原图像的尺寸是1234x2123,直接cv2.resize到512x512,此时的横纵缩放比例分别为512/1234,512/2123,在512x512的图像上进行网络检测,得到的结果(x,y),然后通过横纵缩放比反向映射到原图:(x*1234/512,y*2123/512),是这样吧?
        另外训练的时候是否可以直接对原图(image)及对应的标签(label)直接使用cv2.resize到指定尺寸?
        再次感谢您的解答

        1. O
          Oldpan说道:

          对,原图和标签都要进行transform一致了。你可以微信问我哈,这个评论太长了..

  2. t
    ttyx说道:

    博主您好,请问在制作fcn的数据集时候,用labelme先标记图片然后缩放图,还是先将原图缩放以后再进行标记?

    1. O
      Oldpan说道:

      都可以,可能先标记再缩放更好点,毕竟图像增强缩放变化可以连mask一块处理

  3. V
    Vince说道:

    博主你好,请问数据集中图片的尺寸大小必须一样么?

    1. O
      Oldpan说道:

      训练的时候裁剪成一样的就行,采集的数据集中的图像尺寸无所谓

      1. V
        Vince说道:

        非常谢谢您的回复,不过我不太明白剪成一样的是什么意思,这个剪成是剪裁么?如果是这样余下的部分呢?

        1. O
          Oldpan说道:

          一般称为crop,你设定一个标准,如果输入图像尺寸比标准大,就缩放,然后多余的地方补0,如果输入图像比标准小,就放大,多余的地方补零,就和用4:3的屏幕看16:9的电影一样

          1. V
            Vince说道:

            谢谢您的回复!