视频数据集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框架函数的吧。

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

参考材料

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容