Une brève introduction à la fonction de guidage du flux optique (OFF)

Le code de cet article n'était initialement disponible que dans la version Caffe, et la version pytorch est jointe ici.

JoeHEZHAO/Optical-Flow-Guided-Feature-Pytorch : la fonctionnalité guidée par flux optique pour la reconnaissance d'action-Pytorch (github.com) icon-default.png?t=M85Bhttps://github.com/JoeHEZHAO/Optical-Flow-Guided-Feature-Pytorch est requise pour certaines raisons Je vais démonter la partie OFF du code présentée dans cet article, je vais donc d'abord couvrir les parties pertinentes de l'article, puis expliquer plus en détail. Le niveau est limité et l'expression est inexacte. Veuillez me pardonner.

Portail papier :

Fonctionnalité guidée par flux optique : une représentation de mouvement rapide et robuste pour la reconnaissance d'action vidéo (thecvf.com) icon-default.png?t=M85Bhttps://openaccess.thecvf.com/content_cvpr_2018/papers/Sun_Optical_Flow_Guided_CVPR_2018_paper.pdf Pour CNN traditionnel, lors de l'extraction de fonctionnalités sur vidéo, c'est plus difficile car les vidéos sont différentes des images et les caractéristiques spatiales + caractéristiques temporelles sont plus représentatives des vidéos. Cet article est basé sur la structure du réseau de TSN. Sur cette base, l'auteur a conçu une unité OFF pour extraire les caractéristiques de la dimension temporelle. Si vous êtes intéressé par la structure globale du réseau, vous pouvez consulter l'article. Ici, je ne présenterai que la structure et le code de la partie OFF Unit.

Tout d’abord, examinons la position de l’unité OFF dans la structure globale du réseau :

Figure 1 Structure du réseau papier

Dans la figure ci-dessus, il existe deux sous-réseaux pour l'extraction de fonctionnalités, qui extraient les fonctionnalités à différentes périodes de temps. Un sous-réseau OFF composé d'unités OFF extrait les informations temporelles via les deux sous-réseaux ci-dessus et effectue finalement la classification en fusionnant le score de classe de chacun. sous-réseau. Ensuite, examinez de plus près le diagramme de structure de l'unité OFF :

Figure 2 Structure de l'unité OFF 

Dans l'article, la combinaison de l'opérateur Sobel et de la soustraction est appelée OFF, et avec la convolution 1*1 précédente, elle forme la couche OFF. Les caractéristiques sont convoluées deux fois via l'unité OFF. Une branche utilise l'opérateur Sobel pour extraire les caractéristiques spatiales, et l'opération de soustraction par élément (Subtract) est utilisée pour extraire les informations temporelles. En combinaison avec la figure 1, les informations sortant de l'unité OFF. L'unité entre dans le module suivant via ReseNet.

Le code de base du module OFF est reflété dans le code. Il n'implémente pas OFF séparément en tant que classe. Le document contient plusieurs OFF. Certains OFF ont des numéros et des tailles de canaux d'entrée différents lors de l'exécution de la convolution. Ici, prenez simplement motion_3a comme exemple. Quant à la façon de rejoindre d'autres structures de réseau, des calculs et une conception plus approfondis des formes à l'intérieur doivent être effectués, sinon des problèmes tels que des dimensions incohérentes se produiront. Ici, OFF est implémenté en tant que classe. Le code pertinent est le suivant : Veuillez corriger moi s'il y a des erreurs.

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 pdb

class OFFNet(nn.Module):
    
    def __init__(self, batch, length, in_channels, h, w):
        super(OFFNet, self).__init__()
        self.batch = batch
        self.length = length

        self.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 32
        spatial_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]
#            ]
          ]

Il y a des commentaires dans le code, qui peuvent être lus correspondant à la structure de la couche OFF. Étant donné que le nombre de canaux d'entrée est incohérent lorsque OFF effectue une convolution dans le papier, le nombre de canaux d'entrée est transmis ici en tant que paramètre pour faciliter ajout du module à d'autres modules. Le nombre de canaux de sortie dans ce document est le même et aucun autre traitement n'est effectué. Il peut également être modifié en tant que paramètre. Celui du bas est le nombre de canaux d'entrée pour toutes les opérations OFF dans ce document. . Quant à l'opérateur sobel, etc., si vous souhaitez en savoir plus, vous pouvez vérifier vous-même les informations pertinentes.

Je suppose que tu aimes

Origine blog.csdn.net/Mr___WQ/article/details/127106835
conseillé
Classement