| | |
| | import numpy as np |
| | from typing import List |
| | import fvcore.nn.weight_init as weight_init |
| | import torch |
| | from torch import nn |
| |
|
| | from detectron2.config import configurable |
| | from detectron2.layers import Conv2d, ShapeSpec, get_norm |
| | from detectron2.utils.registry import Registry |
| |
|
| | __all__ = ["FastRCNNConvFCHead", "build_box_head", "ROI_BOX_HEAD_REGISTRY"] |
| |
|
| | ROI_BOX_HEAD_REGISTRY = Registry("ROI_BOX_HEAD") |
| | ROI_BOX_HEAD_REGISTRY.__doc__ = """ |
| | Registry for box heads, which make box predictions from per-region features. |
| | |
| | The registered object will be called with `obj(cfg, input_shape)`. |
| | """ |
| |
|
| |
|
| | |
| | |
| | |
| | @ROI_BOX_HEAD_REGISTRY.register() |
| | class FastRCNNConvFCHead(nn.Sequential): |
| | """ |
| | A head with several 3x3 conv layers (each followed by norm & relu) and then |
| | several fc layers (each followed by relu). |
| | """ |
| |
|
| | @configurable |
| | def __init__( |
| | self, input_shape: ShapeSpec, *, conv_dims: List[int], fc_dims: List[int], conv_norm="" |
| | ): |
| | """ |
| | NOTE: this interface is experimental. |
| | |
| | Args: |
| | input_shape (ShapeSpec): shape of the input feature. |
| | conv_dims (list[int]): the output dimensions of the conv layers |
| | fc_dims (list[int]): the output dimensions of the fc layers |
| | conv_norm (str or callable): normalization for the conv layers. |
| | See :func:`detectron2.layers.get_norm` for supported types. |
| | """ |
| | super().__init__() |
| | assert len(conv_dims) + len(fc_dims) > 0 |
| |
|
| | self._output_size = (input_shape.channels, input_shape.height, input_shape.width) |
| |
|
| | self.conv_norm_relus = [] |
| | for k, conv_dim in enumerate(conv_dims): |
| | conv = Conv2d( |
| | self._output_size[0], |
| | conv_dim, |
| | kernel_size=3, |
| | padding=1, |
| | bias=not conv_norm, |
| | norm=get_norm(conv_norm, conv_dim), |
| | activation=nn.ReLU(), |
| | ) |
| | self.add_module("conv{}".format(k + 1), conv) |
| | self.conv_norm_relus.append(conv) |
| | self._output_size = (conv_dim, self._output_size[1], self._output_size[2]) |
| |
|
| | self.fcs = [] |
| | for k, fc_dim in enumerate(fc_dims): |
| | if k == 0: |
| | self.add_module("flatten", nn.Flatten()) |
| | fc = nn.Linear(int(np.prod(self._output_size)), fc_dim) |
| | self.add_module("fc{}".format(k + 1), fc) |
| | self.add_module("fc_relu{}".format(k + 1), nn.ReLU()) |
| | self.fcs.append(fc) |
| | self._output_size = fc_dim |
| |
|
| | for layer in self.conv_norm_relus: |
| | weight_init.c2_msra_fill(layer) |
| | for layer in self.fcs: |
| | weight_init.c2_xavier_fill(layer) |
| |
|
| | @classmethod |
| | def from_config(cls, cfg, input_shape): |
| | num_conv = cfg.MODEL.ROI_BOX_HEAD.NUM_CONV |
| | conv_dim = cfg.MODEL.ROI_BOX_HEAD.CONV_DIM |
| | num_fc = cfg.MODEL.ROI_BOX_HEAD.NUM_FC |
| | fc_dim = cfg.MODEL.ROI_BOX_HEAD.FC_DIM |
| | return { |
| | "input_shape": input_shape, |
| | "conv_dims": [conv_dim] * num_conv, |
| | "fc_dims": [fc_dim] * num_fc, |
| | "conv_norm": cfg.MODEL.ROI_BOX_HEAD.NORM, |
| | } |
| |
|
| | def forward(self, x): |
| | for layer in self: |
| | x = layer(x) |
| | return x |
| |
|
| | @property |
| | @torch.jit.unused |
| | def output_shape(self): |
| | """ |
| | Returns: |
| | ShapeSpec: the output feature shape |
| | """ |
| | o = self._output_size |
| | if isinstance(o, int): |
| | return ShapeSpec(channels=o) |
| | else: |
| | return ShapeSpec(channels=o[0], height=o[1], width=o[2]) |
| |
|
| |
|
| | def build_box_head(cfg, input_shape): |
| | """ |
| | Build a box head defined by `cfg.MODEL.ROI_BOX_HEAD.NAME`. |
| | """ |
| | name = cfg.MODEL.ROI_BOX_HEAD.NAME |
| | return ROI_BOX_HEAD_REGISTRY.get(name)(cfg, input_shape) |
| |
|