昇騰AI行業(yè)案例(五):基于 DANet 和 Deeplabv3 模型的遙感圖像分割

file

00 - 前言

歡迎學(xué)習(xí)《基于 DANet 和 Deeplabv3 模型的遙感圖像分割》實(shí)驗(yàn)。在本實(shí)驗(yàn)中,你將深入了解如何運(yùn)用計(jì)算機(jī)視覺(CV)領(lǐng)域的 AI 模型,搭建一個(gè)高效精準(zhǔn)的遙感地圖區(qū)域分割系統(tǒng),并利用開源數(shù)據(jù)集和昇騰 AI 芯片對(duì)模型效果加以驗(yàn)證。

學(xué)習(xí)目標(biāo)


在本課程中,您將學(xué)習(xí)一些與使用 AI 圖像處理技術(shù)實(shí)現(xiàn)地圖分割檢測(cè)有關(guān)的重要概念,包括:

  • 遙感圖像數(shù)據(jù)的預(yù)處理方法
  • 采用圖像分割模型對(duì)圖像的不同區(qū)域進(jìn)行標(biāo)識(shí)的方法
  • 圖像分割的后處理方法
  • 端到端深度學(xué)習(xí)工作流

目錄


本實(shí)驗(yàn)分為四個(gè)核心部分。第一部分主要介紹案例的應(yīng)用場(chǎng)景,闡述遙感影像地塊分割的重要性及意義;第二部分會(huì)詳細(xì)闡述端到端的解決方案,搭建起技術(shù)實(shí)現(xiàn)的整體框架;第三部分會(huì)手把手指導(dǎo)您完成代碼編寫與實(shí)現(xiàn);最后一部分給出測(cè)試題,幫助您鞏固學(xué)習(xí)內(nèi)容。

  1. 場(chǎng)景介紹
  2. 解決方案
  3. 代碼實(shí)戰(zhàn)
  4. 課后測(cè)試

JupyterLab

在本實(shí)操實(shí)驗(yàn)中,我們將使用 JupyterLab 管理我們的環(huán)境。JupyterLab 界面是一個(gè)控制面板,可供您訪問交互式 iPython Notebook、所用環(huán)境的文件夾結(jié)構(gòu),以及用于進(jìn)入 Ubuntu 操作系統(tǒng)的終端窗口,只需要點(diǎn)擊菜單欄的小三角就可以運(yùn)行代碼。

嘗試執(zhí)行下方單元中的一條簡(jiǎn)單的 print(打印)語句。

# DO NOT CHANGE THIS CELL
# activate this cell by selecting it with the mouse or arrow keys then use the keyboard shortcut [Shift+Enter] to execute
print('This is just a simple print statement')
This is just a simple print statement

01 場(chǎng)景介紹

遙感圖像在眾多領(lǐng)域有著廣泛的應(yīng)用,如土地利用分類、城市規(guī)劃、農(nóng)業(yè)監(jiān)測(cè)、環(huán)境評(píng)估、災(zāi)害監(jiān)測(cè)等。然而,原始的遙感圖像數(shù)據(jù)量大且復(fù)雜,其中包含了各種地物信息,如建筑物、道路、水體、植被、農(nóng)田等,這些地物在圖像上呈現(xiàn)出不同的特征和模式。準(zhǔn)確地將這些地物從遙感圖像中分割出來,對(duì)于后續(xù)的分析和決策具有至關(guān)重要的意義。

例如,在城市規(guī)劃中,通過對(duì)遙感圖像的分割,可以清晰地了解城市的建筑分布、道路網(wǎng)絡(luò)以及綠地面積等信息,從而為合理規(guī)劃城市布局、優(yōu)化基礎(chǔ)設(shè)施建設(shè)提供有力依據(jù)。在農(nóng)業(yè)監(jiān)測(cè)方面,能夠精確地識(shí)別出農(nóng)田的范圍和農(nóng)作物的類型,有助于評(píng)估農(nóng)作物的生長(zhǎng)狀況、預(yù)測(cè)產(chǎn)量以及實(shí)施精準(zhǔn)農(nóng)業(yè)管理。在災(zāi)害監(jiān)測(cè)領(lǐng)域,如洪水、火災(zāi)等災(zāi)害發(fā)生時(shí),及時(shí)對(duì)遙感圖像進(jìn)行分割,可以快速確定受災(zāi)區(qū)域的范圍和程度,為救援工作提供關(guān)鍵的信息支持。

傳統(tǒng)的遙感圖像分割方法主要包括基于閾值的方法、基于邊緣檢測(cè)的方法和基于區(qū)域生長(zhǎng)的方法等。這些方法在一定程度上能夠?qū)崿F(xiàn)地物的分割,但存在著分割精度不高、對(duì)復(fù)雜場(chǎng)景適應(yīng)性差、需要人工干預(yù)等局限性。隨著人工智能技術(shù),尤其是深度學(xué)習(xí)的快速發(fā)展,基于 AI 的遙感圖像分割方法展現(xiàn)出了強(qiáng)大的優(yōu)勢(shì),AI 模型能夠自動(dòng)學(xué)習(xí)圖像中的特征模式,實(shí)現(xiàn)更加準(zhǔn)確和高效的地物分割,為各領(lǐng)域的應(yīng)用提供了更可靠的技術(shù)手段。

02 解決方案

本實(shí)驗(yàn)教程使用的解決方案如下,首先對(duì)原始圖像進(jìn)行預(yù)處理,使得圖像數(shù)據(jù)符合 AI 模型的輸入要求,然后使用圖像分割模型進(jìn)行推理,預(yù)測(cè)每個(gè)像素點(diǎn)的地形分類標(biāo)簽,最后進(jìn)行后處理,把分割的區(qū)域可視化標(biāo)記出來。

file

以下分別介紹這幾個(gè)核心模塊。

2.1 圖像預(yù)處理模塊

原始的遙感影像往往包含著各種復(fù)雜的信息,直接用于地塊分割可能會(huì)導(dǎo)致較低的準(zhǔn)確性和效率。因此,我們首先對(duì)其進(jìn)行預(yù)處理操作,其中一項(xiàng)重要的處理是將圖像轉(zhuǎn)換為 YUV 格式。

file

YUV 格式相較于常見的 RGB 格式,在顏色信息的表示上具有獨(dú)特的優(yōu)勢(shì)。通過將亮度(Y)與色度(U 和 V)信息分離,能夠更好地突出圖像中的物體輪廓和細(xì)節(jié)特征,為后續(xù)的模型處理提供更加清晰和易于分析的數(shù)據(jù)基礎(chǔ)。在轉(zhuǎn)換過程中,我們運(yùn)用專業(yè)的圖像轉(zhuǎn)換算法,確保圖像的色彩信息得以準(zhǔn)確保留,同時(shí)優(yōu)化了亮度和色度的分布,使得地塊與周圍環(huán)境的對(duì)比度增強(qiáng),從而有效提升了后續(xù)模型對(duì)地塊特征的提取能力,為精準(zhǔn)分割奠定了堅(jiān)實(shí)的基礎(chǔ)。

2.2 圖像分割模型

在數(shù)據(jù)預(yù)處理之后,我們借助先進(jìn)的圖像分割模型 DANet 和 DeepLabV3 進(jìn)行推理操作,以實(shí)現(xiàn)對(duì)每個(gè)像素點(diǎn)的準(zhǔn)確分類,這是整個(gè)解決方案的核心環(huán)節(jié)。

DANet 模型通過其創(chuàng)新性的雙注意力機(jī)制,包括位置注意力模塊和通道注意力模塊,能夠在復(fù)雜的遙感影像中精準(zhǔn)地聚焦于地塊的關(guān)鍵特征區(qū)域。位置注意力模塊能夠捕捉圖像中不同位置像素之間的長(zhǎng)距離依賴關(guān)系,使得模型在處理大面積地塊以及具有復(fù)雜邊界的地塊時(shí),能夠更好地理解地塊的整體結(jié)構(gòu)和連續(xù)性。而通道注意力模塊則對(duì)不同特征通道進(jìn)行權(quán)重分配,突出對(duì)地塊分割有重要貢獻(xiàn)的特征通道,從而提高模型對(duì)地塊類別特征的識(shí)別能力,尤其是對(duì)于不同類型的地塊(如農(nóng)田、建筑用地、水域等),能夠更加準(zhǔn)確地區(qū)分其細(xì)微差異,實(shí)現(xiàn)精準(zhǔn)分類。

file

DeepLabV3 模型則采用了空洞卷積(atrous convolution)和空間金字塔池化技術(shù),進(jìn)一步提升了對(duì)遙感影像多尺度特征的提取能力??斩淳矸e能夠在不增加計(jì)算量和參數(shù)量的前提下,擴(kuò)大卷積核的感受野,使模型能夠同時(shí)獲取到地塊的局部細(xì)節(jié)信息和全局語義信息。空間金字塔池化則通過對(duì)不同尺度的特征圖進(jìn)行池化操作,將多尺度信息進(jìn)行融合,從而增強(qiáng)了模型對(duì)不同大小地塊的適應(yīng)性。在推理過程中,DeepLabV3 模型能夠準(zhǔn)確地識(shí)別出地塊的邊界和內(nèi)部區(qū)域,即使在面對(duì)影像中存在的噪聲、遮擋以及復(fù)雜地形地貌等情況時(shí),依然能夠保持較高的分割準(zhǔn)確性和穩(wěn)定性。

file

我們通過同時(shí)運(yùn)用這兩個(gè)強(qiáng)大的模型進(jìn)行推理,并將它們的輸出結(jié)果進(jìn)行融合,充分發(fā)揮了兩者的優(yōu)勢(shì),實(shí)現(xiàn)了對(duì)遙感影像地塊的全面、準(zhǔn)確且細(xì)致的分割,極大地提高了像素點(diǎn)分類的精度和可靠性。

2.3 后處理模塊

經(jīng)過模型推理得到的初步分割結(jié)果,雖然蘊(yùn)含了地塊的基本分類信息,但還需要經(jīng)過一系列的后處理操作,才能轉(zhuǎn)化為更具實(shí)際應(yīng)用價(jià)值且易于直觀理解的最終地塊分割成果。在此階段,我們主要借助以下幾個(gè)關(guān)鍵函數(shù)來完成相應(yīng)的后處理任務(wù)。

首先是 semantic_to_mask 函數(shù),它在將模型推理輸出的語義圖轉(zhuǎn)換為標(biāo)簽圖。在遙感影像地塊分割的場(chǎng)景中,模型推理得到的結(jié)果往往是以語義圖的形式呈現(xiàn),即每個(gè)像素點(diǎn)對(duì)應(yīng)著各類別的概率分布情況(mask 參數(shù)所代表的語義圖)。而我們?cè)O(shè)定了 8 種語義標(biāo)簽(labels 參數(shù)所代表的 8 種語義標(biāo)簽)來對(duì)地塊及相關(guān)地物進(jìn)行分類標(biāo)識(shí),例如可能包含 water(水域) 、 road(道路) 、buildings(建筑物) 等不同類別。semantic_to_mask 函數(shù)通過 np.argmax 操作,沿著特定的維度(這里是 axis=1)選取每個(gè)像素位置上概率最大的類別索引,然后依據(jù)預(yù)先設(shè)定好的標(biāo)簽編碼(label_codes)進(jìn)行替換,最終返回經(jīng)過標(biāo)簽替換后的最大概率預(yù)測(cè)結(jié)果,也就是將原本基于概率分布的語義表示轉(zhuǎn)化為了明確的類別標(biāo)簽表示,使得每個(gè)像素都有了唯一對(duì)應(yīng)的地塊或地物類別,為后續(xù)進(jìn)一步處理提供了清晰的類別依據(jù)。

file

接著,decode_seg_map 函數(shù)負(fù)責(zé)把經(jīng)過 semantic_to_mask 函數(shù)處理后的標(biāo)簽圖映射為可視化的彩色圖像。它可以將標(biāo)簽圖中對(duì)應(yīng)類別的像素位置賦予相應(yīng)的顏色值,分別對(duì)紅(r)、綠(g)、藍(lán)(b)三個(gè)顏色通道進(jìn)行賦值操作。隨后,將處理后的顏色通道數(shù)據(jù)組合起來,構(gòu)建出符合 RGB 圖像格式要求的三維數(shù)組(rgb),從而實(shí)現(xiàn)了將以標(biāo)簽形式表示的分割結(jié)果轉(zhuǎn)化為彩色圖像的過程,使得我們可以直觀地看到不同地塊和地物在遙感影像中的分布情況,便于進(jìn)行可視化的分析和展示。

file

最后,final_result_create 函數(shù)聚焦于對(duì)生成的彩色分割結(jié)果圖像進(jìn)行更全面的后處理及保存工作。通過這一系列函數(shù)實(shí)現(xiàn)的后處理步驟,我們有效地將模型推理得到的原始結(jié)果轉(zhuǎn)化為了高質(zhì)量、可視化且易于解讀的地塊分割成果,這些成果能夠直接應(yīng)用于城市規(guī)劃、土地資源管理等眾多實(shí)際領(lǐng)域,為相關(guān)決策提供準(zhǔn)確、直觀的數(shù)據(jù)支持,充分發(fā)揮遙感影像地塊分割在各行業(yè)中的應(yīng)用價(jià)值。

03 動(dòng)手實(shí)驗(yàn)

3.1 實(shí)驗(yàn)準(zhǔn)備

數(shù)據(jù)集

實(shí)驗(yàn)所用的測(cè)試圖像數(shù)據(jù)源自gitee項(xiàng)目,該數(shù)據(jù)集包含了15張遙感俯拍圖,每張都包含了道路、建筑、植被等區(qū)域。本實(shí)驗(yàn)把數(shù)據(jù)集下載后取樣存放于當(dāng)前目錄下的 ./test_set 目錄中,方便后續(xù)代碼進(jìn)行讀取和處理。

模型權(quán)重

本實(shí)驗(yàn)采用的 DANetDeeplabv3 模型需要從這個(gè)鏈接下載,里面包含了 onnx 格式的模型,我們后續(xù)將會(huì)用到。

3.2 圖像預(yù)處理

參考原項(xiàng)目configure.cfg 文件,可以知道模型的輸入圖像格式為 YUV420SP_U8 ,含義如下:

YUV:顏色編碼方式,YUV 是一種顏色模型,它將顏色信息分為亮度(Y)和色度(U, V)兩部分。這種編碼方式在圖像和視頻壓縮中非常常見,因?yàn)樗梢愿行У乇硎绢伾畔?,并且允許單獨(dú)壓縮亮度和色度分量。

420:色度采樣(Chroma Subsampling),420表示色度信息的采樣率。在YUV420中,U和V分量的采樣率是亮度分量的一半。具體來說,對(duì)于每四個(gè)Y值,只有一個(gè)U和一個(gè)V值。這種采樣方式減少了色度信息的數(shù)量,可以在保持圖像質(zhì)量的同時(shí)減少數(shù)據(jù)量。

SP:Semi-Planar的縮寫,指的是色度分量的存儲(chǔ)方式。在YUV420SP格式中,U和V分量交錯(cuò)存儲(chǔ)(交錯(cuò)是指在行或列上相鄰存儲(chǔ)),而不是分開存儲(chǔ)。這意味著U和V分量不是完全獨(dú)立的兩個(gè)平面,而是交錯(cuò)在一起。

U8:數(shù)據(jù)類型,U8表示每個(gè)顏色分量(Y、U、V)使用8位無符號(hào)整數(shù)(0-255)來存儲(chǔ)。這表示每個(gè)顏色值可以用一個(gè)字節(jié)來表示。

所以我們需要把原始的 jpg 圖像進(jìn)行預(yù)處理,才能傳給 AI 模型。
第一步,導(dǎo)入所需的三方庫:

import numpy as np
import cv2

接著,創(chuàng)建圖像預(yù)處理函數(shù):

def img_process(img_path):
    image = cv2.imread(img_path)
    # 將圖片從BGR顏色空間轉(zhuǎn)換為YUV顏色空間
    yuv_image = cv2.cvtColor(image, cv2.COLOR_BGR2YUV)
    # 獲取圖片的尺寸
    height, width = yuv_image.shape[:2]
    # 分離Y, U, V分量
    y, u, v = cv2.split(yuv_image)
    # 將U和V分量下采樣到4:2:0
    u = cv2.resize(u, (width // 2, height // 2), interpolation=cv2.INTER_LINEAR)
    v = cv2.resize(v, (width // 2, height // 2), interpolation=cv2.INTER_LINEAR)
    # 交錯(cuò)UV分量,形成NV12格式
    uv = np.zeros((height // 2, width), dtype=yuv_image.dtype)
    uv[:, 0::2] = u
    uv[:, 1::2] = v
    # 將Y分量和交錯(cuò)的UV分量合并
    image = np.concatenate((y, uv), axis=0)
    return image

完成數(shù)據(jù)類的定義后,我們可以對(duì)其進(jìn)行測(cè)試,讀取并預(yù)處理圖像:

source = "./test_set/test_15.jpg"
process_data = img_process(source)
print(process_data.shape)
(3, 480, 640)
(3, 480, 640)
(3, 480, 640)
(3, 480, 640)
(3, 480, 640)
(3, 480, 640)
(3, 480, 640)

3.3 使用 DANet 和 Deeplabv3 模型進(jìn)行分割預(yù)測(cè)

對(duì)于一個(gè)長(zhǎng)寬為 (w, h) 的圖像,DANet 和 Deeplabv3 模型的輸出 shape 為 (8, w, h),代表每個(gè)像素點(diǎn)在8種地形上的分類置信度,這8種地形分別為 water, road, buildings, tree, grass, cultivated_land, soil, others。我們會(huì)把這兩個(gè)模型的預(yù)測(cè)結(jié)果疊加起來,使得檢測(cè)結(jié)果更加準(zhǔn)確。

首先,我們需要把下載的 onnx 格式的模型轉(zhuǎn)成能在昇騰硬件上運(yùn)行的 om 格式的模型,命令如下:

# atc --framework=5 --model=../models/DANet.onnx  --output=../models/DANet --soc_version=芯片型號(hào) --insert_op_conf=./configure.cfg --log=error
# atc --framework=5 --model=../models/Deeplabv3.onnx  --output=../models/Deeplabv3 --soc_version=芯片型號(hào) --insert_op_conf=./configure.cfg --log=error
# config 文件下載地址: https://gitee.com/ascend/mindxsdk-referenceapps/blob/master/contrib/RemoteSensingSegmentation/config/configure.cfg

然后我們構(gòu)建 om 模型的推理類:

import acl

ACL_MEM_MALLOC_HUGE_FIRST = 0
ACL_MEMCPY_HOST_TO_DEVICE = 1
ACL_MEMCPY_DEVICE_TO_HOST = 2

class OmModel:
    def __init__(self, model_path):
        # 初始化函數(shù)
        self.device_id = 5

        # step1: 初始化
        ret = acl.init()
        # 指定運(yùn)算的Device
        ret = acl.rt.set_device(self.device_id)

        # step2: 加載模型,本示例為pfld模型
        # 加載離線模型文件,返回標(biāo)識(shí)模型的ID
        self.model_id, ret = acl.mdl.load_from_file(model_path)
        # 創(chuàng)建空白模型描述信息,獲取模型描述信息的指針地址
        self.model_desc = acl.mdl.create_desc()
        # 通過模型的ID,將模型的描述信息填充到model_desc
        ret = acl.mdl.get_desc(self.model_desc, self.model_id)

        # step3:創(chuàng)建輸入輸出數(shù)據(jù)集
        # 創(chuàng)建輸入數(shù)據(jù)集
        self.input_dataset, self.input_data = self.prepare_dataset('input')
        # 創(chuàng)建輸出數(shù)據(jù)集
        self.output_dataset, self.output_data = self.prepare_dataset('output')

    def prepare_dataset(self, io_type):
        # 準(zhǔn)備數(shù)據(jù)集
        if io_type == "input":
            # 獲得模型輸入的個(gè)數(shù)
            io_num = acl.mdl.get_num_inputs(self.model_desc)
            acl_mdl_get_size_by_index = acl.mdl.get_input_size_by_index
        else:
            # 獲得模型輸出的個(gè)數(shù)
            io_num = acl.mdl.get_num_outputs(self.model_desc)
            acl_mdl_get_size_by_index = acl.mdl.get_output_size_by_index
        # 創(chuàng)建aclmdlDataset類型的數(shù)據(jù),描述模型推理的輸入。
        dataset = acl.mdl.create_dataset()
        datas = []
        for i in range(io_num):
            # 獲取所需的buffer內(nèi)存大小
            buffer_size = acl_mdl_get_size_by_index(self.model_desc, i)
            # 申請(qǐng)buffer內(nèi)存
            buffer, ret = acl.rt.malloc(buffer_size, ACL_MEM_MALLOC_HUGE_FIRST)
            # 從內(nèi)存創(chuàng)建buffer數(shù)據(jù)
            data_buffer = acl.create_data_buffer(buffer, buffer_size)
            # 將buffer數(shù)據(jù)添加到數(shù)據(jù)集
            _, ret = acl.mdl.add_dataset_buffer(dataset, data_buffer)
            datas.append({"buffer": buffer, "data": data_buffer, "size": buffer_size})
        return dataset, datas

    def forward(self, inputs):
        # 執(zhí)行推理任務(wù)
        # 遍歷所有輸入,拷貝到對(duì)應(yīng)的buffer內(nèi)存中
        input_num = len(inputs)
        for i in range(input_num):
            bytes_data = inputs[i].tobytes()
            bytes_ptr = acl.util.bytes_to_ptr(bytes_data)
            # 將圖片數(shù)據(jù)從Host傳輸?shù)紻evice。
            ret = acl.rt.memcpy(self.input_data[i]["buffer"],  # 目標(biāo)地址 device
                                self.input_data[i]["size"],  # 目標(biāo)地址大小
                                bytes_ptr,  # 源地址 host
                                len(bytes_data),  # 源地址大小
                                ACL_MEMCPY_HOST_TO_DEVICE)  # 模式:從host到device
        # 執(zhí)行模型推理。
        ret = acl.mdl.execute(self.model_id, self.input_dataset, self.output_dataset)
        # 處理模型推理的輸出數(shù)據(jù),輸出top5置信度的類別編號(hào)。
        inference_result = []
        for i, item in enumerate(self.output_data):
            buffer_host, ret = acl.rt.malloc_host(self.output_data[i]["size"])
            # 將推理輸出數(shù)據(jù)從Device傳輸?shù)紿ost。
            ret = acl.rt.memcpy(buffer_host,  # 目標(biāo)地址 host
                                self.output_data[i]["size"],  # 目標(biāo)地址大小
                                self.output_data[i]["buffer"],  # 源地址 device
                                self.output_data[i]["size"],  # 源地址大小
                                ACL_MEMCPY_DEVICE_TO_HOST)  # 模式:從device到host
            # 從內(nèi)存地址獲取bytes對(duì)象
            bytes_out = acl.util.ptr_to_bytes(buffer_host, self.output_data[i]["size"])
            # 按照float32格式將數(shù)據(jù)轉(zhuǎn)為numpy數(shù)組
            data = np.frombuffer(bytes_out, dtype=np.float32)
            inference_result.append(data)
        return inference_result

    def __del__(self):
        # 析構(gòu)函數(shù) 按照初始化資源的相反順序釋放資源。
        # 銷毀輸入輸出數(shù)據(jù)集
        for dataset in [self.input_data, self.output_data]:
            while dataset:
                item = dataset.pop()
                ret = acl.destroy_data_buffer(item["data"])  # 銷毀buffer數(shù)據(jù)
                ret = acl.rt.free(item["buffer"])  # 釋放buffer內(nèi)存
        ret = acl.mdl.destroy_dataset(self.input_dataset)  # 銷毀輸入數(shù)據(jù)集
        ret = acl.mdl.destroy_dataset(self.output_dataset)  # 銷毀輸出數(shù)據(jù)集
        # 銷毀模型描述
        ret = acl.mdl.destroy_desc(self.model_desc)
        # 卸載模型
        ret = acl.mdl.unload(self.model_id)
        # 釋放device
        ret = acl.rt.reset_device(self.device_id)
        # acl去初始化
        ret = acl.finalize()

現(xiàn)在測(cè)試一下模型的推理結(jié)果

# 加載模型
da_net_om_model_path = "./models/DANet.om"
da_net_om_model = OmModel(da_net_om_model_path)
deeplab_om_model_path = "./models/Deeplabv3.om"
deeplab_om_model = OmModel(deeplab_om_model_path)
# 推理
output_res_DANet = da_net_om_model.forward([process_data])
output_res_Deeplab = deeplab_om_model.forward([process_data])
tensor_res_DANet = output_res_DANet[0].reshape(1, 8, 256, 256)  # 模型的輸出shape是 (1, 8, 256, 256)
tensor_res_Deeplab = output_res_Deeplab[0].reshape(1, 8, 256, 256)  # 模型的輸出shape是 (1, 8, 256, 256)
print(output_res_DANet.shape)
print(output_res_Deeplab.shape)

3.4 后處理函數(shù)

正如前面提到的,模型的輸出結(jié)果是各個(gè)像素點(diǎn)分類為不同地形的置信度,我們還需要把分類結(jié)果轉(zhuǎn)換成分類標(biāo)簽、融合兩個(gè)模型的預(yù)測(cè)結(jié)果、構(gòu)建不同顏色表示的區(qū)域分割圖片。這一系列操作對(duì)應(yīng)下面的后處理函數(shù):

首先是預(yù)測(cè)結(jié)果融合:

def softmax(x):
    """
        Compute softmax values for each sets of scores in x.
        Args:
            matrix x
        Returns:
            softmax values for x
    """
    mx = np.max(x, axis=1, keepdims=True)
    numerator = np.exp(x - mx)
    denominator = np.sum(numerator, axis=1, keepdims=True)
    return numerator / denominator

pred_DANet = softmax(tensor_res_DANet)
pred_Deeplab = softmax(tensor_res_Deeplab)
pred_final = pred_Deeplab + pred_DANet

然后是轉(zhuǎn)化成分類標(biāo)簽:

def semantic_to_mask(mask, labels):
    """
        Turn Semantic diagram to label diagram
        Args:
            mask: semantic diagram
            labels: 8 kinds of semantic tags
        Returns:
            the maximum probability prediction result after label replacement
    """
    x = np.argmax(mask, axis=1)
    label_codes = np.array(labels)
    x = np.uint16(label_codes[x.astype(np.uint8)])
    return x

生成區(qū)域分割表示圖:

def decode_seg_map(label_mask, labels, label_colours):
    """
        The result is mapped to a picture
        Args:
            label_mask: the maximum probability prediction result after label replacement
            labels: 8 kinds of semantic tags
            label_colours: colors corresponding to 8 semantic tags
        Returns:
            the prediction result after color replacement
    """
    r = label_mask.copy()
    g = label_mask.copy()
    b = label_mask.copy()
    for i, label in enumerate(labels):
        r[label_mask == label] = label_colours[i, 0]
        g[label_mask == label] = label_colours[i, 1]
        b[label_mask == label] = label_colours[i, 2]
    rgb = np.zeros((label_mask.shape[0], label_mask.shape[1], 3))
    rgb[:, :, 0] = r / 255.0
    rgb[:, :, 1] = g / 255.0
    rgb[:, :, 2] = b / 255.0
    return rgb

最后是給不同區(qū)域打上文字標(biāo)簽,并且保存圖片:

def final_result_create(final_res_pic, label_colours, save_path):
    """
        Result image post-processing
        Args:
            final_res_pic: the prediction result after color replacement
            label_colours: colors corresponding to 8 semantic tags
        Returns:
            null
        Output:
            a result picture in directory result/temp_result/result.jpg
    """

    tmp_rgb = np.array(label_colours) / 255.0
    ic_map = mpl.colors.ListedColormap(tmp_rgb, name='my_color')
    # Normalize the data to an interval of [0, 8]
    norm = mpl.colors.Normalize(vmin=0, vmax=8)
    # the size of result picture setting
    plt.figure(figsize=(5, 4))
    h = plt.imshow(final_res_pic, cmap=ic_map, norm=norm)
    c_bar = plt.colorbar(mappable=h)
    # Return evenly spaced numbers over a specified interval
    v = np.linspace(0, 7, 8)
    # Set the location and properties of the ticks
    c_bar.set_ticks((v + 0.5))
    # Set the label of the ticks
    c_bar.set_ticklabels(['water', 'road', 'buildings', 'tree', 'grass', 'cultivated_land', 'soil', 'others'])
    plt.savefig(save_path)
    plt.close()

此外,為了比較原圖和分割結(jié)果,還需要保存原圖和分割結(jié)果的對(duì)比圖:

def enable_contrast_output(arr):
    """
        Enable comparison graph output
        Args:
            arr: arr[0] is the img one, arr[1] is the img two, arr[2] is the output directory of the comparison graph result
        Returns:
            null
        Output:
            a comparison graph result in arr[2]
    """
    img1 = Image.open(arr[0])
    img12 = Image.open(arr[1])
    # create a new image, set the width and height
    toImage = Image.new('RGB', (img1.width + img12.width + 35, img12.height), 'white')
    # paste image1 to the new image, and set the position
    toImage.paste(img1, (35, 80))
    # paste image2 to the new image, and set the position
    toImage.paste(img12, (img1.width + 35, 0))
    # save the result image, and the quality is 100
    toImage.save(arr[2], quality=100)

好了,完成這些后,我們就可以開始進(jìn)行端到端的圖像分割實(shí)驗(yàn)了!

3.5 整合代碼實(shí)現(xiàn)端到端檢測(cè)

我們把前面創(chuàng)建的預(yù)處理函數(shù)、模型推理代碼和后處理函數(shù)組合起來,形成下面的流程:

import os
import glob
from pathlib import Path
from matplotlib import pyplot as plt

img_dir = "./test_set/"
save_path = "./result/"
imgs_path = glob.glob(str(Path(img_dir).resolve()) + '/*.*')
# 8 kinds of semantic tags
labels = [100, 200, 300, 400, 500, 600, 700, 800]
# Colors corresponding to 8 semantic tags
label_colours = np.array(
    [[255, 127, 39], [255, 202, 24], [236, 28, 36], [14, 209, 69], [184, 61, 186],
        [0, 168, 243], [88, 88, 88], [253, 236, 166]])
if os.path.exists(img_dir) != 1:
    print("The test image " + str(img_dir) + " does not exist.")
    exit()
for img_path in imgs_path:
    img_name = img_path.split("/")[-1]
    segment_save_path = save_path + "seg_" + img_name
    compare_save_path = save_path + "compare_" + img_name

    image = img_process(img_path)

    # Get the result of the DANet model
    output_res_DANet = da_net_om_model.forward([image])
    output_res_Deeplab = deeplab_om_model.forward([image])
    if output_res_DANet == [] or output_res_Deeplab == []:
        continue
    # reshape the matrix to (1, 8, 256, 256)
    tensor_res_DANet = output_res_DANet[0].reshape(1, 8, 256, 256)
    pred_DANet = softmax(tensor_res_DANet)

    # Get the result of the Deeplab_v3 model
    # reshape the matrix to (1, 8, 256, 256)
    tensor_res_Deeplab = output_res_Deeplab[0].reshape(1, 8, 256, 256)
    pred_Deeplab = softmax(tensor_res_Deeplab)

    pred_final = pred_Deeplab + pred_DANet

    # Semantic diagram to label diagram
    pred_final = semantic_to_mask(pred_final, labels).squeeze().astype(np.uint16)
    # The result is mapped to a picture
    rgb_res_pic = decode_seg_map(pred_final, labels, label_colours)
    # Result image post-processing
    final_result_create(rgb_res_pic, label_colours, segment_save_path)
    # Enable comparison graph output if true
    enable_contrast_output([img_path, segment_save_path, compare_save_path])
    print('success!')

查看一張分割結(jié)果對(duì)比圖,可以看出基本上把房屋、導(dǎo)入和植物都分割出來了:

file

恭喜你!至此,你已經(jīng)成功完成了基于 DANet 和 Deeplabv3 模型的遙感圖像分割的全部實(shí)驗(yàn)流程,希望你能夠熟練掌握這套技術(shù)方案,并將其應(yīng)用到實(shí)際的遙感地圖分析項(xiàng)目中去!

3.6 依賴軟件

本實(shí)驗(yàn)的依賴軟件版本信息如下:

  1. Python:為了方便開發(fā)者進(jìn)行學(xué)習(xí),本課程采用Python代碼實(shí)現(xiàn),您可以在服務(wù)器上安裝一個(gè)Conda,用于創(chuàng)建Python環(huán)境,本實(shí)驗(yàn)使用的是 python 3.10 ;
  2. pillow:Python的圖像處理庫,本實(shí)驗(yàn)使用的是 11.0.0 版本;
  3. opencv-python:opencv-python 是 OpenCV 庫的 Python 接口,它提供了對(duì) OpenCV 功能的訪問,包括圖像處理、視頻分析、計(jì)算機(jī)視覺算法和實(shí)時(shí)圖像處理等,使得開發(fā)者能夠在 Python 環(huán)境中輕松實(shí)現(xiàn)復(fù)雜的視覺任務(wù),本實(shí)驗(yàn)使用的是 4.10.0.84 版本;
  4. numpy: 開源的Python科學(xué)計(jì)算庫,用于進(jìn)行大規(guī)模數(shù)值和矩陣運(yùn)算,本實(shí)驗(yàn)使用的是 1.26.4 版本;
  5. matplotlib: 用于創(chuàng)建各種靜態(tài)、動(dòng)態(tài)和交互式的可視化圖表,能將數(shù)據(jù)以直觀的圖形方式展示出來,本實(shí)驗(yàn)使用的是 3.9.2 版本;
  6. CANN(Compute Architecture for Neural Networks):Ascend芯片的使能軟件,本實(shí)驗(yàn)使用的是 8.0.rc2 版本。

04 課后測(cè)試

  1. 嘗試用更大的圖片進(jìn)行測(cè)試,觀察分割檢測(cè)結(jié)果;
  2. 嘗試只用 DANet 或者 DeepLabv3 模型進(jìn)行檢測(cè),觀察檢測(cè)結(jié)果。

本文由博客一文多發(fā)平臺(tái) OpenWrite 發(fā)布!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容