深度學習進階,Keras視頻分類
Keras視頻分類
在此教程中,您將學習如何使用Keras、Python 和深度學習執行視頻分類。
具體來說,您將學習:
視頻分類與標準圖像分類的區別
如何使用 Keras 進行圖像分類來訓練一個旋轉神經網絡
如何采取CNN,然后使用它的視頻分類
如何使用滾動預測平均值來減少結果中的"閃爍"
本教程將作為對時間性深度學習概念的介紹,為我們討論長期短期記憶網絡(LSTM)并最終獲得人類活動識別鋪平道路。
要學習如何執行視頻分類與Keras和深度學習,只是繼續閱讀!
視頻可以理解為一系列單獨的圖像;因此,許多深度學習從業者會很快將視頻分類視為執行圖像分類的總次數為 N次,其中N是視頻中幀的總數。
不過, 這種方法有問題。
視頻分類不僅僅是簡單的圖像分類 -*視頻,我們通常可以假設視頻中的后續幀與其*語義內容*相關*。
如果我們能夠利用視頻的時效性,我們就能改進我們的實際視頻分類結果。
神經網絡架構,如長期短期記憶 (LSTM) 和經常性神經網絡 (RNN) 適合時間系列數據 - 我們將在以后的教程中涵蓋兩個主題 - 但在某些情況下,它們可能過于殺戮。當涉及到對數千個視頻文件進行培訓時,它們也非常耗時,你可以想象。
相反,對于某些應用程序,您只需要*滾動平均*超過預測。
在本教程的其余部分,您將學習如何訓練 CNN進行圖像分類(特別是體育分類),然后通過采用滾動平均值將其轉化為更準確的視頻分類器。
在執行圖像分類時,我們:
向我們的 CNN 輸入圖像
從 CNN 獲取預測
選擇具有最大相應概率的標簽
由于視頻只是一系列幀,一個天真的視頻分類方法將是:
在視頻文件中的所有幀上循環
對于每個幀,通過 CNN 傳遞幀
單獨和獨立**地對每個幀進行分類
選擇具有最大相應概率的標簽
標記幀并將輸出幀寫入磁盤
不過,這種方法存在問題 - 如果您曾經嘗試將簡單的圖像分類應用于視頻分類,您可能會遇到某種**“預測閃爍”,**如本節頂部視頻中所示。請注意,在這個可視化中,我們看到我們的 CNN 在兩個預測之間移動:"足球"和正確的標簽"weight_lifting"。
視頻顯然是舉重,我們希望我們的整個視頻被貼上這樣的標簽 - 但我們怎樣才能防止CNN在這兩個標簽之間"閃爍"?
一個簡單而優雅的解決方案是利用滾動預測平均值。
我們的算法現在變成:
在視頻文件中的所有幀上循環
對于每個幀,通過 CNN 傳遞幀
從 CNN 獲取預測
保留最后K預測的列表
計算最后K預測的平均值,并選擇具有最大相應概率的標簽
標記幀并將輸出幀寫入磁盤
此算法的結果可以在這篇文章的頂部的視頻中看到 - 注意預測閃爍是如何消失,整個視頻剪輯被正確標記!
在本教程的其余部分,您將學習如何實現此算法的視頻分類與 Keras。
體育分類數據集
**圖1:**由GitHub 用戶使用Google 圖片搜索策劃的體育數據集"無足小視"。我們將使用此圖像數據集與 Keras 進行視頻分類。(圖片來源)
我們今天在這里使用的數據集用于體育/活動分類。數據集由阿努巴夫·邁蒂策劃,從谷歌圖片下載照片(您也可以使用必應)為以下類別:
游泳
羽毛球
摔跤
奧運射擊
蟋蟀
足球
網球
曲棍球
冰球
卡巴迪
WWE
體育館
舉重
排球
乒乓球
棒球
一級方程式
摩托 GP
棋
拳擊
擊劍
籃球
為了節省時間、計算資源,并演示實際視頻分類算法(本教程的實際點),我們將對運動類型數據集的子集進行培訓:
足球(即足球):799張圖片
網球: 718 圖片
舉重: 577 圖像
讓我們繼續下載我們的數據集!
項目結構
項目結構如下:
$ tree --dirsfirst --filelimit 50 . ├── Sports-Type-Classifier │ ├── data │ │ ├── football [799 entries] │ │ ├── tennis [718 entries] │ │ └── weight_lifting [577 entries] ├── example_clips │ ├── lifting.mp4 │ ├── soccer.mp4 │ └── tennis.mp4 ├── model │ ├── activity.model │ └── lb.pickle ├── output ├── plot.png ├── predict_video.py └── train.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
我們的訓練圖像數據位于 Sports-Type-Classifier/data/ 目錄中,按類別組織。
我從 YouTube 中為我們提取了三個 example_clips/ 來測試我們的模型。三個剪輯的積分位于“Keras 視頻分類結果”部分的底部。
我們的分類器文件位于 model/ 目錄中。包括 activity.model(經過訓練的 Keras 模型)和 lb.pickle(我們的標簽二值化器)。
一個空的 output/ 文件夾是我們將存儲視頻分類結果的位置。
我們將在今天的教程中介紹兩個 Python 腳本:
train.py :一個 Keras 訓練腳本,它抓取我們關心的數據集類圖像,加載 ResNet50 CNN,并應用 ImageNet 權重的轉移學習/微調來訓練我們的模型。訓練腳本生成/輸出三個文件:
model/activity.model :基于 ResNet50 的微調分類器,用于識別運動。
model/lb.pickle :包含我們獨特的類標簽的序列化標簽二值化器。
plot.png :準確率/損失訓練歷史圖。
predict_video.py :從 example_clips/ 加載輸入視頻,然后使用今天的滾動平均方法對視頻進行理想的分類。
實施我們的 Keras 培訓腳本
讓我們繼續實施我們的訓練腳本,用于訓練Keras CNN來識別每一項體育活動。
打開train.py文件并插入以下代碼:
# set the matplotlib backend so figures can be saved in the background import matplotlib matplotlib.use("Agg") # import the necessary packages from tensorflow.keras.preprocessing.image import ImageDataGenerator from tensorflow.keras.layers import AveragePooling2D from tensorflow.keras.applications import ResNet50 from tensorflow.keras.layers import Dropout from tensorflow.keras.layers import Flatten from tensorflow.keras.layers import Dense from tensorflow.keras.layers import Input from tensorflow.keras.models import Model from tensorflow.keras.optimizers import SGD from sklearn.preprocessing import LabelBinarizer from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report from imutils import paths import matplotlib.pyplot as plt import numpy as np import argparse import pickle import cv2 import os
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
導入必要的包來訓練我們的分類器:
matplotlib :用于繪圖。第 3 行設置后端,以便我們可以將訓練圖輸出到 .png 圖像文件。
tensorflow.keras:用于深度學習。也就是說,我們將使用 ResNet50 CNN。我們還將使用您可以在上周的教程中閱讀的 ImageDataGenerator。
sklearn :從 scikit-learn,我們將使用他們的 LabelBinarizer 實現來對我們的類標簽進行單熱編碼。
train_test_split 函數將我們的數據集分割成訓練和測試分割。我們還將以傳統格式打印分類報告。
path :包含用于列出給定路徑中的所有圖像文件的便利函數。從那里我們將能夠將我們的圖像加載到內存中。
numpy :Python 的事實上的數值處理庫。
argparse :用于解析命令行參數。
pickle :用于將我們的標簽二值化器序列化到磁盤。
cv2:OpenCV。
os :操作系統模塊將用于確保我們獲取與操作系統相關的正確文件/路徑分隔符。
現在讓我們繼續解析我們的命令行參數:
# construct the argument parser and parse the arguments ap = argparse.ArgumentParser() ap.add_argument("-d", "--dataset", required=True, help="path to input dataset") ap.add_argument("-m", "--model", required=True, help="path to output serialized model") ap.add_argument("-l", "--label-bin", required=True, help="path to output label binarizer") ap.add_argument("-e", "--epochs", type=int, default=25, help="# of epochs to train our network for") ap.add_argument("-p", "--plot", type=str, default="plot.png", help="path to output loss/accuracy plot") args = vars(ap.parse_args())
1
2
3
4
5
6
7
8
9
10
11
12
13
我們的腳本接受五個命令行參數,其中前三個是必需的:
–dataset :輸入數據集的路徑。
–model :我們輸出 Keras 模型文件的路徑。
–label-bin :我們的輸出標簽二值化器pickle文件的路徑。
–epochs :我們的網絡要訓練多少個時期——默認情況下,我們將訓練 25 個時期,但正如我將在本教程后面展示的,50 個時期可以帶來更好的結果。
–plot :我們的輸出繪圖圖像文件的路徑——默認情況下,它將被命名為 plot.png 并放置在與此訓練腳本相同的目錄中。
解析并掌握我們的命令行參數后,讓我們繼續初始化我們的 LABELS 并加載我們的數據:
# initialize the set of labels from the spots activity dataset we are # going to train our network on LABELS = set(["weight_lifting", "tennis", "football"]) # grab the list of images in our dataset directory, then initialize # the list of data (i.e., images) and class images print("[INFO] loading images...") imagePaths = list(paths.list_images(args["dataset"])) data = [] labels = [] # loop over the image paths for imagePath in imagePaths: # extract the class label from the filename label = imagePath.split(os.path.sep)[-2] # if the label of the current image is not part of of the labels # are interested in, then ignore the image if label not in LABELS: continue # load the image, convert it to RGB channel ordering, and resize # it to be a fixed 224x224 pixels, ignoring aspect ratio image = cv2.imread(imagePath) image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) image = cv2.resize(image, (224, 224)) # update the data and labels lists, respectively data.append(image) labels.append(label)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
定義類 LABELS 的集合。該集合中不存在的所有標簽都將被排除在我們的數據集之外。為了節省訓練時間,我們的數據集將只包含舉重、網球和足球/足球。通過對 LABELS 集進行更改,您可以隨意使用其他類。
初始化我們的數據和標簽列表。
遍歷所有 imagePath。
在循環中,首先我們從 imagePath 中提取類標簽
忽略不在 LABELS 集合中的任何標簽。
加載并預處理圖像。預處理包括將 OpenCV 的顏色通道交換到 Keras 兼容性并將大小調整為 224×224px。在此處閱讀有關調整 CNN 圖像大小的更多信息。要了解有關預處理重要性的更多信息,請務必參閱使用 Python 進行計算機視覺深度學習。
然后分別將圖像和標簽添加到數據和標簽列表中。
繼續,我們將對我們的標簽進行單熱編碼并分區我們的數據:
# convert the data and labels to NumPy arrays data = np.array(data) labels = np.array(labels) # perform one-hot encoding on the labels lb = LabelBinarizer() labels = lb.fit_transform(labels) # partition the data into training and testing splits using 75% of # the data for training and the remaining 25% for testing (trainX, testX, trainY, testY) = train_test_split(data, labels, test_size=0.25, stratify=labels, random_state=42)
1
2
3
4
5
6
7
8
9
10
將我們的數據和標簽列表轉換為 NumPy 數組。
執行one-hot編碼。one-hot編碼是一種通過二進制數組元素標記活動類標簽的方法。 例如,“足球”可能是 array([1, 0, 0]) 而“舉重”可能是 array([0, 0, 1]) 。 請注意在任何給定時間只有一個類是“熱的”。
按照4:1的比例,將我們的數據分成訓練和測試部分。
初始化我們的數據增強對象:
# initialize the training data augmentation object trainAug = ImageDataGenerator( rotation_range=30, zoom_range=0.15, width_shift_range=0.2, height_shift_range=0.2, shear_range=0.15, horizontal_flip=True, fill_mode="nearest") # initialize the validation/testing data augmentation object (which # we'll be adding mean subtraction to) valAug = ImageDataGenerator() # define the ImageNet mean subtraction (in RGB order) and set the # the mean subtraction value for each of the data augmentation # objects mean = np.array([123.68, 116.779, 103.939], dtype="float32") trainAug.mean = mean valAug.mean = mean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
初始化了兩個數據增強對象——一個用于訓練,一個用于驗證。 在計算機視覺的深度學習中,幾乎總是建議使用數據增強來提高模型泛化能力。
trainAug 對象對我們的數據執行隨機旋轉、縮放、移位、剪切和翻轉。 您可以在此處閱讀有關 ImageDataGenerator 和 fit 的更多信息。 正如我們上周強調的那樣,請記住,使用 Keras,圖像將即時生成(這不是附加操作)。
不會對驗證數據 (valAug) 進行擴充,但我們將執行均值減法。
平均像素值,設置 trainAug 和 valAug 的均值屬性,以便在訓練/評估期間生成圖像時進行均值減法。 現在,我們將執行我喜歡稱之為“網絡手術”的操作,作為微調的一部分:
# load the ResNet-50 network, ensuring the head FC layer sets are left # off baseModel = ResNet50(weights="imagenet", include_top=False, input_tensor=Input(shape=(224, 224, 3))) # construct the head of the model that will be placed on top of the # the base model headModel = baseModel.output headModel = AveragePooling2D(pool_size=(7, 7))(headModel) headModel = Flatten(name="flatten")(headModel) headModel = Dense(512, activation="relu")(headModel) headModel = Dropout(0.5)(headModel) headModel = Dense(len(lb.classes_), activation="softmax")(headModel) # place the head FC model on top of the base model (this will become # the actual model we will train) model = Model(inputs=baseModel.input, outputs=headModel) # loop over all layers in the base model and freeze them so they will # *not* be updated during the training process for layer in baseModel.layers: layer.trainable = False
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
加載用 ImageNet 權重預訓練的 ResNet50,同時切掉網絡的頭部。
組裝了一個新的 headModel 并將其縫合到 baseModel 上。
我們現在將凍結 baseModel,以便它不會通過反向傳播進行訓練。
讓我們繼續編譯+訓練我們的模型:
# compile our model (this needs to be done after our setting our # layers to being non-trainable) print("[INFO] compiling model...") opt = SGD(lr=1e-4, momentum=0.9, decay=1e-4 / args["epochs"]) model.compile(loss="categorical_crossentropy", optimizer=opt, metrics=["accuracy"]) # train the head of the network for a few epochs (all other layers # are frozen) -- this will allow the new FC layers to start to become # initialized with actual "learned" values versus pure random print("[INFO] training head...") H = model.fit( x=trainAug.flow(trainX, trainY, batch_size=32), steps_per_epoch=len(trainX) // 32, validation_data=valAug.flow(testX, testY), validation_steps=len(testX) // 32, epochs=args["epochs"])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
以前,TensorFlow/Keras 需要使用一種名為 .fit_generator 的方法來完成數據增強。現在,.fit 方法也可以處理數據增強,從而使代碼更加一致。這也適用于從 .predict_generator 到 .predict 的遷移。請務必查看我關于 fit 和 fit_generator 以及數據增強的文章。
使用隨機梯度下降 (SGD) 優化器編譯我們的模型,初始學習率為 1e-4,學習率衰減。我們使用“categorical_crossentropy”損失來訓練多類。如果您只使用兩個類,請務必使用“binary_crossentropy”損失。
在我們的模型上調用 fit_generator 函數用數據增強和均值減法訓練我們的網絡。 請記住,我們的 baseModel 已凍結,我們只訓練頭部。這被稱為“微調”。要快速了解微調,請務必閱讀我之前的文章。要更深入地了解微調,請獲取使用 Python 進行計算機視覺深度學習的 Practitioner Bundle 的副本。
我們將通過評估我們的網絡并繪制訓練歷史來開始總結:
# evaluate the network print("[INFO] evaluating network...") predictions = model.predict(x=testX.astype("float32"), batch_size=32) print(classification_report(testY.argmax(axis=1), predictions.argmax(axis=1), target_names=lb.classes_)) # plot the training loss and accuracy N = args["epochs"] plt.style.use("ggplot") plt.figure() plt.plot(np.arange(0, N), H.history["loss"], label="train_loss") plt.plot(np.arange(0, N), H.history["val_loss"], label="val_loss") plt.plot(np.arange(0, N), H.history["accuracy"], label="train_acc") plt.plot(np.arange(0, N), H.history["val_accuracy"], label="val_acc") plt.title("Training Loss and Accuracy on Dataset") plt.xlabel("Epoch #") plt.ylabel("Loss/Accuracy") plt.legend(loc="lower left") plt.savefig(args["plot"])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
為了使此繪圖片段與 TensorFlow 2+ 兼容,更新了 H.history 字典鍵以完全拼出“accuracy”無“acc”(H.history[“val_accuracy”] 和 H.歷史[“準確性”])。 “val”沒有拼寫為“validation”,這有點令人困惑; 我們必須學會熱愛 API 并與之共存,并始終記住這是一項正在進行的工作,世界各地的許多開發人員都在為之做出貢獻。
在我們在測試集上評估我們的網絡并打印分類報告
之后,我們繼續使用 matplotlib
繪制準確率/損失曲線。 該圖通過第 164 行保存到磁盤。
最后將我們的模型和標簽二值化器 (lb) 序列化到磁盤:
# serialize the model to disk print("[INFO] serializing network...") model.save(args["model"], save_format="h5") # serialize the label binarizer to disk f = open(args["label_bin"], "wb") f.write(pickle.dumps(lb)) f.close()
1
2
3
4
5
6
7
訓練結果
在我們 使用我們的 CNN 對視頻中的幀進行分類,然后) 利用我們的 CNN 進行視頻分類之前,我們首先需要訓練模型。
確保您已使用本教程的“下載”部分將源代碼下載到此圖像(以及下載運動類型數據集)。
從那里,打開一個終端并執行以下命令:
$ python train.py --dataset Sports-Type-Classifier/data --model model/activity.model \ --label-bin output/lb.pickle --epochs 50 [INFO] loading images... [INFO] compiling model... [INFO] training head... Epoch 1/50 48/48 [==============================] - 10s 209ms/step - loss: 1.4184 - accuracy: 0.4421 - val_loss: 0.7866 - val_accuracy: 0.6719 Epoch 2/50 48/48 [==============================] - 10s 198ms/step - loss: 0.9002 - accuracy: 0.6086 - val_loss: 0.5476 - val_accuracy: 0.7832 Epoch 3/50 48/48 [==============================] - 9s 198ms/step - loss: 0.7188 - accuracy: 0.7020 - val_loss: 0.4690 - val_accuracy: 0.8105 Epoch 4/50 48/48 [==============================] - 10s 203ms/step - loss: 0.6421 - accuracy: 0.7375 - val_loss: 0.3986 - val_accuracy: 0.8516 Epoch 5/50 48/48 [==============================] - 10s 200ms/step - loss: 0.5496 - accuracy: 0.7770 - val_loss: 0.3599 - val_accuracy: 0.8652 ... Epoch 46/50 48/48 [==============================] - 9s 192ms/step - loss: 0.2066 - accuracy: 0.9217 - val_loss: 0.1618 - val_accuracy: 0.9336 Epoch 47/50 48/48 [==============================] - 9s 193ms/step - loss: 0.2064 - accuracy: 0.9204 - val_loss: 0.1622 - val_accuracy: 0.9355 Epoch 48/50 48/48 [==============================] - 9s 192ms/step - loss: 0.2092 - accuracy: 0.9217 - val_loss: 0.1604 - val_accuracy: 0.9375 Epoch 49/50 48/48 [==============================] - 9s 195ms/step - loss: 0.1935 - accuracy: 0.9290 - val_loss: 0.1620 - val_accuracy: 0.9375 Epoch 50/50 48/48 [==============================] - 9s 192ms/step - loss: 0.2109 - accuracy: 0.9164 - val_loss: 0.1561 - val_accuracy: 0.9395 [INFO] evaluating network... precision recall f1-score support football 0.93 0.96 0.95 196 tennis 0.92 0.92 0.92 179 weight_lifting 0.97 0.92 0.95 143 accuracy 0.94 518 macro avg 0.94 0.94 0.94 518 weighted avg 0.94 0.94 0.94 518 [INFO] serializing network...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
如您所見,在體育數據集上對 ResNet50 進行微調后,我們獲得了約 94% 的準確率。 檢查我們的模型目錄,我們可以看到微調模型和標簽二值化器已經序列化到磁盤:
使用 Keras 進行視頻分類和滾動預測平均
我們現在準備通過滾動預測精度使用 Keras 實現視頻分類! 為了創建這個腳本,我們將利用視頻的時間特性,特別是假設視頻中的后續幀將具有相似的語義內容。 通過執行滾動預測準確性,我們將能夠“平滑”預測并避免“預測閃爍”。 讓我們開始吧——打開 predict_video.py 文件并插入以下代碼:
# import the necessary packages from tensorflow.keras.models import load_model from collections import deque import numpy as np import argparse import pickle import cv2 # construct the argument parser and parse the arguments ap = argparse.ArgumentParser() ap.add_argument("-m", "--model", required=True, help="path to trained serialized model") ap.add_argument("-l", "--label-bin", required=True, help="path to label binarizer") ap.add_argument("-i", "--input", required=True, help="path to our input video") ap.add_argument("-o", "--output", required=True, help="path to our output video") ap.add_argument("-s", "--size", type=int, default=128, help="size of queue for averaging") args = vars(ap.parse_args())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
加載必要的包和模塊。 特別是,我們將使用 Python 集合模塊中的 deque 來協助我們的滾動平均算法。
然后,解析五個命令行參數,其中四個是必需的:
–model :從我們之前的訓練步驟生成的輸入模型的路徑。
–label-bin :前一個腳本生成的序列化 pickle 格式標簽二值化器的路徑。
–input :用于視頻分類的輸入視頻的路徑。
–output :我們將保存到磁盤的輸出視頻的路徑。
–size :滾動平均隊列的最大大小(默認為 128)。 對于稍后的一些示例結果,我們將大小設置為 1,以便不執行平均。
有了我們的導入和命令行 args ,我們現在準備執行初始化:
# load the trained model and label binarizer from disk print("[INFO] loading model and label binarizer...") model = load_model(args["model"]) lb = pickle.loads(open(args["label_bin"], "rb").read()) # initialize the image mean for mean subtraction along with the # predictions queue mean = np.array([123.68, 116.779, 103.939][::1], dtype="float32") Q = deque(maxlen=args["size"])
1
2
3
4
5
6
7
8
加載我們的模型和標簽二值化器。
然后設置我們的平均減法值。 我們將使用雙端隊列來實現我們的滾動預測平均。
我們的雙端隊列 Q 使用等于 args[“size”] 值的 maxlen 初始化。
讓我們初始化我們的 cv2.VideoCapture 對象并開始循環視頻幀:
# initialize the video stream, pointer to output video file, and # frame dimensions vs = cv2.VideoCapture(args["input"]) writer = None (W, H) = (None, None) # loop over frames from the video file stream while True: # read the next frame from the file (grabbed, frame) = vs.read() # if the frame was not grabbed, then we have reached the end # of the stream if not grabbed: break # if the frame dimensions are empty, grab them if W is None or H is None: (H, W) = frame.shape[:2]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
抓取一個指向我們的輸入視頻文件流的指針。 我們使用 OpenCV 中的 VideoCapture 類從我們的視頻流中讀取幀。
然后,將我們的視頻編寫器和維度初始化為 None。
開始我們的視頻分類 while 循環。
首先,我們抓取一個幀。 如果幀沒有被抓取,那么我們已經到達了視頻的結尾,此時我們將中斷循環。
如果需要,然后設置我們的框架尺寸。 讓我們預處理我們的框架:
# clone the output frame, then convert it from BGR to RGB # ordering, resize the frame to a fixed 224x224, and then # perform mean subtraction output = frame.copy() frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) frame = cv2.resize(frame, (224, 224)).astype("float32") frame -= mean
1
2
3
4
5
6
7
我們的框架副本用于輸出目的。
然后,我們使用與訓練腳本相同的步驟對幀進行預處理,包括:
交換顏色通道。
調整為 224×224px。
平均減法。
接下來是幀分類推理和滾動預測平均:
# make predictions on the frame and then update the predictions # queue preds = model.predict(np.expand_dims(frame, axis=0))[0] Q.append(preds) # perform prediction averaging over the current history of # previous predictions results = np.array(Q).mean(axis=0) i = np.argmax(results) label = lb.classes_[i]
1
2
3
4
5
6
7
8
9
對當前幀進行預測。 預測結果通過第 64 行添加到 Q。
從那里,對 Q 歷史執行預測平均,從而為滾動平均值生成一個類標簽。 分解后,這些線在平均預測中找到具有最大對應概率的標簽。
現在我們有了結果標簽,讓我們注釋我們的輸出幀并將其寫入磁盤:
# draw the activity on the output frame text = "activity: {}".format(label) cv2.putText(output, text, (35, 50), cv2.FONT_HERSHEY_SIMPLEX, 1.25, (0, 255, 0), 5) # check if the video writer is None if writer is None: # initialize our video writer fourcc = cv2.VideoWriter_fourcc(*"MJPG") writer = cv2.VideoWriter(args["output"], fourcc, 30, (W, H), True) # write the output frame to disk writer.write(output) # show the output image cv2.imshow("Output", output) key = cv2.waitKey(1) & 0xFF # if the `q` key was pressed, break from the loop if key == ord("q"): break # release the file pointers print("[INFO] cleaning up...") writer.release() vs.release()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
在輸出幀上繪制預測。
如有必要,初始化視頻編寫器。 輸出幀被寫入文件(第 85 行)。 在此處閱讀有關使用 OpenCV 寫入視頻文件的更多信息。
輸出也顯示在屏幕上,直到按下 q 鍵(或如上所述到達視頻文件的末尾)。
最后,我們將執行清理。
Keras 視頻分類結果
現在我們已經使用 Keras 實現了我們的視頻分類器,讓我們將其投入使用。
讓我們將視頻分類應用于“網球”剪輯——但讓我們將隊列的 --size 設置為 1,輕松地將視頻分類轉換為標準圖像分類:
$ python predict_video.py --model model/activity.model \ --label-bin model/lb.pickle \ --input example_clips/tennis.mp4 \ --output output/tennis_1frame.avi \ --size 1 Using TensorFlow backend. [INFO] loading model and label binarizer... [INFO] cleaning up...
1
2
3
4
5
6
7
8
Video classification with Keras and Deep Learning - PyImageSearch
正如你所看到的,有相當多的標簽閃爍——我們的 CNN 認為某些幀是“網球”(正確)而其他幀是“足球”(不正確)。
現在讓我們使用默認隊列 --size 128,從而利用我們的預測平均算法來平滑結果:
$ python predict_video.py --model model/activity.model \ --label-bin model/lb.pickle \ --input example_clips/tennis.mp4 \ --output output/tennis_128frames_smoothened.avi \ --size 128 Using TensorFlow backend. [INFO] loading model and label binarizer... [INFO] cleaning up...
1
2
3
4
5
6
7
8
Video classification with Keras and Deep Learning - PyImageSearch
請注意我們如何正確地將此視頻標記為“網球”!
在以后的教程中,我們將介紹更高級的活動和視頻分類方法,包括 LSTM 和 RNN。
總結
在本教程中,您學習了如何使用 Keras 和深度學習執行視頻分類。
一種簡單的視頻分類算法是將視頻的每一幀視為獨立于其他幀。這種類型的實現會導致“標簽閃爍”,即 CNN 為后續幀返回不同的標簽,即使這些幀應該是相同的標簽!
更高級的神經網絡,包括 LSTM 和更通用的 RNN,可以幫助解決這個問題并帶來更高的準確性。然而,LSTMs 和 RNNs 可能會根據你正在做的事情產生戲劇性的矯枉過正——在某些情況下,簡單的滾動預測平均會給你帶來你需要的結果。
使用滾動預測平均,您可以維護來自 CNN 的最后 K 個預測的列表。然后我們取這最后 K 個預測,對它們求平均,選擇概率最大的標簽,然后選擇這個標簽對當前幀進行分類。這里的假設是視頻中的后續幀將具有相似的語義內容。
如果這個假設成立,那么我們可以利用視頻的時間特性,假設前一幀與當前幀相似。 因此,平均使我們能夠平滑預測并形成更好的視頻分類器。
在以后的教程中,我們還將討論更高級的 LSTM 和 RNN。
Keras 機器學習 深度學習 視頻
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。