DeepLabV3+
deeplab v3+ 算是目前來說最先進的語義分割算法,盡管現在有精确到頭發絲的分割方法:Soft Semantic Segmentation. 但谷歌開源了deeplabv3+,我們可以直接使用不同的backbone和資料集來訓練我們自己的分割模型。如果你想做一個分割的應用,那這個方法再合适不過。
在開始之前,讓我們看看我們能做到的分割效果:
當然出來cityspcapes資料集之外你也可以放許多其他的資料集。包括我自己的車道線資料集,分割效果也還不錯,連左右車道線都能分割出來。接下來就教大家如何制作資料集吧。
資料集制作
首先這個資料集制作有個巨大的問題。假設你用labelme或者其他工具标注了你的資料,你的儲存标注可能是polygon的點,也可能是mask。這裡我推薦儲存polygon,因為deeplab中使用的label是單通道以你的類别的id為像素值的标簽,這也非常好了解。這裡貼一個核心代碼:
1import cv2
2import os
3import numpy as np
4import cv2
5import matplotlib.pyplot as plt
6
7cls_label_map = {
8 'background': 0,
9 'a': 1,
10 's': 2,
11 'f': 3,
12 'd': 4,
13 'j': 5,
14 'k': 6,
15 'l': 7,
16 'g': 8,
17 'p': 9,
18 'q': 10,
19 'o': 11,
20 'w': 12,
21 'h': 13,
22 '3': 14,
23}
24
25
26def gen_mask(polygons, img):
27 mask = np.zeros([img.shape[0], img.shape[1]], np.int32)
28 for p in polygons:
29 # cv2.polylines(img, p, 1, 255)
30 # fill polygon
31 pts = np.array(p[:-1])
32 cv2.fillPoly(mask, pts, cls_label_map[p[-1]])
33 return mask
這樣傳回就是label的mask。這個mask實際上是黑色的。咋一看like this:
仔細看是可以隐約看到車道線的。并且其實這裡的車道先不止一種類别。
關于label
關于label有幾點需要注意的,像素值就是label的index,從我的map也能看的出來。
除此之外沒了。另外,如果你的類别裡面沒有ignore_label, 那就直接是idx和0,0就是背景。如果有ignore_label就是255,相應的類别寫進去,顔色值為255就是ignore了。
這隻是第一步,接下來你的生成相應的tfrecords。這裡我也提供一個核心代碼,大家可以相應的進行一些更改。1import math
2import os.path
3import sys
4import build_data
5import tensorflow as tf
6
7FLAGS = tf.app.flags.FLAGS
8
9tf.app.flags.DEFINE_string('image_folder',
10 './VOCdevkit/VOC2012/JPEGImages',
11 'Folder containing images.')
12
13tf.app.flags.DEFINE_string(
14 'semantic_segmentation_folder',
15 './VOCdevkit/VOC2012/SegmentationClassRaw',
16 'Folder containing semantic segmentation annotations.')
17
18tf.app.flags.DEFINE_string(
19 'list_folder',
20 './VOCdevkit/VOC2012/ImageSets/Segmentation',
21 'Folder containing lists for training and validation')
22
23tf.app.flags.DEFINE_string(
24 'output_dir',
25 './tfrecord',
26 'Path to save converted SSTable of TensorFlow examples.')
27
28
29_NUM_SHARDS = 4
30
31def load_images_and_labels(d):
32 all_dirs = [i for i in os.listdir(d) if i.startswith('lane_2018')]
33 all_images = []
34 all_labels = []
35 for d in all_dirs:
36 img_dir = os.path.join(d, 'images')
37 label_dir = os.path.join(d, 'masks')
38 all_images.extend([os.path.join(img_dir, i) for i in os.listdir(os.path.join(d, 'images')) if i.endswith('jpg') or i.endswith('jpeg')])
39 all_labels.extend([os.path.join(img_dir, i) for i in os.listdir(os.path.join(d, 'masks')) if i.endswith('png')])
40 return all_images, all_labels
41
42
43def _convert_dataset(d):
44 '''Converts the specified dataset split to TFRecord format.
45
46 Args:
47 dataset_split: The dataset split (e.g., train, test).
48
49 Raises:
50 RuntimeError: If loaded image and label have different shape.
51 '''
52 sys.stdout.write('Processing...')
53
54 all_image_files, all_label_files = load_images_and_labels(d)
55
56 num_images = len(all_image_files)
57 num_per_shard = int(math.ceil(num_images / float(_NUM_SHARDS)))
58
59 image_reader = build_data.ImageReader('jpeg', channels=3)
60 label_reader = build_data.ImageReader('png', channels=1)
61
62 dataset = 'train'
63 for shard_id in range(_NUM_SHARDS):
64 output_filename = os.path.join(
65 FLAGS.output_dir,
66 '%s-%05d-of-%05d.tfrecord' % (dataset, shard_id, _NUM_SHARDS))
67 with tf.python_io.TFRecordWriter(output_filename) as tfrecord_writer:
68 start_idx = shard_id * num_per_shard
69 end_idx = min((shard_id + 1) * num_per_shard, num_images)
70 for i in range(start_idx, end_idx):
71 sys.stdout.write('\r>> Converting image %d/%d shard %d' % (
72 i + 1, len(filenames), shard_id))
73 sys.stdout.flush()
74 # Read the image.
75 # image_filename = os.path.join(
76 # FLAGS.image_folder, filenames[i] + '.' + FLAGS.image_format)
77 image_data = tf.gfile.FastGFile(all_image_files[i], 'rb').read()
78 height, width = image_reader.read_image_dims(image_data)
79 # Read the semantic segmentation annotation.
80 # seg_filename = os.path.join(
81 # FLAGS.semantic_segmentation_folder,
82 # filenames[i] + '.' + FLAGS.label_format)
83 seg_data = tf.gfile.FastGFile(all_label_files[i], 'rb').read()
84 seg_height, seg_width = label_reader.read_image_dims(seg_data)
85 if height != seg_height or width != seg_width:
86 raise RuntimeError(
87 'Shape mismatched between image and label.')
88 # Convert to tf example.
89 file_name = os.path.basename(all_image_files[i]).split('.')[0]
90 example = build_data.image_seg_to_tfexample(
91 image_data, file_name, height, width, seg_data)
92 tfrecord_writer.write(example.SerializeToString())
93 sys.stdout.write('\n')
94 sys.stdout.flush()
95
96
97def main(unused_argv):
98 d = './'
99 _convert_dataset(d)
100
101if __name__ == '__main__':
102 tf.app.run()
這個代碼十分重要,直接将images和masks生成了相應的tfrecord。這個代碼中有一些關于路徑處理需要根據大家的實際需求修改,再次不敖訴。
deeplab搭建
clone一下models的代碼,然後把slim添加到PYTHON 裡面:
1export PYTHONPATH=$PYTHONPATH:/home/models/research/slim
在models/research下執行:1python3 deeplab/model_test.py
沒有問題的話就ok了。接下來要修改兩個檔案:segmentation_dataset.py
train_utils.py
修改segmentation_dataset.py的110行,添加對應的關于_CAMVID資料集的描述設定:
CamVid描述配置
1# segmentation_dataset.py line 110
2_CAMVID_INFORMATION = DatasetDescriptor(
3 splits_to_sizes={
4 'train': 367, # num of samples in images/training
5 'val': 101, # num of samples in images/validation
6 },
7 num_classes=12,
8 ignore_label=255,
9)
這裡我以CamVid資料集作為示範。這裡的類别是你的分割的類别,但是得加上ignore_label.
CamVid的列别是11類,加上ignore就是12類。
另外在加上:1DATASETS_INFORMATION = {
2 'cityscapes': _CITYSCAPES_INFORMATION,
3 'pascal_voc_seg': _PASCAL_VOC_SEG_INFORMATION,
4 'ade20k': _ADE20K_INFORMATION,
5 'mydata':_MYDATA_INFORMATION, #我自己的資料集
6 'camvid':_CAMVID_INFORMATION, #camvid示例
7}
注冊一下自己的資料集。更改就這兩處,大家代開檔案之後會看到。
更改一下train_utils.py:
1exclude_list = ['global_step','logits']
2if not initialize_last_layer:
3 exclude_list.extend(last_layers)
目的是在加載與訓練的模型的時候不需要top,這個最好更改一下,因為你的類别跟cityscapes不同。
Train
萬事具備隻欠東風了。直接開始訓練,訓練的執行路徑在 models/research下面,我們運作:1python3 deeplab/train.py \
2 --logtostderr \
3 --training_number_of_steps=300 \
4 --train_split='train' \
5 --model_variant='xception_65' \
6 --atrous_rates=6 \
7 --atrous_rates=12 \
8 --atrous_rates=18 \
9 --output_stride=16 \
10 --decoder_output_stride=4 \
11 --train_crop_size=513 \
12 --train_crop_size=513 \
13 --train_batch_size=2 \
14 --dataset='minieye_lane' \
15 --tf_initial_checkpoint='deeplab/checkpoints/deeplabv3_cityscapes_train/model.ckpt'\
16 --train_logdir='./deeplab/log' \
17 --dataset_dir='./deeplab/tfrecord'
相應的與訓練模型下載下傳位址為:https://github.com/tensorflow/models/blob/master/research/deeplab/g3doc/model_zoo.md
然後傳入tfrecord路徑即可開始訓練。。