3v324v23's picture
add
c310e19
raw
history blame
7.33 kB
# #!/usr/bin/env python3
# # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
# import torch
# import torch.nn.functional as F
# from torch import nn
# class FPN(nn.Module):
# """
# Module that adds FPN on top of a list of feature maps.
# The feature maps are currently supposed to be in increasing depth
# order, and must be consecutive
# """
# def __init__(self, in_channels_list, out_channels, top_blocks=None):
# """
# Arguments:
# in_channels_list (list[int]): number of channels for each feature map that
# will be fed
# out_channels (int): number of channels of the FPN representation
# top_blocks (nn.Module or None): if provided, an extra operation will
# be performed on the output of the last (smallest resolution)
# FPN output, and the result will extend the result list
# """
# super(FPN, self).__init__()
# self.inner_blocks = []
# self.layer_blocks = []
# for idx, in_channels in enumerate(in_channels_list, 1):
# inner_block = "fpn_inner{}".format(idx)
# layer_block = "fpn_layer{}".format(idx)
# inner_block_module = nn.Conv2d(in_channels, out_channels, 1)
# layer_block_module = nn.Conv2d(out_channels, out_channels, 3, 1, 1)
# for module in [inner_block_module, layer_block_module]:
# # Caffe2 implementation uses XavierFill, which in fact
# # corresponds to kaiming_uniform_ in PyTorch
# nn.init.kaiming_uniform_(module.weight, a=1)
# nn.init.constant_(module.bias, 0)
# self.add_module(inner_block, inner_block_module)
# self.add_module(layer_block, layer_block_module)
# self.inner_blocks.append(inner_block)
# self.layer_blocks.append(layer_block)
# self.top_blocks = top_blocks
# def forward(self, x):
# """
# Arguments:
# x (list[Tensor]): feature maps for each feature level.
# Returns:
# results (tuple[Tensor]): feature maps after FPN layers.
# They are ordered from highest resolution first.
# """
# last_inner = getattr(self, self.inner_blocks[-1])(x[-1])
# results = []
# results.append(getattr(self, self.layer_blocks[-1])(last_inner))
# for feature, inner_block, layer_block in zip(
# x[:-1][::-1], self.inner_blocks[:-1][::-1], self.layer_blocks[:-1][::-1]
# ):
# inner_top_down = F.interpolate(last_inner, scale_factor=2, mode="nearest")
# inner_lateral = getattr(self, inner_block)(feature)
# # TODO use size instead of scale to make it robust to different sizes
# # inner_top_down = F.upsample(last_inner, size=inner_lateral.shape[-2:],
# # mode='bilinear', align_corners=False)
# last_inner = inner_lateral + inner_top_down
# results.insert(0, getattr(self, layer_block)(last_inner))
# if self.top_blocks is not None:
# last_results = self.top_blocks(results[-1])
# results.extend(last_results)
# return tuple(results)
# class LastLevelMaxPool(nn.Module):
# def forward(self, x):
# return [F.max_pool2d(x, 1, 2, 0)]
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
import torch
import torch.nn.functional as F
from torch import nn
class FPN(nn.Module):
"""
Module that adds FPN on top of a list of feature maps.
The feature maps are currently supposed to be in increasing depth
order, and must be consecutive
"""
def __init__(
self, in_channels_list, out_channels, conv_block, top_blocks=None
):
"""
Arguments:
in_channels_list (list[int]): number of channels for each feature map that
will be fed
out_channels (int): number of channels of the FPN representation
top_blocks (nn.Module or None): if provided, an extra operation will
be performed on the output of the last (smallest resolution)
FPN output, and the result will extend the result list
"""
super(FPN, self).__init__()
self.inner_blocks = []
self.layer_blocks = []
for idx, in_channels in enumerate(in_channels_list, 1):
inner_block = "fpn_inner{}".format(idx)
layer_block = "fpn_layer{}".format(idx)
if in_channels == 0:
continue
inner_block_module = conv_block(in_channels, out_channels, 1)
layer_block_module = conv_block(out_channels, out_channels, 3, 1)
self.add_module(inner_block, inner_block_module)
self.add_module(layer_block, layer_block_module)
self.inner_blocks.append(inner_block)
self.layer_blocks.append(layer_block)
self.top_blocks = top_blocks
def forward(self, x):
"""
Arguments:
x (list[Tensor]): feature maps for each feature level.
Returns:
results (tuple[Tensor]): feature maps after FPN layers.
They are ordered from highest resolution first.
"""
last_inner = getattr(self, self.inner_blocks[-1])(x[-1])
results = []
results.append(getattr(self, self.layer_blocks[-1])(last_inner))
for feature, inner_block, layer_block in zip(
x[:-1][::-1], self.inner_blocks[:-1][::-1], self.layer_blocks[:-1][::-1]
):
if not inner_block:
continue
inner_top_down = F.interpolate(last_inner, scale_factor=2, mode="nearest")
inner_lateral = getattr(self, inner_block)(feature)
# TODO use size instead of scale to make it robust to different sizes
# inner_top_down = F.upsample(last_inner, size=inner_lateral.shape[-2:],
# mode='bilinear', align_corners=False)
last_inner = inner_lateral + inner_top_down
results.insert(0, getattr(self, layer_block)(last_inner))
if isinstance(self.top_blocks, LastLevelP6P7):
last_results = self.top_blocks(x[-1], results[-1])
results.extend(last_results)
elif isinstance(self.top_blocks, LastLevelMaxPool):
last_results = self.top_blocks(results[-1])
results.extend(last_results)
return tuple(results)
class LastLevelMaxPool(nn.Module):
def forward(self, x):
return [F.max_pool2d(x, 1, 2, 0)]
class LastLevelP6P7(nn.Module):
"""
This module is used in RetinaNet to generate extra layers, P6 and P7.
"""
def __init__(self, in_channels, out_channels):
super(LastLevelP6P7, self).__init__()
self.p6 = nn.Conv2d(in_channels, out_channels, 3, 2, 1)
self.p7 = nn.Conv2d(out_channels, out_channels, 3, 2, 1)
for module in [self.p6, self.p7]:
nn.init.kaiming_uniform_(module.weight, a=1)
nn.init.constant_(module.bias, 0)
self.use_P5 = in_channels == out_channels
def forward(self, c5, p5):
x = p5 if self.use_P5 else c5
p6 = self.p6(x)
p7 = self.p7(F.relu(p6))
return [p6, p7]