視頻數(shù)據(jù)集UCF101的處理與加載(未使用深度學習框架)

一 寫在前面

未經(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ù)集準備

  1. 下載UCF101數(shù)據(jù)集UCF101.zip并解壓;
  2. 下載標注文件及訓練數(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é)果:

  1. 將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渐排。

  2. 實現(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ù):

  1. get_className()
    根據(jù)下載號的標注文件中的classInd.txt文件獲取到每個index對應(yīng)的value.

  2. get_train()
    根據(jù)下載好的標注文件TrainList.txt獲取需要訓練的視頻路徑train_x_path和對應(yīng)的類別標注信息train_x瓤帚。

  3. get_label()
    根據(jù)文件名提取該視頻所屬的視頻類別描姚。

  4. get_test()
    根據(jù)下載好的標注文件TestList.txt,得到要測試的路徑名test_x_path戈次,并根據(jù)路徑名調(diào)用上面的函數(shù)得到正確的標注信息test_y_label轩勘,用于計算預(yù)測精度。

  5. get_single_image()
    根據(jù)圖片的路徑名讀取圖片信息怯邪,本文的處理結(jié)果為(3,160,160)大小的Tensor.

  6. get_single_video_x()
    根據(jù)視頻圖像的路徑名隨機獲取16幀連續(xù)的幀绊寻。

  7. set_mode()
    設(shè)置當前要取的數(shù)據(jù)是訓練數(shù)據(jù)還是測試數(shù)據(jù)。

  8. get_minibatches_index()
    根據(jù)總共要訓練(測試)的數(shù)量,以及batch_size澄步,返回每次要訓練(測試)的視頻標號冰蘑。

  9. __getitem__()
    利用了python中的特殊函數(shù),可以使用索引訪問元素村缸,并自動迭代懂缕。所以利用這個特性,用batch_index作為索引王凑,每次根據(jù)當前mode為訓練還是測試搪柑,返回需要的值。

  10. __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ù)的吧渊额。

有問題歡迎簡信交流,謝謝垒拢!

參考材料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末旬迹,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子求类,更是在濱河造成了極大的恐慌奔垦,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件尸疆,死亡現(xiàn)場離奇詭異椿猎,居然都是意外死亡,警方通過查閱死者的電腦和手機寿弱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門犯眠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人症革,你說我怎么就攤上這事筐咧。” “怎么了噪矛?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵量蕊,是天一觀的道長。 經(jīng)常有香客問我摩疑,道長危融,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任雷袋,我火速辦了婚禮吉殃,結(jié)果婚禮上辞居,老公的妹妹穿的比我還像新娘。我一直安慰自己蛋勺,他們只是感情好瓦灶,可當我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著抱完,像睡著了一般贼陶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上巧娱,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天碉怔,我揣著相機與錄音,去河邊找鬼禁添。 笑死撮胧,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的老翘。 我是一名探鬼主播芹啥,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼铺峭!你這毒婦竟也來了墓怀?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤卫键,失蹤者是張志新(化名)和其女友劉穎傀履,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體永罚,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡啤呼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了呢袱。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡翅敌,死狀恐怖羞福,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蚯涮,我是刑警寧澤治专,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站遭顶,受9級特大地震影響张峰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜棒旗,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一喘批、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦饶深、人聲如沸餐曹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽台猴。三九已至,卻和暖如春俱两,著一層夾襖步出監(jiān)牢的瞬間饱狂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工宪彩, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留嗡官,地道東北人。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓毯焕,卻偏偏與公主長得像衍腥,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子纳猫,可洞房花燭夜當晚...
    茶點故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內(nèi)容