深度学习教程计算视频中演员的屏幕时间(使用Python代码)

深度学习教程计算视频中演员的屏幕时间(使用Python代码)

当我开始深度学习之旅时,我学到的第一件事就是图像分类。一旦我掌握了图像分类,我想知道我是否可以将这种学习转移到视频上。

有没有办法建立一个模型,以特定的时间间隔自动识别给定视频中的特定人?事实证明,有,我很高兴与你分享我的方法!

在本文中,我将帮助您了解如何在视频数据上使用深度学习。为此,我们将使用来自流行的TOM和JERRY卡通系列的视频。目的是计算任何给定视频中TOM和JERRY的屏幕时间。

阅读视频并提取帧

有没有听说过翻书?如果你还没有,你错过了!看看下面的一个:

深度学习教程计算视频中演员的屏幕时间(使用Python代码)

我们在书的每一页上都有不同的图像,当我们翻阅这些页面时,我们会得到一个鲨鱼舞动画。你甚至可以把它称为一种视频。我们翻页的速度越快,可视化效果越好。换句话说,该视觉是以特定顺序排列的不同图像的集合。

同样,视频只是一组图像的集合。这些图像称为帧,可以组合以获得原始视频。因此,与视频数据相关的问题与图像分类或对象检测问题没有区别。从视频中提取帧只需要一个额外的步骤。

请记住,我们的挑战是根据给定视频计算Tom和Jerry的屏幕时间。让我首先总结一下我们将在本文中遵循的步骤来解决这个问题:

  • 导入和读取视频,从中提取帧,并将其保存为图像
  • 标记一些图像以训练模型
  • 建立我们的训练数据模型
  • 预测剩余的图像
  • 计算TOM和JERRY的屏幕时间

如何在Python中处理视频文件

让我们从导入所有必要的Python库开始。

  • Numpy
  • Pandas
  • Matplotlib
  • Keras
  • Skimage
  • OpenCV
import cv2 # for capturing videos
import math # for mathematical operations
import matplotlib.pyplot as plt # for plotting the images
%matplotlib inline
import pandas as pd
from keras.preprocessing import image # for preprocessing the images
import numpy as np # for mathematical operations
from keras.utils import np_utils
from skimage.transform import resize # for resizing images

步骤1:阅读视频,从中提取帧并将其保存为图像

现在我们将加载视频并将其转换为帧。您可以从此链接下载用于此示例的视频(https://drive.google.com/file/d/1_DcwBhYo15j7AU-v2gN61qGGd1ZablGK/view?usp=sharing)。我们将首先使用VideoCapture()函数从给定目录中捕获视频,然后我们将从视频中提取帧并使用imwrite()函数将它们保存为图像。我们来看看Python编码:

count = 0
videoFile = "Tom and jerry.mp4"
cap = cv2.VideoCapture(videoFile) # capturing the video from the given path
frameRate = cap.get(5) #frame rate
x=1
while(cap.isOpened()):
 frameId = cap.get(1) #current frame number
 ret, frame = cap.read()
 if (ret != True):
 break
 if (frameId % math.floor(frameRate) == 0):
 filename ="frame%d.jpg" % count;count+=1
 cv2.imwrite(filename, frame)
cap.release()
print ("Done!")

让我们尝试可视化图像(帧)。我们将首先使用matplotlib的imread()函数读取图像,然后使用imshow()函数绘制它。

img = plt.imread('frame0.jpg') # reading image using its name
plt.imshow(img)

深度学习教程计算视频中演员的屏幕时间(使用Python代码)

这是视频的第一帧。我们从视频的整个过程中,每秒钟提取一个帧。由于视频时长为4分58分(298秒),我们现在总共有298张图片。

我们的任务是确定哪幅图像是TOM的,哪幅图像是JERRY的。如果我们提取的图像与当前流行的Imagenet数据集中的图像相似,那么这个挑战可能是轻而易举的。我们可以简单地使用在Imagenet数据上预先训练的模型,并获得高准确度的分数!

我们的是卡通形象,没有任何一个预训练好的模特能在一个视频中识别出TOM和JERRY。

步骤2:标记一些图像以训练模型

那么我们该如何处理呢?一种可能的解决方案是手动为一些图像添加标签并在其上训练模型。一旦模型学会了模式,我们就可以用它来预测一组以前没见过的图像。

我们将其视为一个多类别的分类问题。我定义的类是:

  • 0:JERRY和TOM都没有
  • 1:JERRY
  • 2:TOM

别担心,我已经标记了所有图像,所以你没有必要!继续下载mapping.csv文件(https://drive.google.com/file/d/1NbU8Sdj_YNF5Dl_zbdeBcnqEyU3Xw9TU/view?usp=sharing),其中包含每个图像名称及其对应的类(0或1或2)。

data = pd.read_csv('mapping.csv') # reading the csv file
data.head() # printing first five rows of the file

深度学习教程计算视频中演员的屏幕时间(使用Python代码)

映射文件包含两列:

  • Image_ID:包含每个图像的名称
  • Class. Image_ID:包含每个图像的对应类

下一步是读取图像,我们将根据它们的名称(即Image_ID列)来读取这些图像。

X = [ ] # creating an empty array
for img_name in data.Image_ID:
 img = plt.imread('' + img_name)
 X.append(img) # storing each image in array X
X = np.array(X) # converting list to array

我们现在有了我们的图像。请记住,我们需要两件事来训练我们的模型:

  • 训练图像
  • 他们对应的类

由于有三个类,我们将使用keras.utils的to_categorical()函数对它们进行one hot编码。

y = data.Class
dummy_y = np_utils.to_categorical(y) # one hot encoding Classes

我们将使用一个VGG16预训练模型,它接受的输入图像(224 X 224 X 3)。我们将使用skimage的resize()函数。

image = []
for i in range(0,X.shape[0]):
 a = resize(X[i], preserve_range=True, output_shape=(224,224)).astype(int) # reshaping to 224*224*3
 image.append(a)
X = np.array(image)

所有图像都已重新整形为224 X 224 X 3.但在将任何输入传递给模型之前,我们必须根据模型的要求对其进行预处理。否则,模型将无法运行良好。使用keras.applications.vgg16的preprocess_input()函数执行此步骤。

from keras.applications.vgg16 import preprocess_input
X = preprocess_input(X, mode='tf') # preprocessing the input data

我们还需要一个验证集来检查模型在未出现过的图像上的性能。我们将利用sklearn.model_selection模块的train_test_split()函数将图像随机分成训练和验证集。

from sklearn.model_selection import train_test_split
X_train, X_valid, y_train, y_valid = train_test_split(X, dummy_y, test_size=0.3, random_state=42) # preparing the validation set

第3步:构建模型

下一步是构建我们的模型。如上所述,我们将使用VGG16预训练模型完成此任务。让我们首先导入所需的Python库来构建模型:

from keras.models import Sequential
from keras.applications.vgg16 import VGG16
from keras.layers import Dense, InputLayer, Dropout

我们现在将加载VGG16预训练模型并将其存储为base_model:

base_model = VGG16(weights='imagenet', include_top=False, input_shape=(224, 224, 3)) # include_top=False to remove the top layer

我们将使用此模型对X_train和X_valid进行预测,获取特征,然后使用这些特征重新训练模型。

X_train = base_model.predict(X_train)
X_valid = base_model.predict(X_valid)
X_train.shape, X_valid.shape

X_train和X_valid的shape分别是(208,7,7,512),(90,7,7,512)。为了将它传递给我们的神经网络,我们必须将其reshape 为1-D。

X_train = X_train.reshape(208, 7*7*512) # converting to 1-D
X_valid = X_valid.reshape(90, 7*7*512)

现在,我们将对图像进行预处理,使其为zero-centered,这有助于模型更快地收敛。

train = X_train/X_train.max() # centering the data
X_valid = X_valid/X_train.max()

最后,我们将构建我们的模型。这一步可分为3个子步骤:

  • 建立模型
  • 编译模型
  • 训练模型
# i. Building the model
model = Sequential()
model.add(InputLayer((7*7*512,))) # input layer
model.add(Dense(units=1024, activation='sigmoid')) # hidden layer
model.add(Dense(3, activation='sigmoid')) # output layer

让我们使用summary()函数检查模型的摘要:

model.summary()

深度学习教程计算视频中演员的屏幕时间(使用Python代码)

我们有一个隐藏层,有1,024个神经元,一个输出层有3个神经元(因为我们有3个类可以预测)。现在我们将编译我们的模型:

# ii. Compiling the model
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

在最后一步中,我们将拟合模型并同时检查其在性能,即验证图像:

# iii. Training the model
model.fit(train, y_train, epochs=100, validation_data=(X_valid, y_valid))

深度学习教程计算视频中演员的屏幕时间(使用Python代码)

我们可以看到它在训练和验证图像上的表现非常好。获得了大约85%的准确度。这就是我们如何在视频数据上训练模型以获得每帧的预测。

计算屏幕时间 - 一个简单的解决方案

首先,从这里下载我们将在本节中使用的视频(https://drive.google.com/file/d/1MQHRosZmeYpK2onCWr_A9p5SI93pEDw0/view?usp=sharing)。完成后,继续加载视频并从中提取帧。我们将按照与上述相同的步骤操作:

count = 0
videoFile = "Tom and Jerry 3.mp4"
cap = cv2.VideoCapture(videoFile)
frameRate = cap.get(5) #frame rate
x=1
while(cap.isOpened()):
 frameId = cap.get(1) #current frame number
 ret, frame = cap.read()
 if (ret != True):
 break
 if (frameId % math.floor(frameRate) == 0):
 filename ="test%d.jpg" % count;count+=1
 cv2.imwrite(filename, frame)
cap.release()
print ("Done!")

从新视频中提取帧后,我们现在将加载test.csv文件,其中包含每个提取帧的名称。下载test.csv文件并加载它(https://drive.google.com/open?id=1uIAXp_2WHwb_SLZF3fwpW9lbo9eRaTtp):

test = pd.read_csv('test.csv')

接下来,我们将导入图像进行测试,然后根据上述预训练模型的要求对其进行reshape :

test_image = []
for img_name in test.Image_ID:
 img = plt.imread('' + img_name)
 test_image.append(img)
test_img = np.array(test_image)
test_image = []
for i in range(0,test_img.shape[0]):
 a = resize(test_img[i], preserve_range=True, output_shape=(224,224)).astype(int)
 test_image.append(a)
test_image = np.array(test_image)

我们需要对这些图像进行更改,类似于我们对训练图像所做的更改。我们将预处理图像,使用base_model.predict()函数使用VGG16预训练模型从这些图像中提取特征,将这些图像reshape为1-D形式,并使它们zero-centered:

# preprocessing the images
test_image = preprocess_input(test_image, mode='tf')
 
# extracting features from the images using pretrained model
test_image = base_model.predict(test_image)
 
# converting the images to 1-D form
test_image = test_image.reshape(186, 7*7*512)
 
# zero centered images
test_image = test_image/test_image.max()

由于我们之前已经训练过该模型,因此我们将利用该模型对这些图像进行预测。

步骤-4:对剩余图像进行预测

predictions = model.predict_classes(test_image)

步骤-5计算TOM和JERRY的屏幕时间

Class'1'表示存在JERRY,而Class'2'表示存在TOM。我们将利用上述预测来计算屏幕时间:

print("The screen time of JERRY is", predictions[predictions==1].shape[0], "seconds")
print("The screen time of TOM is", predictions[predictions==2].shape[0], "seconds")

深度学习教程计算视频中演员的屏幕时间(使用Python代码)

我学到的东西

在本节中,我将详细介绍我所面临的一些困难,以及我如何解决它们。之后,我为最终的模型提供了完整的代码,给了我最好的准确性。

首先,我尝试使用预训练模型而不删除top层。结果并不令人满意。可能的原因:可能是这些是卡通图像,我们的预训练模型是在实际图像上训练的,因此无法对这些卡通图像进行分类。为了解决这个问题,我使用少量标记图像重新训练了预训练模型,结果与之前的结果相比更好。

即使对标记图像进行训练,准确率仍不理想。该模型不能很好地处理训练图像本身。所以,我试着增加层的数量。增加层数被证明是提高训练精度的好方法,但是训练和验证精度之间没有同步。模型过度拟合,在不可见数据上的性能不理想。所以我在每一层密集层之后都添加了一个drop - out层,这样在训练和验证精度之间就有了很好的同步。

我注意到类不平衡。TOM有更多的屏幕时间,所以预测是由它主导的,大多数帧被预测为TOM。为了克服这个问题并使类平衡,我使用了sklearn.utils.class_weight模块的compute_class_weight()函数。与具有较高值计数的类相比,它为具有较低值计数的类分配较高权重。

我还使用模型检查点来保存最好的模型,即产生最低验证损失的模型,然后使用该模型进行最后的预测。我将总结上述所有步骤并给出最终代码。可以在testing.csv文件中找到测试图像的实际类。

import cv2
import math
import matplotlib.pyplot as plt
import pandas as pd
%matplotlib inline
from keras.preprocessing import image
import numpy as np
from skimage.transform import resize
count = 0
videoFile = "Tom and jerry.mp4"
cap = cv2.VideoCapture(videoFile)
frameRate = cap.get(5) #frame rate
x=1
while(cap.isOpened()):
 frameId = cap.get(1) #current frame number
 ret, frame = cap.read()
 if (ret != True):
 break
 if (frameId % math.floor(frameRate) == 0):
 filename ="frame%d.jpg" % count;count+=1
 cv2.imwrite(filename, frame)
cap.release()
print ("Done!")

Done!

count = 0
videoFile = "Tom and Jerry 3.mp4"
cap = cv2.VideoCapture(videoFile)
frameRate = cap.get(5) #frame rate
x=1
while(cap.isOpened()):
 frameId = cap.get(1) #current frame number
 ret, frame = cap.read()
 if (ret != True):
 break
 if (frameId % math.floor(frameRate) == 0):
 filename ="test%d.jpg" % count;count+=1
 cv2.imwrite(filename, frame)
cap.release()
print ("Done!")

Done!

data = pd.read_csv('mapping.csv')
test = pd.read_csv('testing.csv')
X = []
for img_name in data.Image_ID:
 img = plt.imread('' + img_name)
 X.append(img)
X = np.array(X)
test_image = []
for img_name in test.Image_ID:
 img = plt.imread('' + img_name)
 test_image.append(img)
test_img = np.array(test_image)
from keras.utils import np_utils
train_y = np_utils.to_categorical(data.Class)
test_y = np_utils.to_categorical(test.Class)
image = []
for i in range(0,X.shape[0]):
 a = resize(X[i], preserve_range=True, output_shape=(224,224,3)).astype(int)
 image.append(a)
X = np.array(image)
test_image = []
for i in range(0,test_img.shape[0]):
 a = resize(test_img[i], preserve_range=True, output_shape=(224,224)).astype(int)
 test_image.append(a)
test_image = np.array(test_image)
from keras.applications.vgg16 import preprocess_input
X = preprocess_input(X, mode='tf')
test_image = preprocess_input(test_image, mode='tf')
from sklearn.model_selection import train_test_split
X_train, X_valid, y_train, y_valid = train_test_split(X, train_y, test_size=0.3, random_state=42)
from keras.models import Sequential
from keras.applications.vgg16 import VGG16
from keras.layers import Dense, InputLayer, Dropout
base_model = VGG16(weights='imagenet', include_top=False, input_shape=(224, 224, 3))
X_train = base_model.predict(X_train)
X_valid = base_model.predict(X_valid)
test_image = base_model.predict(test_image)
X_train = X_train.reshape(208, 7*7*512)
X_valid = X_valid.reshape(90, 7*7*512)
test_image = test_image.reshape(186, 7*7*512)
train = X_train/X_train.max()
X_valid = X_valid/X_train.max()
test_image = test_image/test_image.max()
model = Sequential()
model.add(InputLayer((7*7*512,))) # input layer
model.add(Dense(units=1024, activation='sigmoid')) # hidden layer
model.add(Dropout(0.5)) # adding dropout
model.add(Dense(units=512, activation='sigmoid')) # hidden layer
model.add(Dropout(0.5)) # adding dropout
model.add(Dense(units=256, activation='sigmoid')) # hidden layer
model.add(Dropout(0.5)) # adding dropout
model.add(Dense(3, activation='sigmoid')) # output layer
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
from sklearn.utils.class_weight import compute_class_weight, compute_sample_weight
class_weights = compute_class_weight('balanced',np.unique(data.Class), data.Class) # computing weights of different classes
from keras.callbacks import ModelCheckpoint
filepath="weights.best.hdf5"
checkpoint = ModelCheckpoint(filepath, monitor='val_loss', verbose=1, save_best_only=True, mode='min')
callbacks_list = [checkpoint] # model check pointing based on validation loss
model.fit(train, y_train, epochs=100, validation_data=(X_valid, y_valid), class_weight=class_weights, callbacks=callbacks_list)

深度学习教程计算视频中演员的屏幕时间(使用Python代码)

model.load_weights("weights.best.hdf5")
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
scores = model.evaluate(test_image, test_y)
print("%s: %.2f%%" % (model.metrics_names[1], scores[1]*100))

深度学习教程计算视频中演员的屏幕时间(使用Python代码)

结论

使用此模型,我们在验证数据上获得了大约88%的准确率,在测试数据上获得了64%的准确度。

在测试数据上获得低精度的一个可能原因可能是缺乏训练数据。由于该模型对TOM和JERRY这样的卡通形象知之甚少,因此我们必须在训练过程中为其提供更多图像。我的建议是从不同的TOM和JERRY视频中提取更多帧,相应地标记它们,并使用它们来训练模型。一旦模型看到更多图像,就很有可能导致更好的分类结果。

这些模型可以帮助我们在各个领域:

  • 我们可以计算电影中特定演员的屏幕时间
  • 计算你最喜欢的超级英雄的屏幕时间等。

这些只是可以使用此技术的几个示例。你可以自己想出更多这样的应用程序!

相关推荐