共计 5762 个字符,预计需要花费 15 分钟才能阅读完成。
我的项目背景及赛题介绍
卫星的利用非常宽泛,许多行业每天都依赖其运作。咱们通常会依据高度把人造地球卫星运行的轨道简略地分类为低轨道(200 千米~2000 千米)、中轨道(2000 千米~20000 千米)和高轨道(20000 千米以上)。
地球同步静止轨道属于高轨道,与赤道立体成 0 度角,静止轨道卫星运行方向与地球自转方向雷同,运行周期与地球同步,和地球上的咱们放弃绝对静止,下面运行着很多国家的大型通信卫星。无限的地球同步静止轨道资源宝贵,因而,为使运行中的卫星免受可能的碰撞,对运行空间内空间碎片和天然天体等未知空间指标进行及时的检测、跟踪、预警和编目是“太空捍卫兵士”保障卫星流动失常衰弱的一项重要工作。
本我的项目基于 2020 年欧空局联结阿德莱德大学举办的地球同步静止轨道及左近的指标检测 SpotGEO Challenge 比赛,实现“太空捍卫兵士”——太空空间指标检测零碎,从而检测低成本望远镜采集图像中的强劲空间指标,包含空间碎片、非单干飞行器等等。
我的项目链接:https://aistudio.baidu.com/aistudio/projectdetail/2285947?shared=1
数据集介绍
目前,国内上用于空间指标探测辨认的零碎个别包含地基零碎和天基零碎两局部,观测设施包含巡天望远镜和 CCD 摄像机等等。本较量提供的数据集是由搭载在地基望远镜上的低成本 CMOS 相机在夜间拍摄失去,相机曝光工夫为 40s,间断拍摄 5 帧成为一个序列。
地基望远镜数据采集示意图
在相机曝光工夫内,除了地球静止轨道左近的指标或天体之外的其余背景恒星与地球存在相对速度,所以在图片中背景恒星出现条纹状。而 GEO 指标放弃绝对静止,在图像中大多以斑点或短条纹的状态呈现。帧序列拍摄过程中,相机所在的望远镜做匀速转动,所以空间指标的静止轨迹为直线。
数据集示例
这是较量提供的数据集的构造组成,解压后的数据集同级目录下包含 train 和 test 两个文件夹以及对应的两个标注文件。训练集和测试集别离蕴含若干序列以及组成序列的帧图像组成的两级目录。图片格式为 PNG 格局,尺寸大小是 640×480,整个数据集的图片总数量为 32000 张,其中训练集蕴含 1280 个序列 6400 张,测试集 5120 个序列 25600 张。
标注文件格式:每幅图像的标注包含四组值,别离为是帧序列号、帧图像序号、指标数量以及对应地位坐标 (x,y)。
数据集组成示例
指标个性
咱们把须要解决的问题进一步具体化,概括起来就是星空背景下的序列图像小指标检测。
指标个性: 在图像中,咱们关注的指标在图像里大多以斑点或短条纹的状态呈现,而非齐全以点指标的模式存在,指标整体亮度也较暗,这是因为较长的曝光工夫和大气畸变、传感器缺点等起因产生的像素弥散景象。而且除此之外,因为云层笼罩、大气 / 天气影响、光污染、恒星遮挡(背景恒星碰巧越过轨道物体地位)等各项情况因素也减少了问题的难度。数据集中会存在异样高亮的伪指标、或者指标对象只呈现在序列内 5 帧图像中的某几帧中等等问题。
指标静止个性: 咱们利用标注信息将一个序列内的 5 帧图像的指标地位匹配在一幅图中,出现显著的匀速直线静止轨迹。
问题解析
问题转化
联合指标个性,咱们将数据集中的标注点地位视为 bbox 标记框的核心地位,取中心点上下左右宽度为 3 个像素,失去指标标注框。左上角的坐标和右下角的坐标值依据长宽偏移量计算失去,这样将问题转化为类别数为 2 的指标检测问题。
标注图示
整体开发流程
本我的项目应用飞桨全流程开发工具 PaddleX。整个我的项目工作次要包含数据预处理、模型训练及导出以及模型部署三局部:
开发流程
数据预处理过程的工作次要包含:
1. 数据荡涤
首先将不存在任何指标的帧序列删除;
# 数据荡涤 | |
import json | |
def read_annotation_file(path): | |
annotation_list = json.load(open(path)) | |
# Transform list of annotations into dictionary | |
annotation_dict = {} | |
for annotation in annotation_list: | |
sequence_id = annotation['sequence_id'] | |
if sequence_id not in annotation_dict: | |
annotation_dict[sequence_id] = {} | |
annotation_dict[sequence_id][annotation['frame']] = annotation['object_coords'] #just pull out the object_coords | |
return annotation_dict | |
anopath = 'work/train_anno.json' | |
train_annotation= read_annotation_file(anopath) | |
ori_seq = len(train_annotation) | |
print('\n 原始序列数量:', ori_seq) | |
#依据 num_objects 字段删除训练有效序列 | |
for i in range(1, ori_seq): | |
if len(train_annotation[i][1]) == 0: | |
del train_annotation[i] | |
# train_annotation is data numpy | |
real_seq = len(train_annotation) | |
print('\n 无效序列数量:', real_seq) |
2. 数据集标注格局转换
为方便使用 PaddleX 数据接口,将原始的标注文件从新转换为 VOC 格局
from pycocotools.coco import COCO | |
import os, cv2, shutil | |
from lxml import etree, objectify | |
from tqdm import tqdm | |
from PIL import Image | |
CKimg_dir = './SpotGEOvoc/VOCImages' | |
CKanno_dir = './SpotGEOvoc/VOCAnnotations' | |
def catid2name(coco): # 将名字和 id 号建设一个字典 | |
classes = dict() | |
for cat in coco.dataset['categories']: | |
classes[cat['id']] = cat['name'] | |
# print(str(cat['id'])+":"+cat['name']) | |
return classes | |
def get_CK5(origin_anno_dir, origin_image_dir, verbose=False): | |
dataTypes = ['train'] | |
for dataType in dataTypes: | |
annFile = '{}_anno.json'.format(dataType) | |
annpath = os.path.join(origin_anno_dir, annFile) | |
print(annpath) | |
coco = COCO(annpath) | |
classes = catid2name(coco) | |
imgIds = coco.getImgIds() | |
# imgIds=imgIds[0:1000]# 测试用,抽取 10 张图片,看下存储成果 | |
for imgId in tqdm(imgIds): | |
img = coco.loadImgs(imgId)[0] | |
showbycv(coco, dataType, img, classes, origin_image_dir, verbose=False) | |
def main(): | |
base_dir = './SpotGEOvoc' # step1 这里是一个新的文件夹,寄存转换后的图片和标注 | |
image_dir = os.path.join(base_dir, 'VOCImages') # 在上述文件夹中生成 images,annotations 两个子文件夹 | |
anno_dir = os.path.join(base_dir, 'VOCAnnotations') | |
mkr(image_dir) | |
mkr(anno_dir) | |
origin_image_dir = './SpotGEOv2' # step 2 原始的 coco 的图像寄存地位 | |
origin_anno_dir = './SpotGEOv2' # step 3 原始的 coco 的标注寄存地位 | |
verbose = True # 是否须要看下标记是否正确的开关标记,若是 true, 就会把标记展现到图片上 | |
get_CK5(origin_anno_dir, origin_image_dir, verbose) |
3. 将原始训练数据进行训练集和验证集划分
!paddlex --split_dataset --format VOC --dataset_dir MyDataset --val_value 0.2 --test_value 0
对于模型的抉择和开发:因为我的项目最终面向端侧部署,所以轻量高效的检测模型是首选。这里我应用的是飞桨特色模型 PP-YOLOv2,该模型由百度飞桨团队基于 PP-YOLO 模型进行优化改良和降级,网络结构如下图所示。Baseline Model 为 PP-YOLO,骨干网络为 ResNet50-vd,组合增加优化组件蕴含 Deformable Conv、SSLD、CoordConv、DropBlock、SPP、Larger Batch Size、EMA、IoU Loss、IoU Aware、Grid Sensitive 等 10 个 Tricks。
模型训练主体只须要这些代码,配置好数据集格局和门路,而后批改 num_classes 参数,类别数是除去背景类,也就是总类别数 -1。调整设置好一系列训练参数,而后就开始模型训练。
num_classes = 1 | |
model = pdx.det.PPYOLOv2(num_classes=num_classes, backbone='ResNet50_vd_dcn') | |
model.train( | |
num_epochs=3600, | |
train_dataset=train_dataset, | |
train_batch_size=4, | |
eval_dataset=eval_dataset, | |
learning_rate=0.001 / 8, | |
warmup_steps=1000, | |
warmup_start_lr=0.0, | |
save_interval_epochs=36, | |
lr_decay_epochs=[216, 243], | |
save_dir='output/PPyolov2_r50vd_dcn') |
而后期待模型训练完评估好后果之后再进行模型导出及部署。
!paddlex --export_inference --model_dir=/home/aistudio/output/PPyolov2_r50vd_dcn/best_model --save_dir=/home/aistudio/inference
最初,本我的项目基于 Nvidia Jetson Nano 平台实现了一套理论的演示零碎。
在 Jetson NANO 开发板上进行飞桨模型部署,从后期筹备到推理程序验证的全流程次要包含以下几局部。
软件局部,端侧推理程序次要是以下三局部,不得不再次赞叹 API 是真香!
具体的推理代码如下:
## python 部署流程 | |
import glob | |
import numpy as np | |
import threading | |
import time | |
import random | |
import os | |
import base64 | |
import cv2 | |
import json | |
import paddlex as pdx | |
os.environ['CUDA_VISIBLE_DEVICES']='0' | |
predictor = pdx.deploy.Predictor(model_dir='./infer', use_gpu=True, gpu_id=0, use_trt=True) | |
def get_images(image_path, support_ext=".jpg|.jpeg|.png"): | |
if not os.path.exists(image_path): | |
raise Exception(f"Image path {image_path} invalid") | |
if os.path.isfile(image_path): | |
return [image_path] | |
imgs = [] | |
for item in os.listdir(image_path): | |
ext = os.path.splitext(item)[1][1:].strip().lower() | |
if (len(ext) > 0 and ext in support_ext): | |
item_path = os.path.join(image_path, item) | |
imgs.append(item_path) | |
return imgs | |
def crest_dir_not_exist(path): | |
if not os.path.exists(path): | |
os.mkdir(path) | |
def run(img_path,img_name,save_path): | |
result = predictor.predict(img_file=img_name, warmup_iters=100, repeats=100) | |
time2= time.time() | |
pdx.det.visualize(img_name, result, threshold=0.5, save_dir=save_path) | |
time3 = time.time() | |
print("Visual Time: {}s".format(time3-time2)) | |
if __name__ == "__main__": | |
test_path = 'visual/' | |
save_path = 'output/visual/' | |
crest_dir_not_exist(save_path) | |
L = get_images(test_path) | |
N = len(L) | |
print(N) | |
for i in range(N): | |
print(L[i]) | |
run(test_path, L[i],save_path) |
理论的后果演示动图如下所示。
以上就是我的分享,感兴趣的小伙伴能够点击下方我的项目链接,一起交流学习吧。
https://aistudio.baidu.com/aist