データセットのカスタマイズ¶
新しいデータ形式のサポート¶
新しいデータ形式をサポートするには、既存の形式(COCO形式またはPASCAL形式)に変換するか、中間形式に直接変換します。オフライン(スクリプトによるトレーニング前)またはオンライン(新しいデータセットを実装し、トレーニング時に変換)で変換することもできます。MMDetectionでは、データをCOCO形式に変換し、オフラインで変換することを推奨します。そのため、データの変換後、構成ファイルのデータアノテーションパスとクラスのみを変更する必要があります。
新しいデータ形式を既存の形式に再編成する¶
最も簡単な方法は、データセットを既存のデータセット形式(COCOまたはPASCAL VOC)に変換することです。
COCO形式のアノテーションJSONファイルには、次の必須キーがあります
'images': [
{
'file_name': 'COCO_val2014_000000001268.jpg',
'height': 427,
'width': 640,
'id': 1268
},
...
],
'annotations': [
{
'segmentation': [[192.81,
247.09,
...
219.03,
249.06]], # If you have mask labels, and it is in polygon XY point coordinate format, you need to ensure that at least 3 point coordinates are included. Otherwise, it is an invalid polygon.
'area': 1035.749,
'iscrowd': 0,
'image_id': 1268,
'bbox': [192.81, 224.8, 74.73, 33.43],
'category_id': 16,
'id': 42986
},
...
],
'categories': [
{'id': 0, 'name': 'car'},
]
JSONファイルには3つの必須キーがあります
images
:file_name
、height
、width
、id
などの情報を含む画像のリスト。annotations
: インスタンスアノテーションのリストを含みます。categories
: カテゴリ名とそのIDのリストを含みます。
データの前処理後、既存の形式(例:COCO形式)でカスタマイズされた新しいデータセットをトレーニングするには、ユーザーが実行する手順が2つあります。
カスタマイズされたデータセットを使用するための構成ファイルを変更します。
カスタマイズされたデータセットのアノテーションを確認します。
以下では、COCO形式の5クラスのカスタマイズされたデータセットを使用して、既存のCascade Mask R-CNN R50-FPN検出器をトレーニングする例を示します。
1. カスタマイズされたデータセットを使用するための構成ファイルの変更¶
構成ファイルの変更には、2つの側面が関わっています。
data
フィールド。具体的には、train_dataloader.dataset
、val_dataloader.dataset
、およびtest_dataloader.dataset
にmetainfo=dict(classes=classes)
フィールドを明示的に追加する必要があります。classes
はタプル型である必要があります。model
部分のnum_classes
フィールド。デフォルト値(例:COCOでは80)からクラス数にnum_classes
を明示的に上書きします。
configs/my_custom_config.py
内
# the new config inherits the base configs to highlight the necessary modification
_base_ = './cascade_mask_rcnn_r50_fpn_1x_coco.py'
# 1. dataset settings
dataset_type = 'CocoDataset'
classes = ('a', 'b', 'c', 'd', 'e')
data_root='path/to/your/'
train_dataloader = dict(
batch_size=2,
num_workers=2,
dataset=dict(
type=dataset_type,
# explicitly add your class names to the field `metainfo`
metainfo=dict(classes=classes),
data_root=data_root,
ann_file='train/annotation_data',
data_prefix=dict(img='train/image_data')
)
)
val_dataloader = dict(
batch_size=1,
num_workers=2,
dataset=dict(
type=dataset_type,
test_mode=True,
# explicitly add your class names to the field `metainfo`
metainfo=dict(classes=classes),
data_root=data_root,
ann_file='val/annotation_data',
data_prefix=dict(img='val/image_data')
)
)
test_dataloader = dict(
batch_size=1,
num_workers=2,
dataset=dict(
type=dataset_type,
test_mode=True,
# explicitly add your class names to the field `metainfo`
metainfo=dict(classes=classes),
data_root=data_root,
ann_file='test/annotation_data',
data_prefix=dict(img='test/image_data')
)
)
# 2. model settings
# explicitly over-write all the `num_classes` field from default 80 to 5.
model = dict(
roi_head=dict(
bbox_head=[
dict(
type='Shared2FCBBoxHead',
# explicitly over-write all the `num_classes` field from default 80 to 5.
num_classes=5),
dict(
type='Shared2FCBBoxHead',
# explicitly over-write all the `num_classes` field from default 80 to 5.
num_classes=5),
dict(
type='Shared2FCBBoxHead',
# explicitly over-write all the `num_classes` field from default 80 to 5.
num_classes=5)],
# explicitly over-write all the `num_classes` field from default 80 to 5.
mask_head=dict(num_classes=5)))
2. カスタマイズされたデータセットのアノテーションの確認¶
カスタマイズされたデータセットがCOCO形式であると仮定して、カスタマイズされたデータセットに正しいアノテーションが含まれていることを確認します。
アノテーションの
categories
フィールドの長さは、構成ファイルのclasses
フィールドのタプル長(つまり、クラス数、この例では5)と完全に一致する必要があります。構成ファイルの
classes
フィールドには、アノテーションのcategories
のname
と完全に同じ要素が同じ順序で含まれている必要があります。MMDetectionは、categories
の非連続なid
を連続したラベルインデックスに自動的にマッピングするため、categories
フィールドのname
の文字列順序はラベルインデックスの順序に影響します。同時に、構成ファイルのclasses
の文字列順序は、予測されたバウンディングボックスの視覚化中のラベルテキストに影響します。annotations
フィールドのcategory_id
は有効である必要があります。つまり、category_id
のすべての値は、categories
のid
に属している必要があります。
アノテーションの有効な例を以下に示します。
'annotations': [
{
'segmentation': [[192.81,
247.09,
...
219.03,
249.06]], # if you have mask labels
'area': 1035.749,
'iscrowd': 0,
'image_id': 1268,
'bbox': [192.81, 224.8, 74.73, 33.43],
'category_id': 16,
'id': 42986
},
...
],
# MMDetection automatically maps the uncontinuous `id` to the continuous label indices.
'categories': [
{'id': 1, 'name': 'a'}, {'id': 3, 'name': 'b'}, {'id': 4, 'name': 'c'}, {'id': 16, 'name': 'd'}, {'id': 17, 'name': 'e'},
]
CityScapesデータセットのサポートにはこの方法を使用します。スクリプトはcityscapes.pyにあり、ファインチューニング用のconfigsも提供しています。
注記
インスタンスセグメンテーションデータセットの場合、**MMDetectionは現在、COCO形式のデータセットのマスクAPの評価のみをサポートしています**。
トレーニング前にオフラインでデータをコンバートすることをお勧めします。そのため、
CocoDataset
を引き続き使用でき、アノテーションのパスとトレーニングクラスのみを変更する必要があります。
新しいデータ形式を中間形式に再編成する¶
アノテーション形式をCOCO形式またはPASCAL形式に変換したくない場合でも問題ありません。実際、MMEningeのBaseDatasetでシンプルなアノテーション形式を定義しており、既存のすべてのデータセットは、オンラインまたはオフラインのいずれかでこれと互換性を持つように処理されています。
データセットのアノテーションはjson
またはyaml
、yml
またはpickle
、pkl
形式である必要があります。アノテーションファイルに格納されている辞書には、metainfo
とdata_list
の2つのフィールドが含まれている必要があります。metainfo
は辞書で、クラス情報などのデータセットのメタデータを含みます。data_list
はリストで、リストの各要素は辞書です。辞書は1つの画像の生データを定義し、各生データには1つまたは複数のトレーニング/テストサンプルが含まれています。
例を以下に示します。
{
'metainfo':
{
'classes': ('person', 'bicycle', 'car', 'motorcycle'),
...
},
'data_list':
[
{
"img_path": "xxx/xxx_1.jpg",
"height": 604,
"width": 640,
"instances":
[
{
"bbox": [0, 0, 10, 20],
"bbox_label": 1,
"ignore_flag": 0
},
{
"bbox": [10, 10, 110, 120],
"bbox_label": 2,
"ignore_flag": 0
}
]
},
{
"img_path": "xxx/xxx_2.jpg",
"height": 320,
"width": 460,
"instances":
[
{
"bbox": [10, 0, 20, 20],
"bbox_label": 3,
"ignore_flag": 1,
}
]
},
...
]
}
一部のデータセットでは、crowd/difficult/ignoredバウンディングボックスなどのアノテーションが提供される場合があります。これらに対応するためにignore_flag
を使用します。
上記の標準的なデータアノテーション形式を取得したら、構成でMMDetectionのBaseDetDatasetを直接使用でき、変換は不要です。
カスタマイズされたデータセットの例¶
アノテーションがテキストファイルの新しい形式であると仮定します。バウンディングボックスのアノテーションは、次のようにテキストファイルannotation.txt
に格納されます。
#
000001.jpg
1280 720
2
10 20 40 60 1
20 40 50 60 2
#
000002.jpg
1280 720
3
50 20 40 60 2
20 40 30 45 2
30 40 50 60 3
データをロードするために、mmdet/datasets/my_dataset.py
に新しいデータセットを作成できます。
import mmengine
from mmdet.base_det_dataset import BaseDetDataset
from mmdet.registry import DATASETS
@DATASETS.register_module()
class MyDataset(BaseDetDataset):
METAINFO = {
'classes': ('person', 'bicycle', 'car', 'motorcycle'),
'palette': [(220, 20, 60), (119, 11, 32), (0, 0, 142), (0, 0, 230)]
}
def load_data_list(self, ann_file):
ann_list = mmengine.list_from_file(ann_file)
data_infos = []
for i, ann_line in enumerate(ann_list):
if ann_line != '#':
continue
img_shape = ann_list[i + 2].split(' ')
width = int(img_shape[0])
height = int(img_shape[1])
bbox_number = int(ann_list[i + 3])
instances = []
for anns in ann_list[i + 4:i + 4 + bbox_number]:
instance = {}
instance['bbox'] = [float(ann) for ann in anns.split(' ')[:4]]
instance['bbox_label']=int(anns[4])
instances.append(instance)
data_infos.append(
dict(
img_path=ann_list[i + 1],
img_id=i,
width=width,
height=height,
instances=instances
))
return data_infos
次に、構成でMyDataset
を使用するには、構成を次のように変更できます。
dataset_A_train = dict(
type='MyDataset',
ann_file = 'image_list.txt',
pipeline=train_pipeline
)
データセットラッパーによるデータセットのカスタマイズ¶
MMEngineは、データセットを混合したり、トレーニングのためのデータセットの分布を変更したりするための多くのデータセットラッパーもサポートしています。現在、以下に示す3つのデータセットラッパーをサポートしています。
RepeatDataset
: データセット全体を単純に繰り返します。ClassBalancedDataset
: クラスバランスの方法でデータセットを繰り返します。ConcatDataset
: データセットを連結します。
詳細な使用方法については、MMEngine Dataset Wrapperを参照してください。
データセットクラスの変更¶
既存のデータセットタイプを使用すると、アノテーションのサブセットをトレーニングするために、そのメタ情報を変更できます。たとえば、現在のデータセットの3つのクラスのみをトレーニングする場合、データセットのクラスを変更できます。データセットは、他のクラスのground truthボックスを自動的にフィルタリングします。
classes = ('person', 'bicycle', 'car')
train_dataloader = dict(
dataset=dict(
metainfo=dict(classes=classes))
)
val_dataloader = dict(
dataset=dict(
metainfo=dict(classes=classes))
)
test_dataloader = dict(
dataset=dict(
metainfo=dict(classes=classes))
)
注記:
MMDetection v2.5.0より前では、クラスが設定されていて空のGT画像が存在しない場合、データセットは空のGT画像を自動的にフィルタリングし、設定でそれを無効にする方法はなく、これは望ましくない動作であり、混乱を招きます。なぜなら、クラスが設定されていない場合、データセットは
filter_empty_gt=True
およびtest_mode=False
の場合にのみ空のGT画像をフィルタリングするからです。MMDetection v2.5.0以降、画像フィルタリングプロセスとクラスの変更を切り離しました。つまり、データセットはfilter_cfg=dict(filter_empty_gt=True)
およびtest_mode=False
の場合にのみ空のGT画像をフィルタリングし、クラスが設定されているかどうかに関係なく、設定されたクラスはトレーニングに使用されるクラスのアノテーションのみに影響し、ユーザーは空のGT画像をフィルタリングするかどうかを自分で決定できます。MMEngineの
BaseDataset
またはMMDetectionのBaseDetDataset
を直接使用する場合、設定を変更してもGTのない画像をフィルタリングすることはできませんが、オフラインで解決できます。データセットで
classes
を指定する場合は、ヘッドのnum_classes
も変更してください。v2.9.0(PR#4508以降)から、数値の一貫性をチェックするNumClassCheckHookを実装しました。
COCOパノプティックデータセット¶
現在、COCOパノプティックデータセットをサポートしています。パノプティックアノテーションの形式はCOCO形式とは異なります。アノテーションファイルには、前景と背景の両方が存在します。COCOパノプティック形式のアノテーションJSONファイルには、次の必須キーがあります。
'images': [
{
'file_name': '000000001268.jpg',
'height': 427,
'width': 640,
'id': 1268
},
...
]
'annotations': [
{
'filename': '000000001268.jpg',
'image_id': 1268,
'segments_info': [
{
'id':8345037, # One-to-one correspondence with the id in the annotation map.
'category_id': 51,
'iscrowd': 0,
'bbox': (x1, y1, w, h), # The bbox of the background is the outer rectangle of its mask.
'area': 24315
},
...
]
},
...
]
'categories': [ # including both foreground categories and background categories
{'id': 0, 'name': 'person'},
...
]
さらに、seg
はパノプティックアノテーション画像のパスに設定する必要があります。
dataset_type = 'CocoPanopticDataset'
data_root='path/to/your/'
train_dataloader = dict(
dataset=dict(
type=dataset_type,
data_root=data_root,
data_prefix=dict(
img='train/image_data/', seg='train/panoptic/image_annotation_data/')
)
)
val_dataloader = dict(
dataset=dict(
type=dataset_type,
data_root=data_root,
data_prefix=dict(
img='val/image_data/', seg='val/panoptic/image_annotation_data/')
)
)
test_dataloader = dict(
dataset=dict(
type=dataset_type,
data_root=data_root,
data_prefix=dict(
img='test/image_data/', seg='test/panoptic/image_annotation_data/')
)
)