【C4AI-2022】基于飞桨的跨平台智慧农业遥感监测平台

项目利用深度学习技术,特别是PaddlePaddle框架,创建了一个专注于农业的遥感解译平台,支持卫星和无人机遥感数据,提供多端服务,包括APP和Web,适用于种植农户和乡村振兴扶助人员。平台通过算法优化提升了遥感图像处理能力,实现了农作物监测和产值预估,旨在助力智慧农业和乡村振兴。
摘要由CSDN通过智能技术生成

★★★ 本文源自AI Studio社区精品项目,【点击此处】查看更多精品内容 >>>


作品创意与独创性

近年来,随着遥感技术的进一步发展和新一代高分辨率卫星系统相继投入应用,我国遥感卫星已广泛应用于城市规划、气象预测、环境保护、防灾减灾、农林业监测等领域并取得了良好的经济和社会效益,对遥感数据的分析应用服务需求与日俱增。而在农业遥感领域,我国的相似产品较少,通过团队市场调研发现,市面上有商汤等公司的遥感解译平台,但主体面向科研用户,并未对农业遥感领域有所突破,大多数公司专注于定制农业遥感处理软件,但大多采用传统方式对高分辨率卫星遥感图像的特征刻画、表现为能力差且人工成本高。随着人工智能及深度学习技术快速发展,通过应用深度学习技术可以加速遥感领域智能化应用,所以我们团队立足于此,通过飞桨实现专注于农业的只能遥感解译平台,通过多端适配,大数据可视化提供数据反馈,为农业遥感领域提供快车道。项目目前的意义为提供空中的智能遥感监测平台,解放农村空心化导致的劳动力,目前已超越已有产品的突破性,我们除卫星遥感外,同样支持接入无人机低空遥感摄像头进行算法实时分析,并且我们提供更加丰富的功能与生态去实现、并且提供多端服务,让用户随时可以使用。
针对于此,我们提供APP、WEB两种形式,其中,web提供遥感解译平台,提供农业农事生产活动,APP提供农产品销售等农业生产基本功能

用户定位

项目目前拥有贴近实际、明确的目标用户与使用场景、在实际上,我们通过实地考察调研进行数据反馈,进行后续迭代,可以应用到实际农业生产领域中,在目标用户上,我们主要面向两类用户:
一是种植农户,农户可通过平台进行实时生长、灾害等监测,并且提供易用的操作方法
二是乡村振兴扶助人员,通过遥感平台实时预估当地农作物产值,提供智能化管理
在使用场景上我们主要应用于智慧农业遥感监测领域,通过专注于农业遥感方面,提供多种算法与软件功能,为乡村振兴战略赋能在受众与市场定位上,我们定位于智慧农业系统平台,可结合智惠农业lot平台进行联调协同,为后续生态扩展提供支持,在受众上我们主要面向政府与农户,让其可以随时享受遥感农业数字化带来的福利。

作品技术路线介绍

我们前期通过选用的vue框架、Android进行前端开发,语言采用Typescript、Java,针对前期软件设计,我们考虑了用户的交互型,体验感以及美观性,同时还有算法的回传效率,所以提出了采用前后端搭配设计的模型架构,通过前端接收信号,设计Flask分布式多线程后端、Spring后端达到负载均衡的效果,同时,我们提供多种赛题外的功能丰富整个平台,使得整个软件模块生态丰富,可以随时与算法解耦使用,进行算法模块的迭代更新
在算法设计上,我们采用PaddlePaddle作为基础深度学习算法框架,使用套件为PaddleLite(Android部署)、PaddleRs(Web部署)除此以外提供多种内置模型使用,围绕赛题提出的遥感影像四大板块,目标检测,变换检测,目标提取和地块分割,我们分别采用PP-yolo2,BIT,Unet++以及Deeplabv3+模型,通过数据调优以及图像增强等多种优化策略,使其检测精度得到有效提升,除此以外,我们通过调整anchor,修改backbone、图像增强、前处理和后处理等多种方式进行算法优化,并将其部署在ECS弹性分布式服务器中进行实时响应计算。
在服务端模块中,通过集群部署、高可用数据库,ECS弹性服务器使得其可以高性能结合运行,通过分布式数据库服务使得数据可以快速响应,信息处理得以快速解决,同时我们支持鲲鹏服务器集群迁移,并取得微认证能力

技术方向

以遥感分类的PaddleLite(Android)部署、Web部署为例进行介绍
在Android端上,为确保轻量级分类,采用的模型套件为MobileNetv3,以实现本地部署(本地化算力并不能支撑太大的模型,对于绝大多数人来说(高档型手机除外))

APP端部署样例

技术路线是Paddlex -> PaddleLite -> Android部署(java)

MobileNet3

mobilenet-v3是Google继mobilenet-v2之后的又一力作,作为mobilenet系列的新成员,自然效果会提升,mobilenet-v3提供了两个版本,分别为mobilenet-v3 large 以及mobilenet-v3 small,分别适用于对资源不同要求的情况,论文中提到,mobilenet-v3 small在imagenet分类任务上,较mobilenet-v2,精度提高了大约3.2%,时间却减少了15%,mobilenet-v3 large在imagenet分类任务上,较mobilenet-v2,精度提高了大约4.6%,时间减少了5%,mobilenet-v3 large 与v2相比,在COCO上达到相同的精度,速度快了25%,同时在分割算法上也有一定的提高。 并且它在bottlenet结构中加入了SE结构,并且放在了depthwise filter之后。而且使用h-swish替换swish,进行非线性改变以及修改头部卷积核channel数量和尾部结构。
## 安装
!pip install paddlex==2.1.0

# 解压数据集到data目录下
!unzip -oq /home/aistudio/data/data53125/test.zip

!!tree test -L 1

import matplotlib
matplotlib.use('Agg') 
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
import paddlex as pdx
from paddlex import transforms as T
train_transforms = T.Compose([
    T.Resize(224, interp='LINEAR'), # 裁剪大小
    T.RandomHorizontalFlip(),  # 随机水平翻转 
    T.RandomVerticalFlip(), # 随机垂直翻转 
    T.Normalize() # 图像归一化
    ])

eval_transforms = T.Compose([
    T.Resize(224, interp='LINEAR'), # 裁剪大小
    T.Normalize() # 图像归一化
])

图像增强

train_dataset = pdx.datasets.ImageNet(
    data_dir='test',
    file_list='test/train_list.txt',
    label_list='test/labels.txt',
    transforms=train_transforms,
    shuffle=True)
    
eval_dataset = pdx.datasets.ImageNet(
    data_dir='test',
    file_list='test/val_list.txt',
    label_list='test/labels.txt',
    transforms=eval_transforms)
num_classes = len(train_dataset.labels)
model = pdx.cls.MobileNetV3(num_classes=num_classes)

配置参数

from paddle.optimizer import Momentum
from paddle.optimizer.lr import CosineAnnealingDecay
from paddle.regularizer import L2Decay
# 总训练轮数
Epochs = 30
# 数据集读取的批次大小
Batch_size = 64
# 每轮的训练步数,需要根据数据集大小自行设定
Step_each_epoch = 394
# 配置学习率
Lr=CosineAnnealingDecay(learning_rate=0.06, T_max=Step_each_epoch * Epochs)
# 配置优化器
Optimizer = Momentum(learning_rate=Lr,
                     momentum=0.9,
                     weight_decay=L2Decay(1e-4),
                     parameters=model.net.parameters())

开始训练

model.train(
    num_epochs=Epochs,
    train_dataset=train_dataset,
    train_batch_size=Batch_size,
    eval_dataset=eval_dataset,
    optimizer=Optimizer,
    pretrain_weights=None,
    save_dir='output/resnet50')

验证

# 载入模型
model = pdx.load_model("output/resnet50/best_model")
# 进行验证
model.evaluate(eval_dataset, batch_size=Batch_size)

预测

# 将test_list.txt加载成列表
import os
test_files = []
with open('NWPU-RESISC45/test_list.txt', 'r') as file_to_read:
    for line in file_to_read:
        test_files.append(os.path.join('NWPU-RESISC45/',line.strip().split(' ')[0]))
        lien = line.strip().split(' ')

# 批量预测
import paddlex as pdx
# 模型载入(记得根据模型修改路径)
model = pdx.load_model('output/resnet50/best_model')
# 进行预测
result_list = model.predict(test_files)
2022-03-10 09:54:12 [INFO]	Model[ResNet50] loaded.

批量预测效果展示

%matplotlib inline
import numpy as np
import cv2
import matplotlib.pyplot as plt
# 搭建label.txt的映射列表
test_list = []
with open('test/labels.txt', 'r') as labels:
    for line in labels:
        test_list.append(line.strip())
# 画图
fig, axs = plt.subplots(nrows=5, ncols=1,figsize=(20,20))
for i in range(5):
    img = cv2.imread(test_files[i+10],1)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    ax = axs[i]
    ax.get_yaxis().set_visible(False)
    ax.get_xaxis().set_visible(False)
    ax.imshow(img)
    ax.set_title('Real: %s \n Predict: %s'%(test_list[i+10],result_list[i+10][0].get('category')))

安卓部署(重点)


#安装下载
!pip install paddlelite -i https://pypi.tuna.tsinghua.edu.cn/simple
!pip install matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple
!pip install chardet -i https://pypi.tuna.tsinghua.edu.cn/simple
!pip install mpimg -i https://pypi.tuna.tsinghua.edu.cn/simple
!pip install imgaug
!paddlex --export_inference --model_dir=output/mobilenetv3/best_model --save_dir=./inference_model --fixed_input_shape=[224,224]

!paddle_lite_opt \
    --model_file=inference_model/__model__ \
    --param_file=inference_model/__params__ \
    --optimize_out_type=naive_buffer \
    --optimize_out=cpu_model \
    --valid_targets=arm

打开Android Stuido 查看文件结构

修改本地配置

PaddleRS套件(Web部署)

以目标检测为例
# 解压数据集
!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

# 安装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
import sys
sys.path.append('/home/aistudio/PaddleRS')
import random
import os.path as osp
from os import listdir
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

RNG_SEED = 114514
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):
    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')


SEED = 114514
DATA_DIR = '/home/aistudio/data/data52980/rsod/'
TRAIN_FILE_LIST_PATH = '/home/aistudio/data/data52980/rsod/train.txt'
VAL_FILE_LIST_PATH = '/home/aistudio/data/data52980/rsod/val.txt'
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


random.seed(SEED)
np.random.seed(SEED)
paddle.seed(SEED)
train_transforms = T.Compose([
    T.RandomDistort(),
    T.RandomExpand(),
    T.RandomCrop(),
    T.RandomHorizontalFlip(),
    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
)
# PP-YOLO2模型
model = pdrs.tasks.PPYOLO2(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
)
model.train(
    num_epochs=100,
    train_dataset=train_dataset,
    train_batch_size=8,
    eval_dataset=val_dataset,
    save_interval_epochs=10,
    log_interval_steps=10,
    save_dir=EXP_DIR,
    pretrain_weights='COCO',
    learning_rate=0.0001,
    warmup_steps=0,
    warmup_start_lr=0.0,
    use_vdl=True
)
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'], 
    )
)
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)

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 = []
    model.labels = test_dataset.labels
    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()
= 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())

Web部署

比较简单哈,首先做一个flask后端(这个比较快比较简单,性价比比较高,当然实际落地是不能用这个的),然后将每一个算法剥离出来,坐uwsig、nginx部署,建议多用几个服务器,分散开,或者在一个服务器中搭建一个集群,这样是分布式的,一个掉了不会影响到其他的算法,但是服务器价格没有低的
如果不考虑价格,有学校自己的服务器,可以用内网
遵循restful返回数据就可以了,注意跨域问题,其他都好说,注意多线程问题以免发生错乱

请点击此处

Please click here for more detailed instructions.

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值