Correspondance stéréo - Analyse de la structure du réseau GC-Net

  1. Regardez d'abord le code correspondant au diagramme de structure du réseau et au tableau de la couche réseau.
    Insérez la description de l'image ici
    Insérez la description de l'image ici

1. Extraction de fonctionnalités de fonctionnalités unaires

1. Utilisez la convolution 2D pour extraire des fonctionnalités profondes. Tout d'abord, utilisez conv2d avec la taille du filtre: 5 * 5 et la foulée: 2 pour réduire la dimension d'entrée (1 / 2H, 1 / 2W).

 imgl0=F.relu(self.bn0(self.conv0(imgLeft)))
 imgr0=F.relu(self.bn0(self.conv0(imgRight)))
self.conv0=nn.Conv2d(3,32,5,2,2)
self.bn0=nn.BatchNorm2d(32)

2. Suivi d'un réseau résiduel à 8 couches.
Insérez la description de l'image ici

  • Notez qu'ici num_block [0], où la valeur est 8 représente les résidus à huit niveaux, et les variables sont définies ci-dessous
 self.res_block=self._make_layer(block,self.in_planes,32,num_block[0],stride=1)
 def _make_layer(self,block,in_planes,planes,num_block,stride):
        strides=[stride]+[1]*(num_block-1)
        layers=[]
        for step in strides:
            layers.append(block(in_planes,planes,step))
        return nn.Sequential(*layers)
  • Notez que ce paramètre'num_block 'est un tableau [8,1],
  • Cette boucle for doit faire attention, car la structure résiduelle pénètre num_block = 8, donc ici strides = [[1], [1], [1], [1], [1], [1], [1] , [1]], step prend la valeur à chaque fois, donc la foulée passée au bloc est stride = 1

def GcNet(height,width,maxdisp):
    return GC_NET(BasicBlock,ThreeDConv,[8,1],height,width,maxdisp)
  • Les détails du code du résidu à 8 niveaux sont analysés en détail ci-dessous
  • La structure résiduelle se compose de deux couches de conv (input = 32, output = 32, kernel_size = 3, stride = 1, padding = 1), un total de huit couches. La fonction principale de cette couche résiduelle est d'extraire les `` traits unaires '' des images gauche et droite
class BasicBlock(nn.Module):  #basic block for Conv2d
    def __init__(self,in_planes,planes,stride=1):
        super(BasicBlock,self).__init__()
        self.conv1=nn.Conv2d(in_planes,planes,kernel_size=3,stride=stride,padding=1)
        self.bn1=nn.BatchNorm2d(planes)
        self.conv2=nn.Conv2d(planes,planes,kernel_size=3,stride=1,padding=1)
        self.bn2=nn.BatchNorm2d(planes)
        self.shortcut=nn.Sequential()
    def forward(self, x):
        out=F.relu(self.bn1(self.conv1(x)))
        out=self.bn2(self.conv2(out))
        out+=self.shortcut(x)
        out=F.relu(out)
        return out
  • Enfin, une couche de convolution (pas de RELU, pas de BN) est passée. Je ne comprends pas le rôle de cette couche. Peut-être est-ce d’élargir le champ de la perception?
   self.conv1=nn.Conv2d(32,32,3,1,1)

2. Former un volume de coût

  • Les `` caractéristiques unaires '' formées par la couche résiduelle, à travers l'épissage de colonne (pourquoi w est l'épissage de colonne, veuillez consulter le résultat d'impression de mon PSMNet ), formant un corps de coût de taille (1,64,96,1 / 2H, 1/2W) . J'ai estimé cela en me référant à la sortie de PSMNet. Je n'ai pas exécuté le code. Pour configurer l'environnement, je comprends principalement la pensée.
 def cost_volume(self,imgl,imgr):
        B, C, H, W = imgl.size()
        cost_vol = torch.zeros(B, C * 2, self.maxdisp , H, W).type_as(imgl)
        for i in range(self.maxdisp):
            if i > 0:
                cost_vol[:, :C, i, :, i:] = imgl[:, :, :, i:]
                cost_vol[:, C:, i, :, i:] = imgr[:, :, :, :-i]
            else:
                cost_vol[:, :C, i, :, :] = imgl
                cost_vol[:, C:, i, :, :] = imgr
        return cost_vol
 cost_volum = self.cost_volume(imgl1, imgr1)
  • Grâce à cette méthode de jonction, les dimensions et les dimensions de la fonction sont conservées unary features, de sorte que le réseau puisse apprendre absolute representationet puisse être combiné avec le contexte. Cette méthode de jonction est meilleure que la fonction de mesure de distance (L1, L2, cosinus)
  • L'explication suivante du
    volume de coût est très vivante: (Pour une certaine entité, le volume de coût correspondant est un carré en trois dimensions, la première couche est la carte d'entités lorsque la disparité est de 0 et la deuxième couche est la carte de caractéristiques lorsque la disparité est de 1. Par analogie, il y a un total de parallaxe maximum + 1 couche, la longueur et la largeur sont respectivement la taille de la carte d'entités, en supposant qu'un total de 10 entités sont extraites, il y a 10 de ces carrés tridimensionnels)
    Insérez la description de l'image ici

3. Sous-échantillonnage convolutif 3D (encodeur)

1.La taille de la fonction fusionnée `` volume de coût '' = 64, la taille de la fonction est réduite à 32 grâce à deux couches de conv3d.

        self.conv3d_1 = nn.Conv3d(64, 32, 3, 1, 1)
        self.bn3d_1 = nn.BatchNorm3d(32)
        self.conv3d_2 = nn.Conv3d(32, 32, 3, 1, 1)
        self.bn3d_2 = nn.BatchNorm3d(32)

2. La première couche sous-échantillonnée fait 1/2 en 1/4.

self.block_3d_1 = self._make_layer(block_3d, 64, 64, num_block[1], stride=2)
  • À ce moment, num_block [1] = 1. Exécutez le module de convolution 3D, où stride = 2 est utilisé pour le sous-échantillonnage.
class ThreeDConv(nn.Module):
    def __init__(self,in_planes,planes,stride=1):
        super(ThreeDConv, self).__init__()
        self.conv1 = nn.Conv3d(in_planes, planes, kernel_size=3, stride=stride, padding=1)
        self.bn1 = nn.BatchNorm3d(planes)
        self.conv2 = nn.Conv3d(planes, planes, kernel_size=3, stride=1, padding=1)
        self.bn2 = nn.BatchNorm3d(planes)
        self.conv3=nn.Conv3d(planes,planes,kernel_size=3,stride=1,padding=1)
        self.bn3=nn.BatchNorm3d(planes)

    def forward(self, x):
        out=F.relu(self.bn1(self.conv1(x)))
        out=F.relu(self.bn2(self.conv2(out)))
        out=F.relu(self.bn3(self.conv3(out)))
        return out

Insérez la description de l'image ici

  • La description dans le texte original est que la couche de sous-échantillonnage est suivie de deux couches de conv3d () avec remuer = 2. Communiquant avec l'auteur du code, le kernel_size = 1 de la deuxième couche n'a joué qu'un rôle dans le changement de canal.
 self.conv3d_3 = nn.Conv3d(64, 64, 3, 2, 1)
        self.bn3d_3 = nn.BatchNorm3d(64)

3. Les deuxième et troisième couches de sous-échantillonnage sont similaires, parlons donc simplement de la quatrième couche de sous-échantillonnage.

  • Notez que le canal de sortie de cette couche devient 128.
  self.block_3d_4 = self._make_layer(block_3d, 64, 128, num_block[1], stride=2)

4. Suréchantillonnage (décodeur)

1. La description du texte original est que si le sous-échantillonnage augmente la vitesse et augmente le champ de réception, il perd également des détails. L'auteur utilise la couche résiduelle pour mettre en cascade la carte d'entités haute résolution avec la couche de sous-échantillonnage. L'image haute résolution est obtenue en utilisant la convolution transposée nn.ConvTranspose3d (). Voyons comment se forme la structure résiduelle.

  • Convolution transposée: notez que la taille de la caractéristique est changée en 2F = 64
      # deconv3d
        self.deconv1 = nn.ConvTranspose3d(128, 64, 3, 2, 1, 1)
        self.debn1 = nn.BatchNorm3d(64)

Insérez la description de l'image ici

  • Suréchantillonner directement les résultats du sous-échantillonnage, vous perdrez de nombreuses fonctionnalités et en cascade avec la sortie de la couche de sous-échantillonnage haute résolution pour compenser les détails manquants. Il existe quatre niveaux de suréchantillonnage et quatre structures résiduelles qui ne sont pas décrites une par une.
  deconv3d = F.relu(self.debn1(self.deconv1(conv3d_block_4)) + conv3d_block_3)
  • La dernière couche est sur-échantillonnée et en sortie
    Insérez la description de l'image ici
    2. Enfin, ajoutez une couche de convolution transposée avec un canal de sortie de '1', compressez le 'coût volum' en une couche de la carte de disparité initiale, restaurez la taille (1 D H W), notez le premier La sortie de conv2d d' un calque de 5 5 est (1 / 2H, 1 / 2W), ici restaure la taille de l'image d'origine
 original_size = [1, self.maxdisp*2, imgLeft.size(2), imgLeft.size(3)]
  • Voici la syntaxe de view ()
  self.deconv5 = nn.ConvTranspose3d(32, 1, 3, 2, 1, 1)
  out = deconv3d.view( original_size)

V. Régression de parallaxe

  1. Pour ce volume de coût correspondant, nous pouvons estimer la valeur de la disparité en utilisant l'opération soft argmin dans la dimension de disparité. La fonction présente deux caractéristiques:
  • Différentiable, vous pouvez utiliser l'optimiseur pour le calcul du gradient
  • Peut être retourné, la perte peut être transmise
 prob = F.softmax(-out, 1)
  • Régression de parallaxe
 disp1 = self.regression(prob)

Insérez la description de l'image ici

Six. Optimiseur et perte

    criterion = SmoothL1Loss().to(device)
    optimizer = optim.Adam(model.parameters(), lr=0.001)

Je suppose que tu aimes

Origine blog.csdn.net/weixin_41405284/article/details/109381542
conseillé
Classement