本文主要介紹如何標(biāo)注梯轻、存放和劃分 VOC
格式的自定義數(shù)據(jù)集。
目錄:
- MMDetection v2 目標(biāo)檢測(1):環(huán)境搭建
- MMDetection v2 目標(biāo)檢測(2):數(shù)據(jù)準(zhǔn)備
- MMDetection v2 目標(biāo)檢測(3):配置修改
- MMDetection v2 目標(biāo)檢測(4):模型訓(xùn)練和測試
服務(wù)器的環(huán)境配置:
-
Ubuntu
:18.04.5 -
CUDA
:10.1.243 -
Python
:3.7.9 -
PyTorch
:1.5.1 -
MMDetection
:2.16.0
1 標(biāo)注數(shù)據(jù)
MMDetection
目前支持 Pascal VOC
靡狞、MS COCO
褪那、Cityscapes
半醉、LVIS
疚俱、Wider Face
、Deep Fashion
等多個(gè)公開數(shù)據(jù)集缩多。
而其他公開數(shù)據(jù)集呆奕,例如:KITTI
等养晋,可在網(wǎng)上搜索腳本,將標(biāo)注文件轉(zhuǎn)化成 VOC
或 COCO
格式梁钾。
如果是自定義數(shù)據(jù)集绳泉,則可使用數(shù)據(jù)標(biāo)注工具 LabelImg
,來生成 VOC
格式的標(biāo)注文件姆泻。
下面介紹如何在 Win 10
上安裝和使用 LabelImg
零酪。
1.1 安裝 LabelImg
pip
安裝:
pip install labelimg
1.2 使用 LabelImg
- 將數(shù)據(jù)集按照以下格式存放:
data
├─ Annotations
├─ JPEGImages
└─ predefined_classes.txt
-
Annotations
:存放標(biāo)注文件 -
JPEGImages
:存放圖像文件 -
predefined_classes.txt
:定義標(biāo)簽類別
- 在
predefined_classes.txt
中,定義標(biāo)簽類別拇勃,例如:
Car
Pedestrian
Cyclist
- 打開
data
目錄:
cd data
- 運(yùn)行
LabelImg
:
labelimg JPEGImages predefined_classes.txt
選擇圖像文件夾
JPEGImages
四苇。進(jìn)入
LabelImg
界面:
-
Open Dir
:打開存放圖像文件的目錄路徑,選擇JPEGImages
-
Change Save Dir
:更換存放標(biāo)注文件的目錄路徑方咆,選擇Annotations
-
PascalVOC
/YOLO
:切換標(biāo)注保存的格式 -
Create\nRectBox
:創(chuàng)建標(biāo)注 -
Save
:保存標(biāo)注
- 其他設(shè)置:
-
Auto Save mode
:自動(dòng)保存模式月腋,不用每標(biāo)注一張圖像,都要點(diǎn)擊一次保存標(biāo)注 -
Display Labels
:顯示標(biāo)簽瓣赂,標(biāo)注好的標(biāo)簽會(huì)自動(dòng)顯示出來 -
Advanced Mode
:高級(jí)模式罗售,不用每標(biāo)注一個(gè)目標(biāo),都要點(diǎn)擊一次創(chuàng)建標(biāo)注
2 存放數(shù)據(jù)
- 將數(shù)據(jù)集上傳到服務(wù)器钩述,并將圖像和標(biāo)注文件按照
VOC
格式存放:
data
└─ VOCdevkit
└─ MyDataset
├─ Annotations
├─ ImageSets
│ └─ Main
│ ├─ test.txt
│ ├─ train.txt
│ ├─ trainval.txt
│ └─ val.txt
└─ JPEGImages
-
Annotations
:存放標(biāo)注文件 -
ImageSets
:存放劃分文件 -
JPEGImages
:存放圖像文件
Tips:
目錄中的MyDataset
可改為任意自定義數(shù)據(jù)集的名字。
- 推薦使用符號(hào)鏈接穆碎,將數(shù)據(jù)集放到
mmdetection
目錄下:
ln -s ~/data ~/mmdetection
mmdetection
的目錄結(jié)構(gòu):
mmdetection
├─ mmdet
├─ tools
├─ configs
├─ checkpoints
├─ data
3 劃分?jǐn)?shù)據(jù)
打開 MyDataset
目錄:
cd data/VOCdevkit/MyDataset
3.1 劃分?jǐn)?shù)據(jù)集
運(yùn)行 split_dataset.py
:
python split_dataset.py
具體代碼如下:
import os
import random
trainval_percent = 0.9
train_percent = 0.9
total_xml = os.listdir('./Annotations')
num = len(total_xml)
nums = range(num)
tv = int(num * trainval_percent)
tr = int(tv * train_percent)
trainval = random.sample(nums, tv)
train = random.sample(trainval, tr)
ftrainval = open('./ImageSets/Main/trainval.txt', 'w')
ftrain = open('./ImageSets/Main/train.txt', 'w')
fval = open('./ImageSets/Main/val.txt', 'w')
ftest = open('./ImageSets/Main/test.txt', 'w')
for i in nums:
name = total_xml[i][:-4] + '\n'
if i in trainval:
ftrainval.write(name)
if i in train:
ftrain.write(name)
else:
fval.write(name)
else:
ftest.write(name)
ftrainval.close()
ftrain.close()
fval.close()
ftest.close()
在 ./ImageSets/Main
目錄下牙勘,生成四個(gè) txt
文件:
-
test.txt
:測試集 -
train.txt
:訓(xùn)練集 -
trainval.txt
:訓(xùn)練和驗(yàn)證集 -
val.txt
:驗(yàn)證集
每個(gè)文件分別記錄了對應(yīng)劃分?jǐn)?shù)據(jù)集包含的圖像文件名(不含后綴名)。
3.2 統(tǒng)計(jì)數(shù)據(jù)個(gè)數(shù)
運(yùn)行 cal_data_num.py
:
python cal_data_num.py
具體代碼如下:
import os
names_txt = os.listdir('./ImageSets/Main')
print(f'共有文件:{len(names_txt)} 個(gè)')
for name_txt in names_txt:
with open(os.path.join('./ImageSets/Main', name_txt)) as f:
lines = f.readlines()
print(f'{name_txt} 共有數(shù)據(jù):{len(lines)} 個(gè)')
得到每個(gè)劃分?jǐn)?shù)據(jù)集的數(shù)據(jù)個(gè)數(shù)所禀。
4 轉(zhuǎn)換數(shù)據(jù)
另外方面,還可以將 VOC
格式轉(zhuǎn)換成 COCO
格式。
- 打開
./tools/convert_datasets/pascal_voc.py
如果標(biāo)注文件中不存在 difficult
標(biāo)簽色徘,需要將 difficult
設(shè)為 None
:
def parse_xml(args):
xml_path, img_path = args
tree = ET.parse(xml_path)
root = tree.getroot()
size = root.find('size')
w = int(size.find('width').text)
h = int(size.find('height').text)
for obj in root.findall('object'):
name = obj.find('name').text
label = label_ids[name]
try:
difficult = int(obj.find('difficult').text)
except AttributeError:
difficult = None
# difficult = int(obj.find('difficult').text)
如果是自定義數(shù)據(jù)集的名字恭金,需要修改數(shù)據(jù)集的路徑 filelist
、xml_paths
褂策、img_paths
:
def cvt_annotations(devkit_path, years, split, out_file):
if not isinstance(years, list):
years = [years]
annotations = []
for year in years:
filelist = osp.join(devkit_path,
f'{year}/ImageSets/Main/{split}.txt')
# filelist = osp.join(devkit_path,
# f'VOC{year}/ImageSets/Main/{split}.txt')
if not osp.isfile(filelist):
print(f'filelist does not exist: {filelist}, '
f'skip {year} {split}')
# print(f'filelist does not exist: {filelist}, '
# f'skip voc{year} {split}')
return
img_names = mmcv.list_from_file(filelist)
xml_paths = [
osp.join(devkit_path, f'{year}/Annotations/{img_name}.xml')
for img_name in img_names
]
# xml_paths = [
# osp.join(devkit_path, f'VOC{year}/Annotations/{img_name}.xml')
# for img_name in img_names
# ]
img_paths = [
f'{year}/JPEGImages/{img_name}.png' for img_name in img_names
]
# img_paths = [
# f'VOC{year}/JPEGImages/{img_name}.jpg' for img_name in img_names
# ]
part_annotations = mmcv.track_progress(parse_xml,
list(zip(xml_paths, img_paths)))
annotations.extend(part_annotations)
mmcv.dump(annotations, out_file)
return annotations
如果是自定義數(shù)據(jù)集的名字横腿,需要添加 mydataset
:
def main():
args = parse_args()
devkit_path = args.devkit_path
out_dir = args.out_dir if args.out_dir else devkit_path
mmcv.mkdir_or_exist(out_dir)
years = []
if osp.isdir(osp.join(devkit_path, 'VOC2007')):
years.append('VOC2007')
if osp.isdir(osp.join(devkit_path, 'VOC2012')):
years.append('VOC2012')
if 'VOC2007' in years and 'VOC2012' in years:
years.append(['VOC2007', 'VOC2012'])
# ------------------------------
if osp.isdir(osp.join(devkit_path, 'MyDataset')):
years.append('mydataset')
# ------------------------------
if not years:
raise IOError(f'The devkit path {devkit_path} contains neither '
'"VOC2007" nor "VOC2012" subfolder')
for year in years:
if year == 'VOC2007':
prefix = 'voc07'
elif year == 'VOC2012':
prefix = 'voc12'
elif year == ['VOC2007', 'VOC2012']:
prefix = 'voc0712'
# ------------------------------
elif year = 'mydataset':
prefix = 'mydataset'
# ------------------------------
for split in ['train', 'val', 'trainval']:
dataset_name = prefix + '_' + split
print(f'processing {dataset_name} ...')
cvt_annotations(devkit_path, year, split,
osp.join(out_dir, dataset_name + '.pkl'))
if not isinstance(year, list):
dataset_name = prefix + '_test'
print(f'processing {dataset_name} ...')
cvt_annotations(devkit_path, year, 'test',
osp.join(out_dir, dataset_name + '.pkl'))
print('Done!')
- 運(yùn)行
./tools/convert_datasets/pascal_voc.py
命令格式:
python tools/convert_datasets/pascal_voc.py ${DEVKIT_PATH} [--out-dir ${OUT_DIR}]
命令參數(shù):
-
devkit_path
:VOC
格式數(shù)據(jù)集的目錄路徑 -
--out-dir
:輸出COCO
格式標(biāo)注的目錄路徑
示例:
python tools/convert_datasets/pascal_voc.py data/VOCdevkit data/coco
5 結(jié)語
有幫助的話,點(diǎn)個(gè)贊再走吧斤寂,謝謝~
參考: