
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)容。
- 場(chǎng)景介紹
- 解決方案
- 代碼實(shí)戰(zhàn)
- 課后測(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)記出來。

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

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)分類。

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)定性。

我們通過同時(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ù)。

接著,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)行可視化的分析和展示。

最后,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)采用的 DANet 和 Deeplabv3 模型需要從這個(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)入和植物都分割出來了:

恭喜你!至此,你已經(jīng)成功完成了基于 DANet 和 Deeplabv3 模型的遙感圖像分割的全部實(shí)驗(yàn)流程,希望你能夠熟練掌握這套技術(shù)方案,并將其應(yīng)用到實(shí)際的遙感地圖分析項(xiàng)目中去!
3.6 依賴軟件
本實(shí)驗(yàn)的依賴軟件版本信息如下:
- Python:為了方便開發(fā)者進(jìn)行學(xué)習(xí),本課程采用Python代碼實(shí)現(xiàn),您可以在服務(wù)器上安裝一個(gè)Conda,用于創(chuàng)建Python環(huán)境,本實(shí)驗(yàn)使用的是
python 3.10; - pillow:Python的圖像處理庫,本實(shí)驗(yàn)使用的是
11.0.0版本; - 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版本; - numpy: 開源的Python科學(xué)計(jì)算庫,用于進(jìn)行大規(guī)模數(shù)值和矩陣運(yùn)算,本實(shí)驗(yàn)使用的是
1.26.4版本; - matplotlib: 用于創(chuàng)建各種靜態(tài)、動(dòng)態(tài)和交互式的可視化圖表,能將數(shù)據(jù)以直觀的圖形方式展示出來,本實(shí)驗(yàn)使用的是
3.9.2版本; -
CANN(Compute Architecture for Neural Networks):Ascend芯片的使能軟件,本實(shí)驗(yàn)使用的是
8.0.rc2版本。
04 課后測(cè)試
- 嘗試用更大的圖片進(jìn)行測(cè)試,觀察分割檢測(cè)結(jié)果;
- 嘗試只用 DANet 或者 DeepLabv3 模型進(jìn)行檢測(cè),觀察檢測(cè)結(jié)果。
本文由博客一文多發(fā)平臺(tái) OpenWrite 發(fā)布!