视频数据集UCF101的处理与加载(未使用深度学习框架)

一 写在前面

未经允许,不得转载,谢谢~

这篇文章是对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 数据集准备

  1. 下载UCF101数据集UCF101.zip并解压;
  2. 下载标注文件及训练数据和测试数据的列表文件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源码即可。

这里说明一下怎么使用以及执行结果:

  1. 将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。

  2. 实现每个视频的帧数(图像数量)统计。
    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`

以下依次具体介绍各个函数:

  1. get_className()
    根据下载号的标注文件中的classInd.txt文件获取到每个index对应的value.

  2. get_train()
    根据下载好的标注文件TrainList.txt获取需要训练的视频路径train_x_path和对应的类别标注信息train_x

  3. get_label()
    根据文件名提取该视频所属的视频类别。

  4. get_test()
    根据下载好的标注文件TestList.txt,得到要测试的路径名test_x_path,并根据路径名调用上面的函数得到正确的标注信息test_y_label,用于计算预测精度。

  5. get_single_image()
    根据图片的路径名读取图片信息,本文的处理结果为(3,160,160)大小的Tensor.

  6. get_single_video_x()
    根据视频图像的路径名随机获取16帧连续的帧。

  7. set_mode()
    设置当前要取的数据是训练数据还是测试数据。

  8. get_minibatches_index()
    根据总共要训练(测试)的数量,以及batch_size,返回每次要训练(测试)的视频标号。

  9. __getitem__()
    利用了python中的特殊函数,可以使用索引访问元素,并自动迭代。所以利用这个特性,用batch_index作为索引,每次根据当前mode为训练还是测试,返回需要的值。

  10. __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框架函数的吧。

有问题欢迎简信交流,谢谢!

参考材料

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 121,193评论 16 134
  • 最近江歌案事件传得沸沸扬扬,其中过程网上已经讲得很清楚,就不多加阐述,对此我来聊聊,此案得到广泛关注后,在网上十分...
    路无涯阅读 410评论 0 0