【AI达人创造营 EdgeBoard部署】森林护卫者:无人机航拍图像火情监测

news/2024/11/29 9:36:57/

森林护卫者:无人机航拍图像火情监测

本项目旨在将无人机技术与计算机视觉技术相结合,开发一套应用于无人机森林火灾监测的高效林火识别与火灾区域分割系统。

>

一、项目背景

森林火灾是一种突发性强、处置救助较为困难的自然灾害,具有火情隐蔽且易热累积等特点。其破坏性巨大,对森林生态系统与人类健康生活有较强的危害。据相关资料显示,近年来全世界每年平均发生森林火灾20余万次,烧毁的森林覆盖总面积数百万公顷,造成大量的人员伤亡和巨额的经济损失。为应对森林火灾,森林消防员时刻承担着巨大的生命危险。然而,森林环境复杂,火情易受天气原因突变。因此,对森林火情态势迅速、准确地检测预警,以进行及时资源调度、采取扑救措施,有着十分重要的意义。

目前,森林火灾的主要监测手段以消防瞭望塔、人工巡检以及视频监控系统为主。消防瞭望塔与人工巡检在传统森林防火中扮演重要角色,主要依靠人工经验观察,主观性强,准确性低。且其还受地形和天气的限制,覆盖范围小,检测中存在盲点和缺口。如遇雾霾或夜晚,则观测受阻。视频监控系统是传统城市监控方式的延伸,依赖于有限监控设备的高密度布设,但对于大森林环境,监控领域有限,难以覆盖全境。近年来新提出的卫星森林火警系统,虽然其监控范围广,但星载观测设备的空间分辨率较低,难以精确定位,易受天气、云层以及轨道周期的影响。以上几种森林防火对热积累、弱明火区域的快速检测可能性较小,有效性受限。

近年来,由于越来越多科研、生产力量的投入,无人机研产加速,消费级市场的不断开拓创新,被更多行业市场认可。作为飞行平台,无人机可以搭载多种设备,对各种行业应用都具有较高可适性。随着飞控技术的发展,无人机以其结构简单、飞行灵活、成本低的优势逐渐应用于各种应急救援领域。

二、应用场景

本项目面向无人机平台,同时可以搭载热红外相机等机载设备进行协同工作,实时监测森林火情。

  1. 该系统机动性能强,能够在空中俯瞰监测区域,监测范围广,通过规划合理的巡检航拍轨迹,能快速全面获取被测森林的实时监测图像。
  2. 基于深度学习方法的火情识别分析,系统能捕捉到隐藏于复杂环境深处的微弱明火区域,从而实现初期火情的有效预警,降低损失,具备更高的时效性。
无人机森林火灾监测

三、数据集

本项目基于北亚利桑那大学学者公开的森林火情航拍图像数据集FLAME,该数据集拍摄的地点是在亚利桑那州松树林,由研究人员操作无人机在当地规定的燃烧堆积杂物期间收集。该数据集包含的内容比较丰富,原始数据为视频格式,作者又进一步从视频中分割图像帧并进行标注,最终形成两部分图像数据集,可分别用于图像分类和分割研究。。

FLAME数据集可视化

FLAME分类数据集结构如下图4所示。其中分类数据集包含训练集和测试集两部分,训练集包括Fire和No_Fire两类共计39375张jpg格式图片,其中Fire类25018帧,No_Fire类14357帧。测试集数量为8617,解压后用于分类模型训练的数据集同级目录下包括Training、Test两个文件夹。

FLAME分割数据集包含训练集、验证集和测试集三部分,数量比为501:102:1400,共计2003张带标注图像。

FLAME数据集结构可视化

四、模型介绍

本项目面向无人机移动平台应用,因此我们优先选择轻量级模型进行开发,目前通过结果对比我们最终选择使用EfficientNetB0和BiSeNetV2。

接下来将分别展示火情图像分类和火灾区域分割两部分内容。

五、火情识别

相关库安装

# 安装paddleclas以及相关三方包
!git clone https://gitee.com/paddlepaddle/PaddleClas.git -b release/2.2

5.1 数据处理

解压Classification数据集到PaddleClas/dataset文件夹

  • 需要注意的是:
    • 数据集存放的位置与生成的数据列表文件中的数据路径需要与配置文件对应,这也是初学者时常出现问题的地方。
    • 数据列表文件中路径与标签之间的分割符号,行与行之间的换行符号
    • 有些特定字符转义之后出现问题

PaddleClas下载并安装相关依赖库

!mkdir /home/aistudio/PaddleClas/dataset/Fire
!unzip -q data/data106443/Training.zip
!mv Training PaddleClas/dataset/Fire/Training
!unzip -q data/data106443/Test.zip
!mv Test PaddleClas/dataset/Fire

数据集划分

原始数据集:训练集:39375 测试集:8617

按照4:1重新划分训练集:验证集 = 31395 :7980

#生成对应训练集、验证集及测试集路径对应文本文件
import codecs
import os
import random
import shutil
from PIL import Imagetrain_ratio = 4.0 / 5
data_dir = 'PaddleClas/dataset/Fire/'
all_file_dir= 'PaddleClas/dataset/Fire/Training'
class_list = [c for c in os.listdir(all_file_dir) if os.path.isdir(os.path.join(all_file_dir ,c)) and not c.endswith('Set') and not c.startswith('.')]
class_list.sort()
print(class_list)  #['Fire', 'No_Fire']train_image_dir = os.path.join(data_dir, "trainImageSet")
if not os.path.exists(train_image_dir):os.makedirs(train_image_dir)eval_image_dir = os.path.join(data_dir, "evalImageSet")
if not os.path.exists(eval_image_dir):os.makedirs(eval_image_dir)train_file = codecs.open(os.path.join(data_dir, "train.txt"), 'w')
eval_file = codecs.open(os.path.join(data_dir, "eval.txt"), 'w')
label_id = 1with codecs.open(os.path.join(data_dir, "label_list.txt"), "w") as label_list:for class_dir in class_list: label_list.write("{0}\t{1}\n".format(label_id, class_dir))index = 0image_path_pre = os.path.join(all_file_dir, class_dir)print(image_path_pre)for file in os.listdir(image_path_pre):try:img = Image.open(os.path.join(image_path_pre, file))index = index + 1if random.uniform(0, 1) <= train_ratio:shutil.copyfile(os.path.join(image_path_pre, file), os.path.join(train_image_dir, file))train_file.write("{0} {1}\n".format(os.path.join("trainImageSet", file), label_id))else:shutil.copyfile(os.path.join(image_path_pre, file), os.path.join(eval_image_dir, file))eval_file.write("{0} {1}\n".format(os.path.join("evalImageSet", file), label_id))except Exception as e:pass     label_id = label_id - 1print(index)train_file.close()
eval_file.close()
#images number 39375:8617
['Fire', 'No_Fire']
PaddleClas/dataset/Fire/Training/Fire
25018
PaddleClas/dataset/Fire/Training/No_Fire
14357
#写一个测试集txt作为批量评估结果
import codecs
import os
import random
import shutil
from PIL import Imagedata_dir = 'dataset/Fire/'
all_file_dir= 'dataset/Fire/Test'
class_list = [c for c in os.listdir(all_file_dir) if os.path.isdir(os.path.join(all_file_dir ,c)) and not c.endswith('Set') and not c.startswith('.')]
class_list.sort()
print(class_list)  #['Fire', 'No_Fire']test_file = codecs.open(os.path.join(data_dir, "test.txt"), 'w')
label_id = 1with codecs.open(os.path.join(data_dir, "label_list.txt"), "r") as label_list:for class_dir in class_list:index = 0 #label_list.write("{0}\t{1}\n".format(label_id, class_dir))image_path_pre = os.path.join(all_file_dir, class_dir)print(image_path_pre)for file in os.listdir(image_path_pre):try:#img = Image.open(os.path.join(image_path_pre, file))index = index + 1test_file.write("{0} {1}\n".format(os.path.join(image_path_pre, file), label_id))except Exception as e:passprint(label_id, index)label_id = label_id - 1test_file.close()
#images number 39375:8617
['Fire', 'No_Fire']
dataset/Fire/Test/Fire
1 5137
dataset/Fire/Test/No_Fire
0 3480

5.2 模型训练

一些尝试用过的模型

!python3 tools/train.py \-c ./ppcls/configs/quick_start/professional/ResNet50_vd_CIFAR100.yaml \-o Global.output_dir="output_Fire" \-o Arch.pretrained=True \-o Arch.use_ssld=True!python3 tools/train.py \-c ./ppcls/configs/quick_start/ResNet50_vd.yaml \-o Arch.pretrained=False \-o Global.device=gpu!python3 tools/train.py \-c ./ppcls/configs/quick_start/Res2Net200_vd.yaml \-o Global.output_dir="output_Fire_mix" \-o Global.checkpoints='./output_Fire_mix/Res2Net200_vd_26w_4s/latest'

修改配置文件:最基本的是修改路径、num_worker、epoch等

# global configs
Global:checkpoints: nullpretrained_model: nulloutput_dir: ./output/device: gpusave_interval: 50eval_during_train: Trueeval_interval: 1epochs: 400print_batch_step: 10use_visualdl: False# used for static mode and model exportimage_shape: [3, 224, 224]save_inference_dir: ./inference# model architecture
Arch:name: EfficientNetB0class_num: 2# loss function config for traing/eval process
Loss:Train:- CELoss:weight: 1.0epsilon: 0.1Eval:- CELoss:weight: 1.0Optimizer:name: RMSPropmomentum: 0.9rho: 0.9epsilon: 0.001lr:name: Cosinelearning_rate: 0.032regularizer:name: 'L2'coeff: 0.00001# data loader for train and eval
DataLoader:Train:dataset:name: ImageNetDatasetimage_root: ./dataset/Fire/cls_label_path: ./dataset/Fire/train.txttransform_ops:- DecodeImage:to_rgb: Truechannel_first: False- RandCropImage:size: 224- RandFlipImage:flip_code: 1- AutoAugment:- NormalizeImage:scale: 1.0/255.0mean: [0.485, 0.456, 0.406]std: [0.229, 0.224, 0.225]order: ''sampler:name: DistributedBatchSamplerbatch_size: 64drop_last: Falseshuffle: Trueloader:num_workers: 0use_shared_memory: TrueEval:dataset: name: ImageNetDatasetimage_root: ./dataset/Fire/cls_label_path: ./dataset/Fire/eval.txttransform_ops:- DecodeImage:to_rgb: Truechannel_first: False- ResizeImage:resize_short: 256- CropImage:size: 224- NormalizeImage:scale: 1.0/255.0mean: [0.485, 0.456, 0.406]std: [0.229, 0.224, 0.225]order: ''sampler:name: DistributedBatchSamplerbatch_size: 128drop_last: Falseshuffle: Falseloader:num_workers: 0use_shared_memory: TrueInfer:infer_imgs: docs/images/whl/demo.jpgbatch_size: 10transforms:- DecodeImage:to_rgb: Truechannel_first: False- ResizeImage:resize_short: 256- CropImage:size: 224- NormalizeImage:scale: 1.0/255.0mean: [0.485, 0.456, 0.406]std: [0.229, 0.224, 0.225]order: ''- ToCHWImage:PostProcess:name: Topktopk: 1class_id_map_file: ./dataset/Fire/label_list.txtMetric:Train:- TopkAcc:topk: [1]Eval:- TopkAcc:topk: [1]
#环境设置
%cd PaddleClas
import os 
os.environ['PYTHONPATH']="/home/aistudio/PaddleClas"
!python3 tools/train.py \-c ./ppcls/configs/quick_start/efficientB0.yaml \-o Global.output_dir="output_Fire" \-o Arch.pretrained=True \-o Arch.use_ssld=True \-o Global.checkpoints='./output_Fire/EfficientNetB0/latest'

5.3 模型验证

!python3 tools/eval.py \-c dataset/Fire/testeff.yaml \-o Global.pretrained_model=./output_Fire/EfficientNetB0/best_model
#[2021/10/26 17:09:22] root INFO: [Eval][Epoch 0][Avg]CELoss: 0.66194, loss: 0.66194, top1: 0.71069 best_model
#[2021/10/19 16:21:07] root INFO: [Eval][Epoch 0][Avg]CELoss: 0.6413, loss: 0.65825, top1: 0.72786 best_model
#[2021/10/26 17:20:31] root INFO: [Eval][Epoch 0][Avg]CELoss: 0.65331, loss: 0.65331, top1: 0.70721 latest
#[2021/10/27 08:11:27] root INFO: [Eval][Epoch 0][Avg]CELoss: 0.65825, loss: 0.65825, top1: 0.71777 best_model

一些模型记录

#开始评估
!python3 tools/eval.py \-c dataset/Fire/test.yaml \-o Global.pretrained_model=./output/ResNet50_vd/best_model
#[2021/10/06 12:34:49] root INFO: [Eval][Epoch 0][Avg]CELoss: 1.27037, loss: 1.27037, top1: 0.65394#开始评估
!python3 tools/eval.py \-c /home/aistudio/work/eval.yaml \-o Global.pretrained_model=./output_Fire_mix/MobileNetV3_large_x1_0/best_model
#[2021/10/13 23:31:57] root INFO: [Eval][Epoch 0][Avg]CELoss: 0.75095, loss: 0.75095, top1: 0.40385#开始评估
!python3 tools/eval.py \-c dataset/Fire/testplus.yaml \-o Global.pretrained_model=./output_Fire/ResNet50_vd/best_model
#[2021/10/13 23:27:52] root INFO: [Eval][Epoch 0][Avg]CELoss: 1.86942, loss: 1.86942, top1: 0.61181!python3 tools/eval.py \-c dataset/Fire/testres2net.yaml \-o Global.pretrained_model=./output_Fire_mix/Res2Net200_vd_26w_4s/best_model
#[2021/10/13 23:26:13] root INFO: [Eval][Epoch 0][Avg]CELoss: 0.68440, loss: 0.68440, top1: 0.54033
!python3 tools/infer.py \-c ./ppcls/configs/quick_start/efficientB0.yaml \-o Infer.infer_imgs=/home/aistudio/work/testimg/no/resized_test_nofire_frame21.jpg \-o Global.pretrained_model=./output_Fire/EfficientNetB0/best_model

5.4 模型导出

#模型推理-1 导出
!python tools/export_model.py \-c ./ppcls/configs/quick_start/efficientB0.yaml \-o Global.pretrained_model=./output_Fire/EfficientNetB0/best_model \-o Global.output_dir=/home/aistudio/work/classinf/ \-o Global.save_inference_dir=/home/aistudio/work/classinf/

5.5 结果展示

六、火灾区域分割

PaddleSeg下载并安装相关依赖库

%cd /home/aistudio/
# 安装PaddleSeg
! pip install paddleseg
#下载PaddleSeg代码
!git clone https://gitee.com/PaddlePaddle/PaddleSeg.git
!pip install -r PaddleSeg/requirements.txt

6.1 数据处理

解压Segmentation数据集及Masks

!rar x data/data106593/Images01.rar PaddleSeg/data/FireSeg/train/
!rar x data/data106593/Images02.rar PaddleSeg/data/FireSeg/val/
!rar x data/data106593/Images03.rar PaddleSeg/data/FireSeg/test/!unzip -d /home/aistudio/PaddleSeg/data/FireSeg/ /home/aistudio/255Masks.zip

数据可视化

%matplotlib inline
%config InlineBackend.figure_format = 'svg'
from PIL import Image
import cv2
import pandas as pd
import matplotlib.pyplot as plt
imgpath = '/home/aistudio/PaddleSeg/data/FireSeg/Masks/image_1008.png'print(imgpath)
img = Image.open(imgpath)
img = img.convert('L')plt.figure("Image")
# 这里必须加 cmap='gray' ,否则尽管原图像是灰度图(下图1),但是显示的是伪彩色图像(下图2)(如果不加的话)
plt.imshow(img,cmap='gray')
plt.axis('on')
plt.title('image')
plt.show()
/home/aistudio/PaddleSeg/data/FireSeg/Masks/image_1008.png

在这里插入图片描述

生成数据集索引列表

!pwd
%cd PaddleSeg
/home/aistudio
/home/aistudio/PaddleSeg
import os
from tqdm import tqdm
from random import shuffledataset = 'data/FireSeg'
train_txt = os.path.join(dataset, 'train_list.txt')
val_txt = os.path.join(dataset, 'val_list.txt')
test_txt = os.path.join(dataset, 'test_list.txt')
lbl_txt = os.path.join(dataset, 'labels.txt')classes = ['Fire','background']with open(lbl_txt, 'w') as f:for l in classes:f.write(l+'\n')xml_base = 'Masks'
train_img_base = 'train'
eval_img_base = 'val'
test_img_base = 'test'trains = [v for v in os.listdir(os.path.join(dataset, train_img_base)) if v.endswith('.jpg')]
#xmls.sort(key=lambda x: int(x[6:-4]))
shuffle(trains)  #随机打乱vals = [v for v in os.listdir(os.path.join(dataset, eval_img_base)) if v.endswith('.jpg')]
#xmls.sort(key=lambda x: int(x[6:-4]))
shuffle(vals)  #随机打乱tests = [v for v in os.listdir(os.path.join(dataset, test_img_base)) if v.endswith('.jpg')]
#xmls.sort(key=lambda x: int(x[6:-4]))
shuffle(tests)  #随机打乱with open(train_txt, 'w') as f:for x in tqdm(trains):m = x[:-4]+'.png'xml_path = os.path.join(xml_base, m)img_path = os.path.join(train_img_base, x)f.write('{} {}\n'.format(img_path, xml_path))with open(val_txt, 'w') as f:for x in tqdm(vals):m = x[:-4]+'.png'xml_path = os.path.join(xml_base, m)img_path = os.path.join(eval_img_base, x)f.write('{} {}\n'.format(img_path, xml_path))with open(test_txt, 'w') as f:for x in tqdm(tests):m = x[:-4]+'.png'xml_path = os.path.join(xml_base, m)img_path = os.path.join(test_img_base, x)f.write('{} {}\n'.format(img_path, xml_path))
100%|██████████| 501/501 [00:00<00:00, 21850.10it/s]
100%|██████████| 102/102 [00:00<00:00, 192278.21it/s]
100%|██████████| 1400/1400 [00:00<00:00, 261211.10it/s]

6.2 模型训练

修改配置文件

batch_size: 4
iters: 200train_dataset:type: OpticDiscSegdataset_root: data/Firesegtransforms:- type: Resizetarget_size: [512, 512]- type: RandomHorizontalFlip- type: Normalizemode: trainval_dataset:type: OpticDiscSegdataset_root: data/Firesegtransforms:- type: Resizetarget_size: [512, 512]- type: Normalizemode: valoptimizer:type: sgdmomentum: 0.9weight_decay: 4.0e-5lr_scheduler:type: PolynomialDecaylearning_rate: 0.01end_lr: 0power: 0.9loss:types:- type: CrossEntropyLosscoef: [1, 1, 1, 1, 1]model:type: BiSeNetV2pretrained: Null
!python train.py \--config /home/aistudio/work/seg/bisenet_optic_disc_512x512_1k.yml \--do_eval \ #表示一遍训练一遍验证--save_interval 200 \ #表示每经过200个iters,进行一个模型的保存--save_dir /home/aistudio/v1output #模型保存路径
#--resume_model output/iter_6000 \

6.3 模型验证

!python val.py \--config /home/aistudio/work/seg/bisenet_optic_disc_512x512_1k.yml \--model_path /home/aistudio/v1output/best_model/model.pdparams

6.3 模型预测

! python predict.py --config /home/aistudio/work/seg/bisenet_optic_disc_512x512_1k.yml \--model_path /home/aistudio/v1output/best_model/model.pdparams \--image_path data/FireSeg/test/image_1088.jpg \--save_dir /home/aistudio/segresult/

6.3 模型导出

为了方便用户进行工业级的部署,PaddleSeg提供了一键动转静的功能,即将训练出来的动态图模型文件转化成静态图形式。

!python /home/aistudio/PaddleSeg/export.py -h
!python export.py \--config /home/aistudio/work/seg/bisenet_optic_disc_512x512_1k.yml \--model_path /home/aistudio/v1output/best_model/model.pdparams \--save_dir /home/aistudio/v1output/inference
  • 结果文件
output├── deploy.yaml            # 部署相关的配置文件├── model.pdiparams        # 静态图模型参数├── model.pdiparams.info   # 参数额外信息,一般无需关注└── model.pdmodel          # 静态图模型文件

6.3 模型推理

! python deploy/python/infer.py \--config output/deploy.yaml \--image_path /home/aistudio/work/image_577.jpg \
无需关注└── model.pdmodel          # 静态图模型文件

6.3 模型推理

! python deploy/python/infer.py \--config output/deploy.yaml \--image_path /home/aistudio/work/image_577.jpg \--save_dir /home/aistudio/seg_infer_result/
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/setuptools/depends.py:2: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative usesimport imp
Traceback (most recent call last):File "deploy/python/infer.py", line 215, in <module>main(args)File "deploy/python/infer.py", line 206, in mainraise RuntimeError("The installed Paddle doesn't support GPU."
RuntimeError: The installed Paddle doesn't support GPU.Please change to use CPU or reinstall Paddle.

6.4 部分结果展示

七、总结

本项目设计了一套面向无人机应用的火情监测系统,提出基于百度EdgeBoard作为端侧部署平台,同时搭载EfficientNetB0和BiSeNetV2模型,实现针对无人机航拍森林图像的火灾识别和分割功能。在这个项目中我们使用PaddleClas分类套件和 PaddleSeg 分割套件对航拍森林图像进行火灾识别和火灾区域分割,现在的两个模型准确率还有较大的提升空间,后续可以进一步优化改进。


http://www.ppmy.cn/news/637215.html

相关文章

打开pix4d无有效许可_PIX4D无人机航拍测量软件mapper pro

PIX4D无人机航拍软件 代 理(一年内免费升级) PIX4D支持所输入的数据: 航空(垂直和倾斜)和地面影像支持 处理各种影像,包括从任意角度,地面,无人机或常规航摄拍摄的影像 PIX4D从视频(MP4或AVI格式)图像支持 软件自动从视频中提取帧并创建项目 任意相机(袖珍,单反,多光谱,GoP…

应用机器学习进行无人机航拍影像质量评估

近年来&#xff0c;随着无人机在测绘地理信息领域应用&#xff0c;大量测绘、遥感等任务都在使用无人机开展。无人机主要以空中对地拍摄方式采集地面影像&#xff0c;之后再使用相应软件处理成正射影像、三维模型等成果&#xff0c;之后再进行矢量化采集、分析统计等。 然而无…

使用Nuxt3搭建自己的网站

我的目录 Nuxt3介绍一.创建Nuxt3项目创建Nuxt3项目创建出错时&#xff0c;使用以下方法使用vscode进入项目目录安装相关依赖运行项目 二.完善项目的结构1.创建页面(pages)2.创建公共布局&#xff08;layouts&#xff09;创建默认布局创建自定义布局 3.创建公共组件&#xff08;…

Java父类型引用指向子类型

在java编程中&#xff0c;经常见到父类型引用指向子类型。我理解这样的好处&#xff1a; 可以保证代码的灵活性、隔离性&#xff0c;将代码不变&#xff08;或者少变&#xff09;的部分和多变的部分分离&#xff0c;从而将代码后续的扩展修改控制在尽量小的范围&#xff0c;扩…

2021疫情之后,广州餐饮行业经营建议

2021疫情之后&#xff0c;广州餐饮行业经营建议

数字化餐饮| 刘大厨湘菜馆进杭州,开场及巅峰

盼了几年的刘大厨辣椒炒肉终于来杭州了&#xff0c;但我却没有吃到&#xff0c;小钱对雨科网说&#xff1a;驱车三十里&#xff0c;排队三小时都没吃上&#xff0c;原来他们是每天10点开始放号&#xff0c;11点开餐&#xff0c;去的晚就吃不到。 5月20日&#xff0c;刘大厨在杭…

Access食堂点餐系统

Access食堂点餐系统没有Word系统分析报告&#xff0c;只有一个数据库文件&#xff0c;有需要的自己去下载吧。 链接&#xff1a;https://pan.baidu.com/s/1fV7QVCWBwSgwdoRA2_2eMg 提取码&#xff1a;mtik 以下是系统相关截图。

总结餐饮行业现状痛点

餐饮行业的痛点总结一句话:人才少,竞争激烈&#xff0c;成本高&#xff0c;利润低。 人才少&#xff1a;从业人员受教育程度不高&#xff0c;缺乏科学的企业运营管理经验和手段。 竞争激烈&#xff1a;目前餐饮行业虽然有4万亿的市场&#xff0c;但市场份额分散程度极高&…