ショートカット

規約

MMDetection を独自のプロジェクトとして変更したい場合は、以下の規約を確認してください。

画像形状の順序について

OpenMMLab 2.0 では、OpenCV の入力引数と一貫性を持たせるために、データ変換パイプラインにおける画像形状に関する引数は常に (width, height) の順序になります。一方、計算の便宜上、データパイプラインとモデルを通過するフィールドの順序は (height, width) です。具体的には、各データ変換パイプラインで処理された結果では、フィールドとその値の意味は以下のようになります。

  • img_shape: (高さ, 幅)

  • ori_shape: (高さ, 幅)

  • pad_shape: (高さ, 幅)

  • batch_input_shape: (高さ, 幅)

例として、Mosaic の初期化引数は以下のようになります。

@TRANSFORMS.register_module()
class Mosaic(BaseTransform):
    def __init__(self,
                img_scale: Tuple[int, int] = (640, 640),
                center_ratio_range: Tuple[float, float] = (0.5, 1.5),
                bbox_clip_border: bool = True,
                pad_val: float = 114.0,
                prob: float = 1.0) -> None:
       ...

       # img_scale order should be (width, height)
       self.img_scale = img_scale

    def transform(self, results: dict) -> dict:
        ...

        results['img'] = mosaic_img
        # (height, width)
        results['img_shape'] = mosaic_img.shape[:2]

損失

MMDetection では、損失とメトリックを含む dictmodel(**data) によって返されます。

例えば、bbox ヘッドでは、

class BBoxHead(nn.Module):
    ...
    def loss(self, ...):
        losses = dict()
        # classification loss
        losses['loss_cls'] = self.loss_cls(...)
        # classification accuracy
        losses['acc'] = accuracy(...)
        # bbox regression loss
        losses['loss_bbox'] = self.loss_bbox(...)
        return losses

bbox_head.loss() はモデルのフォワード中に呼び出されます。返される dict には、'loss_bbox''loss_cls''acc' が含まれます。'loss_bbox''loss_cls' のみがバックプロパゲーション中に使用され、'acc' はトレーニングプロセスを監視するためのメトリックとしてのみ使用されます。

デフォルトでは、キーに 'loss' を含む値のみがバックプロパゲートされます。この動作は、BaseDetector.train_step() を変更することで変更できます。

空の提案

MMDetection では、2段階の空の提案に対する特別な処理とユニットテストを追加しました。バッチ全体と単一画像の空の提案を同時に処理する必要があります。例えば、CascadeRoIHead では、

# simple_test method
...
# There is no proposal in the whole batch
if rois.shape[0] == 0:
    bbox_results = [[
        np.zeros((0, 5), dtype=np.float32)
        for _ in range(self.bbox_head[-1].num_classes)
    ]] * num_imgs
    if self.with_mask:
        mask_classes = self.mask_head[-1].num_classes
        segm_results = [[[] for _ in range(mask_classes)]
                        for _ in range(num_imgs)]
        results = list(zip(bbox_results, segm_results))
    else:
        results = bbox_results
    return results
...

# There is no proposal in the single image
for i in range(self.num_stages):
    ...
    if i < self.num_stages - 1:
          for j in range(num_imgs):
                # Handle empty proposal
                if rois[j].shape[0] > 0:
                    bbox_label = cls_score[j][:, :-1].argmax(dim=1)
                    refine_roi = self.bbox_head[i].regress_by_class(
                         rois[j], bbox_label, bbox_pred[j], img_metas[j])
                    refine_roi_list.append(refine_roi)

カスタム RoIHead を作成した場合は、上記のメソッドを参照して空の提案を処理できます。

Coco Panoptic データセット

MMDetection では、COCO Panoptic データセットをサポートしています。ここでは、CocoPanopticDataset の実装に関するいくつかの規約を明確にします。

  1. mmdet<=2.16.0 では、セマンティックセグメンテーションにおける前景と背景のラベルの範囲が MMDetection のデフォルト設定とは異なります。ラベル 0VOID ラベルを表し、カテゴリラベルは 1 から始まります。mmdet=2.17.0 以降では、セマンティックセグメンテーションのカテゴリラベルは 0 から始まり、ラベル 255 はバウンディングボックスのラベルとの整合性を保つために VOID を表します。これを実現するために、Pad パイプラインは seg のパディング値を設定することをサポートしています。

  2. 評価では、パノプティックの結果は元の画像と同じ形状のマップになります。結果マップの各値は、instance_id * INSTANCE_OFFSET + category_id の形式を持ちます。