SiameseFC-TensorFlow 程式碼詳細註解(二):訓練資料VID2015預處理
說明:該系列部落格原始碼連結為:https://github.com/bilylee/SiamFC-TensorFlow,是實驗室同小組的師兄用TensorFlow實現SiameseFC演算法的最終公開版本,經過了長時間的打磨,各個模組功能明確,整體可讀性和可移植性極好,我相信這對做Tracking的小夥伴來說,是個入門SiameseFC Tracker的特別好的選擇。哈哈,覺得程式碼很棒的小夥伴們可以點個Star哦,也歡迎交流學習和指教。
上一篇介紹跑SiameseFC程式碼的主要流程,這一篇主要介紹開始訓練自己的模型的第一步:訓練資料預處理。
1:VID2015相關連結
ImageNet比賽官網 :http://image-net.org/challenges/LSVRC/,可以瞭解比賽資訊,包含各比賽實驗資料。
VID2015資料下載連結:http://bvisionweb1.cs.unc.edu/ilsvrc2015/download-videos-3j16.php#vid
2:ILSVRC2015 VID資料
解壓縮之前,資料大小約86G,解壓縮之後的資料夾目錄如下:
ImageSets:包含一些.txt檔案,子資料集的相關描述,預處理過程中用不到,可忽略。
Data :儲存的所有資料資訊,包括了圖片(val,train,test)和視訊片段(snippets)。
Annotations:對應的Data中圖片的註釋資訊,包括val和train部分。
Data子資料夾VID下的檔案目錄如下:
檢視當前檔案目錄下的檔案數量命令:(R代表包含子目錄)
ls -lR|grep"^-"| wc -l
可通過上面的指令進入到對應的資料夾中統計VID資料的詳細資訊如下:
Snippets : 3862+ 555 + 937 = 5354 videos
Train : 3862 videos ( 1122397 images )
Val : 555 videos ( 176126 images )
Test : 937 videos ( 315175 images )
Annotations子資料夾VID下的檔案目錄如下:
只包含了val和train部分,和上面Data/VID/val和train相對應,儲存的是每個圖片的.xml格式的標註資訊。
挑出來一個.xml檔案看看裡面長什麼樣,如下:
.xml註釋檔案有我們需要用到的folder,filename,size,trackid,bndbox等資訊,後面會解析出相關的資訊的,說明一下這個trackid很重要,同一個image中有多個trackid說明包含該image的短視訊存在著很多個目標,在後面就會解析出來更多的videos,記住這點對看後面的資料處理有幫助。
3:VID2015訓練資料預處理
我們下載了VID資料之後也知道資料長什麼樣了,那如何處理這些資料使得可供SiameseFC訓練呢?第一步就需要按照SiameseFC論文中Data Curation的部分對資料進行預處理,接著將訓練資料儲存為.pickle檔案方便訓練載入,最後還需要下載一些預訓練模型和一個測試的視訊,詳細內容參見下面的註解。
3.1 Data Curation
核心檔案:scripts/preprocess_VID_data.py
核心功能:根據SiamsesFC論文中的Curation方式進行圖片預處理。
#! /usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright © 2017 bily Huazhong University of Science and Technology
#
# Distributed under terms of the MIT license.
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import os
import os.path as osp
import sys
import xml.etree.ElementTree as ET # 解析xml檔案的模組
from glob import glob
from multiprocessing.pool import ThreadPool # 多程式加速
import cv2
from cv2 import imread, imwrite # 使用 opencv 讀寫影像
CURRENT_DIR = osp.dirname(__file__)
ROOT_DIR = osp.join(CURRENT_DIR, '..')
sys.path.append(ROOT_DIR) # 新增搜尋路徑
from utils.infer_utils import get_crops, Rectangle, convert_bbox_format # 自己寫的小函式
from utils.misc_utils import mkdir_p
def get_track_save_directory(save_dir, split, subdir, video): # 儲存路徑的一個簡單對映函式,簡化儲存目錄
subdir_map = {'ILSVRC2015_VID_train_0000': 'a', # 將train對映到(a,b,c,d),val對映到(e)
'ILSVRC2015_VID_train_0001': 'b',
'ILSVRC2015_VID_train_0002': 'c',
'ILSVRC2015_VID_train_0003': 'd',
'': 'e'}
return osp.join(save_dir, 'Data', 'VID', split, subdir_map[subdir], video)
def process_split(root_dir, save_dir, split, subdir='', ): # 資料處理核心函式,spilt就是['val','train']中的值
data_dir = osp.join(root_dir, 'Data', 'VID', split) # 待處理圖片資料路徑
anno_dir = osp.join(root_dir, 'Annotations', 'VID', split, subdir) # 待處理圖片的註釋資料路徑
video_names = os.listdir(anno_dir) # train和val資料集所有的video names,因為我們只有這兩個資料集的annotations
for idx, video in enumerate(video_names):
print('{split}-{subdir} ({idx}/{total}): Processing {video}...'.format(split=split, subdir=subdir,
idx=idx, total=len(video_names),
video=video))
video_path = osp.join(anno_dir, video)
xml_files = glob(osp.join(video_path, '*.xml')) # 獲得當前video的所有圖片對應的.xml檔案
for xml in xml_files: # 使用ET處理單個圖片(.jpeg && .xml)
tree = ET.parse(xml)
root = tree.getroot()
folder = root.find('folder').text # 解析.xml檔案的folder
filename = root.find('filename').text # 解析.xml檔案中的filename
# Read image
img_file = osp.join(data_dir, folder, filename + '.JPEG') # 將.xml檔名對應到相同的圖片檔名
img = None
# Get all object bounding boxes
bboxs = []
for object in root.iter('object'): # 找到所有的objects
bbox = object.find('bndbox') # 找到 box項,.xml檔案中名稱是bndbox
xmax = float(bbox.find('xmax').text) # 找到對應的 xmax並轉換為float型別的box資料,以下類似
xmin = float(bbox.find('xmin').text)
ymax = float(bbox.find('ymax').text)
ymin = float(bbox.find('ymin').text)
width = xmax - xmin + 1 # 計算width 和 height
height = ymax - ymin + 1
bboxs.append([xmin, ymin, width, height]) # 返回的box的形式是[xmin,ymin,wedth,height]
for idx, object in enumerate(root.iter('object')):
id = object.find('trackid').text # 獲取object的trackid,因為同一個video中可能存在多個需要跟蹤的目標,加以區分
class_name = object.find('name').text # 所屬類別名稱(VID中的30個大類)
track_save_dir = get_track_save_directory(save_dir, 'train', subdir, video) # 獲取儲存路徑
mkdir_p(track_save_dir)
savename = osp.join(track_save_dir, '{}.{:02d}.crop.x.jpg'.format(filename, int(id)))
if osp.isfile(savename): continue # skip existing images # 檔案存在的情況下無需儲存
if img is None:
img = imread(img_file) # 讀取image檔案
# Get crop
target_box = convert_bbox_format(Rectangle(*bboxs[idx]), 'center-based') # box格式轉換
crop, _ = get_crops(img, target_box,
size_z=127, size_x=255,
context_amount=0.5, )
imwrite(savename, crop, [int(cv2.IMWRITE_JPEG_QUALITY), 90]) # 圖片儲存,質量為90
if __name__ == '__main__':
vid_dir = osp.join(ROOT_DIR, 'data/ILSVRC2015') # VID原始資料集儲存的根目錄,可以跟改為自己儲存資料的絕對路徑
# Or, you could save the actual curated data to a disk with sufficient space
# then create a soft link in `data/ILSVRC2015-VID-Curation`
save_dir = 'data/ILSVRC2015-VID-Curation' # 處理之後的資料儲存的位置,可以修改為絕對路徑
pool = ThreadPool(processes=5) # 開啟5個執行緒加快處理速度
one_work = lambda a, b: process_split(vid_dir, save_dir, a, b) # 執行函式,下面呼叫的時候每個會在每個執行緒中執行
results = []
results.append(pool.apply_async(one_work, ['val', ''])) # 非阻塞方式,執行緒1呼叫one_work處理val資料集,下類似
results.append(pool.apply_async(one_work, ['train', 'ILSVRC2015_VID_train_0000']))
results.append(pool.apply_async(one_work, ['train', 'ILSVRC2015_VID_train_0001']))
results.append(pool.apply_async(one_work, ['train', 'ILSVRC2015_VID_train_0002']))
results.append(pool.apply_async(one_work, ['train', 'ILSVRC2015_VID_train_0003']))
ans = [res.get() for res in results] # 獲取子執行緒結果,作用就是阻塞主執行緒,等待子執行緒執行完成
3.2 將訓練資料儲存為pickle格式
核心檔案:scripts/build_VID2015_imdb.py
核心功能:將Data Curation處理後的訓練資料儲存為.pickle格式(train_imdb.pickle 和 validation_imdb.pickle)
#! /usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright © 2017 bily Huazhong University of Science and Technology
#
# Distributed under terms of the MIT license.
"""Save the paths of crops from the ImageNet VID 2015 dataset in pickle format"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import glob
import os
import os.path as osp
import pickle # 匯入pickle模組,將VID資料儲存為pickle格式
import sys
import numpy as np
import tensorflow as tf
CURRENT_DIR = osp.dirname(__file__)
sys.path.append(osp.join(CURRENT_DIR, '..')) # 新增搜尋路徑
from utils.misc_utils import sort_nicely
class Config: # 引數配置
### Dataset
# directory where curated dataset is stored
dataset_dir = '/home/ssd_datasets/ILSVRC2015_curated' # 之前curated資料測儲存路徑,可使用絕對路徑或相對路徑
save_dir = '/workspace/czx/Projects/SiamFC-TensorFlow/assets/' # imdb檔案儲存路徑
# percentage of all videos for validation
validation_ratio = 0.1 # validation所佔的資料比例
class DataIter:
"""Container for dataset of one iteration"""
pass
class Dataset: # 資料集類
def __init__(self, config): # 初始化引數設定
self.config = config
def _get_unique_trackids(self, video_dir): # 根據video_dir來獲取特定的跟蹤目標
"""Get unique trackids within video_dir"""
x_image_paths = glob.glob(video_dir + '/*.crop.x.jpg') # 取得video_dir下所有crop圖片儲存的檔名
trackids = [os.path.basename(path).split('.')[1] for path in x_image_paths]# 根據檔名獲取對應trackid
unique_trackids = set(trackids) # 因為每個視訊snippet都可能有很多段小視訊,每個小視訊裡面又會有多個跟蹤的目標
return unique_trackids
def dataset_iterator(self, video_dirs): # 將所有的videos進行處理
video_num = len(video_dirs) # videos的數量
iter_size = 150 # 每次迴圈處理的視訊個數
iter_num = int(np.ceil(video_num / float(iter_size)))
for iter_ in range(iter_num):
iter_start = iter_ * iter_size
iter_videos = video_dirs[iter_start: iter_start + iter_size] # 每次處理的videos數量為150
data_iter = DataIter()
num_videos = len(iter_videos)
instance_videos = []
for index in range(num_videos):
print('Processing {}/{}...'.format(iter_start + index, video_num))
video_dir = iter_videos[index] # 單個視訊路徑
trackids = self._get_unique_trackids(video_dir) # 該視訊對應到有多少個target trackid
for trackid in trackids:
instance_image_paths = glob.glob(video_dir + '/*' + trackid + '.crop.x.jpg') #trackid所有圖片的路徑
# sort image paths by frame number
instance_image_paths = sort_nicely(instance_image_paths) # 根據image number排序
# get image absolute path # 獲得排序好的圖片計算對應的圖片的絕對路徑
instance_image_paths = [os.path.abspath(p) for p in instance_image_paths]
instance_videos.append(instance_image_paths)
data_iter.num_videos = len(instance_videos) # 150個視訊裡面trackid的數量,>=150
data_iter.instance_videos = instance_videos
yield data_iter # yield返回一個生成器
def get_all_video_dirs(self): # 獲取VID資料集中所有videos的路徑
ann_dir = os.path.join(self.config.dataset_dir, 'Data', 'VID')
all_video_dirs = []
# 根據之前預處理的訓練資料儲存的檔案結構進行解析獲取所有trackid的路徑
# We have already combined all train and val videos in ILSVRC2015 and put them in the `train` directory.
# The file structure is like:
# train
# |- a
# |- b
# |_ c
# |- ILSVRC2015_train_00024001
# |- ILSVRC2015_train_00024002
# |_ ILSVRC2015_train_00024003
# |- 000045.00.crop.x.jpg
# |- 000046.00.crop.x.jpg
# |- ...
train_dirs = os.listdir(os.path.join(ann_dir, 'train'))
for dir_ in train_dirs:
train_sub_dir = os.path.join(ann_dir, 'train', dir_)
video_names = os.listdir(train_sub_dir)
train_video_dirs = [os.path.join(train_sub_dir, name) for name in video_names]
all_video_dirs = all_video_dirs + train_video_dirs
return all_video_dirs
def main():
# Get the data.
config = Config() # 載入引數設定
dataset = Dataset(config) # dataset例項
all_video_dirs = dataset.get_all_video_dirs() # 獲取所有VID中videos的路徑
num_validation = int(len(all_video_dirs) * config.validation_ratio) # validation資料數量
### validation 資料
validation_dirs = all_video_dirs[:num_validation]
validation_imdb = dict()
validation_imdb['videos'] = []
for i, data_iter in enumerate(dataset.dataset_iterator(validation_dirs)):
validation_imdb['videos'] += data_iter.instance_videos
validation_imdb['n_videos'] = len(validation_imdb['videos']) # 視訊數量
validation_imdb['image_shape'] = (255, 255, 3) # 圖片大小
### train 資料
train_dirs = all_video_dirs[num_validation:]
train_imdb = dict()
train_imdb['videos'] = []
for i, data_iter in enumerate(dataset.dataset_iterator(train_dirs)):
train_imdb['videos'] += data_iter.instance_videos
train_imdb['n_videos'] = len(train_imdb['videos'])
train_imdb['image_shape'] = (255, 255, 3)
if not tf.gfile.IsDirectory(config.save_dir): # imdb資料儲存路徑
tf.logging.info('Creating training directory: %s', config.save_dir)
tf.gfile.MakeDirs(config.save_dir)
with open(os.path.join(config.save_dir, 'validation_imdb.pickle'), 'wb') as f:
pickle.dump(validation_imdb, f)
with open(os.path.join(config.save_dir, 'train_imdb.pickle'), 'wb') as f:
pickle.dump(train_imdb, f)
if __name__ == '__main__':
main()
4: 小結
1:核心介紹了VID2015資料集的結構是怎麼樣的,以及進行SiameseFC訓練之前需要對該資料集如何處理。
2:這部分資料處理因為很早之前就已經處理好了,這裡只貼一下一個樣例吧。
原始圖片樣例:/Data/VID/train/ILSVRC2015_VID_train_0000/ILSVRC2015_train_00005004/000000.jpeg
原始的圖片大小:1280*256。(下圖是原圖片截圖)
Data Curation之後儲存的圖片:255*255,這就是訓練過程中載入的search image。(也是截圖)
這裡還貼一個訓練過程中crop出來的一個127*127的examplar吧。(還是截圖)
3:訓練資料VID2015的準備工作到這基本就做好了,接下來該聊聊如何構建模型了。要是覺得這份程式碼還不錯,就順手點個star吧,哈哈。
相關文章
- 模型訓練:資料預處理和預載入模型
- UCI資料集詳解及其資料處理(附148個資料集及處理程式碼)
- Android註解處理初探:使用註解處理器消除樣板程式碼Android
- Flutter 註解處理及程式碼生成Flutter
- 資料視覺化詳解+程式碼演練視覺化
- 詳解AI開發中的資料預處理(清洗)AI
- Spring 註解學習 詳細程式碼示例Spring
- Java註解處理器使用詳解Java
- 第五篇:資料預處理(二) - 異常值處理
- sklearn 第二篇:資料預處理
- 機器學習演算法-K近鄰(KNN)演算法(三):馬絞痛資料--kNN資料預處理+kNN分類pipeline(程式碼附詳細註釋)機器學習演算法KNN
- 資料預處理
- CANN訓練:模型推理時資料預處理方法及歸一化引數計算模型
- 萬字詳解AI開發中的資料預處理(清洗)AI
- 文字預處理技術詳解
- #翻譯#使用註解處理器生成程式碼-1 註解型別型別
- MySQL資料庫的事務處理用法與例項程式碼詳解MySql資料庫
- 【LLM訓練系列】NanoGPT原始碼詳解和中文GPT訓練實踐NaNGPT原始碼
- 自然語言處理中的語言模型預訓練方法自然語言處理模型
- 資料預處理-資料清理
- 資料分析--資料預處理
- 資料預處理 demo
- Python資料處理(二):處理 Excel 資料PythonExcel
- Tarjan演算法及其應用 總結+詳細講解+詳細程式碼註釋演算法
- LUSE: 無監督資料預訓練短文字編碼模型模型
- PyTorch 模型訓練實⽤教程(程式碼訓練步驟講解)PyTorch模型
- EventBus原始碼解讀詳細註釋(6)從事件釋出到事件處理,究竟發生了什麼!原始碼事件
- 資料預處理-資料歸約
- 資料預處理–資料降維
- 大語言模型訓練資料常見的4種處理方法模型
- nlp 中文資料預處理
- 機器學習一:資料預處理機器學習
- 資料預處理規則
- 資料預處理的形式
- 飛槳帶你瞭解:基於百科類資料訓練的 ELMo 中文預訓練模型模型
- 註解處理器
- Yolov3程式碼分析與訓練自己資料集YOLO
- hover事件延遲處理程式碼例項詳解事件