转自AI Studio,原文链接:【官方】第十一届 “中国软件杯”百度遥感赛项:目标检测功能 - 飞桨AI Studio

第十一届 “中国软件杯”百度遥感赛项:目标检测功能

1 比赛介绍


“中国软件杯”大学生软件设计大赛是一项面向中国在校学生的公益性赛事,是2021年全国普通高校大学生竞赛榜单内竞赛。大赛由国家工业和信息化部、教育部、江苏省人民政府共同主办,致力于正确引导我国在校学生积极参加软件科研活动,切实增强自我创新能力和实际动手能力,为我国软件和信息技术服务业培养出更多高端、优秀的人才。2022年,百度飞桨承办了A组和B组两个赛道,本赛题为A组。

比赛官网链接

2 数据集介绍


本项目使用RSOD遥感影像目标检测数据集。RSOD数据集由武汉大学于2017年发布,包含4种类型的遥感地物目标(本项目仅使用其中的playground类)。影像提取自Google Earth、天地图等平台。

数据集链接

[1] Y. Long, Y. Gong, Z. Xiao and Q. Liu, "Accurate Object Localization in Remote Sensing Images Based on Convolutional Neural Networks," in IEEE Transactions on Geoscience and Remote Sensing, vol. 55, no. 5, pp. 2486-2498, May 2017. doi: 10.1109/TGRS.2016.2645610
[2] Z Xiao, Q Liu, G Tang, X Zhai, "Elliptic Fourier transformation-based histograms of oriented gradients for rotationally invariant object detection in remote-sensing images", International Journal of Remote Sensing, vol. 36, no. 2, 2015

3 数据预处理


In [ ]

# 解压数据集
!unzip -oq -d /home/aistudio/data/data52980/rsod/ /home/aistudio/data/data52980/RSOD-Dataset.zip
!unzip -oq -d /home/aistudio/data/data52980/rsod/ /home/aistudio/data/data52980/rsod/RSOD-Dataset/playground.zip

In [ ]

# 划分训练集/验证集/测试集,并生成文件名列表
# 所有样本从RSOD数据集的playground子集中选取

import random
import os.path as osp
from os import listdir


# 随机数生成器种子
RNG_SEED = 52980
# 调节此参数控制训练集数据的占比
TRAIN_RATIO = 0.9
# 调节此参数控制验证集数据的占比
VAL_RATIO = 0.05
# 数据集路径
DATA_DIR = '/home/aistudio/data/data52980/rsod/'

# 目标类别
CLASS = 'playground'


def write_rel_paths(phase, names, out_dir):
    """将文件相对路径存储在txt格式文件中"""
    with open(osp.join(out_dir, phase+'.txt'), 'w') as f:
        for name in names:
            f.write(
                ' '.join([
                    osp.join(CLASS, 'JPEGImages', name),
                    osp.join(CLASS, 'Annotation', 'xml', name.replace('.jpg', '.xml'))
                ])
            )
            f.write('\n')


random.seed(RNG_SEED)

names = listdir(osp.join(DATA_DIR, CLASS, 'JPEGImages'))
# 对文件名进行排序,以确保多次运行结果一致
names.sort()
random.shuffle(names)
len_train = int(len(names)*TRAIN_RATIO)
len_val = int(len(names)*VAL_RATIO)
write_rel_paths('train', names[:len_train], DATA_DIR)
write_rel_paths('val', names[len_train:len_train+len_val], DATA_DIR)
write_rel_paths('test', names[len_train+len_val:], DATA_DIR)

# 写入类别信息
with open(osp.join(DATA_DIR, 'labels.txt'), 'w') as f:
    f.write(CLASS+'\n')

print("数据集划分已完成。")

4 模型训练与测试


4.1 依赖安装

In [ ]

# 安装matplotlib
!pip install matplotlib==3.4 > /dev/null

# 安装PaddleRS(AI Studio上缓存的版本)
!unzip -o -d /home/aistudio/ /home/aistudio/data/data135375/PaddleRS-develop.zip > /dev/null
!mv /home/aistudio/PaddleRS-develop /home/aistudio/PaddleRS
!pip install -e /home/aistudio/PaddleRS > /dev/null
# 因为`sys.path`可能没有及时更新,这里选择手动更新
import sys
sys.path.append('/home/aistudio/PaddleRS')

4.2 模型训练

In [ ]

# 导入需要用到的库

import random
import os.path as osp

import cv2
import numpy as np
import paddle
import paddlers as pdrs
from paddlers import transforms as T
from paddlers.tasks.utils.visualize import visualize_detection
from matplotlib import pyplot as plt
from PIL import Image

In [ ]

# 定义全局变量

# 随机种子
SEED = 52980
# 数据集存放目录
DATA_DIR = '/home/aistudio/data/data52980/rsod/'
# 训练集`file_list`文件路径
TRAIN_FILE_LIST_PATH = '/home/aistudio/data/data52980/rsod/train.txt'
# 验证集`file_list`文件路径
VAL_FILE_LIST_PATH = '/home/aistudio/data/data52980/rsod/val.txt'
# 测试集`file_list`文件路径
TEST_FILE_LIST_PATH = '/home/aistudio/data/data52980/rsod/test.txt'
# 数据集类别信息文件路径
LABEL_LIST_PATH = '/home/aistudio/data/data52980/rsod/labels.txt'
# 实验目录,保存输出的模型权重和结果
EXP_DIR =  '/home/aistudio/exp/'
# 目标类别
CLASS = 'playground'
# 模型验证阶段输入影像尺寸
INPUT_SIZE = 608

In [ ]

# 固定随机种子,尽可能使实验结果可复现

random.seed(SEED)
np.random.seed(SEED)
paddle.seed(SEED)

In [ ]

# 构建数据集

train_transforms = T.Compose([
    # 对输入影像施加随机色彩扰动
    T.RandomDistort(),
    # 在影像边界进行随机padding
    T.RandomExpand(),
    # 随机裁剪,裁块大小在一定范围内变动
    T.RandomCrop(),
    # 随机水平翻转
    T.RandomHorizontalFlip(),
    # 对batch进行随机缩放,随机选择插值方式
    T.BatchRandomResize(
        target_sizes=[320, 352, 384, 416, 448, 480, 512, 544, 576, 608],
        interp='RANDOM'
    ),
    # 影像归一化
    T.Normalize(
        mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]
    )
])

eval_transforms = T.Compose([
    # 使用双三次插值将输入影像缩放到固定大小
    T.Resize(
        target_size=INPUT_SIZE, interp='CUBIC'
    ),
    # 验证阶段与训练阶段的归一化方式必须相同
    T.Normalize(
        mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]
    )
])

# 分别构建训练和验证所用的数据集
train_dataset = pdrs.datasets.VOCDetection(
    data_dir=DATA_DIR,
    file_list=TRAIN_FILE_LIST_PATH,
    label_list=LABEL_LIST_PATH,
    transforms=train_transforms,
    shuffle=True
)

val_dataset = pdrs.datasets.VOCDetection(
    data_dir=DATA_DIR,
    file_list=VAL_FILE_LIST_PATH,
    label_list=LABEL_LIST_PATH,
    transforms=eval_transforms,
    shuffle=False
)

In [ ]

# 构建PP-YOLO模型
model = pdrs.tasks.PPYOLO(num_classes=len(train_dataset.labels))
model.net_initialize(
    pretrain_weights='COCO',
    save_dir=osp.join(EXP_DIR, 'pretrain'),
    resume_checkpoint=None,
    is_backbone_weights=False
)

In [ ]

# 执行模型训练
model.train(
    num_epochs=100,
    train_dataset=train_dataset,
    train_batch_size=8,
    eval_dataset=val_dataset,
    # 每多少个epoch存储一次检查点
    save_interval_epochs=10,
    # 每多少次迭代记录一次日志
    log_interval_steps=10,
    save_dir=EXP_DIR,
    # 指定预训练权重
    pretrain_weights='COCO',
    # 初始学习率大小
    learning_rate=0.0001,
    # 学习率预热(learning rate warm-up)步数与初始值
    warmup_steps=0,
    warmup_start_lr=0.0,
    # 是否启用VisualDL日志功能
    use_vdl=True
)

4.3 模型测试

In [ ]

# 构建测试集
test_dataset = pdrs.datasets.VOCDetection(
    data_dir=DATA_DIR,
    file_list=TEST_FILE_LIST_PATH,
    label_list=LABEL_LIST_PATH,
    transforms=eval_transforms,
    shuffle=False
)


# 为模型加载历史最佳权重
state_dict = paddle.load(osp.join(EXP_DIR, 'best_model/model.pdparams'))
model.net.set_state_dict(state_dict)

# 执行测试
test_result = model.evaluate(test_dataset)
print(
    "测试集上指标:bbox mAP为{:.2f}".format(
        test_result['bbox_map'], 
    )
)

4.4 预测结果可视化

In [25]

# 预测结果可视化
# 重复运行本单元可以查看不同结果

def read_rgb(path):
    im = cv2.imread(path)
    im = im[...,::-1]
    return im


def show_images_in_row(ims, fig, title='', lut=None):
    n = len(ims)
    fig.suptitle(title)
    axs = fig.subplots(nrows=1, ncols=n)
    for idx, (im, ax) in enumerate(zip(ims, axs)):
        # 去掉刻度线和边框
        ax.spines['top'].set_visible(False)
        ax.spines['right'].set_visible(False)
        ax.spines['bottom'].set_visible(False)
        ax.spines['left'].set_visible(False)
        ax.get_xaxis().set_ticks([])
        ax.get_yaxis().set_ticks([])

        ax.imshow(im)


# 需要展示的样本个数
num_imgs_to_show = 4
# 随机抽取样本
chosen_indices = random.choices(range(len(test_dataset)), k=num_imgs_to_show)

# 参考 https://stackoverflow.com/a/68209152
fig = plt.figure(constrained_layout=True)
fig.suptitle("Test Results")

subfigs = fig.subfigures(nrows=2, ncols=1)

# 读取输入影像并显示
ims = [read_rgb(test_dataset.file_list[idx]['image']) for idx in chosen_indices]
show_images_in_row(ims, subfigs[0], title='Image')

# 绘制目标框
with paddle.no_grad():
    vis_res = []
    for idx, im in zip(chosen_indices, ims):
        sample = test_dataset[idx]
        gt = [
            {
                'category_id': cid[0], 
                'category': CLASS, 
                'bbox': [bbox[0], bbox[1], bbox[2]-bbox[0], bbox[3]-bbox[1]], 
                'score': 1.0
            } 
            for cid, bbox in zip(sample['gt_class'], sample['gt_bbox'])
        ]

        im = cv2.resize(im[...,::-1], (INPUT_SIZE, INPUT_SIZE), interpolation=cv2.INTER_CUBIC)
        pred = model.predict(im, eval_transforms)

        vis = im
        # 用绿色画出预测目标框
        if len(pred) > 0:
            vis = visualize_detection(
                np.array(vis), pred, 
                color=np.asarray([[0,255,0]], dtype=np.uint8), 
                threshold=0.2, save_dir=None
            )
        # 用蓝色画出真实目标框
        if len(gt) > 0:
            vis = visualize_detection(
                np.array(vis), gt, 
                color=np.asarray([[0,0,255]], dtype=np.uint8), 
                save_dir=None
            )
        vis_res.append(vis)
show_images_in_row(vis_res, subfigs[1], title='Detection')

# 渲染结果
fig.canvas.draw()
Image.frombytes('RGB', fig.canvas.get_width_height(), fig.canvas.tostring_rgb())

<PIL.Image.Image image mode=RGB size=640x480 at 0x7F401CA38B10>
Logo

学大模型,用大模型上飞桨星河社区!每天8点V100G算力免费领!免费领取ERNIE 4.0 100w Token >>>

更多推荐