视频预处理--经典方法卷积3D

1.视频转化为"图像"

首先我们的视频不能直接输入神经网络,必须要采样转化成帧。一般利用opencv来采样。

python 复制代码
 def process_video(self, video, action_name, save_dir):
        # Initialize a VideoCapture object to read video data into a numpy array
        video_filename = video.split('.')[0]
        if not os.path.exists(os.path.join(save_dir, video_filename)):
            os.mkdir(os.path.join(save_dir, video_filename))

        capture = cv2.VideoCapture(os.path.join(self.root_dir, action_name, video))

        frame_count = int(capture.get(cv2.CAP_PROP_FRAME_COUNT))
        frame_width = int(capture.get(cv2.CAP_PROP_FRAME_WIDTH))
        frame_height = int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT))

        # Make sure splited video has at least 16 frames
        EXTRACT_FREQUENCY = 4
        if frame_count // EXTRACT_FREQUENCY <= 16:
            EXTRACT_FREQUENCY -= 1
            if frame_count // EXTRACT_FREQUENCY <= 16:
                EXTRACT_FREQUENCY -= 1
                if frame_count // EXTRACT_FREQUENCY <= 16:
                    EXTRACT_FREQUENCY -= 1

        count = 0
        i = 0
        retaining = True

        while (count < frame_count and retaining):
            retaining, frame = capture.read()
            if frame is None:
                continue

            if count % EXTRACT_FREQUENCY == 0:
                if (frame_height != self.resize_height) or (frame_width != self.resize_width):
                    frame = cv2.resize(frame, (self.resize_width, self.resize_height))
                cv2.imwrite(filename=os.path.join(save_dir, video_filename, '0000{}.jpg'.format(str(i))), img=frame)
                i += 1
            count += 1

        # Release the VideoCapture once it is no longer needed
        capture.release()

1.参数定义

self:类的实例对象,用于访问类的属性和方法。

video:要处理的视频文件名,是一个字符串。

action_name:视频所在的动作类别名称,用于定位视频文件的路径。

save_dir:提取的帧图像要保存的目录

2.初始化操作

video_filename:通过 split('.')[0] 提取视频文件名(不包含扩展名)。用.当分割符

检查是否存在以视频文件名命名的目录,如果不存在则创建该目录,用于保存提取的帧图像。

cv2.VideoCapture:使用 OpenCV 的 VideoCapture 类打开视频文件,视频文件的完整路径由 self.root_diraction_namevideo 组合而成。

3.获取操作

frame_count = int(capture.get(cv2.CAP_PROP_FRAME_COUNT))

frame_width = int(capture.get(cv2.CAP_PROP_FRAME_WIDTH))

frame_height = int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT))

4.采样帧

python 复制代码
# Make sure splited video has at least 16 frames
EXTRACT_FREQUENCY = 4
if frame_count // EXTRACT_FREQUENCY <= 16:
    EXTRACT_FREQUENCY -= 1 #整除
    if frame_count // EXTRACT_FREQUENCY <= 16:
        EXTRACT_FREQUENCY -= 1
        if frame_count // EXTRACT_FREQUENCY <= 16:
            EXTRACT_FREQUENCY -= 1

这个代码无疑是证明为 检查按照当前频率提取的帧数是否小于等于 16,如果是,则将提取频率减 1,最多进行三次调整,以确保提取的帧数不少于 16 帧。

5.逐帧提取

  • count:记录当前读取的帧数。
  • i:记录保存的帧图像的编号。
  • retaining:表示是否成功读取到帧。
  • capture.read():逐帧读取视频,返回一个布尔值 retaining 和当前帧 frame
  • 如果 frameNone,则跳过当前循环,继续读取下一帧。
  • 如果当前帧数 countEXTRACT_FREQUENCY 的倍数,则进行以下操作:
    • 检查当前帧的尺寸是否与 self.resize_heightself.resize_width 一致,如果不一致,则使用 cv2.resize 函数将帧图像调整为指定尺寸。
    • 使用 cv2.imwrite 函数将帧图像保存为 JPEG 文件,文件名格式为 0000{}.jpg,其中 {} 是帧图像的编号 i
    • i 加 1。
  • count 加 1。

2.3D卷积神经网络

经过上面的转化是不是感觉,2D也可以处理视频了,毕竟处理的图像2D也是一把手。

下面我来解说一下2D.

python 复制代码
conv2d()-输入[B,inchannel,H,W] 返回也是[B,outchannel,H,W]

你可以理解为只是对一个图像进行处理!!!

对于一个视频而言,2D卷积网络在提取局部的空间信息有非常强大的功能,但是图像只是一部分(你可以理解为空间信息),但是一个视频往往蕴含十分重要的时间信息!!! ,所以3D网络也就出来了。

在三维卷积操作中,我们处理的是三维张量,其形状为 [T,H,W],其中:

  • T 表示时间维度(例如视频中的帧数)。

  • H 和 W 分别表示图像的高度和宽度。

这种数据结构非常适合处理视频或其他时间序列图像数据。通过三维卷积,我们可以在时间维度和空间维度上同时提取特征,从而捕捉到动态变化和局部信息。

三维卷积的卷积核

假设我们使用一个 3×3×3 的卷积核进行操作,这意味着:

  • 时间维度:卷积核的大小为 3,表示它会同时覆盖 3 个连续的帧。可以将其视为一个长度为 3 的滑动窗口,每次选取 3 个连续的帧进行卷积操作。

  • 空间维度:在每个时间窗口内,卷积核还会在 3×3 的空间区域内滑动,提取局部特征。

步长(Stride)和填充(Padding)

在卷积操作中,步长(stride)和填充(padding)是两个重要的参数,它们会影响输出的形状和特征提取的效果:

  • 步长(Stride):定义了卷积核在时间维度和空间维度上滑动的步长。步长越大,输出的特征图越小,但计算效率更高。

  • 填充(Padding):通过在输入张量的边界上添加额外的零值像素(或帧),可以控制输出特征图的大小,甚至保持输入和输出的尺寸一致。

示例说明

假设输入张量的形状为 [T=10,H=32,W=32],表示有 10 帧图像,每帧的大小为 32×32。我们使用一个 3×3×3 的卷积核,并设置以下参数:

  • 步长(Stride):时间维度 ST​=1,空间维度 SH​=SW​=1。

  • 填充(Padding):时间维度 PT​=1,空间维度 PH​=PW​=1。

输出形状的计算

根据卷积操作的公式,输出的形状可以通过以下方式计算:

因此,最终的输出张量形状为 [10,32,32]。

步长和填充的作用

  • 步长:通过调整步长,可以控制卷积核滑动的速度。较大的步长会减少输出特征图的大小,但可以减少计算量,适合捕捉更全局的特征。

  • 填充:通过添加零填充,可以保持输入和输出的尺寸一致,或者根据需要调整输出的大小。这在处理边界信息时尤为重要,避免因卷积核超出边界而导致信息丢失。

总结

在三维卷积操作中,卷积核不仅在空间维度上滑动,还在时间维度上滑动,从而同时捕捉时间和空间上的特征。通过合理设置步长和填充,可以灵活控制输出特征图的大小和特征提取的效果。你的理解非常准确:三维卷积核就像是一个长度为 3 的滑动窗口,每次选取 3 个连续的帧进行卷积操作,而步长和填充则进一步优化了这一过程。


3.构建一个3D卷积神经网络

python 复制代码
class C3D(nn.Module):
    """
    The C3D network.
    """

    def __init__(self, num_classes, pretrained=False):
        super(C3D, self).__init__()

        self.conv1 = nn.Conv3d(3, 64, kernel_size=(3, 3, 3), padding=(1, 1, 1))
        self.pool1 = nn.MaxPool3d(kernel_size=(1, 2, 2), stride=(1, 2, 2))

        self.conv2 = nn.Conv3d(64, 128, kernel_size=(3, 3, 3), padding=(1, 1, 1))
        self.pool2 = nn.MaxPool3d(kernel_size=(2, 2, 2), stride=(2, 2, 2))

        self.conv3a = nn.Conv3d(128, 256, kernel_size=(3, 3, 3), padding=(1, 1, 1))
        self.conv3b = nn.Conv3d(256, 256, kernel_size=(3, 3, 3), padding=(1, 1, 1))
        self.pool3 = nn.MaxPool3d(kernel_size=(2, 2, 2), stride=(2, 2, 2))

        self.conv4a = nn.Conv3d(256, 512, kernel_size=(3, 3, 3), padding=(1, 1, 1))
        self.conv4b = nn.Conv3d(512, 512, kernel_size=(3, 3, 3), padding=(1, 1, 1))
        self.pool4 = nn.MaxPool3d(kernel_size=(2, 2, 2), stride=(2, 2, 2))

        self.conv5a = nn.Conv3d(512, 512, kernel_size=(3, 3, 3), padding=(1, 1, 1))
        self.conv5b = nn.Conv3d(512, 512, kernel_size=(3, 3, 3), padding=(1, 1, 1))
        self.pool5 = nn.MaxPool3d(kernel_size=(2, 2, 2), stride=(2, 2, 2), padding=(0, 1, 1))

        self.fc6 = nn.Linear(8192, 4096)
        self.fc7 = nn.Linear(4096, 4096)
        self.fc8 = nn.Linear(4096, num_classes)

        self.dropout = nn.Dropout(p=0.5)

        self.relu = nn.ReLU()

        self.__init_weight()

        if pretrained:
            self.__load_pretrained_weights()

    def forward(self, x):
        print ('1:',x.size())
        x = self.relu(self.conv1(x))
        print ('2:',x.size())
        x = self.pool1(x)
        print ('3:',x.size())

        x = self.relu(self.conv2(x))
        print ('4:',x.size())
        x = self.pool2(x)
        print ('5:',x.size())

        x = self.relu(self.conv3a(x))
        print ('6:',x.size())
        x = self.relu(self.conv3b(x))
        print ('7:',x.size())
        x = self.pool3(x)
        print ('8:',x.size())

        x = self.relu(self.conv4a(x))
        print ('9:',x.size())
        x = self.relu(self.conv4b(x))
        print ('10:',x.size())
        x = self.pool4(x)
        print ('11:',x.size())

        x = self.relu(self.conv5a(x))
        print ('12:',x.size())
        x = self.relu(self.conv5b(x))
        print ('13:',x.size())
        x = self.pool5(x)
        print ('14:',x.size())

        x = x.view(-1, 8192)
        print ('15:',x.size())
        x = self.relu(self.fc6(x))
        print ('16:',x.size())
        x = self.dropout(x)
        x = self.relu(self.fc7(x))
        x = self.dropout(x)

        logits = self.fc8(x)
        #print ('17:',logits.size())
        return logits
相关推荐
Wis4e14 分钟前
基于PyTorch的深度学习3——基于autograd的反向传播
人工智能·pytorch·深度学习
西猫雷婶1 小时前
神经网络|(十四)|霍普菲尔德神经网络-Hebbian训练
人工智能·深度学习·神经网络
梦丶晓羽2 小时前
自然语言处理:文本分类
人工智能·python·自然语言处理·文本分类·朴素贝叶斯·逻辑斯谛回归
SuperCreators2 小时前
DeepSeek与浏览器自动化AI Agent构建指南
人工智能·自动化
美狐美颜sdk2 小时前
什么是美颜SDK?从几何变换到深度学习驱动的美颜算法详解
人工智能·深度学习·算法·美颜sdk·第三方美颜sdk·视频美颜sdk·美颜api
訾博ZiBo2 小时前
AI日报 - 2025年3月10日
人工智能
waicsdn_haha2 小时前
Postman v11 安装与API测试入门教程(Windows平台)
人工智能·windows·测试工具·mysql·postman·dbeaver·rest
一尘之中2 小时前
从青铜巨人到硅基生命:机器人文明的意识觉醒之路--三千年人类想象与科技突破的双螺旋演进)
人工智能·科技·机器人
liruiqiang052 小时前
如何理解检索增强生成
人工智能
秦南北2 小时前
国内领先的宠物类电商代运营公司品融电商
大数据·人工智能·电商