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