SSD目標檢測(2):如何製作自己的資料集(詳細說明附原始碼)
前言:因為要依賴上一章SSD目標檢測(1):圖片+視訊版物體定位(附原始碼)來訓練預測自己的資料集,所以建立自己的資料集是一個複雜且避不開的步驟,以下給出了製作自己的資料集所要經過的簡單步驟,而後也有更詳細的說明奉上。
- VOC2007資料集簡介;
- 規定資料夾名稱,圖片名稱、格式;如何生成txt檔案;
- 如何使用labelImg工具給圖片上標籤,並生成.xml檔案;
- 最後如何將.xml檔案生成SSD所需要的.tfrecords檔案;
以下過程詳細,如有不明請留言提醒,詳細過程如下:
–—-—-—-—-—-—-—-—-—-—-—-—–—-—-—-—-—-—-—-——-—-—-—-—-—-—-—-—-
–—-—-—-—-—-—-—-—-—-—-—-—–—-—-—-—-—-—-—-——-—-—-—-—-—-—-—-—-—-—-—-—-—-——-
1、VOC2007資料集簡介
知己知彼,方百戰不殆。想製作自己的資料集當然要先了解SSD使用的資料集VOC2007長啥樣。VOC2007下載連結 ,密碼是:m5io。(VOC2007完整下載有3個壓縮包+1個PDF,上面連結裡只包含其中一個壓縮包VOCtrainval_06-Nov-2007)。開啟壓縮包就如下圖:
VOC2007詳細介紹在這裡,提供給大家有興趣作了解。而製作自己的資料集只需用到前三個資料夾,所以請事先建好這三個資料夾放入同一資料夾內,同時ImageSets
Main
資料夾
- JPEGImages:用於存放訓練、測試的圖片(圖片格式最好為.jpg)
- Annatations:用於存放.xml格式的檔案,也就是圖片對應的標籤,每個.xml檔案都對應於JPEGImages資料夾的一張圖片
- ImageSets:內含Main資料夾,在../ImageSets/Main資料夾下包含test.txt、train.txt、val.txt、trainval.txt四個檔案,生成的方式第二步有詳細說明
–—-—-—-—-—-—-—-—-—-—-—-—–—-—-—-—-—-—-—-——-—-—-—-—-—-—-—-—-—-—-—-—-—-——-
2、製作自己的資料集
第一步:下載圖片,存入JPEGImages資料夾——你可以直接從各種渠道下載得到所需要的圖片集,存入到JPEGImages資料夾下,命名格式統一為“00xxxx.jpg”,如下圖:
第二步:生成Main資料夾下的.txt檔案——在主目錄下執行以下程式碼既可生成test.txt、train.txt、val.txt、trainval.txt四個檔案,請注意每一個path
地址是否正確(其實這四個txt檔案在後續並沒有什麼用處)
# -*- coding:utf-8 -*-
# -*- author:zzZ_CMing CSDN address:https://blog.csdn.net/zzZ_CMing
# -*- 2018/07/18; 15:19
# -*- python3.5
import os
import random
trainval_percent = 0.7
train_percent = 0.8
xmlfilepath = 'Annotations'
txtsavepath = 'ImageSets/Main'
total_xml = os.listdir(xmlfilepath)
num = len(total_xml)
list = range(num)
tv = int(num*trainval_percent)
tr = int(tv*train_percent)
trainval = random.sample(list,tv)
train = random.sample(trainval,tr)
ftrainval = open(txtsavepath+'/trainval.txt', 'w')
ftest = open(txtsavepath+'/test.txt', 'w')
ftrain = open(txtsavepath+'/train.txt', 'w')
fval = open(txtsavepath+'/val.txt', 'w')
for i in list:
name = total_xml[i][:-4]+'\n'
if i in trainval:
ftrainval.write(name)
if i in train:
ftrain.write(name)
else:
fval.write(name)
else:
ftest.write(name)
ftrainval.close()
ftrain.close()
fval.close()
ftest .close()
print('Well Done!!!')
執行完成,得到如下檔案:可以開啟看一看,內容就是各個圖片的索引,意味著哪些圖片用做訓練,哪些用做測試。
——這是最重要的一步。如果你的python已經pip install lxml
下載了lxml
,就可以直接在我網盤下載labelImg工具windows版使用,密碼:gyf3。
通過以上網盤下載得到工具檔案後,開啟../data/predefined_classes.txt檔案,可以發現這裡都是圖片標籤——把你將要用到的標籤都事先存入在這裡,注意標籤不能有中文。每次使用都把.exe
、data
這兩個檔案拖到桌面上(如果直接在資料夾內執行.exe
會報錯不能執行),開啟labelImg.exe檔案,執行介面如下:就可以開始給圖片打標籤了
labelImg工具簡單的使用步驟就是:
- 開啟單個檔案,或者開啟一個圖片資料夾
- 給目標物體建立box邊框
- 對box邊框內的物體貼上標籤
- 把一張圖片內所有目標物都打上各自標籤後,再儲存生成.xml檔案,注意存入Annatations資料夾,檔名也要與當前圖片儲存一致
- 然後next下一張圖片繼續打標籤,直到所有圖片內物體都打上了標籤,最後exit
–—-—-—-—-—-—-—-—-—-—-—-—–—-—-—-—-—-—-—-——-—-—-—-—-—-—-—-—-—-—-—-—-—-——-
3、用.xml標籤,生成.tfrecord檔案
說明:SSD框架所用到的標籤檔案並不直接是.xml格式檔案,而是.tfrecord檔案,因為這一部分比較重要,程式碼先貼上——只為想研究如何生成.tfrecord檔案的同學準備,想要了解 SSD目標檢測(2):使用自己的資料集做識別(詳細說明附原始碼),請繼續點選,詳細過程講解+原始碼即刻奉上
特別注意:要在主目錄提前建好tfrecords_
資料夾,不然會報錯找不到目標資料夾
特別注意:要在主目錄提前建好tfrecords_
資料夾,不然會報錯找不到目標資料夾
# -*- coding:utf-8 -*-
# -*- author:zzZ_CMing CSDN address:https://blog.csdn.net/zzZ_CMing
# -*- 2018/07/17; 13:18
# -*- python3.5
"""
特別注意: path地址是否正確、要在主目錄下提前建立“tfrecords_”資料夾
"""
import os
import sys
import random
import numpy as np
import tensorflow as tf
import xml.etree.ElementTree as ET # 操作xml檔案
# 我的標籤定義只有兩類,要根據自己的圖片而定
VOC_LABELS = {
'none': (0, 'Background'),
'aiaitie': (1, 'Product')
}
# 圖片和標籤存放的資料夾.
DIRECTORY_ANNOTATIONS = 'Annotations/'
DIRECTORY_IMAGES = 'JPEGImages/'
# 隨機種子.
RANDOM_SEED = 4242
SAMPLES_PER_FILES = 3 # 每個.tfrecords檔案包含幾個.xml樣本
# 生成整數型,浮點型和字串型的屬性
def int64_feature(value):
if not isinstance(value, list):
value = [value]
return tf.train.Feature(int64_list=tf.train.Int64List(value=value))
def float_feature(value):
if not isinstance(value, list):
value = [value]
return tf.train.Feature(float_list=tf.train.FloatList(value=value))
def bytes_feature(value):
if not isinstance(value, list):
value = [value]
return tf.train.Feature(bytes_list=tf.train.BytesList(value=value))
# 圖片處理
def _process_image(directory, name):
# Read the image file.
filename = directory + DIRECTORY_IMAGES + name + '.jpg'
image_data = tf.gfile.FastGFile(filename, 'rb').read()
# Read the XML annotation file.
filename = os.path.join(directory, DIRECTORY_ANNOTATIONS, name + '.xml')
tree = ET.parse(filename)
root = tree.getroot()
# Image shape.
size = root.find('size')
shape = [int(size.find('height').text),
int(size.find('width').text),
int(size.find('depth').text)]
# Find annotations.
bboxes = []
labels = []
labels_text = []
difficult = []
truncated = []
for obj in root.findall('object'):
label = obj.find('name').text
labels.append(int(VOC_LABELS[label][0]))
labels_text.append(label.encode('ascii')) # 變為ascii格式
if obj.find('difficult'):
difficult.append(int(obj.find('difficult').text))
else:
difficult.append(0)
if obj.find('truncated'):
truncated.append(int(obj.find('truncated').text))
else:
truncated.append(0)
bbox = obj.find('bndbox')
a = float(bbox.find('ymin').text) / shape[0]
b = float(bbox.find('xmin').text) / shape[1]
a1 = float(bbox.find('ymax').text) / shape[0]
b1 = float(bbox.find('xmax').text) / shape[1]
a_e = a1 - a
b_e = b1 - b
if abs(a_e) < 1 and abs(b_e) < 1:
bboxes.append((a, b, a1, b1))
return image_data, shape, bboxes, labels, labels_text, difficult, truncated
# 轉化樣例
def _convert_to_example(image_data, labels, labels_text, bboxes, shape,
difficult, truncated):
xmin = []
ymin = []
xmax = []
ymax = []
for b in bboxes:
assert len(b) == 4
# pylint: disable=expression-not-assigned
[l.append(point) for l, point in zip([ymin, xmin, ymax, xmax], b)]
# pylint: enable=expression-not-assigned
image_format = b'JPEG'
example = tf.train.Example(features=tf.train.Features(feature={
'image/height': int64_feature(shape[0]),
'image/width': int64_feature(shape[1]),
'image/channels': int64_feature(shape[2]),
'image/shape': int64_feature(shape),
'image/object/bbox/xmin': float_feature(xmin),
'image/object/bbox/xmax': float_feature(xmax),
'image/object/bbox/ymin': float_feature(ymin),
'image/object/bbox/ymax': float_feature(ymax),
'image/object/bbox/label': int64_feature(labels),
'image/object/bbox/label_text': bytes_feature(labels_text),
'image/object/bbox/difficult': int64_feature(difficult),
'image/object/bbox/truncated': int64_feature(truncated),
'image/format': bytes_feature(image_format),
'image/encoded': bytes_feature(image_data)}))
return example
# 增加到tfrecord
def _add_to_tfrecord(dataset_dir, name, tfrecord_writer):
image_data, shape, bboxes, labels, labels_text, difficult, truncated = \
_process_image(dataset_dir, name)
example = _convert_to_example(image_data, labels, labels_text,
bboxes, shape, difficult, truncated)
tfrecord_writer.write(example.SerializeToString())
# name為轉化檔案的字首
def _get_output_filename(output_dir, name, idx):
return '%s/%s_%03d.tfrecord' % (output_dir, name, idx)
def run(dataset_dir, output_dir, name='voc_train', shuffling=False):
if not tf.gfile.Exists(dataset_dir):
tf.gfile.MakeDirs(dataset_dir)
path = os.path.join(dataset_dir, DIRECTORY_ANNOTATIONS)
filenames = sorted(os.listdir(path)) # 排序
if shuffling:
random.seed(RANDOM_SEED)
random.shuffle(filenames)
i = 0
fidx = 0
while i < len(filenames):
# Open new TFRecord file.
tf_filename = _get_output_filename(output_dir, name, fidx)
with tf.python_io.TFRecordWriter(tf_filename) as tfrecord_writer:
j = 0
while i < len(filenames) and j < SAMPLES_PER_FILES:
sys.stdout.write(' Converting image %d/%d \n' % (i + 1, len(filenames))) # 終端列印,類似print
sys.stdout.flush() # 緩衝
filename = filenames[i]
img_name = filename[:-4]
_add_to_tfrecord(dataset_dir, img_name, tfrecord_writer)
i += 1
j += 1
fidx += 1
print('\nFinished converting the Pascal VOC dataset!')
# 原資料集路徑,輸出路徑以及輸出檔名,要根據自己實際做改動
dataset_dir = "C:/Users/Admin/Desktop/"
output_dir = "./tfrecords_"
name = "voc_train"
def main(_):
run(dataset_dir, output_dir, name)
if __name__ == '__main__':
tf.app.run()
得到的.tfrecords檔案如下:
到這裡,用於SSD的自己的資料集就建立完成了,主要需要的就是.tfrecords
檔案。下一章 SSD目標檢測(2):使用自己的資料集做識別(詳細說明附原始碼)介紹如何用自己的資料集開展訓練預測