Optical Flow Guided Feature(OFF)简单介绍

2023-10-17 21:40

本文主要是介绍Optical Flow Guided Feature(OFF)简单介绍,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

这篇论文的code最初只有Caffe版本,这里附上pytorch版本。

JoeHEZHAO/Optical-Flow-Guided-Feature-Pytorch: Optical Flow Guided Feature for Action Recognition-Pytorch (github.com)icon-default.png?t=M85Bhttps://github.com/JoeHEZHAO/Optical-Flow-Guided-Feature-Pytorch由于一些原因,需要对这篇论文介绍的OFF部分代码进行拆解,因此先上论文中的相关部分,再做进一步解释,水平有限,表达不准确敬请谅解。

论文传送门:

Optical Flow Guided Feature: A Fast and Robust Motion Representation for Video Action Recognition (thecvf.com)icon-default.png?t=M85Bhttps://openaccess.thecvf.com/content_cvpr_2018/papers/Sun_Optical_Flow_Guided_CVPR_2018_paper.pdf对于传统的CNN来说,在对视频进行特征提取时比较困难,因为视频不同于图像,空间特征+时间特征更能代表视频。这篇论文基于TSN的网络结构,在此基础上作者设计了一个OFF Unit用来进行时间维度特征的提取,网络的整体结构有兴趣的可以查看论文,在此我仅仅介绍OFF Unit的结构以及代码部分。

首先看看OFF Unit在整体网络结构的位置:

图 1 论文网络结构

上图中有两个特征提取的子网络,提取不同时段的特征,一个由OFF Unit构成的OFF子网通过上面两个子网提取时间信息,最后通过每个子网的Class Score进行融合来进行分类。接下来进一步看OFF Unit的结构图:

图 2 OFF Unit 结构 

论文中Sobel算子和Subtract的结合称为OFF,再加上前面的1*1卷积构成OFF layer。特征经过OFF Unit进行两次卷积,一个分支利用Sobel算子提取空间特征,利用逐元素减法(Subtract)操作提取时间信息,结合图一,经OFF Unit出来的信息通过ReseNet进入下一模块。

OFF模块基本的代码在代码中有所体现,其并未将OFF单独作为Class进行实现,论文中含有多个OFF,部分OFF在进行卷积时输入的通道数以及尺寸均有所不同,此处仅仅以motion_3a为例,至于如何加入到其他的网络结构,需要对里面的shape进行更进一步的计算和设计,否则会出现维度不一致等问题,此处将OFF作为一个Class进行实现,相关代码如下,如有错误敬请指正。

from __future__ import print_function, division, absolute_import
import torch
import torch.nn as nn
import torch.utils.model_zoo as model_zoo
import os
import sys
from torch.autograd import Variable
from util import SobelFilter, SobelFilter_Diagonal
from basic_ops import *
import pdbclass OFFNet(nn.Module):def __init__(self, batch, length, in_channels, h, w):super(OFFNet, self).__init__()self.batch = batchself.length = lengthself.motion_conv_gen = nn.Conv2d(in_channels[0], 128, kernel_size=(1, 1), stride=(1,1))self.motion_spatial_down = nn.Conv2d(in_channels[1], 32, kernel_size=(1,1), stride=(1,1))self.motion_spatial_grad = nn.Conv2d(in_channels[2], 32, kernel_size=(3,3), stride=(1,1), padding=(1,1), groups=32, bias=True)self.motion_relu_gen = nn.ReLU()self.dropout = nn.Dropout(p=0.8)def forward(self, x):# print(x.shape)# motion operating on [batch * length, c, h, w] level# motion_conv_gen = self.motion_conv_gen(x)motion_conv_gen = self.motion_conv_gen(x)motion_relu_gen = self.motion_relu_gen(motion_conv_gen)channel_size = motion_relu_gen.shape[1] # reshape_rgb_frames = motion_relu_gen.view(self.batch, -1, self.h, self.w)# print(reshape_rgb_frames.shape)last_frames = reshape_rgb_frames[:, channel_size:, :, :]# print(last_frames.shape)first_frames = reshape_rgb_frames[:, :-channel_size, :, :]# print(first_frames.shape)eltwise_motion = last_frames - first_frames# print(eltwise_motion.shape)# convert back to [batch * (time - 1), c, h, w]temporal_grad = eltwise_motion.view(-1, channel_size, self.h, self.w) spatial_frames = x[:self.batch * (self.length - 1), :, :, :]# downgrade dimension to 32spatial_down = self.motion_spatial_down(spatial_frames) spatial_grad = self.motion_spatial_grad(spatial_down)spatial_grad = self.dropout(spatial_grad)# print(spatial_grad.shape)# concatenate temporal and spatial dimension# import pdb;pdb.set_trace()# print(spatial_grad.shape)# print(temporal_grad.shape)motion = torch.cat((spatial_grad, temporal_grad), dim=1)return motion#in_channels[motion_3a:[256,256,32],
#            motion_3b:[320,320,32],
#            motion_3c:[576,576,32],
#            motion_4a:[576,576,32],
#            motion_4b:[576,576,32],
#            motion_4c:[608,608,32],
#            motion_4d:[608,608,32],
#            motion_5a:[1024,1024,32],
#            motion_5b:[1024,1024,32]
#            ]]

代码中均有注释,对应于OFF layer的结构即可读懂,由于论文中OFF进行卷积时,输入通道数不一致,此处将输入通道数作为参数传入,方便将模块加入其他模块中,此论文中输出通道数一致,不做进一步处理,也可自行更改作为参数处理, 最下面的为本文中的所有OFF中操作的输入通道数。至于里面的sobel算子等,想要进一步了解的可以自行查阅相关资料。

这篇关于Optical Flow Guided Feature(OFF)简单介绍的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/228115

相关文章

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

redis群集简单部署过程

《redis群集简单部署过程》文章介绍了Redis,一个高性能的键值存储系统,其支持多种数据结构和命令,它还讨论了Redis的服务器端架构、数据存储和获取、协议和命令、高可用性方案、缓存机制以及监控和... 目录Redis介绍1. 基本概念2. 服务器端3. 存储和获取数据4. 协议和命令5. 高可用性6.

JAVA调用Deepseek的api完成基本对话简单代码示例

《JAVA调用Deepseek的api完成基本对话简单代码示例》:本文主要介绍JAVA调用Deepseek的api完成基本对话的相关资料,文中详细讲解了如何获取DeepSeekAPI密钥、添加H... 获取API密钥首先,从DeepSeek平台获取API密钥,用于身份验证。添加HTTP客户端依赖使用Jav

四种Flutter子页面向父组件传递数据的方法介绍

《四种Flutter子页面向父组件传递数据的方法介绍》在Flutter中,如果父组件需要调用子组件的方法,可以通过常用的四种方式实现,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录方法 1:使用 GlobalKey 和 State 调用子组件方法方法 2:通过回调函数(Callb

Python进阶之Excel基本操作介绍

《Python进阶之Excel基本操作介绍》在现实中,很多工作都需要与数据打交道,Excel作为常用的数据处理工具,一直备受人们的青睐,本文主要为大家介绍了一些Python中Excel的基本操作,希望... 目录概述写入使用 xlwt使用 XlsxWriter读取修改概述在现实中,很多工作都需要与数据打交

利用Python编写一个简单的聊天机器人

《利用Python编写一个简单的聊天机器人》这篇文章主要为大家详细介绍了如何利用Python编写一个简单的聊天机器人,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 使用 python 编写一个简单的聊天机器人可以从最基础的逻辑开始,然后逐步加入更复杂的功能。这里我们将先实现一个简单的

java脚本使用不同版本jdk的说明介绍

《java脚本使用不同版本jdk的说明介绍》本文介绍了在Java中执行JavaScript脚本的几种方式,包括使用ScriptEngine、Nashorn和GraalVM,ScriptEngine适用... 目录Java脚本使用不同版本jdk的说明1.使用ScriptEngine执行javascript2.

Python实现NLP的完整流程介绍

《Python实现NLP的完整流程介绍》这篇文章主要为大家详细介绍了Python实现NLP的完整流程,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 编程安装和导入必要的库2. 文本数据准备3. 文本预处理3.1 小写化3.2 分词(Tokenizatio

使用IntelliJ IDEA创建简单的Java Web项目完整步骤

《使用IntelliJIDEA创建简单的JavaWeb项目完整步骤》:本文主要介绍如何使用IntelliJIDEA创建一个简单的JavaWeb项目,实现登录、注册和查看用户列表功能,使用Se... 目录前置准备项目功能实现步骤1. 创建项目2. 配置 Tomcat3. 项目文件结构4. 创建数据库和表5.

使用PyQt5编写一个简单的取色器

《使用PyQt5编写一个简单的取色器》:本文主要介绍PyQt5搭建的一个取色器,一共写了两款应用,一款使用快捷键捕获鼠标附近图像的RGB和16进制颜色编码,一款跟随鼠标刷新图像的RGB和16... 目录取色器1取色器2PyQt5搭建的一个取色器,一共写了两款应用,一款使用快捷键捕获鼠标附近图像的RGB和16