paddle 45 使用paddledetection做旋转框预测(预测自己的dota数据集)

paddledetection的最新develop版本支持旋转框预测
2022.11.15:发布基于PP-YOLOE+扩展的旋转框、小目标检测SOTA模型
项目地址:https://gitee.com/paddlepaddle/PaddleDetection/tree/develop/configs/rotate#%E7%AE%80%E4%BB%8B
旋转框检测模型PP-YOLOE-R
Anchor-free旋转框检测SOTA模型,精度速度双高
云边一体,s/m/l/x四个模型适配不用算力硬件
部署友好,避免使用特殊算子,能够轻松使用TensorRT加速

特别说明

支持选择框训练与部署的目前是paddledetection的最新develop版本,因此请注意自己的paddledetection版本,develop版下载地址为:
https://gitee.com/paddlepaddle/PaddleDetection/tree/develop

1、编译旋转框插件

如果编译失败可以纠正一下GCC和paddle版本,然后删除上次失败残余的文件,重新执行命令
博主操作成功的GCC版本为 GCC8.0,paddle版本为2.4(补充说明,linux系统支持安装多个gcc版本,只需将系统默认的gcc、g++重新链接到新安装的gcc、g++即可,不用卸载以前安装的旧版本)

cd ~/HPG/PaddleDetection/ppdet/ext_op/
python setup.py install

如果更新paddle版本,在import paddle报错的话,请使用以下命令添加LD_LIBRARY_PATH,注意其中的路径要修改为自己python环境中的paddle/lib路径

export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/home/{username}/anaconda3/envs/paddle/lib

2、设置数据路径

这里通过export 命令设置零时环境变量,用于后续数据格式转换

export train_dir='/home/{username}/MyDota/train/'
export val_dir='/home/{username}/MyDota/val/'

对应数据的路径存储如下所示,这是标准的dota,已经划分好了数据集。

2.1 DOTA数据介绍

DOTA数据集是一个大规模的遥感图像数据集,包含旋转框和水平框的标注。可以从DOTA数据集官网下载数据集并解压,解压后的数据集目录结构如下所示:

${DOTA_ROOT}
├── test
│ └── images
├── train
│ ├── images
│ └── labelTxt
└── val
├── images
└── labelTxt
对于有标注的数据,每一张图片会对应一个同名的txt文件,文件中每一行为一个旋转框的标注,其格式如下:

x1 y1 x2 y2 x3 y3 x4 y4 class_name difficult
示例数据:
532 604 534 592 565 608 567 595 ship 0
384 251 393 231 443 275 452 256 ship 0

3、数据格式转换

3.1 修改configs/rotate/tools/prepare_data.py

原始的prepare_data.py文件是针对原始dota数据集的,在转换数据时需要设置图像切片参数,而博主的数据集已经完成切片操作了,故此对prepare_data.py文件进行修改。主要就是添加了一个命令行参数only_change_format,用于控制脚本只转换数据格式,不进行切片。

打开configs/rotate/tools/prepare_data.py,使用以下内容进行替换
同时,需要按照自己的数据情况修改 class_names,博主的dota数据中只有ship和car两个类别,故此 class_names = [‘ship’,‘car’]

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import os
import argparse
from convert import load_dota_infos, data_to_coco
from slicebase import SliceBase

wordname_15 = [
    'plane', 'baseball-diamond', 'bridge', 'ground-track-field',
    'small-vehicle', 'large-vehicle', 'ship', 'tennis-court',
    'basketball-court', 'storage-tank', 'soccer-ball-field', 'roundabout',
    'harbor', 'swimming-pool', 'helicopter'
]

wordname_16 = wordname_15 + ['container-crane']
wordname_18 = wordname_16 + ['airport', 'helipad']

DATA_CLASSES = {
    'dota10': wordname_15,
    'dota15': wordname_16,
    'dota20': wordname_18
}

def parse_args():
    parser = argparse.ArgumentParser('prepare data for training')
    parser.add_argument(
        '--input_dirs',
        nargs='+',
        type=str,
        default=None,
        help='input dirs which contain image and labelTxt dir')

    parser.add_argument(
        '--output_dir',
        type=str,
        default=None,
        help='output dirs which contain image and labelTxt dir and coco style json file'
    )

    parser.add_argument(
        '--coco_json_file',
        type=str,
        default='',
        help='coco json annotation files')

    parser.add_argument('--subsize', type=int, default=1024, help='patch size')

    parser.add_argument('--gap', type=int, default=200, help='step size')

    parser.add_argument(
        '--data_type', type=str, default='dota10', help='data type')

    parser.add_argument(
        '--rates',
        nargs='+',
        type=float,
        default=[1.],
        help='scales for multi-sclace training')

    parser.add_argument(
        '--nproc', type=int, default=8, help='the processor number')

    parser.add_argument(
        '--iof_thr',
        type=float,
        default=0.5,
        help='the minimal iof between a object and a window')
    
    parser.add_argument(
        '--only_change_format',
        action='store_true',
        default=False,
        help='only processing image')
    
    parser.add_argument(
        '--image_only',
        action='store_true',
        default=False,
        help='only processing image')

    args = parser.parse_args()
    return args


def load_dataset(input_dir, nproc, data_type):
    if 'dota' in data_type.lower():
        infos = load_dota_infos(input_dir, nproc)
    else:
        raise ValueError('only dota dataset is supported now')

    return infos


def main():
    args = parse_args()
    infos = []
    for input_dir in args.input_dirs:
        infos += load_dataset(input_dir, args.nproc, args.data_type)
    if args.only_change_format==False:
        slicer = SliceBase(
            args.gap,
            args.subsize,
            args.iof_thr,
            num_process=args.nproc,
            image_only=args.image_only)
        slicer.slice_data(infos, args.rates, args.output_dir)
    else:
        args.output_dir=args.input_dirs[0]
        print("only_change_format,input_dirs should contain image and labelTxt folder:")
        print(args.output_dir)
    if args.coco_json_file:
        infos = load_dota_infos(args.output_dir, args.nproc)
        coco_json_file = os.path.join(args.output_dir, args.coco_json_file)
        class_names = ['ship','car']#DATA_CLASSES[args.data_type]
        data_to_coco(infos, coco_json_file, class_names, args.nproc)

if __name__ == '__main__':
    main()

3.2 进行格式转换

通过以下命令可以将原始的dota数据转换为coco格式,

python configs/rotate/tools/prepare_data.py     --input_dirs ${train_dir}   --only_change_format    --coco_json_file DOTA_train.json 
python configs/rotate/tools/prepare_data.py     --input_dirs ${val_dir}     --only_change_format    --coco_json_file DOTA_val.json 

补充说明

paddledetection官网关于旋转框使用标准COCO数据格式,你可以将你的数据集转换成COCO格式以训练模型。COCO标准数据格式的标注信息中包含以下信息:

‘annotations’: [
{
‘id’: 2083, ‘category_id’: 9, ‘image_id’: 9008,
‘bbox’: [x, y, w, h], # 水平框标注
‘segmentation’: [[x1, y1, x2, y2, x3, y3, x4, y4]], # 旋转框标注

}

]
需要注意的是bbox的标注是水平框标注,segmentation为旋转框四个点的标注(顺时针或逆时针均可)。在旋转框训练时bbox是可以缺省,一般推荐根据旋转框标注segmentation生成。 在PaddleDetection 2.4及之前的版本,bbox为旋转框标注[x, y, w, h, angle],segmentation缺省,目前该格式已不再支持,请下载最新数据集或者转换成标准COCO格式。在进行选择框训练时,仅会从segmentation字段中加载boxes信息

3.3 转化为标准格式

mv命令用于移动文件,通过该命令将生成好的json文件存入到dota的目录下
ln命令用于创建软连接,通过软连接(快捷方式)的形式可以避免图像复制,节省存储空间

mv ${train_dir}/DOTA_train.json dataset/dota/
mv ${val_dir}/DOTA_val.json dataset/dota/

ln -s ${val_dir}/images dataset/dota/val_images -r
ln -s ${train_dir}/images dataset/dota/train_images -r

3.4 修改dota.yml

文件具体路径为:configs/datasets/dota.yml
这里也要注意num_classes的设置,按照实际情况设置。

metric: RBOX
num_classes: 2

TrainDataset:
  !COCODataSet
    image_dir: train_images
    anno_path: DOTA_train.json
    dataset_dir: dataset/dota/
    data_fields: ['image', 'gt_bbox', 'gt_class', 'is_crowd', 'gt_poly']

EvalDataset:
  !COCODataSet
    image_dir: val_images
    anno_path: DOTA_val.json
    dataset_dir: dataset/dota/
    data_fields: ['image', 'gt_bbox', 'gt_class', 'is_crowd', 'gt_poly']

TestDataset:
  !ImageFolder
    anno_path: test1024/DOTA_test1024.json
    dataset_dir: dataset/dota/

4、训练模型

在paddledetection中训练旋转框和正框的命令都是一样的,可以参考paddle 31 安装paddledetection并训练自己的数据集(支持voc与coco数据集)

python -m paddle.distributed.launch --gpus 1 tools/train.py     \
        -c configs/rotate/ppyoloe_r/ppyoloe_r_crn_s_3x_dota.yml --eval      \
        -o pretrian_weights=output/ppyoloe_r_crn_s_3x_dota/best_model     \
        --use_vdl=true       
        --vdl_log_dir=vdl_dir/scalar

此外,paddledetection也支持旋转框模型的导出与部署,且与正常模型的导出部署流程是一样的,后续将会补充实现onnx部署教程。

你可能感兴趣的:(paddle,深度学习,人工智能)