GDAL库的一些细节

5.2. GDAL库的一些细节#

== 关于ReadRaster ==

=== 缩放 ===

下面是我的一个小例子:我使用了!ReadAsArray,返回的是Numeric模块定义的Array,我喜欢它的是因为它排列很工整,看起来很好看。 (这里的数据是用GRASS从spearfish示例数据转出的aspect数据集)

#! python
>>> import gdal
>>> from gdalconst import *
>>> dataset = gdal.Open("f:/pyproj/gtif/aspect.tif")
>>> band = dataset.GetRasterBand(1)
>>> band.ReadAsArray(100,100,5,5,10,10)
array([[ 61,  61,  58,  58,  90,  90,  87,  87,  45,  45],
  [ 61,  61,  58,  58,  90,  90,  87,  87,  45,  45],
  [ 36,  36,  59,  59, 113, 113,  88,  88,  37,  37],
  [ 36,  36,  59,  59, 113, 113,  88,  88,  37,  37],
  [255, 255,  82,  82, 135, 135,  72,  72,  22,  22],
  [255, 255,  82,  82, 135, 135,  72,  72,  22,  22],
  [ 45,  45, 129, 129, 144, 144, 255, 255, 255, 255],
  [ 45,  45, 129, 129, 144, 144, 255, 255, 255, 255],
  [ 90,  90, 110, 110,  98,  98,  35,  35,  45,  45],
  [ 90,  90, 110, 110,  98,  98,  35,  35,  45,  45]],'b')
>>> band.ReadAsArray(100,100,10,10,10,10)
array([[ 61,  58,  90,  87,  45, 255, 255, 117,  65,  50],
  [ 36,  59, 113,  88,  37,  26,  63, 165,  23,  74],
  [255,  82, 135,  72,  22,  29,  67, 118,  77, 120],
  [ 45, 129, 144, 255, 255,  36,  94, 108,  88,  97],
  [ 90, 110,  98,  35,  45,  88, 109, 121,  92,  73],
  [ 94, 108,  85,  45,  55,  97, 144, 167, 135,  21],
  [ 96, 106,  82,  45,  45,  45, 255, 230, 251, 255],
  [246, 236, 255, 255,   3, 255, 255, 247, 254, 255],
  [236, 249, 255, 255, 255, 255, 255, 255, 255, 255],
  [194, 212, 255, 255, 255, 255, 255, 255, 255, 255]],'b')
>>> band.ReadAsArray(100,100,20,20,10,10)
array([[ 54,  95,  91, 150,  53, 135, 164, 139,  35,  37],
  [128, 152,  86,  97,  96,  91, 116, 199, 255, 200],
  [101,  66,  71, 135,  80, 152, 255, 255, 255, 210],
  [171, 159,  87, 247, 254, 202, 104, 255, 255, 160],
  [223, 255, 255, 255, 255, 206, 147, 193, 218, 121],
  [227, 255, 255, 116, 150, 238, 255, 255, 137, 162],
  [230, 255, 231, 247, 145, 156, 134,  30, 130, 136],
  [155, 196, 252, 230, 187,  19, 134, 195, 126, 144],
  [ 80,  54,  85, 105, 163, 140, 192,  95, 154, 146],
  [150,  83,  71, 161, 173, 255, 255, 120, 149, 180]],'b')
>>>
}}}

5.2.1. ReadAsArray的原型#

::

#! python >>> help(band.ReadAsArray) Help on method ReadAsArray in module gdal: ReadAsArray(self, xoff=0, yoff=0, win_xsize=None, win_ysize=None, buf_xsize=None , buf_ysize=None, buf_obj=None) method of gdal.Band instance

看看函数的几个参数的意义。头两个100是取值窗口的左上角在实际数据中所处象元的xy位置。后两个是取值窗口覆盖的区域大小,再后面 是取值窗口取出数组进行缩放后数组的大小。这里需要注意的是这里的buffer大小是根据参数自动分配的,可以不指定,如果不指定,则和第3,4个参数一致。经过5,6两个参数的设置,可以进行缩放。比如真的取值窗口大小可以是20*20,而取出后数组就可以人工设置大小。让他称为10*10的数组,或者是40*40的数组。如果设置20*40则取出的数组对于真实图像来说有了变形。

#! python
>>> band.ReadAsArray(100,100,10,10)
array([[ 61,  58,  90,  87,  45, 255, 255, 117,  65,  50],
       [ 36,  59, 113,  88,  37,  26,  63, 165,  23,  74],
       [255,  82, 135,  72,  22,  29,  67, 118,  77, 120],
       [ 45, 129, 144, 255, 255,  36,  94, 108,  88,  97],
       [ 90, 110,  98,  35,  45,  88, 109, 121,  92,  73],
       [ 94, 108,  85,  45,  55,  97, 144, 167, 135,  21],
       [ 96, 106,  82,  45,  45,  45, 255, 230, 251, 255],
       [246, 236, 255, 255,   3, 255, 255, 247, 254, 255],
       [236, 249, 255, 255, 255, 255, 255, 255, 255, 255],
       [194, 212, 255, 255, 255, 255, 255, 255, 255, 255]],'b')

通 过几个例子可以看到,读取的4个size参数的作用就是把硬盘上指定区域(前四个参数定义)的数据按比例缩放到用户指定区域(后两个定义)内,必要的时候 进行缩放。这里需要注意的是缩的情况(缩的时候是取几个周围点的平均值)如果是调色板数据就可能引发问题(是否可以设置重采样方式我还要再研究一下)。

补:在[[http://lists.maptools.org/pipermail/gdal-dev/2005-May/005646.html|这里]]发现了一个帖子,发现RasterIO用的都是最临近(都是最临近?),而要设置重采样方式只能在!BuildPyramid的时候设置了。现在看来!BuildPyramid还是有些用的。

=== 范围 ===

现在还有一个问题。就是取值窗口超过实际数据最大的范围怎么办?

#! python
>>> band.XSize
634
>>> band.YSize
478
>>> band.ReadAsArray(630,100,10,10)
ERROR 5: Access window out of range in RasterIO().  Requested
(630,100) of size 10x10 on raster of 634x478.
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "D:\Python24\lib\gdal.py", line 837, in ReadAsArray
    buf_xsize, buf_ysize, buf_obj )
  File "D:\Python24\lib\gdalnumeric.py", line 178, in BandReadAsArray
    buf_xsize, buf_ysize, datatype, buf_obj )
TypeError: Access window out of range in RasterIO().  Requested
(630,100) of size 10x10 on raster of 634x478.
>>>

可以看到,出错了。获取数据的时候不能越界,很不好。还要调用的时候去判断越界没。还好用python的Numeric模块可以很方便地扩展矩阵。

=== 效率 ===

对于gtif来说,从横向读取和重纵向读取的效率会相差十分之大(那不是一点的大)! 可以写一个python文件来验证一下

#! python
# gtif.py
import gdal
import time
dataset = gdal.Open("f:/gisdata/gtif/zz.tif")
band = dataset.GetRasterBand(1)
width = dataset.RasterXSize
height = dataset.RasterYSize
bw = 128
bh = 128
bxsize = width/128
bysize = width/128
start = time.time()
band.ReadAsArray(0,0,width,height)
print time.time()-start
start2 = time.time()
for i in range(bysize):
    for j in range(bxsize):
        band.ReadAsArray(bw*j,bh*i,bw,bh)
print time.time()-start2

运行一下得到结果

F:\pyproj>gtif.py
2.35899996758
0.766000032425
}}}

然后把最后一段的循环的两个for位置调换一下(缩进要注意),变成:

::

#! python for j in range(bxsize):

for i in range(bysize):

band.ReadAsArray(bw*j,bh*i,bw,bh)

运行结果是:

::

F:pyproj>gtif.py 2.48400020599 24.140999794


天!运行时间是第一次的N倍!切注意!切注意!

另外,我们可以看到,如果把图像分块读取,比不分块读取同样大小的图像效率明显要高出许多。

== 关于ColorMap ==

=== ColorMap的颜色定义 ===

!ColorMap的定义在[[http://www.gdal.org/gdal_datamodel.html|下面]]有详细的解释 :

颜色表: 一个颜色表可能包含一个或者更多的如下面这种C结构描述的颜色元素。

typedef struct
{
    /- gray, red, cyan or hue -/
    short      c1;
    /- green, magenta, or lightness -/
    short      c2;
    /- blue, yellow, or saturation -/
    short      c3;
    /- alpha or blackband -/
    short      c4;
} GDALColorEntry;

颜色表也有一个色彩解析值。(GDALPaletteInterp)这个值有可能是下面值的一种。并且描述了C1,c2,c3,c4该如何解释。

::

GPI_GRAY: c1表示灰度值 GPI_RGB: c1表示红,c2表示绿,c3表示蓝,c4表示Alpha GPI_CYMP: c1表示青,c2表示品,c3表示黄,c4表示黑 GPI_HLS: c1表示色调,c2表示亮度,c3表示饱和度


虽然有颜色表示数的区别,但是用!GetColorEntry读出的都是4个值,根据!PaletteInterp枚举值看截取其中的几个值形成颜色。

=== ColorMap颜色变动 ===

需要注意的是在gdal使用!ColorMap的时候,对原始的!ColorMap已经做过变动了。比如geotiff的!ColorMap的数据类型是 short,默认的范围是在0~65535,但是在gdal读取出来以后,已经经过了范围压缩。压到了0~255。虽然都是short类型,但是值已经变化。

参考[[http://wiki.woodpecker.org.cn/moin/lilin/gdal-introduce#head-ddf209b471ede52abec271c974ff6e89c5daed49|快速开始]]那张颜色表,可以看到颜色表中的数据是经过(原始数据/65535.0*255)调整过的。 这里在使用的时候可能比较方便,但是如果你是有读取过geotiff原始数据背景的,则需要小心。可能会有习惯性思维的陷阱。因为两者的类型都是short,但是实际的数值不一样(我就曾经以为gdal读取的是geotiff内部的原始!ColorMap数据)。

=== 反馈 ===

如果您发现我写的东西中有问题,或者您对我写的东西有意见,请登陆[[http://groups.google.com/group/geosings?hl=zh-CN|这个论坛]]。