一 寫在前面
未經(jīng)允許盼产,不得轉(zhuǎn)載瓤逼,謝謝~
這篇文章是對UCF101視頻數(shù)據(jù)集處理以及加載的一個記錄踢关,也適用于其他的視頻數(shù)據(jù)集伞鲫。
1 需求所在
PyTorch提供了像對CIFAR10這樣計算機視覺中經(jīng)常會用到的數(shù)據(jù)集的接口,直接調(diào)用即可方便的獲取到我們想要的train_x, train_y, test_x, test_y. 而我這次需要的UCF101還沒有得到這樣的待遇签舞,所以首先要完成數(shù)據(jù)的讀入秕脓,才能進行后面的網(wǎng)絡(luò)訓練及測試工作。
簡單來說儒搭,這篇文章實現(xiàn)了對UCF101的處理及加載吠架,使其能夠每次根據(jù)batch_size的大小,返回需要的train_x, train_y, test_x, test_y用于視頻分類任務(wù).
2 不足之處
本文的處理方式簡單粗暴搂鲫,也適用于其他的數(shù)據(jù)集傍药。
但是在您往下看之前,雖然文章標題已經(jīng)注明未使用深度學習的框架,但為了不浪費您寶貴的時間拐辽,還是要說明一下拣挪,在寫這個代碼時候只想到不能直接使用PyTorch封裝好的接口,忘記了它還提供了像DataLoader
這樣用于數(shù)據(jù)加載的函數(shù)俱诸。
所以在數(shù)據(jù)處理的效率及內(nèi)存開銷方面應(yīng)該是有很大的改進空間的~~~
二 UCF101數(shù)據(jù)集
簡單介紹一下UCF101數(shù)據(jù)集菠劝。
- 內(nèi)含13320 個短視頻
- 視頻來源:YouTube
- 視頻類別:101 種
- 主要包括這5大類動作 :人和物體交互,只有肢體動作,人與人交互睁搭,玩音樂器材赶诊,各類運動
三 具體實現(xiàn)思路
1 數(shù)據(jù)集準備
- 下載UCF101數(shù)據(jù)集
UCF101.zip
并解壓; - 下載標注文件及訓練數(shù)據(jù)和測試數(shù)據(jù)的列表文件
The Train/Test Splits for Action Recognition on UCF101 data set
:
內(nèi)含:
以上兩個文件都在UCF數(shù)據(jù)集官網(wǎng)可以下載园骆。
2 預(yù)處理
- 參考代碼:two-stream-action-recognition
- 預(yù)處理主要分為講視頻分解為幀舔痪,統(tǒng)計每個視頻的幀數(shù)這兩個步驟。
- 這兩部分的代碼在以上的參考文件中給出了锌唾,去下載
video_jpg_ucf101_hmdb51.py
以及n_frames_ucf101_hmdb51.py
源碼即可锄码。
這里說明一下怎么使用以及執(zhí)行結(jié)果:
將UCF101中的視頻保持結(jié)構(gòu)不變逐幀視頻分解為圖像。
python utils_fyq/video_jpg_ucf101_hmdb51.py /home/hl/Desktop/lovelyqian/CV_Learning/UCF101 /home/hl/Desktop/lovelyqian/CV_Learning/UCF101_jpg
將UCF101中的視頻保持結(jié)構(gòu)不變都逐幀視頻分解為圖像鸠珠,每個視頻幀數(shù)目都不一樣巍耗,150幀左右秋麸,圖片大小都是320*240渐排。實現(xiàn)每個視頻的幀數(shù)(圖像數(shù)量)統(tǒng)計。
python utils_fyq/n_frames_ucf101_hmdb51.py /home/hl/Desktop/lovelyqian/CV_Learning/UCF101_jpg
執(zhí)行結(jié)果是每個視頻幀文件夾內(nèi)都有一個n_frames.txt
文件灸蟆,記錄該視頻幀的數(shù)目驯耻。
3 后續(xù)處理
定義了UCF101
類,具體目標:
- train_x: [batch_size,16,3,160,160]
- test_x : [batch_size,16,3,160,160]
- 每個視頻取隨機取16個連續(xù)幀
- 圖片為3通道炒考,大小隨機取(160,160)
- 總共101類可缚,所以label值為:0-100
- train_y: [batch_size] 返回對應(yīng)的label值;
- test_y_label: [batch_size] 根據(jù)視頻名稱返回對應(yīng)的label斋枢,用于與預(yù)測值進行對比帘靡。
- classNames[101]: index表示label, value表示具體的類別,例如classNames[0]='ApplyEyeMakeup`
以下依次具體介紹各個函數(shù):
get_className()
根據(jù)下載號的標注文件中的classInd.txt
文件獲取到每個index對應(yīng)的value.get_train()
根據(jù)下載好的標注文件TrainList.txt
獲取需要訓練的視頻路徑train_x_path
和對應(yīng)的類別標注信息train_x
瓤帚。get_label()
根據(jù)文件名提取該視頻所屬的視頻類別描姚。get_test()
根據(jù)下載好的標注文件TestList.txt
,得到要測試的路徑名test_x_path
戈次,并根據(jù)路徑名調(diào)用上面的函數(shù)得到正確的標注信息test_y_label
轩勘,用于計算預(yù)測精度。get_single_image()
根據(jù)圖片的路徑名讀取圖片信息怯邪,本文的處理結(jié)果為(3,160,160)大小的Tensor.get_single_video_x()
根據(jù)視頻圖像的路徑名隨機獲取16幀連續(xù)的幀绊寻。set_mode()
設(shè)置當前要取的數(shù)據(jù)是訓練數(shù)據(jù)還是測試數(shù)據(jù)。get_minibatches_index()
根據(jù)總共要訓練(測試)的數(shù)量,以及batch_size澄步,返回每次要訓練(測試)的視頻標號冰蘑。__getitem__()
利用了python中的特殊函數(shù),可以使用索引訪問元素村缸,并自動迭代懂缕。所以利用這個特性,用batch_index
作為索引王凑,每次根據(jù)當前mode
為訓練還是測試搪柑,返回需要的值。__init__()
一些初始化工作索烹,以及調(diào)用get_train()
和get_test()
先獲得各自的視頻路徑列表和label信息工碾。
4 使用方法
myUCF101=UCF101()
# get classNames
className=myUCF101.get_className()
# train
batch_num=myUCF101.set_mode('train')
for batch_index in range(batch_num):
train_x,train_y=myUCF101[batch_index]
print (train_x,train_y)
print ("train batch:",batch_index)
#TEST
batch_num=myUCF101.set_mode('test')
for batch_index in range(batch_num):
test_x,test_y_label=myUCF101[batch_index]
print test_x,test_y_label
print ("test batch: " ,batch_index)
四 完整代碼
from PIL import Image
import random
from skimage import io, color, exposure
from skimage.transform import resize
import os
import numpy as np
import pandas as pd
import torch
class UCF101:
def __init__(self,mode='train'):
self.videos_path='/home/hl/Desktop/lovelyqian/CV_Learning/UCF101_jpg'
self.csv_dir_path='/home/hl/Desktop/lovelyqian/CV_Learning/UCF101_TrainTestlist/'
self.label_csv_path = os.path.join(self.csv_dir_path, 'classInd.txt')
# self.batch_size=128
self.batch_size=8
self.mode= mode
self.get_train()
self.get_test()
def get_className(self):
data = pd.read_csv(self.label_csv_path, delimiter=' ', header=None)
labels = []
# labels.append("0")
for i in range(data.shape[0]):
labels.append(data.ix[i, 1])
return labels
def get_train(self):
train_x_path = []
train_y = []
for index in range(1,4):
tmp_path='trainlist0'+str(index)+'.txt'
train_csv_path = os.path.join(self.csv_dir_path, tmp_path)
# print (train_csv_path)
data = pd.read_csv(train_csv_path, delimiter=' ', header=None)
for i in range(data.shape[0]):
train_x_path.append(data.ix[i,0])
# train_y.append(data.ix[i,1])
train_y.append(data.ix[i,1]-1)
self.train_num=len(train_x_path)
self.train_x_path=train_x_path
self.train_y=train_y
return train_x_path,train_y
def get_test(self):
test_x_path=[]
test_y_label=[]
for index in range(1,4):
temp_path='testlist0'+str(index)+'.txt'
test_csv_path=os.path.join(self.csv_dir_path,temp_path)
# print (test_csv_path)
data=pd.read_csv(test_csv_path,delimiter=' ',header=None)
for i in range(data.shape[0]):
test_x_path.append(data.ix[i,0])
label=self.get_label(data.ix[i,0])
test_y_label.append(label)
self.test_num=len(test_x_path)
self.test_x_path=test_x_path
self.test_y_label=test_y_label
return test_x_path,test_y_label
def get_label(self,video_path):
slash_rows = video_path.split('/')
class_name = slash_rows[0]
return class_name
def get_single_image(self,image_path):
image=resize(io.imread(image_path),output_shape=(160,160),preserve_range= True) #240,320,3--160,160,3
# io.imshow(image.astype(np.uint8))
# io.show()
image =image.transpose(2, 0, 1) #3,160,160
return torch.from_numpy(image) #range[0,255]
def get_single_video_x(self,train_x_path):
slash_rows=train_x_path.split('.')
dir_name=slash_rows[0]
video_jpgs_path=os.path.join(self.videos_path,dir_name)
##get the random 16 frame
data=pd.read_csv(os.path.join(video_jpgs_path,'n_frames'),delimiter=' ',header=None)
frame_count=data[0][0]
train_x=torch.Tensor(16,3,160,160)
image_start=random.randint(1,frame_count-17)
image_id=image_start
for i in range(16):
s="%05d" % image_id
image_name='image_'+s+'.jpg'
image_path=os.path.join(video_jpgs_path,image_name)
single_image=self.get_single_image(image_path)
train_x[i,:,:,:]=single_image
image_id+=1
return train_x
def get_minibatches_index(self, shuffle=True):
"""
:param n: len of data
:param minibatch_size: minibatch size of data
:param shuffle: shuffle the data
:return: len of minibatches and minibatches
"""
if self.mode=='train':
n=self.train_num
elif self.mode=='test':
n=self.test_num
minibatch_size=self.batch_size
index_list = np.arange(n, dtype="int32")
# shuffle
if shuffle:
random.shuffle(index_list)
# segment
minibatches = []
minibatch_start = 0
for i in range(n // minibatch_size):
minibatches.append(index_list[minibatch_start:minibatch_start + minibatch_size])
minibatch_start += minibatch_size
# processing the last batch
if (minibatch_start != n):
minibatches.append(index_list[minibatch_start:])
if self.mode=='train':
self.minibatches_train=minibatches
elif self.mode=='test':
self.minibatches_test=minibatches
return
def __getitem__(self, index):
if self.mode=='train':
batches=self.minibatches_train[index]
N=batches.shape[0]
train_x=torch.Tensor(N,16,3,160,160)
train_y=torch.Tensor(N)
for i in range (N):
tmp_index=batches[i]
tmp_video_path=self.train_x_path[tmp_index]
tmp_train_x= self.get_single_video_x(tmp_video_path)
tmp_train_y=self.train_y[tmp_index]
train_x[i,:,:,:]=tmp_train_x
train_y[i]=tmp_train_y
train_x=train_x.permute(0,2,1,3,4)
return train_x,train_y
elif self.mode=='test':
batches=self.minibatches_test[index]
N=batches.shape[0]
test_x=torch.Tensor(N,16,3,160,160)
test_y_label=[]
for i in range (N):
tmp_index=batches[i]
tmp_video_path=self.test_x_path[tmp_index]
tmp_test_x= self.get_single_video_x(tmp_video_path)
tmp_test_y=self.test_y_label[tmp_index]
test_x[i,:,:,:]=tmp_test_x
test_y_label.append(tmp_test_y)
test_x=test_x.permute(0,2,1,3,4)
return test_x,test_y_label
def set_mode(self,mode):
self.mode=mode
if mode=='train':
self.get_minibatches_index()
return self.train_num // self.batch_size
elif mode=='test':
self.get_minibatches_index()
return self.test_num // self.batch_size
## usage
if __name__=="__main__":
myUCF101=UCF101()
className=myUCF101.get_className()
# train
batch_num=myUCF101.set_mode('train')
for batch_index in range(batch_num):
train_x,train_y=myUCF101[batch_index]
print (train_x,train_y)
print ("train batch:",batch_index)
#TEST
batch_num=myUCF101.set_mode('test')
for batch_index in range(batch_num):
test_x,test_y_label=myUCF101[batch_index]
print test_x,test_y_label
print ("test batch: " ,batch_index)
五 寫在最后
竟然沒有想到可以用PyTorch提供的函數(shù)來實現(xiàn)了,還是太年輕了哈哈哈哈哈哈哈哈~
但也算是整理了一下整個的數(shù)據(jù)集處理的實現(xiàn)思路百姓,總歸是沒有壞處的嘻嘻
下次再寫一個用PyTorch框架函數(shù)的吧渊额。
有問題歡迎簡信交流,謝謝垒拢!