1. 程式人生 > >基於Landsat-8 OLI影像的鄱陽湖資訊提取(python實現)

基於Landsat-8 OLI影像的鄱陽湖資訊提取(python實現)

一、背景

之前寫過基於雙峰閾值分割的冰湖提取演算法,近期需要做一個湖泊提取的簡單程式,就以鄱陽湖為例吧。本文從零開始介紹如何提取鄱陽湖資訊,並製作shp檔案。

二、資料獲取及預處理

為了獲取鄱陽湖的Landsat-8 OLI影像,首先需要知道鄱陽湖的位置,利用百度直接搜尋,可以查詢到鄱陽湖的經緯度資訊:

鄱陽湖位於北緯28°22′至29°45′,東經115°47′至116°45′。根據鄱陽湖的經緯度資訊,查詢其相應的Landsat-8 OLI的行列號資訊,查詢地址為:https://landsat.usgs.gov/wrs-2-pathrow-latitudelongitude-converter

查詢結果如下:

我用北緯29°和東經116°進行查詢,可知,鄱陽湖所在地區的行列號約為121/40左右,為了下載資料方便,這裡我使用了地理空間資料雲進行資料下載,當然也可以用USGS,只不過慢一點,開啟地理空間資料雲進行資料的查詢:

從縮圖種可以看出,行列號為121/40的影像幾乎包含了鄱陽湖的所有範圍,因此我們只下載這一景影像即可,下載時候需注意,雲量要儘可能的少,因此我找了2017年11月01日的Landsat-8 OLI影像,運量為0.04,直接下載即可。

下載好並解壓之後,我們可以在ENVI軟體中開啟看一下:

影象的質量還不錯,水體非常清晰,因此接下來的工作是關於影象預處理了。

(1)像元DN值轉TOA(top of atmosphere)

因為像元DN值沒有任何物理意義,只表示了影象的明暗程度。我們常常使用的水體指數,其計算的是波段反射率,因此我們需要將DN值轉換為TOA,一般來說,地表引數真實反演的情況下需要計算地表真實反射率,只是提取水體的話,只需要計算感測器入瞳處的反射率,即天頂反射率TOA即可。計算的公式為:

                                                                      TOA = gain\times DN + offset

其中,gain表示增益,offset表示偏置,這兩個引數都能夠從Landsat-8的標頭檔案中查詢到:

可知,所有可見光波段的gain=0.00002,offset=-0.1,在ENVI中對其進行轉換即可。轉換完成後檢視其各個波段的反射率統計,反射率基本介於[0 1]說明轉換成功。

(2)影象裁剪

為了去除影象中的冗餘資訊,需要對影象進行簡單裁剪。這裡我只是簡單的用了一下矩形裁剪。

儲存方式選擇tiff即可:

儲存好之後,即可開始我們的提取試驗了。

三、載入影象並呼叫方法進行提取main.py

首先定義一個main.py檔案作為主要檔案,這個檔案中需要定義兩個函式,一個是載入影像函式,一個是根據提取湖泊二值圖製作shp檔案的函式。

首先需要讀取預處理後的tiff檔案,相應的程式碼為:

from osgeo import ogr, osr                  # 匯入處理shp檔案的庫
from osgeo import gdal, gdal_array          # 匯入讀取遙感影像的庫
from NDWI import *                          # 匯入湖泊提取方法,這裡以NDWI為例


def main(img_dir):
    # 載入影像,使用gdal將其載入到numpy中
    img = gdal_array.LoadFile(img_dir)

    # 呼叫湖泊提取方法,返回一個二值影像
    extracted_img = NDWI(img)
    # 儲存二值影像
    gdal_array.SaveArray(extracted_img.astype(gdal_array.numpy.uint8),
                         'extracted_img.tif', format="GTIFF", prototype='')
    # 對提取的結果進行去噪處理,並將其轉化為shp檔案
    raster2shp()

這裡需要注意的是NDWI是接下來定義的湖泊提取方法,因為先寫的main.py檔案,所以即使這裡先報錯也不要急,後面會補上NDWI檔案。

四、編寫湖泊提取演算法NDWI.py

一般的NDWI是利用的green和NIR波段,也就是Landsat-8 的第三和第五波段,根據這個原理,我們直接載入第三和第五波段來計算NDWI,這裡需要注意的就是計算完的NDWI中有很多噪音,因此這裡考慮利用形態學開運算去除噪音。下面直接給出程式碼:

import numpy as np
import cv2


def NDWI(img, threshold=0.4):
    """
    該函式採用最簡單的NDWI進行水體的提取。
    需要輸入的引數為載入好的影像img和閾值threshold
    返回為提取好的水體掩模
    """
    # NDWI用到了Landsat 8 OLI的第3和第5波段,先找到這兩個波段
    green = img[2]
    nir = img[4]

    # 計算NDWI並建立掩模
    ndwi = (green - nir) / (green + nir)

    # 根據閾值來確定掩模的值
    for row in range(ndwi.shape[0]):
        for col in range(ndwi.shape[1]):
            if ndwi[row, col] >= threshold:
                # ndwi_mask[row, col] = 1
                ndwi[row, col] = 1
            else:
                ndwi[row, col] = 0

    # 最後對影象進行開運算進行去噪,即先腐蝕後膨脹
    kernel = np.ones((5, 5), np.uint8)
    opening = cv2.morphologyEx(ndwi, cv2.MORPH_OPEN, kernel)
    return opening

五、提取湖泊並製作shp檔案main.py

最後一步是我們根據NDWI提取好的掩模結果來製作shp檔案,之前我是matlab做這個部分的,如果是matlab可以考慮用contour製作,python的製作方法我是參考我之前的文章:遙感影象處理中常用的python操作中的第五節來做的,基本沒有做任何改動,下面直接給出main.py後面部分的程式碼:

def raster2shp(src="extracted_img.tif"):
    """
    函式輸入的是一個二值影像,利用這個二值影像,建立shp檔案
    """
    # src = "extracted_img.tif"
    # 輸出的shapefile檔名稱
    tgt = "extract.shp"
    # 圖層名稱
    tgtLayer = "extract"
    # 開啟輸入的柵格檔案
    srcDS = gdal.Open(src)
    # 獲取第一個波段
    band = srcDS.GetRasterBand(1)
    # 讓gdal庫使用該波段作為遮罩層
    mask = band
    # 建立輸出的shapefile檔案
    driver = ogr.GetDriverByName("ESRI Shapefile")
    shp = driver.CreateDataSource(tgt)
    # 拷貝空間索引
    srs = osr.SpatialReference()
    srs.ImportFromWkt(srcDS.GetProjectionRef())
    layer = shp.CreateLayer(tgtLayer, srs=srs)
    # 建立dbf檔案
    fd = ogr.FieldDefn("DN", ogr.OFTInteger)
    layer.CreateField(fd)
    dst_field = 0
    # 從圖片中自動提取特徵
    extract = gdal.Polygonize(band, mask, layer, dst_field, [], None)


if __name__ == '__main__':
    main('poyangLake.tif')

六、試驗結果

製作好main.py和NDWI.py,就可以直接執行main.py檔案,執行的結果會生成一個二值掩模影象和shp檔案,在ENVI中開啟最終的效果:

效果算不上好,還有很大的提升空間。後續有空我再進行優化。

七、分析

1.雖然寫的方法比較簡單,不過提供了一種思路。這裡我認為可以改進的地方有:(1)是湖泊提取方法的改進,NDWI的閾值分割法是最最初級的湖泊提取方法,對於眾多的水體指數而言思路的差別不大。(2)提取結果的後處理,可以看到提取結果非常破碎,很多細小的河流被提取了出來,這部分可以考慮用形狀指數進行限制,還有就是湖泊內部的很多斑點,需要進一步填充處理。

2.製作shp檔案的地理參照過程,我個人感覺還有一點小問題,這似乎與你開啟影像的左上角座標有關。個人感覺可以利用contour來繪製湖泊邊界。另外在預處理過程中,對原始影像進行裁剪的時候,裁剪資料的右上角其實是空值,在後續進行處理的時候需要對其置空處理。關於使用contour方法提取二值影象邊界的程式碼:

# 可以參考https://www.cnblogs.com/denny402/p/5160955.html
from skimage import measure

# 檢測所有圖形的輪廓
contours = measure.find_contours(img, 0.5)

3.如果改進了演算法,也可以直接套用這個思路,需要改的地方只有main.py檔案中的第3和第11行

4.所有檔案的結構為:

-- poyangLake.tif            # 預處理好的影像資料
-- main.py
    {
    from xxx import...

    def main(img_dir):...

    def raster2shp(src="extracted_img.tif"):...

    if __name__ == '__main__':...
    }
-- NDWI.py
    {
    import...

    def NDWI(img, threshold=0.8):...
    }