mirror of https://github.com/open-mmlab/mmyolo.git
Add docstring
parent
6882e09084
commit
c26a12ed32
|
@ -37,7 +37,7 @@ conda install pytorch torchvision cpuonly -c pytorch
|
|||
|
||||
### 最佳实践
|
||||
|
||||
**步骤 0.** 使用 [MIM](https://github.com/open-mmlab/mim) 安装 [MMEngine](https://github.com/open-mmlab/mmengine)、 [MMCV](https://github.com/open-mmlab/mmcv) 和 [MMDetection](https://github.com/open-mmlab/mmdetection)。
|
||||
**步骤 0.** 使用 [MIM](https://github.com/open-mmlab/mim) 安装 [MMEngine](https://github.com/open-mmlab/mmengine)、 [MMCV](https://github.com/open-mmlab/mmcv) 和 [MMDetection](https://github.com/open-mmlab/mmdetection) 。
|
||||
|
||||
```shell
|
||||
pip install -U openmim
|
||||
|
@ -52,7 +52,7 @@ pip install -r requirements/albu.txt
|
|||
|
||||
a. 在 MMCV-v2.x 中,`mmcv-full` 改名为 `mmcv`,如果你想安装不包含 CUDA 算子精简版,可以通过 `mim install mmcv-lite>=2.0.0rc1` 来安装。
|
||||
|
||||
b. 如果使用 albumentations,我们建议使用 pip install -r requirements/albu.txt 或者 pip install -U albumentations --no-binary qudida,albumentations 进行安装。 如果简单地使用 pip install albumentations==1.0.1 进行安装,则会同时安装 opencv-python-headless(即便已经安装了 opencv-python 也会再次安装)。我们建议在安装 albumentations 后检查环境,以确保没有同时安装 opencv-python 和 opencv-python-headless,因为同时安装可能会导致一些问题。更多细节请参考官方文档。
|
||||
b. 如果使用 albumentations,我们建议使用 pip install -r requirements/albu.txt 或者 pip install -U albumentations --no-binary qudida,albumentations 进行安装。 如果简单地使用 pip install albumentations==1.0.1 进行安装,则会同时安装 opencv-python-headless(即便已经安装了 opencv-python 也会再次安装)。我们建议在安装 albumentations 后检查环境,以确保没有同时安装 opencv-python 和 opencv-python-headless,因为同时安装可能会导致一些问题。更多细节请参考 [官方文档](https://albumentations.ai/docs/getting_started/installation/#note-on-opencv-dependencies) 。
|
||||
|
||||
**步骤 1.** 安装 MMYOLO
|
||||
|
||||
|
|
|
@ -132,6 +132,15 @@ class YOLOv6HeadModule(BaseModule):
|
|||
kernel_size=1))
|
||||
|
||||
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
||||
"""Forward features from the upstream network.
|
||||
|
||||
Args:
|
||||
x (Tuple[Tensor]): Features from the upstream network, each is
|
||||
a 4D-tensor.
|
||||
Returns:
|
||||
Tuple[List]: A tuple of multi-level classification scores, bbox
|
||||
predictions.
|
||||
"""
|
||||
assert len(x) == self.num_levels
|
||||
return multi_apply(self.forward_single, x, self.stems, self.cls_convs,
|
||||
self.cls_preds, self.reg_convs, self.reg_preds)
|
||||
|
@ -219,8 +228,29 @@ class YOLOv6Head(YOLOv5Head):
|
|||
self,
|
||||
cls_scores: Sequence[Tensor],
|
||||
bbox_preds: Sequence[Tensor],
|
||||
objectnesses: Sequence[Tensor],
|
||||
batch_gt_instances: Sequence[InstanceData],
|
||||
batch_img_metas: Sequence[dict],
|
||||
batch_gt_instances_ignore: OptInstanceList = None) -> dict:
|
||||
"""Calculate the loss based on the features extracted by the detection
|
||||
head.
|
||||
|
||||
Args:
|
||||
cls_scores (Sequence[Tensor]): Box scores for each scale level,
|
||||
each is a 4D-tensor, the channel number is
|
||||
num_priors * num_classes.
|
||||
bbox_preds (Sequence[Tensor]): Box energies / deltas for each scale
|
||||
level, each is a 4D-tensor, the channel number is
|
||||
num_priors * 4.
|
||||
batch_gt_instances (list[:obj:`InstanceData`]): Batch of
|
||||
gt_instance. It usually includes ``bboxes`` and ``labels``
|
||||
attributes.
|
||||
batch_img_metas (list[dict]): Meta information of each image, e.g.,
|
||||
image size, scaling factor, etc.
|
||||
batch_gt_instances_ignore (list[:obj:`InstanceData`], optional):
|
||||
Batch of gt_instances_ignore. It includes ``bboxes`` attribute
|
||||
data that is ignored during training and testing.
|
||||
Defaults to None.
|
||||
Returns:
|
||||
dict[str, Tensor]: A dictionary of losses.
|
||||
"""
|
||||
raise NotImplementedError('Not implemented yet!')
|
||||
|
|
|
@ -74,26 +74,32 @@ class BaseYOLONeck(BaseModule, metaclass=ABCMeta):
|
|||
|
||||
@abstractmethod
|
||||
def build_reduce_layer(self, idx: int):
|
||||
"""build reduce layer."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def build_upsample_layer(self, idx: int):
|
||||
"""build upsample layer."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def build_top_down_layer(self, idx: int):
|
||||
"""build top down layer."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def build_downsample_layer(self, idx: int):
|
||||
"""build downsample layer."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def build_bottom_up_layer(self, idx: int):
|
||||
"""build bottom up layer."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def build_out_layer(self, idx: int):
|
||||
"""build out layer."""
|
||||
pass
|
||||
|
||||
def _freeze_all(self):
|
||||
|
|
|
@ -64,6 +64,14 @@ class YOLOv5PAFPN(BaseYOLONeck):
|
|||
m.reset_parameters()
|
||||
|
||||
def build_reduce_layer(self, idx: int) -> nn.Module:
|
||||
"""build reduce layer.
|
||||
|
||||
Args:
|
||||
idx (int): layer idx.
|
||||
|
||||
Returns:
|
||||
nn.Module: The reduce layer.
|
||||
"""
|
||||
if idx == 2:
|
||||
layer = ConvModule(
|
||||
make_divisible(self.in_channels[idx], self.widen_factor),
|
||||
|
@ -77,9 +85,18 @@ class YOLOv5PAFPN(BaseYOLONeck):
|
|||
return layer
|
||||
|
||||
def build_upsample_layer(self, *args, **kwargs) -> nn.Module:
|
||||
"""build upsample layer."""
|
||||
return nn.Upsample(scale_factor=2, mode='nearest')
|
||||
|
||||
def build_top_down_layer(self, idx: int):
|
||||
"""build top down layer.
|
||||
|
||||
Args:
|
||||
idx (int): layer idx.
|
||||
|
||||
Returns:
|
||||
nn.Module: The top down layer.
|
||||
"""
|
||||
if idx == 1:
|
||||
return CSPLayer(
|
||||
make_divisible(self.in_channels[idx - 1] * 2,
|
||||
|
@ -111,6 +128,14 @@ class YOLOv5PAFPN(BaseYOLONeck):
|
|||
act_cfg=self.act_cfg))
|
||||
|
||||
def build_downsample_layer(self, idx: int) -> nn.Module:
|
||||
"""build downsample layer.
|
||||
|
||||
Args:
|
||||
idx (int): layer idx.
|
||||
|
||||
Returns:
|
||||
nn.Module: The downsample layer.
|
||||
"""
|
||||
return ConvModule(
|
||||
make_divisible(self.in_channels[idx], self.widen_factor),
|
||||
make_divisible(self.in_channels[idx], self.widen_factor),
|
||||
|
@ -121,6 +146,14 @@ class YOLOv5PAFPN(BaseYOLONeck):
|
|||
act_cfg=self.act_cfg)
|
||||
|
||||
def build_bottom_up_layer(self, idx: int) -> nn.Module:
|
||||
"""build bottom up layer.
|
||||
|
||||
Args:
|
||||
idx (int): layer idx.
|
||||
|
||||
Returns:
|
||||
nn.Module: The bottom up layer.
|
||||
"""
|
||||
return CSPLayer(
|
||||
make_divisible(self.in_channels[idx] * 2, self.widen_factor),
|
||||
make_divisible(self.in_channels[idx + 1], self.widen_factor),
|
||||
|
@ -130,4 +163,5 @@ class YOLOv5PAFPN(BaseYOLONeck):
|
|||
act_cfg=self.act_cfg)
|
||||
|
||||
def build_out_layer(self, *args, **kwargs) -> nn.Module:
|
||||
"""build out layer."""
|
||||
return nn.Identity()
|
||||
|
|
|
@ -60,6 +60,14 @@ class YOLOv6RepPAFPN(BaseYOLONeck):
|
|||
init_cfg=init_cfg)
|
||||
|
||||
def build_reduce_layer(self, idx: int) -> nn.Module:
|
||||
"""build reduce layer.
|
||||
|
||||
Args:
|
||||
idx (int): layer idx.
|
||||
|
||||
Returns:
|
||||
nn.Module: The reduce layer.
|
||||
"""
|
||||
if idx == 2:
|
||||
layer = ConvModule(
|
||||
in_channels=make_divisible(self.in_channels[idx],
|
||||
|
@ -76,6 +84,14 @@ class YOLOv6RepPAFPN(BaseYOLONeck):
|
|||
return layer
|
||||
|
||||
def build_upsample_layer(self, idx: int) -> nn.Module:
|
||||
"""build upsample layer.
|
||||
|
||||
Args:
|
||||
idx (int): layer idx.
|
||||
|
||||
Returns:
|
||||
nn.Module: The upsample layer.
|
||||
"""
|
||||
return nn.ConvTranspose2d(
|
||||
in_channels=make_divisible(self.out_channels[idx - 1],
|
||||
self.widen_factor),
|
||||
|
@ -86,6 +102,14 @@ class YOLOv6RepPAFPN(BaseYOLONeck):
|
|||
bias=True)
|
||||
|
||||
def build_top_down_layer(self, idx: int) -> nn.Module:
|
||||
"""build top down layer.
|
||||
|
||||
Args:
|
||||
idx (int): layer idx.
|
||||
|
||||
Returns:
|
||||
nn.Module: The top down layer.
|
||||
"""
|
||||
layer0 = RepStageBlock(
|
||||
in_channels=make_divisible(
|
||||
self.out_channels[idx - 1] + self.in_channels[idx - 1],
|
||||
|
@ -109,6 +133,14 @@ class YOLOv6RepPAFPN(BaseYOLONeck):
|
|||
return nn.Sequential(layer0, layer1)
|
||||
|
||||
def build_downsample_layer(self, idx: int) -> nn.Module:
|
||||
"""build downsample layer.
|
||||
|
||||
Args:
|
||||
idx (int): layer idx.
|
||||
|
||||
Returns:
|
||||
nn.Module: The downsample layer.
|
||||
"""
|
||||
return ConvModule(
|
||||
in_channels=make_divisible(self.out_channels[idx],
|
||||
self.widen_factor),
|
||||
|
@ -121,6 +153,14 @@ class YOLOv6RepPAFPN(BaseYOLONeck):
|
|||
act_cfg=self.act_cfg)
|
||||
|
||||
def build_bottom_up_layer(self, idx: int) -> nn.Module:
|
||||
"""build bottom up layer.
|
||||
|
||||
Args:
|
||||
idx (int): layer idx.
|
||||
|
||||
Returns:
|
||||
nn.Module: The bottom up layer.
|
||||
"""
|
||||
return RepStageBlock(
|
||||
in_channels=make_divisible(self.out_channels[idx] * 2,
|
||||
self.widen_factor),
|
||||
|
@ -130,6 +170,7 @@ class YOLOv6RepPAFPN(BaseYOLONeck):
|
|||
block=self.block)
|
||||
|
||||
def build_out_layer(self, *args, **kwargs) -> nn.Module:
|
||||
"""build out layer."""
|
||||
return nn.Identity()
|
||||
|
||||
def init_weights(self):
|
||||
|
|
|
@ -57,6 +57,14 @@ class YOLOXPAFPN(BaseYOLONeck):
|
|||
init_cfg=init_cfg)
|
||||
|
||||
def build_reduce_layer(self, idx: int) -> nn.Module:
|
||||
"""build reduce layer.
|
||||
|
||||
Args:
|
||||
idx (int): layer idx.
|
||||
|
||||
Returns:
|
||||
nn.Module: The reduce layer.
|
||||
"""
|
||||
if idx == 2:
|
||||
layer = ConvModule(
|
||||
self.in_channels[idx],
|
||||
|
@ -70,9 +78,18 @@ class YOLOXPAFPN(BaseYOLONeck):
|
|||
return layer
|
||||
|
||||
def build_upsample_layer(self, *args, **kwargs) -> nn.Module:
|
||||
"""build upsample layer."""
|
||||
return nn.Upsample(scale_factor=2, mode='nearest')
|
||||
|
||||
def build_top_down_layer(self, idx: int) -> nn.Module:
|
||||
"""build top down layer.
|
||||
|
||||
Args:
|
||||
idx (int): layer idx.
|
||||
|
||||
Returns:
|
||||
nn.Module: The top down layer.
|
||||
"""
|
||||
if idx == 1:
|
||||
return CSPLayer(
|
||||
self.in_channels[idx - 1] * 2,
|
||||
|
@ -98,6 +115,14 @@ class YOLOXPAFPN(BaseYOLONeck):
|
|||
act_cfg=self.act_cfg))
|
||||
|
||||
def build_downsample_layer(self, idx: int) -> nn.Module:
|
||||
"""build downsample layer.
|
||||
|
||||
Args:
|
||||
idx (int): layer idx.
|
||||
|
||||
Returns:
|
||||
nn.Module: The downsample layer.
|
||||
"""
|
||||
return ConvModule(
|
||||
self.in_channels[idx],
|
||||
self.in_channels[idx],
|
||||
|
@ -108,6 +133,14 @@ class YOLOXPAFPN(BaseYOLONeck):
|
|||
act_cfg=self.act_cfg)
|
||||
|
||||
def build_bottom_up_layer(self, idx: int) -> nn.Module:
|
||||
"""build bottom up layer.
|
||||
|
||||
Args:
|
||||
idx (int): layer idx.
|
||||
|
||||
Returns:
|
||||
nn.Module: The bottom up layer.
|
||||
"""
|
||||
return CSPLayer(
|
||||
self.in_channels[idx] * 2,
|
||||
self.in_channels[idx + 1],
|
||||
|
@ -117,6 +150,14 @@ class YOLOXPAFPN(BaseYOLONeck):
|
|||
act_cfg=self.act_cfg)
|
||||
|
||||
def build_out_layer(self, idx: int) -> nn.Module:
|
||||
"""build out layer.
|
||||
|
||||
Args:
|
||||
idx (int): layer idx.
|
||||
|
||||
Returns:
|
||||
nn.Module: The out layer.
|
||||
"""
|
||||
return ConvModule(
|
||||
self.in_channels[idx],
|
||||
self.out_channels,
|
||||
|
|
|
@ -9,13 +9,20 @@ from mmyolo.registry import TASK_UTILS
|
|||
|
||||
@TASK_UTILS.register_module()
|
||||
class YOLOv5BBoxCoder(BaseBBoxCoder):
|
||||
"""YOLOv5 BBox coder.
|
||||
|
||||
This decoder decodes pred bboxes (delta_x, delta_x, w, h) to bboxes (tl_x,
|
||||
tl_y, br_x, br_y).
|
||||
"""
|
||||
|
||||
def encode(self, **kwargs):
|
||||
"""Encode deltas between bboxes and ground truth boxes."""
|
||||
pass
|
||||
|
||||
def decode(self, priors: torch.Tensor, pred_bboxes: torch.Tensor,
|
||||
stride: Union[torch.Tensor, int]) -> torch.Tensor:
|
||||
"""Apply transformation `pred_bboxes` to `decoded_bboxes`.
|
||||
"""Decode regression results (delta_x, delta_x, w, h) to bboxes (tl_x,
|
||||
tl_y, br_x, br_y).
|
||||
|
||||
Args:
|
||||
priors (torch.Tensor): Basic boxes or points, e.g. anchors.
|
||||
|
|
|
@ -9,13 +9,20 @@ from mmyolo.registry import TASK_UTILS
|
|||
|
||||
@TASK_UTILS.register_module()
|
||||
class YOLOXBBoxCoder(BaseBBoxCoder):
|
||||
"""YOLOX BBox coder.
|
||||
|
||||
This decoder decodes pred bboxes (delta_x, delta_x, w, h) to bboxes (tl_x,
|
||||
tl_y, br_x, br_y).
|
||||
"""
|
||||
|
||||
def encode(self, **kwargs):
|
||||
"""Encode deltas between bboxes and ground truth boxes."""
|
||||
pass
|
||||
|
||||
def decode(self, priors: torch.Tensor, pred_bboxes: torch.Tensor,
|
||||
stride: Union[torch.Tensor, int]) -> torch.Tensor:
|
||||
"""Apply transformation `pred_bboxes` to `decoded_bboxes`.
|
||||
"""Decode regression results (delta_x, delta_x, w, h) to bboxes (tl_x,
|
||||
tl_y, br_x, br_y).
|
||||
|
||||
Args:
|
||||
priors (torch.Tensor): Basic boxes or points, e.g. anchors.
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
from ._utils import get_detector_cfg
|
||||
|
||||
__all__ = ['get_detector_cfg']
|
|
@ -0,0 +1,53 @@
|
|||
# Copyright (c) OpenMMLab. All rights reserved.
|
||||
import copy
|
||||
from os.path import dirname, exists, join
|
||||
|
||||
import numpy as np
|
||||
from mmengine.config import Config
|
||||
|
||||
|
||||
def _get_config_directory():
|
||||
"""Find the predefined detector config directory."""
|
||||
try:
|
||||
# Assume we are running in the source mmyolo repo
|
||||
repo_dpath = dirname(dirname(dirname(__file__)))
|
||||
except NameError:
|
||||
# For IPython development when this __file__ is not defined
|
||||
import mmyolo
|
||||
repo_dpath = dirname(dirname(mmyolo.__file__))
|
||||
config_dpath = join(repo_dpath, 'configs')
|
||||
if not exists(config_dpath):
|
||||
raise Exception('Cannot find config path')
|
||||
return config_dpath
|
||||
|
||||
|
||||
def _get_config_module(fname):
|
||||
"""Load a configuration as a python module."""
|
||||
config_dpath = _get_config_directory()
|
||||
config_fpath = join(config_dpath, fname)
|
||||
config_mod = Config.fromfile(config_fpath)
|
||||
return config_mod
|
||||
|
||||
|
||||
def get_detector_cfg(fname):
|
||||
"""Grab configs necessary to create a detector.
|
||||
|
||||
These are deep copied to allow for safe modification of parameters without
|
||||
influencing other tests.
|
||||
"""
|
||||
config = _get_config_module(fname)
|
||||
model = copy.deepcopy(config.model)
|
||||
return model
|
||||
|
||||
|
||||
def _rand_bboxes(rng, num_boxes, w, h):
|
||||
"""Randomly generate a specified number of bboxes."""
|
||||
cx, cy, bw, bh = rng.rand(num_boxes, 4).T
|
||||
|
||||
tl_x = ((cx * w) - (w * bw / 2)).clip(0, w)
|
||||
tl_y = ((cy * h) - (h * bh / 2)).clip(0, h)
|
||||
br_x = ((cx * w) + (w * bw / 2)).clip(0, w)
|
||||
br_y = ((cy * h) + (h * bh / 2)).clip(0, h)
|
||||
|
||||
bboxes = np.vstack([tl_x, tl_y, br_x, br_y]).T
|
||||
return bboxes
|
|
@ -5,10 +5,11 @@ from unittest import TestCase
|
|||
|
||||
import torch
|
||||
from mmdet.structures import DetDataSample
|
||||
from mmdet.testing import demo_mm_inputs, get_detector_cfg
|
||||
from mmdet.testing import demo_mm_inputs
|
||||
from mmengine.logging import MessageHub
|
||||
from parameterized import parameterized
|
||||
|
||||
from mmyolo.testing import get_detector_cfg
|
||||
from mmyolo.utils import register_all_modules
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue