5. GDAL python教程(4)——用GDAL讀取柵格數據

 

GDAL原生支持超過100種柵格數據類型,涵蓋所有主流GIS與RS數據格式,包括

  • ArcInfo grids, ArcSDE raster, Imagine, Idrisi, ENVI, GRASS, GeoTIFF
  • HDF4, HDF5
  • USGS DOQ, USGS DEM
  • ECW, MrSID
  • TIFF, JPEG, JPEG2000, PNG, GIF, BMP

完整的支持列表可以參考 http://www.gdal.org/formats_list.html

5.1. 導入GDAL支持庫

舊版本(1.5以前):

import gdal, gdalconst

新版本(1.6以後):

from osgeo import gdal, gdalconst

gdal和gdalconst最好都要導入,其中gdalconst中的常量都加了前綴,力圖與其他的module衝突最小。所以對gdalconst你可以直接這樣導入:

from osgeo.gdalconst import *

GDAL數據驅動,與OGR數據驅動類似,需要先創建某一類型的數據驅動,再創建響應的柵格數據集。 一次性註冊所有的數據驅動,但是隻能讀不能寫:gdal.AllRegister() 單獨註冊某一類型的數據驅動,這樣的話可以讀也可以寫,可以新建數據集:

driver = gdal.GetDriverByName('HFA')
driver.Register()

打開已有的柵格數據集:

fn = 'aster.img'
ds = gdal.Open(fn, GA_ReadOnly)
if ds is None:
   print 'Could not open ' + fn
    sys.exit(1)

讀取柵格數據集的x方向像素數,y方向像素數,和波段數

cols = ds.RasterXSize
rows = ds.RasterYSize
bands = ds.RasterCount

注意後面沒有括號,因爲他們是屬性(properties)不是方法(methods) 讀取地理座標參考信息(georeference info)

GeoTransform是一個list,存儲着柵格數據集的地理座標信息

adfGeoTransform[0] /* top left x 左上角x座標*/
adfGeoTransform[1] /* w--e pixel resolution 東西方向上的像素分辨率*/
adfGeoTransform[2] /* rotation, 0 if image is "north up" 如果北邊朝上,地圖的旋轉角度*/
adfGeoTransform[3] /* top left y 左上角y座標*/
adfGeoTransform[4] /* rotation, 0 if image is "north up" 如果北邊朝上,地圖的旋轉角度*/
adfGeoTransform[5] /* n-s pixel resolution 南北方向上的像素分辨率*/

注意柵格數據集的座標一般都是以左上角爲基準的。

下面的例子是從一個柵格數據集中取出Geotransform作爲一個list,然後讀取其中的數據

geotransform = ds.GetGeoTransform()
originX = geotransform[0]
originY = geotransform[3]originY = geotransform[3]
pixelWidth = geotransform[1]
pixelHeight = geotransform[5]

計算某一座標對應像素的相對位置(pixel offset),也就是該座標與左上角的像素的相對位置,按像素數計算,計算公式如下:

xOffset = int((x – originX) / pixelWidth)
yOffset = int((y – originY) / pixelHeight)

讀取某一像素點的值,需要分兩步 首先讀取一個波段(band):GetRasterBand(<index>),其參數爲波段的索引號 然後用ReadAsArray(<xoff>, <yoff>, <xsize>, <ysize>),讀出從(xoff,yoff)開始,大小爲(xsize,ysize)的矩陣。如果將矩陣大小設爲1X1,就是讀取一個像素了。但是這一方法只能將讀出的數據放到矩陣中,就算只讀取一個像素也是一樣。例如:

band = ds.GetRasterBand(1)
data = band.ReadAsArray(xOffset, yOffset, 1, 1)

如果想一次讀取一整張圖,那麼將offset都設定爲0,size則設定爲整個圖幅的size,例如:

data = band.ReadAsArray(0, 0, cols, rows)

但是要注意,從data中讀取某一像素的值,必須要用data[yoff, xoff]。注意不要搞反了。數學中的矩陣是[row,col],而這裏恰恰相反!這裏面row對應y軸,col對應x軸。

注意在適當的時候釋放內存,例如band = None 或者dataset = None。尤其當圖很大的時候

如何更有效率的讀取柵格數據?顯然一個一個的讀取效率非常低,將整個柵格數據集都塞進二維數組也不是個好辦法,因爲這樣佔的內存還是很多。更好的方法是按塊(block)來存取數據,只把要用的那一塊放進內存。本週的樣例代碼中有一個utils模塊,可以讀取block大小。

例如:

import utils
blockSize = utils.GetBlockSize(band)
xBlockSize = blockSize[0]
yBlockSize = blockSize[1]

平鋪(tiled),即柵格數據按block存儲。有的格式,例如GeoTiff沒有平鋪,一行是一個block。Erdas imagine格式則按64x64像素平鋪。 如果一行是一個block,那麼按行讀取是比較節省資源的。 如果是平鋪的數據結構,那麼設定ReadAsArray()的參數值,讓它一次只讀入一個block,就是效率最高的方法了。例如:

rows = 13, cols = 11, xBSize = 5, yBSize = 5
for i in range(0, rows, yBSize):
if i + yBSize < rows:
    numRows = yBSize
else:
    numRows = rows – i
    for j in range(0, cols, xBSize):
        if j + xBSize < cols:
            numCols = xBSize
        else:
            numCols = colsnumCols = cols – j
        data = band.ReadAsArray(j, i, numCols, numRows)

這一段代碼具有通用性,可以時常拿來用的。

下面介紹一點二維數組的處理技巧 這裏要用到兩個庫,Numeric和numpy。Numeric比較老了,FWTools用它。自己安裝配置的話還是配功能更強的numpy。 數據類型轉換:

data = band.ReadAsArray(j, i, nCols, nRows)
data = data.astype(Numeric.Float) # Numeric
data = data.astype(numpy.float) # numpy

或者簡單點只寫一句

data = band.ReadAsArray(j, i, nCols, nRows).astype(Numeric.Float)

掩膜mask 這是Numeric和numpy庫的功能,輸入一個數組和條件,輸出一個二值數組。例如

mask = Numeric.greater(data, 0)mask = Numeric.greater(data, 0)
>>> a = Numeric.array([0, 4, 6, 0, 2])
>>> print a
[0 4 6 0 2]
>>> mask = Numeric.greater(a, 0)
>>> print mask
[0 1 1 0 1]

數組求和

>>> a = Numeric.array([0, 4, 6, 0, 2])
>>> print a>>> print a
[0 4 6 0 2]
>>> print Numeric.sum(a)
12

如果是二維數組,那sum就會返回一個一維數組

>>> b = Numeric.array([a, [5, 10, 0, 3, 0]])
>>> print b
[[ 0      4  6  0  2]
[ 5 10  0  3  0]]
>>> print Numeric.sum(b)>>> print Numeric.sum(b)
[ 5 14  6  3  2]

所以,二維數組的求和就要這樣

>>> print Numeric.sum(Numeric.sum(b))
30

這裏有一個小技巧,統計大於0的像素個數,可以聯合運用mask和sum兩個函數

>>> print a
[0 4 6 0 2]
>>> mask = Numeric.greater(a, 0)
>>> print mask
[0 1 1 0 1]
>>> print Numeric.sum(mask)
3
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章