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

作者: 与阳光共进早餐 | 来源:发表于2018-01-30 11:14 被阅读967次

写在前面

这篇文章是对UCF101视频数据集处理以及加载的一个记录,也适用于其他的视频数据集。

需求所在

PyTorch提供了像对CIFAR10这样计算机视觉中经常会用到的数据集的接口,直接调用即可方便的获取到我们想要的train_x, train_y, test_x, test_y. 而我这次需要的UCF101还没有得到这样的待遇,所以首先要完成数据的读入,才能进行后面的网络训练及测试工作。

简单来说,这篇文章实现了对UCF101的处理及加载,使其能够每次根据batch_size的大小,返回需要的train_x, train_y, test_x, test_y用于视频分类任务.

不足之处

本文的处理方式简单粗暴,也适用于其他的数据集。

但是在您往下看之前,虽然文章标题已经注明未使用深度学习的框架,但为了不浪费您宝贵的时间,还是要说明一下,在写这个代码时候只想到不能直接使用PyTorch封装好的接口,忘记了它还提供了像DataLoader这样用于数据加载的函数。

所以在数据处理的效率及内存开销方面应该是有很大的改进空间的~~~

UCF101

简单介绍一下UCF101数据集。

  • 内含13320 个短视频
  • 视频来源:YouTube
  • 视频类别:101 种
  • 主要包括这5大类动作 :人和物体交互,只有肢体动作,人与人交互,玩音乐器材,各类运动

具体实现思路

数据集准备

  1. 下载UCF101数据集UCF101.zip并解压;
  2. 下载标注文件及训练数据和测试数据的列表文件The Train/Test Splits for Action Recognition on UCF101 data set:
    内含:

    以上两个文件都在UCF数据集官网可以下载。

预处理

  • 参考代码: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文件,记录该视频帧的数目。

后续处理

定义了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信息。

使用方法

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

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

参考材料

相关文章

网友评论

  • ce2ddef5effd:感谢大佬小姐姐指点,能否分享一份代码给我?邮箱:1229827640@qq.com
    与阳光共进早餐:@好帅_a8e4 已发送
  • Katherine的小世界:可以看看你的代码是怎么处理验证那一块的吗
  • Katherine的小世界:hello,我看到你这里提取了训练集和测试集,但是我们实验过程中不是经常需要验证吗。想知道验证集要怎么去提取呢
    与阳光共进早餐:@Katherine的小世界 我没有做验证集
  • 人间失格HR:感谢大佬小姐姐指点,能否分享一份代码给我?邮箱:1291156784@qq.com
    与阳光共进早餐:@人间失格HR 已发送~~
  • 3eca2b255816:感谢小姐姐分享的思路,最近在学习行为识别,想参考一下代码(包括双流模型),方便的话能否发一下邮箱188124324@qq.com 感谢
    与阳光共进早餐:@OutskiDDinG 已发送
  • WaitAMinute:您好 我没有找到标注文件 能不能发我一下下载地址 或者您方便的话发我邮件1091354718@qq.com 谢谢~
    与阳光共进早餐:已发送到您的邮箱:innocent:
  • 808b96fe474b:您好大神~ 请问双流法的代码能不能发一份给我呢? 提前谢谢啦!中秋节快乐
    michaelyin920@yahoo.com
    与阳光共进早餐:@请把小熊还给我吧 已发送,中秋同乐嘻嘻嘻
  • e24ed8bac165:小姐姐写的超级详细!有幸阅读您的笔记,最近刚开始入门视频相关的,想多学一点,希望您能在百忙中发下双流法的代码~谢谢啦😜798816403@qq.com
    与阳光共进早餐:@时光不负有心人fighting 客气~
    e24ed8bac165:@与阳光共进早餐 已收到~感谢(❁´ω`❁)
    与阳光共进早餐:@时光不负有心人fighting ok, 已经发送到您的邮箱了~:clap:
  • 84a92a963df7:感谢大神分享,能不能发一份代码给我,万分感激 quezhi930716@gmail.com
    与阳光共进早餐:@Whiplash_b6d5 已发送~
  • 9d7a34551928:感谢大神的分享,同样拜托您发一份双流法的代码给我,十分感谢。253577046@qq.com
    与阳光共进早餐:@ShangriLa_9e43 已发送
  • 6e3fd48667cf:作者,您好,写的非常好,受益匪浅。双流法代码链接失效,能麻烦您发一份给我吗?邮箱791264473@qq.com
    与阳光共进早餐:@重生_2826 不客气
    6e3fd48667cf:@与阳光共进早餐 非常感谢,代码已收到,之前看过这方面论文,就是不知道咋实现的。
    与阳光共进早餐:@重生_2826 已发送
  • 7a01f9ccb573:麻烦给我也发一下 liu_peng_fly@163.com
    与阳光共进早餐:@Centos_ec3c 已发送~
  • 夏日小白:您好,链接失效,麻烦给一份代码呗 - - , 454940304@qq.com
    与阳光共进早餐:@夏日小白 已发送~
  • b58590de4ef1:您好,可以求一份双流法预处理代码吗,感谢!422542453@qq.com
    与阳光共进早餐:@风沙_504f 不客气~
    b58590de4ef1:@与阳光共进早餐 已收到,谢谢
    与阳光共进早餐:@风沙_504f 不好意思这么晚回复~已经发送到您的邮箱了。
  • 琳子里的鸟呀:您好,我在GitHub上找的代码都执行不了,希望可以也给我发一份双流法预处理代码,多谢!842746902@qq.com
    与阳光共进早餐:@琳子里的鸟呀 已发送~
  • zhnidj:楼主你好,你的文章写的很清楚很棒,希望可以也给我发一份双流法预处理代码,多谢!1192464684@qq.com
    与阳光共进早餐:@Fighting_Zhn 已发送
  • d4e2f9e0d27f:您好,可以求一份双流法预处理代码吗,感谢!1154661023@qq.com
    d4e2f9e0d27f:@与阳光共进早餐 谢谢
    与阳光共进早餐:@H_0b7f 已发送~
  • 031adde33da5:作者您好,同求双流法预处理代码,非常感谢,285544895@qq.com
    与阳光共进早餐:@kakityuen 不客气~
    031adde33da5:@与阳光共进早餐 已收到,谢谢作者
    与阳光共进早餐:@kakityuen 已发送
  • 78454ad356fb:作者你好,同求双流法预处理代码,万分感谢,760708406@qq.com
    78454ad356fb:@与阳光共进早餐 谢谢已收到
    与阳光共进早餐:@恛忆零落 已发送~
  • d02dd20c5959:作者你好, 940265904@qq.com 万分感谢
    d02dd20c5959:@与阳光共进早餐 谢谢 已收到
    与阳光共进早餐:@曹大大_d2e7 已发送~注意查收一下哦
  • 58ce74518122:作者你好,我也求一份双流预处理的代码,最近在看视频分类搞得头都大了823152066@qq.com 谢谢
    与阳光共进早餐:@Vino_5d9d 恩~不客气ヾ(◍°∇°◍)ノ゙
    58ce74518122:@与阳光共进早餐 收到啦,谢谢博主!
    与阳光共进早餐:@Vino_5d9d 已经发送啦~
  • LiYunfei:作者你好,求一份双流代码,350896791@qq.com 谢谢
    与阳光共进早餐:@MrLi0618 已发送,查收一下~
  • d52bc319b509:你好,双流例子的链接失效了,可以分享一下嘛?谢谢
    与阳光共进早餐:@PPPENO 已经发送到您的邮箱啦
    d52bc319b509:@与阳光共进早餐 panna1995@163.com 谢谢!
    与阳光共进早餐:把邮箱给我吧~
  • 476bbc6b361e:你好。写的很棒~谢谢你的分享。
    我有点小问题想和你讨论一下。
    image_start=random.randint(1,frame_count-17) 这个的randint的参数是不是有点问题呢?
    我觉得应该是random.randint(1,frame_count-14)
    比如,一个20帧的视频,它最大可以取的应该是5吧
    与阳光共进早餐:@王江柳 嗯嗯你说的没错,我当时为了保险没仔细算…⊙▽⊙
  • 米斯特郭:你好,双流法的代码连接失效了,能麻烦您发一份给我吗?
    与阳光共进早餐:@米斯特郭 可以啊~\(≧▽≦)/~

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

本文链接:https://www.haomeiwen.com/subject/ieiaaxtx.html