【变化检测】基于STANet建筑物(LEVIR-CD)变化检测实战及ONNX推理

news2024/9/16 15:41:00

主要内容如下:

1、LEVIR-CD数据集介绍及下载
2、运行环境安装
3、STANet模型训练与预测
4、Onnx运行及可视化

运行环境:Python=3.8,torch1.12.0+cu113
likyoo变化检测源码:https://github.com/likyoo/open-cd
使用情况:代码风格属于openmmlab那套,通过修改配置文件config进行模型选择和训练、环境配置简单、训练速度一般。
训练资源消耗:默认参数配置,且batch_size为8时,显存占用4G左右,RTX4080迭代40000大概1.5小时。
ONNX推理:显存占用2G左右,RTX4080推理耗时约65ms

1 LEVIR-CD数据集介绍

1.1 简介

LEVIR-CD 由 637 个超高分辨率(VHR,0.5m/像素)谷歌地球(GE)图像块对组成,大小为 1024 × 1024 像素。这些时间跨度为 5 到 14 年的双时态图像具有显着的土地利用变化,尤其是建筑增长。LEVIR-CD涵盖别墅住宅、高层公寓、小型车库、大型仓库等各类建筑。在这里,我们关注与建筑相关的变化,包括建筑增长(从土壤/草地/硬化地面或在建建筑到新的建筑区域的变化)和建筑衰退。这些双时态图像由遥感图像解释专家使用二进制标签(1 表示变化,0 表示不变)进行注释。我们数据集中的每个样本都由一个注释者进行注释,然后由另一个进行双重检查以生成高质量的注释。
数据来源:https://justchenhao.github.io/LEVIR/
论文地址:https://www.mdpi.com/2072-4292/12/10/1662
快速下载链接:https://aistudio.baidu.com/datasetdetail/104390/1

1.2 示例

在这里插入图片描述

2 运行环境安装

2.1 基础环境安装

【超详细】跑通YOLOv8之深度学习环境配置1-Anaconda安装
【超详细】跑通YOLOv8之深度学习环境配置2-CUDA安装

创建Python环境及换源可借鉴如下:
【超详细】跑通YOLOv8之深度学习环境配置3-YOLOv8安装

2.2 likyoo变化检测代码环境安装

2.2.1 代码下载

Git:git clone https://github.com/likyoo/open-cd.git
在这里插入图片描述

2.2.2 环境安装
# 1 创建环境
conda create -n likyoo python=3.8
conda activate likyoo

# 2 安装torch
# 方式1:
conda install pytorch==1.12.0 torchvision==0.13.0 torchaudio==0.12.0 cudatoolkit=11.3 -c pytorch
# 方式2:
pip install torch==1.12.0+cu113 torchvision==0.13.0+cu113 torchaudio==0.12.0 --extra-index-url https://download.pytorch.org/whl/cu113

# 3 验证torch安装是否为gpu版
import torch
print(torch.__version__)  # 打印torch版本
print(torch.cuda.is_available())  # True即为成功
print(torch.version.cuda)
print(torch.backends.cudnn.version())

# 4 安装其他依赖库
cd ./open-cd-main

# 4.1 安装 OpenMMLab 相关工具
pip install -U openmim
mim install mmengine
mim install "mmcv==2.0.0"
mim install "mmpretrain>=1.0.0rc7"  # (本地安装版本为1.2.0)
pip install "mmsegmentation==1.2.2"
pip install "mmdet==3.0.0"

# 4.2 编译安装open-cd
pip install -v -e .

# 5 可能缺少的库
pip install ftfy

3 STANet模型训练与预测

3.1 STANet模型介绍

3.1.1 简介

在这里插入图片描述

摘要:进行遥感图像变化检测(CD)以识别所需的显著变化在双时态图像之间。给定在不同时间拍摄的两个共同配准的图像变化和配准误差压倒了真实物体的变化。探索关系在不同的时空像素之间可以提高CD方法的性能。在我们的工作中,我们提出了一种新的基于孪生的时空注意力神经网络与之前的方法不同,这些方法在不参考任何数据的情况下对双时图像进行单独编码有用的时空依赖性,我们设计了一个CD自我注意机制来模拟时空关系。我们在程序中集成了一个新的CD自我关注模块特征提取。我们的自我注意力模块计算任意两个像素之间的注意力权重在不同的时间和位置,并使用它们来生成更具辨别力的特征。考虑到考虑到对象可能具有不同的尺度,我们将图像划分为多尺度子区域在每个子区域引入自我关注。通过这种方式,我们可以捕捉空间-时间在不同尺度上的依赖关系,从而生成更好的表示来适应对象各种尺寸。我们还介绍了一个CD数据集LEVIR-CD,它大了两个数量级与该领域的其他公共数据集相比。LEVIR-CD由一组大型双时态谷歌地球组成图像,637个图像对(1024×1024)和超过31k个独立标记的变化实例。我们提出的注意力模块将我们的基线模型的F1得分从83.9提高到87.3可接受的计算开销。公共遥感图像光盘的实验结果数据集显示,我们的方法优于其他几种最先进的方法。
论文地址:https://www.mdpi.com/2072-4292/12/10/1662
主要贡献包括

  1. 提出时空注意力网络(STANet):论文提出了一种基于孪生网络的时空注意力方法,用于遥感图像变化检测。这种方法通过设计一种变化检测自注意力机制,模拟不同时间点和位置的像素之间的时空关系,以生成更具辨识性的特征。
  2. 引入新的注意力模块:论文设计了两种注意力模块,基础时空注意力模块(BAM)和金字塔时空注意力模块(PAM)。BAM通过计算不同时间点和位置的像素之间的注意力权重来更新特征图,而PAM则在多个尺度上集成这些注意力表示,以捕捉不同尺度的时空依赖性。
  3. 发布新的遥感图像变化检测数据集LEVIR-CD:为了促进遥感图像变化检测技术的发展,论文提供了一个新的大规模数据集LEVIR-CD,包含大量的高分辨率遥感图像对和超过31k个独立标注的变化实例。
  4. 实验验证:通过在LEVIR-CD和SZTAKI数据集上的实验,论文证明了所提出的方法在变化检测任务上优于现有的最先进技术,特别是在处理由成像角度变化、季节变化和照明条件变化引起的误检时。
3.1.2 模型结构

在这里插入图片描述

3.1.3 实验结果

在这里插入图片描述
在这里插入图片描述

3.2 模型训练与预测

3.2.1 修改训练配置文件

(1)选择训练配置
在这里插入图片描述

(2)修改configs\common\standard_256x256_40k_levircd.py
关键修改:输入数据集路径data_root一定要对!!!
batch_size和迭代数量等按自己需要调整,预测为1024大小。
在这里插入图片描述

3.2.2 模型训练与测试
# 训练,--config配置文件+保存文件夹名
python tools/train.py configs/stanet/stanet_base_256x256_40k_levircd.py --work-dir ./work_dirs/stanet

# 测试==》得到评价指标
python tools/test.py configs/stanet/stanet_base_256x256_40k_levircd.py  ./work_dirs/stanet/iter_40000.pth
3.2.3 结果显示
# 测试==》得到结果图
python tools/test.py configs/stanet/stanet_base_256x256_40k_levircd.py  ./work_dirs/stanet/iter_40000.pth --show-dir tmp_infer

在这里插入图片描述

4 Onnx运行及可视化

4.1 Onnx导出静态和动态文件

(1)修改tools/export.py脚本,导出onnx,复制如下内容【修改了输入尺寸和增加一个动态onnx导出】:

# Copyright (c) Open-CD. All rights reserved.
import argparse
import logging

import torch
from mmengine import Config
from mmengine.registry import MODELS, init_default_scope

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger('opencd')


def main(args):
    # must be called before using opencd
    init_default_scope('opencd')

    config_path = args.config
    checkpoint_path = args.checkpoint
    inputs = args.inputs
    model_name = args.model_name
    model_name_dy = args.model_name_dy

    config = Config.fromfile(config_path, import_custom_modules=True)
    model = MODELS.build(config.model)

    ckpt = torch.load(checkpoint_path)
    state_dict = ckpt['state_dict']
    model.load_state_dict(state_dict, strict=True)
    model.eval()

    input_shape0 = tuple(map(int, inputs[0].split(',')))
    input_shape1 = tuple(map(int, inputs[1].split(',')))
    input0 = torch.rand(input_shape0)
    input1 = torch.rand(input_shape1)
    images = torch.concat((input0, input1), dim=1).to(args.device)
    model.to(args.device)

    # 导出静态onnx
    torch.onnx.export(
        model,
        (images),
        model_name,
        input_names=['images'],
        output_names=['output'],
        verbose=False,
        opset_version=11,
    )

    # 导出动态onnx
    torch.onnx.export(
        model,
        (images),
        model_name_dy,
        input_names=['images'],
        output_names=['output'],
        verbose=False,
        opset_version=11,
        dynamic_axes={
                       "images": {0 :"batch_size", 2: "input_height", 3: "input_width"},
                       "output": {0 :"batch_size", 2: "output_height", 3: "output_width"}
                   }
    )


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--config', '-c', type=str, default='')
    parser.add_argument('--checkpoint', '-m', type=str, default='')
    parser.add_argument('--device', type=str, default='cpu')
    parser.add_argument(
        '--inputs',
        '-i',
        type=str,
        nargs='+',
        default=['1,3,1024,1024', '1,3,1024,1024'])
    parser.add_argument('--model-name', '-mn', type=str, default='model.onnx')
    parser.add_argument('--model-name_dy', '-mndy', type=str, default='model_dy.onnx')
    args = parser.parse_args()
    logger.info(args)
    main(args)

运行命令如下【结果生成两个onnx文件】:

# 注意报错ValueError: SyncBatchNorm expected input tensor to be on GPU
# 解决措施:device选择GPU进行导出
python tools/export.py --config configs/stanet/stanet_base_256x256_40k_levircd.py --checkpoint ./work_dirs/stanet/iter_40000.pth  --device cuda:0

(2)查看模型结构
https://netron.app/
静态onnx
在这里插入图片描述
动态onnx
在这里插入图片描述

注意:其中的[1,6,1024,1024]表示两个[1,3,1024,1024]堆叠一起输入。

4.2 Onnx运行及可视化

4.2.1 Onnx推理运行

注意:由于LEVIR-CD图像为1024*1024大小,输入改成1024输入。

import os
import cv2
import time
import argparse
import numpy as np
import onnxruntime as ort  # 使用onnxruntime推理用上,pip install onnxruntime-gpu -i https://pypi.tuna.tsinghua.edu.cn/simple
 

class CD(object):
    def __init__(self, onnx_model, in_shape=1024):
        self.in_shape = in_shape  # 图像输入尺度
        self.mean = [0.485, 0.456, 0.406]  # 定义均值和标准差(确保它们与图像数据的范围相匹配)  
        self.std = [0.229, 0.224, 0.225]  # 基于0-1范围的

        # 构建onnxruntime推理引擎
        self.ort_session = ort.InferenceSession(onnx_model,
                                providers=['CUDAExecutionProvider', 'CPUExecutionProvider']
                                if ort.get_device() == 'GPU' else ['CPUExecutionProvider'])

    # 归一化 
    def normalize(self, image, mean, std):  
        # 如果均值和标准差是基于0-255范围的图像计算的,那么需要先将图像转换为0-1范围  
        image = image / 255.0  
        image = image.astype(np.float32)  
        image_normalized = np.zeros_like(image)  

        for i in range(3):  # 对于 RGB 的每个通道  
            image_normalized[:, :, i] = (image[:, :, i] - mean[i]) / std[i]  
        return image_normalized
    

    def preprocess(self, img_a, img_b):
        # resize为1024大小
        
        if img_a.shape[0] != self.in_shape and img_a.shape[1] != self.in_shape:
            img_a = cv2.resize(img_a, (self.in_shape, self.in_shape), interpolation=cv2.INTER_LINEAR)
        if img_b.shape[0] != self.in_shape and img_b.shape[1] != self.in_shape:
            img_b = cv2.resize(img_b, (self.in_shape, self.in_shape), interpolation=cv2.INTER_LINEAR)

        # 应用归一化  
        img_a = self.normalize(img_a, self.mean, self.std)
        img_b = self.normalize(img_b, self.mean, self.std)

        img_a = np.ascontiguousarray(np.einsum('HWC->CHW', img_a)[::-1], dtype=np.single)  # (1024, 1024, 3)-->(3, 1024, 1024), BGR-->RGB
        img_b = np.ascontiguousarray(np.einsum('HWC->CHW', img_b)[::-1], dtype=np.single)  # np.single 和 np.float32 是等价的
        img_a = img_a[None] if len(img_a.shape) == 3 else img_a  # (1, 3, 1024, 1024)
        img_b = img_b[None] if len(img_b.shape) == 3 else img_b
      
        concat_img = np.concatenate((img_a, img_b), axis=1) 
        return  concat_img
    
    # 推理
    def infer(self, img_a, img_b):
        concat_img = self.preprocess(img_a, img_b)  # (1024, 1024, 3)+(1024, 1024, 3) --> (1, 6, 1024, 1024)
        preds = self.ort_session.run(None, {self.ort_session.get_inputs()[0].name: concat_img})[0]  # (1, n, 1024, 1024) 
        if preds.shape[1] == 1:
            out_img = (np.clip(preds[0][0], 0, 1) * 255).astype("uint8")
            out_img = cv2.resize(out_img, (img_a.shape[1], img_a.shape[0])) 
        else:
            out_img = (np.argmax(preds, axis=1)[0] * 255).astype("uint8")
        return out_img
    

if __name__ == '__main__':
    # Create an argument parser to handle command-line arguments
    parser = argparse.ArgumentParser()
    parser.add_argument('--model', type=str, default='weights/model_dy.onnx', help='Path to ONNX model')
    parser.add_argument('--source_A', type=str, default=str('E:/datasets/LEVIR-CD/test/A/test_7.png'), help='A期图像')
    parser.add_argument('--source_B', type=str, default=str('E:/datasets/LEVIR-CD/test/B/test_7.png'), help='B期图像')
    parser.add_argument('--in_shape', type=int, default=1024, help='输入模型图像尺度')
    args = parser.parse_args()

    # 实例化变化检测模型
    cd= CD(args.model, args.in_shape)
    
    # Read image by OpenCV
    img_a = cv2.imread(args.source_A)
    img_b = cv2.imread(args.source_B)

    # 推理+输出
    t1 = time.time()
    out = cd.infer(img_a, img_b)
    print('推理耗时:{}'.format(time.time() - t1))
    
    # 保存结果
    cv2.imwrite('./result/test_7_res.png', out)
    
4.2.2 结果可视化

在这里插入图片描述

4.2.3 进一步处理:

(1)可加入腐蚀膨胀处理,消除一些小白点等区域;
(2)将变化区域绘制在第二期图上,便于观察;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2117086.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

力扣周赛:第414场周赛

👨‍🎓作者简介:爱好技术和算法的研究生 🌌上期文章:[首期文章] 📚订阅专栏:力扣周赛 希望文章对你们有所帮助 本科打ACM所以用的都是C,未来走的是Java,所以现在敲算法还…

探索未来住宿新体验:酒店智能开关引领的智慧生活

酒店智能开关作为智慧酒店的重要组成部分,正悄然改变着我们的旅行住宿方式,让每一次入住都成为一场科技与舒适的完美邂逅。 智能开关:重新定义酒店房间的每一个角落 传统酒店中,房间的灯光、空调、窗帘等设备的控制往往依赖于手动…

LCD字符图片显示——FPGA学习笔记11

一、字模显示原理 字模数据:将这个0/1矩阵按照屏幕扫描的顺序以字节的形式体现。 取模软件设计: 点阵数要按照实际情况填写 二、实验任务 本节的实验任务是通过开发板上的RGB TFT-LCD接口,在RGB LCD液晶屏的左上角位置从上到下依次显示图片以…

【数据结构】希尔排序(缩小增量排序)

目录 一、基本思想 1.1 引入希尔排序的原因 1.2 基本思想 二、思路分析 三、gap分组问题 四、代码实现 4.1 代码一(升序) 4.2 代码二(升序) 五、易错提醒 六、时间复杂度分析 七、排序小tips 一、基本思想 1.1 引入希尔…

Vue3:<Teleport>传送门组件的使用和注意事项

你好&#xff0c;我是沐爸&#xff0c;欢迎点赞、收藏、评论和关注。 Vue3 引入了一个新的内置组件 <Teleport>&#xff0c;它允许你将子组件树渲染到 DOM 中的另一个位置&#xff0c;而不是在父组件的模板中直接渲染。这对于需要跳出当前组件的 DOM 层级结构进行渲染的…

15.1 JDBC数据库编程1

目录 15 引言 15.1.1 数据库语言SQL 15.2 JDBC体系结构 15.2.1 JDBC访问数据库 15.2.2 JDBC API介绍 15 引言 数据库系统&#xff08;database system,DBS&#xff09;由一个互相关联的数据集合和一组用以访问这些数据的程序组成。这个数据集合通常称为数据库。 …

音频-语言大模型原理

重磅推荐专栏: 《大模型AIGC》 《课程大纲》 《知识星球》 本专栏致力于探索和讨论当今最前沿的技术趋势和应用领域,包括但不限于ChatGPT和Stable Diffusion等。我们将深入研究大型模型的开发和应用,以及与之相关的人工智能生成内容(AIGC)技术。通过深入的技术解析和实践经…

YOLOv8改进实战 | 注意力篇 | CloFormer: 注意力机制与卷积的完美融合CloAtention,即插即用

YOLOv8专栏导航:点击此处跳转 前言 YOLOv8 是由 YOLOv5 的发布者 Ultralytics 发布的最新版本的 YOLO。它可用于对象检测、分割、分类任务以及大型数据集的学习,并且可以在包括 CPU 和 GPU 在内的各种硬件上执行。 YOLOv8 是一种尖端的、最先进的 (SOTA) 模型,它建立在以前…

(C++) 6大作用域

文章目录 &#x1f365;前言&#x1f365;C 6大作用域&#x1f41f;块&#x1f41f;名字空间&#x1f41f;类&#x1f41f;函数参数&#x1f41f;枚举&#x1f41f;模板参数 ⭐END&#x1f31f;交流方式 &#x1f365;前言 在 C core guidelines 中有一个准则&#xff1a; ES.…

深入探索Unity协程:揭开CSharp迭代器背后的神秘面纱

协程是一种特殊类型的迭代器方法&#xff0c;允许你在多个帧之间分段执行代码。可以用来处理时间延迟、异步操作和顺序执行的任务&#xff0c;而不阻塞主线程。Unity协程的实现依赖于C#语言提供的迭代器相关的语言特性&#xff0c;所以想要弄清楚Unity协程的底层原理&#xff0…

web群集--nginx配置文件location匹配符的优先级顺序详解及验证

文章目录 前言优先级顺序优先级顺序(详解)1. 精确匹配&#xff08;Exact Match&#xff09;2. 正则表达式匹配&#xff08;Regex Match&#xff09;3. 前缀匹配&#xff08;Prefix Match&#xff09; 匹配规则的综合应用验证优先级 前言 location的作用 在 NGINX 中&#xff0…

Idea Mac代码调试常用快捷键~

Mac截图 commandShift4 idea英文大写转小写 commandShiftU 功能&#xff1a;查看类的实现和继承父类的方法 快捷键 fncommandF12 鼠标点击打开 功能&#xff1a;查看当前方法的上游方法 选中方法&#xff0c;controloptionH 功能&#xff1a;CommandB是查看本类的方法 功能&…

Matlab simulink建模与仿真 第十一章(端口及子系统库)【下】

参考视频&#xff1a;simulink1.1simulink简介_哔哩哔哩_bilibili 八、触发使能子系统 1、Enabled and Triggered Subsystem触发使能子系统概述 触发使能子系统其实是触发子系统和使能子系统二者的结合&#xff0c;当触发端口传来触发信号时&#xff0c;使能端口的输入需要大…

TitleBar:打造高效Android标题栏的新选择

在Android应用开发中&#xff0c;标题栏是用户界面的重要组成部分。一个好的标题栏不仅能够提升应用的专业感&#xff0c;还能增强用户体验。然而&#xff0c;传统的标题栏实现方式往往存在代码冗余、样式不统一、性能开销大等问题。今天&#xff0c;我们将介绍一个名为TitleBa…

自定义类型:结构体(续)

目录 一. 结构体的内存对齐 1.1 为什么存在内存对齐&#xff1f; 1.2 修改默认对齐数 二. 结构体传参 三. 结构体实现位段 一. 结构体的内存对齐 在前面的文章里我们已经讲过一部分的内存对齐的知识&#xff0c;并举出了两个例子&#xff0c;我们再举出两个例子继续说明&…

python进阶篇-day08-数据结构与算法(线性结构介绍与链表实现)

数据的存储和组织形式 程序 数据结构 算法 一. 算法介绍 概述目的 都是可以提高程序的效率(性能), 面试高频考点 数据结构介绍 数据的存储和组织形式, 同样的空间, 不同的结构, 存储的数据不同, 操作方式也不同 算法介绍 为了解决实际的业务问题, 而考虑出来的方法和思路 …

龙芯+FreeRTOS+LVGL实战笔记(新)——06添加二级按钮

本专栏是笔者另一个专栏《龙芯+RT-Thread+LVGL实战笔记》的姊妹篇,主要的区别在于实时操作系统的不同,章节的安排和任务的推进保持一致,并对源码做了完善与优化,各位可以先到本人主页下去浏览另一专栏的博客列表(目前已撰写36篇,图1所示),再决定是否订阅。此外,也可以…

超强的截图工具:PixPin

你是否还在为寻找一款功能强大、操作简便的截图工具而烦恼&#xff1f;市面上那么多工具&#xff0c;常常让人无从选择。今天&#xff0c;想给大家安利一款神器——PixPin&#xff0c;一款真正解放双手的截图工具。 想象一下&#xff0c;你只需要按下快捷键就能轻松完成多种截…

雷电9模拟器安装magisk和lsposed

模拟器环境配置 1、开启root 2、开启System.vmdk可写入 安装magisk 1、新建模拟器、开启root权限、并安装debug版magisk 下载地址去上面吾爱论坛作者文章下载吧&#xff01;支持他一下&#xff01; 2、打开magisk的app&#xff0c;点击安装 如果弹出获取权限&#xff0c;直接…

【Socket网络编程原理实践】

socket 基于 TCP/IP协议实现&#xff0c;在网络模型中属于传输层 Java 网络编程中的核心概念 IP 地址&#xff1a;用于标识网络中的计算机端口号&#xff1a;用于标识计算机上的应用程序或进程Socket&#xff08;套接字&#xff09;&#xff1a;网络通信的基本单位&#xff0…