mmdetection 是时下比较常用的目标检测工具箱,集成了faster ,cascade,yolo 等性能比较好的检测算法,熟练使用mmdetection进行自定义数据的目标检测就成为了很多AIer的必经之路。
项目仓库地址:https://github.com/open-mmlab/mmdetection
安装,装环境就略过了。
装完之后,想要训练自己的数据集,首先要将自己的数据整理成COCO的格式:
--------data
---------------coco
-----------------------annotations
---------------------------------------train.json,val.json
------------------------images
---------------------------------------......jpg
annotations文件夹下包含了train.json和val.json,images文件夹下则为全部图像数据。
自己的数据标签格式需要整理为json才行,具体的转换方法可以直接去搜程序,也可以自己写。这里给出一部分,可能不适应直接用。
1.xml转coco
import os.path as osp
import xml.etree.ElementTree as ET
import mmcv
from mmdet.core import sea_classes
from glob import glob
from tqdm import tqdm
from PIL import Image
label_ids = {name: i + 1 for i, name in enumerate(sea_classes())}
def get_segmentation(points):
return [points[0], points[1], points[2] + points[0], points[1],
points[2] + points[0], points[3] + points[1], points[0], points[3] + points[1]]
def parse_xml(xml_path, img_id, anno_id):
tree = ET.parse(xml_path)
# print(str(xml_path))
root = tree.getroot()
annotation = []
for obj in root.findall('object'):
try:
name = obj.find('name').text
# if name in ['lighter','powerbank','zippooil','firecrackers','handcuffs','pressure','slingshot','nailpolish']:
# continue
category_id = label_ids[name]
bnd_box = obj.find('bndbox')
xmin = int(bnd_box.find('xmin').text)
ymin = int(bnd_box.find('ymin').text)
xmax = int(bnd_box.find('xmax').text)
ymax = int(bnd_box.find('ymax').text)
if xmin >= xmax or ymin >= ymax:
continue
w = xmax - xmin + 1
h = ymax - ymin + 1
area = w * h
segmentation = get_segmentation([xmin, ymin, w, h])
annotation.append({
"segmentation": segmentation,
"area": area,
"iscrowd": 0,
"image_id": img_id,
"bbox": [xmin, ymin, w, h],
"category_id": category_id,
"id": anno_id,
"ignore": 0})
anno_id += 1
except:
continue
return annotation, anno_id
def cvt_annotations(img_path, xml_path, out_file):
images = []
annotations = []
# xml_paths = glob(xml_path + '/*.xml')
img_id = 1
anno_id = 1
for img_path in tqdm(glob(img_path + '/*.jpg')):
w, h = Image.open(img_path).size
img_name = osp.basename(img_path)
img = {"file_name": img_name, "height": int(h), "width": int(w), "id": img_id}
images.append(img)
xml_file_name = img_name.split('.')[0] + '.xml'
xml_file_path = osp.join(xml_path, xml_file_name)
annos, anno_id = parse_xml(xml_file_path, img_id, anno_id)
annotations.extend(annos)
img_id += 1
categories = []
for k, v in label_ids.items():
categories.append({"name": k, "id": v})
final_result = {"images": images, "annotations": annotations, "categories": categories}
mmcv.dump(final_result, out_file)
return annotations
def main():
xml_path = "/.../mmdetection-master/data/coco/xmlannotations/"####xml文件文件夹
img_path = "/.../mmdetection-master/data/coco/images/"####所有图像
print('processing {} ...'.format("xml format annotations"))
cvt_annotations(img_path, xml_path, '/.../mmdetection-master/data/coco/annotations/train.json')#####train.json文件保存地址
print('Done!')
if __name__ == '__main__':
main()
2. train.json文件分成训练集和验证集
import os
import os.path as pt
import json
import funcy
from pycocotools.coco import COCO
from pycocotools.cocoeval import COCOeval
from sklearn.model_selection import train_test_split
def split_train_val(annotations, trainpath, valpath, splitrate=0.99):
with open(annotations, 'rt', encoding='UTF-8') as annotations:
coco = json.load(annotations)
images = coco['images']
annotations = coco['annotations']
categories = coco['categories']
# number_of_images = len(images)
train, val = train_test_split(images, train_size=splitrate)
save_coco(trainpath, train, filter_annotations(annotations, train),
categories)
save_coco(valpath, val, filter_annotations(annotations, val),
categories)
print("Saved {} entries in {} and {} in {}".format(
len(train), trainpath, len(val), valpath))
def save_coco(file, images, annotations, categories):
with open(file, 'wt', encoding='UTF-8') as coco:
json.dump(
{
'images': images,
'annotations': annotations,
'categories': categories
},
coco,
indent=4,
sort_keys=True)
def filter_annotations(annotations, images):
image_ids = funcy.lmap(lambda i: int(i['id']), images)
return funcy.lfilter(lambda a: int(a['image_id']) in image_ids,
annotations)
def main():
COCO_FORMAT_JSON_PATH = '/.../mmdetection-master/data/coco/annotations/'###json文件保存文件夹
annotations = pt.join(COCO_FORMAT_JSON_PATH, 'train.json')###待分割json
trainpath = pt.join(COCO_FORMAT_JSON_PATH, 'train_.json')###分割后
valpath = pt.join(COCO_FORMAT_JSON_PATH, 'val.json')
split_train_val(annotations, trainpath, valpath)
if __name__ == '__main__':
main()
训练
数据准备完毕后,就要修改配置文件,使mmdetection中的分类类别,类别数与自己的数据集一样,需要修改的地方如下:
1./mmdetection-master/mmdet/datasets/coco.py
将class CocoDataset(CustomDataset):
CLASSES = (
'holothurian','echinus','scallop','starfish'
)
中的CLASSES元组列表中80类替换为自己的。如上我的为4类,注意:只有一个类别是需要改成这样:CLASSES=('pig',) 必须要有一个逗号才行。
2 . /mmdetection-master/mmdet/core/evaluation/classes_name.py中
def coco_classes():
return [
'holothurian','echinus','scallop','starfish'
]
中的80类也替换为自己的,此处类别为一类的话就不需要加逗号了。
3. 修改你要跑的模型的配置文件,比如faster_rpn_50
/mmdetection-master/configs/faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py
打开可以看到_base_ = [
'../_base_/models/faster_rcnn_r50_fpn.py',
'../_base_/datasets/coco_detection.py',
'../_base_/schedules/schedule_1x.py', '../_base_/default_runtime.py'
]
说明其网络配置在_base_/models/faster_rcnn_r50_fpn.py中,训练数据加载在_base_/datasets/coco_detection.py中,可以到_base_/datasets/coco_detection.py中直接修改训练文件路径:
dataset_type = 'CocoDataset'
data_root = '/home1/dzzHD/mmdetection-master/data/coco/'
data = dict(
samples_per_gpu=2,
workers_per_gpu=2,
train=dict(
type=dataset_type,
ann_file=data_root + 'annotations/train.json',
img_prefix=data_root + 'train/',
pipeline=train_pipeline),
val=dict(
type=dataset_type,
ann_file=data_root + 'annotations/val.json',
img_prefix=data_root + 'val/',
pipeline=test_pipeline),
test=dict(
type=dataset_type,
ann_file=data_root + 'annotations/val.json',
img_prefix=data_root + 'test/',
pipeline=test_pipeline))
evaluation = dict(interval=1, metric='bbox')
然后就可以运行训练
python tools/train.py configs/faster_rcnn/.....py --work-dir +自己建立的结果保存路径即可
我用的2.0的,这样直接运行会报错,说类别数还是没有改成功,只能通过重新编译才行,但是我没有重新编译的权限,所以想了一个曲线救国的办法:
运行出错后,仍然会才输出文件路径保存一个你自己的配置文件,这个配置文件中有你选择的网络、数据、训练方式等,可以直接在该配置文件中设定自己的类别数:
######################################################
max_per_img=200)))
dataset_type = 'CocoDataset'
data_root = '/home1/dzzHD/mmdetection-master/data/coco/'
CLASSES = ('holothurian', 'echinus', 'scallop', 'starfish')#####直接添加,一类要加逗号
img_norm_cfg = dict(
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
####################################################
#######在data中的train,val ,test都添加:
data = dict(
samples_per_gpu=2,
workers_per_gpu=2,
train=dict(
type='CocoDataset',
classes=CLASSES,#############添加该句
ann_file=
'/home1/dzzHD/mmdetection-master/data/coco/sea_2021/jsooon/train_.json',
img_prefix='/home1/dzzHD/mmdetection-master/data/coco/sea_2021/images/',
pipeline=[
dict(type='LoadImageFromFile'),
dict(type='LoadAnnotations', with_bbox=True),
dict(
type='Resize',
img_scale=[(1780, 600), (1780, 1000)],
multiscale_mode='range',
keep_ratio=True),
之后再训练,配置文件直接选择改好的:
python tools/train.py 自己的输出路径下的配置文件 --work-dir +自己建立的结果保存路径即可
测试:
mmdetection中自带的test.py仅仅是个例子,只能跑一张图像,是个demo,可以自己写test文件并输出自己指定格式的文件:下面是我的,输出格式为txt文件:
#自定义test.py mmdetection中test是仅对一张图片进行的,自定义对多张图片进行,并且保存结果随后对结果进行整形,整到可以提交的结果
####每次运行要清空savepath和finaltxt路径下的内容
from mmdet.apis import inference_detector, init_detector,show_result_pyplot
import cv2
import os
def transformation(txtpath,newtxt):####结果整形,整到提交格式
f=open(txtpath,'r')
files=f.readlines()
# print(files[0][7:])
files[0] = files[0][7:]
mm = len(files)
# print(files[mm-1][:-17])
files[mm - 1] = files[mm - 1][:-17]
# print(files)
files1=files[1::2]
files2=files[::2]
num=len(files2)####pignumber
with open (newtxt,'w', encoding="utf-8") as new:
for i in range(num):
# print(item)
item=files2[i]
item=item.replace('[','')
item=item.replace(']','')
item=item.replace('\n','')
item2=files1[i]
item2 = item2.replace('[', '')
item2 = item2.replace(']', '')
item2 = item2.replace('\n', '')
item2=item2.replace(',','')
#print(item.split(','))
item=['pig',
float(item2),
int(0.5*float(item.split(',')[0])+0.5*float(item.split(',')[2])),
int(0.5*float(item.split(',')[1])+0.5*float(item.split(',')[3])),
int(float(item.split(',')[2])-float(item.split(',')[0])),
int(float(item.split(',')[3])-float(item.split(',')[1]))]
item = str(item)
item = ''.join(item.split())
item=item.replace(',',' ')
item=item.replace('\'','')
new.write(str(item)[1:-1])
new.write('\r\n')
config_file = 'output/train/faster_rcnn_r50_fpn_1x_coco.py'#####模型配置文件
checkpoint_file = 'output/train/latest.pth'#####训练好的模型
#
model = init_detector(config_file, checkpoint_file)####初始化模型
imgpath = 'data/coco/test/'#####测试集图像路径
savepath='/home1/dzzHD/mmdetection-master/output/result_v1/'####测试结果txt保存路径
finaltxt='/home1/dzzHD/mmdetection-master/output/test/'###最终整形结果保存路径
imgs=os.listdir(imgpath)
for item in imgs:#######逐张检测测试集图像并保存结果
print(item)
imgname=item.split('.')[0]
img=cv2.imread(imgpath+item)
result = inference_detector(model, img)
txtpath1=savepath+imgname+'.txt'
finaltxtpath=finaltxt+imgname+'.txt'
file=open(txtpath1,'w', encoding="utf-8")
file.write(str(result))
file.close()
transformation(txtpath1,finaltxtpath)
###结果文件整形
#print('result saved')
#print(type(result))
#print(img.split('/'))
#show_result_pyplot(model,img, result, score_thr=0.3) #这里将结果图片进行保存,其余参数为默认
OK,thats all mthank you .