前言
这篇博客记录的是如何制作自己的数据集,并使用FCN模型训练数据,前提要搭建caffe框架,可以参考这篇博客,我制作的数据集是仿照voc2012数据集来在做的
制作图像标签
这一部分是最难的部分,在制作标签之前要搞清楚你的图像共分为几类
- 调整图像尺寸
将图像尺寸改为256*256,其实这一步可以不用操作,但是很多教程都有这一步,为了避免出错,所以最好执行这一步骤。
addpath('dealImg');
addpath('Img');
ListName=dir('Img\*.jpg');
[Pm,Pn]=size(ListName);
for iPm=1:1:Pm %读取文件夹所有图片循环
oriImg=imread(ListName(iPm).name); %readImg
cutImg=imresize(oriImg,[256,256]); %把ai转成256x256的大小
iDealName=ListName(iPm).name;
iDealAddress='dealImg\';
iDealAll=strcat(iDealAddress,iDealName);
ID=imresize(cutImg,1);
imwrite(ID,iDealAll);
end
-
制作索引图(即标签)
这就是最难也是最烦的部分,很多博客都说用labelme工具制作标签,这工具确实方便,但是我试了之后才发现会出现问题,比如说第一张图片有汽车,有小猫,这是两个不同的类将会标注为两个不同的颜色,如果第二张图片没有汽车只有小猫的话,那这张图片小猫将会和汽车标注成同一张颜色。可能我这样解释会有点蒙,你可以参考labelme安装和json批量转化数据两篇博客试试,有人解释说是因为labelme的版本变高自动生成颜色,但是并没有给出解决办法,于是在这一步的操作我选择了使用photoshop工具。
对应的voc数据集颜色类别为:类别名称 R G B
background 0 0 0 背景
aeroplane 128 0 0 飞机
bicycle 0 128 0
bird 128 128 0
boat 0 0 128
bottle 128 0 128 瓶子
bus 0 128 128 大巴
car 128 128 128
cat 64 0 0 猫
chair 192 0 0
cow 64 128 0
diningtable 192 128 0 餐桌
dog 64 0 128
horse 192 0 128
motorbike 64 128 128
person 192 128 128
pottedplant 0 64 0 盆栽
sheep 128 64 0
sofa 0 192 0
train 128 192 0
tvmonitor 0 64 128 显示器
这里有21个类别,因为我的图片只有4类加上背景共5类,所以只用到了前五中颜色。如下面的图是我是做的标签,我是将所有一类放在一起制作的,这样不容易乱,但是在训练数据的时候,要将所有图片打乱顺序,以免出现训练效果差的结果。(下面有好的方法不用打乱图像顺序)
用ps制作成的图像保存为png格式,这时你的图片是24位的,接下来需要将图像改为8位的,下面是matlab代码可以将整个文件夹的图片改成8位。
dirs=dir('D:\24-testlabel\*.png');
map =labelcolormap(6); %6为图像的类别数+1
for n=1:numel(dirs)
strname=strcat('D:\24-testlabel\',dirs(n).name);
img=imread(strname);
x=rgb2ind(img,map);
newname=strcat('D:\8-testlabel\',dirs(n).name);
imwrite(x,map,newname,'png');
end
其中labelcolormap.m代码为如下
function cmap = labelcolormap(N)
if nargin==0
N=256
end
cmap = zeros(N,3);
for i=1:N
id = i-1; r=0;g=0;b=0;
for j=0:7
r = bitor(r, bitshift(bitget(id,1),7 - j));
g = bitor(g, bitshift(bitget(id,2),7 - j));
b = bitor(b, bitshift(bitget(id,3),7 - j));
id = bitshift(id,-3);
end
cmap(i,1)=r; cmap(i,2)=g; cmap(i,3)=b;
end
cmap = cmap / 255;
转化完成后在Python中检查一下图片的格式。
在命令窗口输入idle(前提你装了python),输入
import PIL.Image
import numpy as np
img = PIL.Image.open('D:/8-testlabel/1l.png')
np.unique(img)
如果输出为如下,则说明标签制作成功。
array([0, 1, 2], dtype=uint8)
训练数据集
- benchmark数据集下载地址:benchmark数据集
VOC2012数据集下载地址:VOC2012数据集
benchmark数据集是用来存放训练的数据,VOC2012数据集用来存放测试数据。 - 进入fcn-master/data目录,新建sbdd文件夹(如果没有),将benchmark解压到sbdd中,进入dataset文件夹,你将看到如图所示五个文件,cls是用来存放索引图(标签),img用来存放训练数据(原训练图像),train.txt用来存放训练数据的名称,val.txt用来存放验证集的名称(这里并没有用到)。
将你的训练图片复制到img文件夹下,图片标签复制到cls文件夹下,将你训练图片的名称写入到train.txt文件中,如下图所示
因为打乱图片顺序比较麻烦,所以可以将图片名称顺序打乱,这样方便的多。
进入cls文件夹,这里原本需要存放mat格式的文件,但是制作mat文件有点麻烦,参考了网上的资料,修改代码,使得这里也可以直接存放索引图。
方式如下:
修改fcn目录下的voc_layers.py
# 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
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.
"""
im = Image.open('{}/cls/{}.png'.format(self.sbdd_dir, idx))
label = np.array(im, dtype=np.uint8)
label = label[np.newaxis, ...]
return label
测试集的制作简单一写,进入VOC2012,进入JPEGImages文件夹,里面存放测试用的原图,然后进入SegmentationClass,里面存放测试用的索引图,最后进入ImageSets/Segmentation,新建seg11valid.txt的文件,它和train.txt的性质一样,存放测试用的图片名。
到此,数据集就准备完成了。
-
修改网络参数
(1)下载VGG16的预训练模型和VGG_ILSVRC_16_layers_deploy.prototxt并放在FCN源码文件夹中的ilsvrc-nets文件夹下
(2)进入fcn-master目录下,将所有py文件全部复制到voc-fcn32s(这里用voc-fcn32s模型训练数据),新建snapshot文件夹。
(3)打开solve.py文件
为了避免运行程序时候出现no module named caffe ,在代码中包含import caffe的py文件的第一行加入import sys sys.path.append('C:/caffe/caffe-master/python')
其中,C:/caffe/caffe-master/python为你下载的caffe源码中python文件夹的路径
(4)修改solve.py文件``` import caffe import surgery, score import numpy as np import os import sys sys.path.append('C:/caffe/caffe-master/python') try: import setproctitle setproctitle.setproctitle(os.path.basename(os.getcwd())) except: pass #weights = '../ilsvrc-nets/vgg16-fcn.caffemodel' vgg_weights = '../ilsvrc-nets/vgg16-fcn.caffemodel' vgg_proto = '../ilsvrc-nets/VGG_ILSVRC_16_layers_deploy.prototxt' weights = '../ilsvrc-nets/vgg16-fcn.caffemodel' # init #caffe.set_device(int(sys.argv[1])) caffe.set_mode_gpu() caffe.set_device(0) #solver = caffe.SGDSolver('solver.prototxt') #solver.net.copy_from(weights) solver = caffe.SGDSolver('solver.prototxt') vgg_net=caffe.Net(vgg_proto,vgg_weights,caffe.TRAIN) surgery.transplant(solver.net,vgg_net) del vgg_net # surgeries interp_layers = [k for k in solver.net.params.keys() if 'up' in k] surgery.interp(solver.net, interp_layers) # scoring val = np.loadtxt('D:/caffe/caffe-master/fcn-master/data/pascal/VOCdevkit/VOC2012/ImageSets/Segmentation/seg11valid.txt', dtype=str) #根据你电脑自己的路径修改 for _ in range(25): solver.step(4000) score.seg_tests(solver, False, val, layer='score') ```
(5)solver.prototxt文件修改
```
train_net: "train.prototxt"
test_net: "val.prototxt"
test_iter: 2 #因为我的图片少,所以这里测试图片参数就写少一点,如果图片多的话可以使用默认参数
# make test net, but don't invoke it from the solver itself
test_interval: 999999999
display: 20
average_loss: 20
lr_policy: "fixed"
# lr for unnormalized softmax
base_lr: 1e-10
# high momentum
momentum: 0.99
# no gradient accumulation
iter_size: 1
max_iter: 100000
weight_decay: 0.0005
snapshot: 4000
snapshot_prefix: "snapshot1/train"
test_initialization: false
```
(6)修改训练,测试文件参数
用vs2013打开train.prototxt文件,根据自己电脑的路径代码修改为:
param_str: "{\'sbdd_dir\': \'D:/caffe/caffe-master/fcn-master/data/sbdd/benchmark/benchmark_RELEASE/dataset\', \'seed\': 1337, \'split\': \'train\', \'mean\': (104.00699, 116.66877, 122.67892)}"
所有num_output 为21 的地方都修改为自己分类数 + 1 (加的1是背景)
用vs2013打开val.prototxt文件,根据自己电脑的路径代码修改为:
param_str: "{\'voc_dir\': \'D:/caffe/caffe-master/fcn-master/data/pascal/VOCdevkit/VOC2012\', \'seed\': 1337, \'split\': \'seg11valid\', \'mean\': (104.00699, 116.66877, 122.67892)}"
所有num_output 为21 的地方都修改为自己分类数 + 1 (加的1是背景)
到此所有工作准备完成了,接下来开始训练数据
(7)训练数据
开cmd命令窗口,将目录指定到voc-fcn32s路径下,输入
python.exe solve.py
如下图所示
(8)单张测试
在fcn-32s文件夹中找到infer.py文件,修改代码为
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
import caffe
import vis
import sys
sys.path.append('D:/caffe/caffe-master/python')
# the demo image is "2007_000129" from PASCAL VOC
# load image, switch to BGR, subtract mean, and make dims C x H x W for Caffe
im = Image.open('D:/caffe/caffe-master/fcn-master/voc-fcn32s/data/1.jpg') #可以自己创建路径
in_ = np.array(im, dtype=np.float32)
in_ = in_[:,:,::-1]
in_ -= np.array((104.00698793,116.66876762,122.67891434))
in_ = in_.transpose((2,0,1))
# load net
#net = caffe.Net('voc-fcn8s/deploy.prototxt', 'voc-fcn8s/fcn8s-heavy-pascal.caffemodel', caffe.TEST)
net = caffe.Net('D:/caffe/caffe-master/fcn-master/voc-fcn32s/deploy.prototxt', 'D:/caffe/caffe-master/fcn-master/voc-fcn32s/snapshot/train_iter_100000.caffemodel', caffe.TEST)
# shape for input (data blob is N x C x H x W), set data
net.blobs['data'].reshape(1, *in_.shape)
net.blobs['data'].data[...] = in_
# run net and take argmax for prediction
net.forward()
out = net.blobs['score'].data[0].argmax(axis=0)
# visualize segmentation in PASCAL VOC colors
voc_palette = vis.make_palette(21)
out_im = Image.fromarray(vis.color_seg(out, voc_palette))
out_im.save('data/output3.png')
masked_im = Image.fromarray(vis.vis_seg(im, out, voc_palette))
masked_im.save('data/visualization3.jpg')
fcn-32s和fcn-16s是没有deploy文件,很多博客都是说参考train.prototxt创建deploy文件,我就是按照这样的方法,但是根本不能正确测试图片,这让我一度怀疑自己的方向是不是错的,走了很多弯路。后来我找到别人的deploy文件,直接下载复制到对应为文件夹下就行。
deploy下载地址:deploy.prototxt
至此所有的工作已完成,在这探索的过程真的很心酸,所以想把过程记录下来以方便以后的学习。