FCN制作自己的数据集,训练,测试

FCN从制作自己的数据集,到训练,测试中间感谢很多朋友的帮助!现在整理出来,希望能帮助到更多的人。以voc_fcn8s为例,详细说明一下整个过程。

制作数据集:

按照voc的数据格式,分为benchmark和VOC2011,benchmark为训练集:VOC2011为测试集,benchmarck在 fcn.berkeleyvision.org/data/sbdd
VOC2011在 fcn.berkeleyvision.org/data/pascal
训练集的原图为.jpg/.png,标签为.mat. 测试集的原图和标签图都是.png/.jpg。这一点,从voc_layer.py中的读取数据的方式可以看出,VOCSegDataLayer()是读取测试集的方法,SBDDSegDataLayer()是读取训练集的方法。为了简单,我们在制作数据集的时候,我们将训练图片和测试图片都准备成图片形式,标签也是索引图。

制作标签:

假如我有两类,背景为0,目标为1,标签图就是单通道的灰度图。其中,背景的灰度值为0,目标的灰度值为1(从0开始,背景为0,连续的灰度值)这样制作完之后,标签图看起来是全黑(8位深),接下来,再将标签图制成索引图,至于什么是索引图,简单来说,就是做了一个map的映射,比如,将我前面设置的灰度值为0的背景,映射成一个自己设置的新的颜色(r,g,b),将灰度值为1的,映射成一个新的颜色(r,g,b)。索引图形式的label是P格式的,有新颜色的,此时的位深就等于制作索引图事对应了几个颜色,比如我的对应了0,1,此时的位深是1,如果对应了0,1,2,3,4,此时的位深是4。下面的这段代码可以帮助你查看图片的信息,如果输出[0,1]说明制作正确。

import PIL
from PIL import Image
import numpy as np
image = PIL.Image.open('.....图片路径')
print(np.unique(image))

将灰度图转成索引图的代码如下:

# -*- coding: utf-8 -*-
__author__ = 'cheese'

from random import randint
from PIL import Image
import os

'''
根据灰度图的值制作索引图
'''

path  = "C:\\Users\\Administrator\\Desktop\\Tarim20\\indexplan\\"
os.chdir(path) 
imgList = os.listdir(path) 

for pic in imgList:
    img = Image.open(pic)
    img.putpalette([0,0,0,255,0,0]) #灰度值为0的改为(0,0,0),灰度值为1的改为(255,0,0),按顺序来,这个长度决定了位深
    img.save(pic)

print ("done!")

修改文件

sbdd/benchmark/benchmark_RELEASE/dataset/img下面放训练原图,cls下面放索引图,train.txt里面逐行写入训练图片名,注意不带后缀:

1511664041284
1511664333882
1511664361617
1511666437817
1511666511922
1511666520225
1511666741161
1511666868122
1511666942753
1511666950786

pascal/VOC2011/JPEGImages下面放验证原图,SegmentationClass下面放索引图
segvalid11.txt在/data/pascal/VOC2011/ImageSets/Segmentation/下,原本的是val.txt,复制一份产生segvalid11.txt.里面写验证集的文件名,注意不带后缀
solve.py和val.txt中都指向这个文件。

cls中本来应该存放.mat文件,我们放入的是索引图,需要修改读取数据的代码,仿照voc_layer.py中VOCSegDataLayer类中读取label的方法,我们将SBDDSegDataLayer读取label的方法改为读取.png格式的:

def load_label(self, idx):
        """
        Load label image as 1 x height x width integer array of label indices.
        The leading singleton dimension is required by the loss.
        """
        # import scipy.io
        # mat = scipy.io.loadmat('{}/cls/{}.mat'.format(self.sbdd_dir, idx))
        # label = mat['GTcls'][0]['Segmentation'][0].astype(np.uint8)
        # label = label[np.newaxis, ...]
        # return label

        im = Image.open('{}/cls/{}.png'.format(self.sbdd_dir, idx))
        label = np.array(im, dtype=np.uint8)
        label = label[np.newaxis, ...]
        return label

修改:
1.solver.prototxt中的一些超参数的设置
2.train.prototxt/val.prototxt :
a:修改读取文件路径
b: 因为我改变了最终输出的类别,本来以为将最后的输出类别改成2即可,但是发现会报错,因为fcn的反卷积层固定了输入和输出大小一致。这样,就不得不从后往前把所有的21都改掉,我但同时又想使用fcn8s-heavy-pascal.caffemodel预训练模型,不想修改那么多shape,所以在最后一个反卷积层之后加一个num_output:2 的卷积层。我这里就不需要修改solve.py中copy_form函数,因为与训练模型的参数有的我的网络结构全都有,不存在强行复制的情况,这里说一下,copy_formtransplant的区别:
copy_from:

  • 新旧网络中,名字相同且shape相同的层,copy。
  • 新旧网络中,名字相同shape不同,error。
  • 新旧网络中,名字不同,drop。

transplant:

  • 新旧网络中,名字相同且shape相同的层,copy。
  • 新旧网络中,名字相同shape不同,将就网络中的参数强制拷贝到新网络中。
  • 新旧网络中,名字不同,drop。

如果训练fcn-16s,则可以直接copy_from自己的fcn-32s的model的权重,不需要transplant
如果训练fcn-8s,则可以直接copy_from自己的fcn-16s的model的权重,不需要transplant
如果是用vgg-16的与训练模型来训练fcn-32s,需要transplant(因为vgg-16中fc6,fc7的网络结构和fcn中fc6,fc7的网络结构不同,直接copy_from便会报错)

layer {
  name: "upscore8"
  type: "Deconvolution"
  bottom: "fuse_pool3"
  top: "upscore8"
  param {
    lr_mult: 0
  }
  convolution_param {
    num_output: 21
    bias_term: false
    kernel_size: 16
    stride: 8
  }
}
layer {
   name: "end2"
   type: "Convolution"
   bottom: "upscore8"
   top: "end2"
   param {
     lr_mult: 1
     decay_mult: 1
   }
   param {
     lr_mult: 2
     decay_mult: 0
   }
   convolution_param {
     num_output:5
     pad: 0
     kernel_size: 1
   }
 }
layer {
name: "score"
  type: "Crop"
  bottom: "end2" #"upscore8"
  bottom: "data"
  top: "score"
  crop_param {
    axis: 2
    offset: 31
  }
}

训练

在voc-fcn8s下面输入:
python solve.py 即可开始训练。
如果报错IndexError: list index out of range*
执行python solve.py 0 即可
训练的时候,初始的loss很高,但是不用预训练模型的话,loss会收敛的很慢,

测试

单张测试:
1.修改deploy.prototxt [这里面参考train.prototxt,添加了一个卷积层]
修改voc-fcn8s/infer.py:

1.传入训练好的模型路径和deploy.prototxt
# load net
net = caffe.Net('/new/wc/FCN/fcn.berkeleyvision.org/voc-fcn8s/deploy.prototxt', 
'/new/wc/FCN/fcn.berkeleyvision.org/voc-fcn8s/snapshot/train/_iter_1000.caffemodel', caffe.TEST)

2.将索引图对应回彩图,可以在后面加上这段代码:

 palette=[]
 for i in range(256):
     palette.extend((i,i,i))

 #将预测出的第一类,对应回[0,0,0],将预测出的第二类,对应回[255,0,0]
 palette[:3*2]=np.array([[0, 0, 0],
                         [255,0,0],], dtype='uint8').flatten()

 im.putpalette(palette)
 im.show()
 #保存结果
 im.save('/new/wc/FCN/fcn.berkeleyvision.org/voc-fcn8s/testpic/res/'+imgName)

你可能感兴趣的:(语义分割,深度学习,教程)