1. 程式人生 > >FCN製作自己的資料集、訓練和測試 caffe

FCN製作自己的資料集、訓練和測試 caffe

花了兩三週的時間,在導師的催促下,把FCN的全部流程走了一遍,期間走了很多彎路,現在記錄一下。系統環境:ubuntu 16.04LTS

一、資料集的製作 
注:我的資料集是仿照VOC資料集進行製作的

1.resize 資料集

我的GPU視訊記憶體4G,跑過大的圖片帶不動,需要resize圖片大小,放幾個修改圖片大小的程式。 
(1)單張圖片resize

# coding = utf-8  
import Image  

def  convert(width,height):
    im = Image.open("C:\\xxx\\test.jpg")
    out = im.resize((width, height),Image.ANTIALIAS)
    out.save("C:\\xxx\\test.jpg"
) if __name__ == '__main__': convert(256,256)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

(2)resize整個資料夾裡的圖片

# coding = utf-8
import Image
import os

def convert(dir,width,height):
    file_list = os.listdir(dir)
    print(file_list)
    for filename in file_list:
        path = ''
        path = dir+filename
        im = Image.open(path)
        out = im.resize((256
,256),Image.ANTIALIAS) print "%s has been resized!"%filename out.save(path) if __name__ == '__main__': dir = raw_input('please input the operate dir:') convert(dir,256,256)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

(3)按比例resize

# coding = utf-8
import Image
import os

def convert(dir,width,height)
:
file_list = os.listdir(dir) print(file_list) for filename in file_list: path = '' path = dir+filename im = Image.open(path) out = im.resize((256,256),Image.ANTIALIAS) print "%s has been resized!"%filename out.save(path) if __name__ == '__main__': dir = raw_input('please input the operate dir:') convert(dir,256,256)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

2.製作索引圖 
(1)下載labelme

(2)生成dataset資料夾 
在終端輸入指令:

labelme_json_to_dataset _static/apc2016_obj3.json //這裡的檔名根據自己的實際情況更改
  • 1

(3)為資料夾下的label.png著色 
首先需要對照VOC分割的顏色進行著色,一定要保證顏色的準確性。Matlab程式碼:

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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

對應的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 顯示器
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

然後使用Python的skimage庫進行顏色填充,具體函式是skimage.color.label2rgb(),程式碼較長,有需要可私信。

————————-2017年9月26日 新增————————- 
太多人和我私信要程式碼,前一段時間很忙都沒有及時回覆大家,所以這裡放個連結,需要自取。 
https://github.com/hitzoro/FCN-ColorLabel

3.將填充後的label.png轉為灰度圖 
如果不轉,在訓練的時候回報錯,轉換matlab程式碼如下:

dirs=dir('F:/xxx/*.png');
for n=1:numel(dirs)
     strname=strcat('F:/xxx/',dirs(n).name);
     img=imread(strname);
     [x,map]=rgb2ind(img,256);
     newname=strcat('F:/xxx/',dirs(n).name);
     imwrite(x,map,newname,'png');
end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

轉化後,在python中檢查一下圖片的格式:

In [23]: img = PIL.Image.open('000001_json/label.png')
In [24]: np.unique(img)
Out[24]: array([0, 1, 2], dtype=uint8)
  • 1
  • 2
  • 3

如果輸出一致,則索引圖製作正確。

二、FCN訓練自己的資料集

2.資料集準備 
這裡我們需要兩個資料集包,benchmark和VOC2012,進入fcn/data,新建sbdd資料夾(如果沒有),將benchmark解壓到sbdd中,將VOC2012解壓到data下的pascal資料夾下。 
這兩個在網上都可以找得到。 
這兩個資料集有什麼用呢?在FCN中VOC資料集的訓練需要他倆,benchmark中的dataset用來存放訓練時的資料,VOC2012存放測試時的資料。

  • 先製作訓練時的資料 
    進入dataset中的img資料夾,這裡存放訓練用的原圖,把原來的原圖替換為你自己的原圖。接著修改train.txt ,寫入你訓練圖片的名字,注意不要加字尾。如下即可:
000001
000003
000005
000007
000009
000011
000013
000015
000017
000019
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

進入cls資料夾,這裡原本需要存放mat格式的檔案,但是製作mat檔案有點麻煩,參考了網上的資料,修改程式碼,使得這裡也可以直接存放索引圖。 
方式如下:

  • 修改fcn目錄下的voc_layers.py

註釋掉原本的load_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.
#        """
#        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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 製作測試集資料

測試集的製作簡單一寫,進入VOC2012,進入JPEGImages資料夾,裡面存放測試用的原圖,然後進入SegmentationClass,裡面存放測試用的索引圖,最後進入ImageSets/Segmentation,有一個名為seg11valid.txt的檔案,它和train.txt的性質一樣,存放測試用的圖片名。

到此,資料集就準備完成了。

3.修改網路引數

為了避免執行程式時候出現no module named caffe 
在程式碼中包含import caffe的py檔案(solve.py)的第一行加入

import sys  
sys.path.append('/home/hitoia/caffe/python')
  • 1
  • 2

其中,/home/hitoia/caffe/python為你下載的caffe原始碼中python資料夾的路徑

cd進入fcn原始碼路徑 
以個人路徑為例:/home/hitoia/fcn.berkeleyvision.org/ 
將其中所有的py檔案,例如surgery.py等等,全部複製到voc-fcn32s資料夾中

  • solver.prototxt檔案修改 
    進入voc-fcn32s資料夾 開啟solver.prototxt 
    其中snapshot:10000 表示訓練10000次儲存一次模型 
    snapshot_prefix:”/home/hitoia/fcn.berkeleyvision.org/voc-fcn32s/snapshot/train” 
    表示訓練得到的模型,也就是model存放的路徑 
    在此,我附上個人的solver.prototxt供大家參考
train_net: "/home/hitoia/fcn.berkeleyvision.org/voc-fcn32s/train.prototxt"
test_net: "/home/hitoia/fcn.berkeleyvision.org/voc-fcn32s/val.prototxt"
test_iter: 736
# 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: "/home/hitoia/fcn.berkeleyvision.org/voc-fcn32s/snapshot/train"
test_initialization: false
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • solve.py的修改 
    在這裡鄭重宣告一下:如果訓練fcn32s的網路模型,一定要修改solve.py利用transplant的方式獲取vgg16的網路權重。 
    具體操作為:
import sys  
sys.path.append('/home/hitoia/caffe/python')
import caffe
import surgery, score

import numpy as np
import os
import sys

try:
    import setproctitle
    setproctitle.setproctitle(os.path.basename(os.getcwd()))
except:
    pass

vgg_weights = '../ilsvrc-nets/vgg16-fcn.caffemodel'  
vgg_proto = '../ilsvrc-nets/VGG_ILSVRC_16_layers_deploy.prototxt'  
weights = '../ilsvrc-nets/vgg16-fcn.caffemodel'
#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('/home/hitoia/fcn.berkeleyvision.org/data/pascal/VOCdevkit/VOC2012/ImageSets/Segmentation/seg11valid.txt', dtype=str)

for _ in range(25):
    solver.step(1000)
    score.seg_tests(solver, False, val, layer='score')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

如果訓練fcn16s,則可以直接copy自己的fcn32s的model的權重,不需要transplant,也就是不需要修改solve.py 
如果訓練fcn8s,則可以直接copy自己的fcn16s的model的權重,不需要transplant,也就是不需要修改solve.py 
只有如此,才能避免loss高居不下的情況 
這裡的:

 for _ in range(25):
    solver.step(1000)
    score.seg_tests(solver, False, val, layer='score')
  • 1
  • 2
  • 3

奇怪的現象:修改solver.prototxt中的max_iter: 100000沒有改變最大迭代次數,只有改變這個step裡的數字才有用,這裡最大迭代次數等於25*1000 = 25000次。

  • train.prototxt / val.prototxt 修改 
    所有num_output 為21 的地方都修改為自己分類數 + 1 (加的1是背景),最開始的param_str需要根據自己的情況修改,放一下我自己的

train.prototxt:

param_str: "{\'sbdd_dir\': \'/home/hitoia/fcn.berkeleyvision.org/data/sbdd/benchmark/benchmark_RELEASE/dataset\', \'seed\': 1337, \'split\': \'train\', \'mean\': (104.00699, 116.66877, 122.67892)}"
  • 1

val.prototxt:

param_str: "{\'voc_dir\': \'/home/hitoia/fcn.berkeleyvision.org/data/pascal/VOCdevkit/VOC2012\', \'seed\': 1337, \'split\': \'seg11valid\', \'mean\': (104.00699, 116.66877, 122.67892)}"
  • 1

準備完成,在voc-fcn32s路徑下輸入

python solve.py
  • 1

就可以開始訓練

三、單張測試 
在fcn原始碼資料夾,找到infer.py。

import numpy as np
from PIL import Image
import matplotlib.pyplot as plt


import sys  
sys.path.append('/home/hitoia/caffe/python')
import caffe

# load image, switch to BGR, subtract mean, and make dims C x H x W for Caffe
im = Image.open('000030.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('voc-fcn32s/deploy.prototxt', 'voc-fcn32s/snapshot/train_iter_24000.caffemodel', caffe.TEST)
#net = caffe.Net('voc-fcn8s/deploy.prototxt', 'siftflow-fcn32s/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)

#plt.imshow(out,cmap='gray');
plt.imshow(out);
plt.axis('off')
plt.savefig('000030_out32.png')
#plt.show()

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

其中,net = caffe.Net(‘voc-fcn32s/deploy.prototxt’, ‘voc-fcn32s/snapshot/train_iter_24000.caffemodel’, caffe.TEST),其中train_iter_24000.caffemodel’是我訓練後得到的模型。

如果沒有deploy檔案,可以參考如下方法: 
首先,根據你利用的模型,例如模型是voc-fcn32s的,那麼你就去voc-fcn32s的資料夾,裡面有train.prototxt檔案,將檔案開啟,全選,複製,新建一個名為deploy.prototxt檔案,貼上進去, 
然後ctrl+F 尋找所有名為loss的layer 只要有loss 無論是loss還是geo_loss 將這個layer統統刪除,這就是此次的deploy.prototxt。

大功告成,至此整個流程全部完成。整個過程心酸不斷,fcn的資料不多,求助了很多人,在此感謝

無奈的小心酸深度學習思考者

對我的幫助。