一 写在前面
未经允许,不得转载,谢谢~
这篇文章是对UCF101视频数据集处理以及加载的一个记录,也适用于其他的视频数据集。
1 需求所在
PyTorch提供了像对CIFAR10这样计算机视觉中经常会用到的数据集的接口,直接调用即可方便的获取到我们想要的train_x, train_y, test_x, test_y. 而我这次需要的UCF101还没有得到这样的待遇,所以首先要完成数据的读入,才能进行后面的网络训练及测试工作。
简单来说,这篇文章实现了对UCF101的处理及加载,使其能够每次根据batch_size的大小,返回需要的train_x, train_y, test_x, test_y用于视频分类任务.
2 不足之处
本文的处理方式简单粗暴,也适用于其他的数据集。
但是在您往下看之前,虽然文章标题已经注明未使用深度学习的框架,但为了不浪费您宝贵的时间,还是要说明一下,在写这个代码时候只想到不能直接使用PyTorch封装好的接口,忘记了它还提供了像DataLoader
这样用于数据加载的函数。
所以在数据处理的效率及内存开销方面应该是有很大的改进空间的~~~
二 UCF101数据集
简单介绍一下UCF101数据集。
- 内含13320 个短视频
- 视频来源:YouTube
- 视频类别:101 种
- 主要包括这5大类动作 :人和物体交互,只有肢体动作,人与人交互,玩音乐器材,各类运动
三 具体实现思路
1 数据集准备
- 下载UCF101数据集
UCF101.zip
并解压; - 下载标注文件及训练数据和测试数据的列表文件
The Train/Test Splits for Action Recognition on UCF101 data set
:
内含:
以上两个文件都在UCF数据集官网可以下载。
2 预处理
- 参考代码:two-stream-action-recognition
- 预处理主要分为讲视频分解为帧,统计每个视频的帧数这两个步骤。
- 这两部分的代码在以上的参考文件中给出了,去下载
video_jpg_ucf101_hmdb51.py
以及n_frames_ucf101_hmdb51.py
源码即可。
这里说明一下怎么使用以及执行结果:
将UCF101中的视频保持结构不变逐帧视频分解为图像。
python utils_fyq/video_jpg_ucf101_hmdb51.py /home/hl/Desktop/lovelyqian/CV_Learning/UCF101 /home/hl/Desktop/lovelyqian/CV_Learning/UCF101_jpg
将UCF101中的视频保持结构不变都逐帧视频分解为图像,每个视频帧数目都不一样,150帧左右,图片大小都是320*240。实现每个视频的帧数(图像数量)统计。
python utils_fyq/n_frames_ucf101_hmdb51.py /home/hl/Desktop/lovelyqian/CV_Learning/UCF101_jpg
执行结果是每个视频帧文件夹内都有一个n_frames.txt
文件,记录该视频帧的数目。
3 后续处理
定义了UCF101
类,具体目标:
- train_x: [batch_size,16,3,160,160]
- test_x : [batch_size,16,3,160,160]
- 每个视频取随机取16个连续帧
- 图片为3通道,大小随机取(160,160)
- 总共101类,所以label值为:0-100
- train_y: [batch_size] 返回对应的label值;
- test_y_label: [batch_size] 根据视频名称返回对应的label,用于与预测值进行对比。
- classNames[101]: index表示label, value表示具体的类别,例如classNames[0]='ApplyEyeMakeup`
以下依次具体介绍各个函数:
get_className()
根据下载号的标注文件中的classInd.txt
文件获取到每个index对应的value.get_train()
根据下载好的标注文件TrainList.txt
获取需要训练的视频路径train_x_path
和对应的类别标注信息train_x
。get_label()
根据文件名提取该视频所属的视频类别。get_test()
根据下载好的标注文件TestList.txt
,得到要测试的路径名test_x_path
,并根据路径名调用上面的函数得到正确的标注信息test_y_label
,用于计算预测精度。get_single_image()
根据图片的路径名读取图片信息,本文的处理结果为(3,160,160)大小的Tensor.get_single_video_x()
根据视频图像的路径名随机获取16帧连续的帧。set_mode()
设置当前要取的数据是训练数据还是测试数据。get_minibatches_index()
根据总共要训练(测试)的数量,以及batch_size,返回每次要训练(测试)的视频标号。__getitem__()
利用了python中的特殊函数,可以使用索引访问元素,并自动迭代。所以利用这个特性,用batch_index
作为索引,每次根据当前mode
为训练还是测试,返回需要的值。__init__()
一些初始化工作,以及调用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提供的函数来实现了,还是太年轻了哈哈哈哈哈哈哈哈~
但也算是整理了一下整个的数据集处理的实现思路,总归是没有坏处的嘻嘻
下次再写一个用PyTorch框架函数的吧。
有问题欢迎简信交流,谢谢!