GDAL创建影像
创建单波段影像
driver.Create(Driver self, char const * utf8_path, int xsize, int ysize, int bands=1, GDALDataType eType=GDT_Byte, char ** options=None)
该函数用于创建影像。
char const * utf8_path
指保存的路径和文件名称;
int xsize, int ysize
指生成影像的长和宽;
int bands
指生成影像的波段数目,默认是1;
GDALDataType eType=GDT_Byte
指影像的DataType
,一般与原始影像的DT相同。
from osgeo import gdal, osr, gdal_array
from osgeo.gdalconst import *
import numpy
dataset = gdal.Open('I:\\MasterLife\\资源与环境遥感\\实验作业一\\caijian2.tif')
band1 = dataset.GetRasterBand(1)
driver = gdal.GetDriverByName('gtiff')
# help(driver.Create)
# print(dataset.GetGeoTransform()) # (435435.0, 30.0, 0.0, 4467435.0, 0.0, -30.0)
# print(band1.XSize, band1.YSize) # 2387 2637
# 文件夹路径必须存在,否则无法正确生成tif
new_filename = 'temp/demo8_create.tif'
# 通过driver创建一个影像
dst_ds = driver.Create(new_filename, 2700, 2700, 1, gdal.GDT_Byte)
# 设置影像的地理仿式坐标
dst_ds.SetGeoTransform([435435.0, 30.0, 0.0, 4467435.0, 0.0, -30.0])
# 设置空间参考
# 原文的Projection是自定义的
# 我们根据原有图像进行赋值
'''srs = osr.SpatialReference()
srs.SetUTM(11, 1)
srs.SetWellKnownGeogCS('NAD27')
dst_ds.SetProjection(srs.ExportToWkt())'''
dst_ds.SetProjection(dataset.GetProjection())
# 生成一个影像范围的0值矩阵
raster = numpy.zeros((2700, 2700))
# 将上述矩阵写入影像
dst_ds.GetRasterBand(1).WriteArray(raster)
生成影像的基本流程是:
- 先声明一个指定图像格式的
Driver
; - 通过
Driver.Create
函数生成初始化的影像,同时指明路径、大小、波段和DT; - 为
Create
后的影像设置Projection
、GeoTransform
; - 写入具有值的矩阵到新影像中。
注意:SetGeoTransform
之前的new_filename
一定要自己创建完整路径,gdal
并不会贴心的给我创建文件夹。
创建多波段影像
from osgeo import gdal
from osgeo.gdalconst import *
# 获取原始数据集
dataset = gdal.Open('I:\\MasterLife\\资源与环境遥感\\实验作业一\\caijian2.tif')
projection = dataset.GetProjection()
geotransform = dataset.GetGeoTransform()
width = dataset.RasterXSize
height = dataset.RasterYSize
bands = dataset.RasterCount
datas = dataset.ReadAsArray(0, 0, width, height)
driver = gdal.GetDriverByName('gtiff')
filename = "temp/demo10_create.tif"
# 读取数据集不需要eType吧, 有默认值
# 新建数据集
# 写入数据
new_dataset = driver.Create(filename, width, height, bands=bands, options=["INTERLEAVE=PIXEL"])
new_dataset.WriteRaster(0, 0, width, height, datas, width, height, band_list=[1, 2, 3])
new_dataset.SetProjection(projection)
new_dataset.SetGeoTransfrom(geotransform)
创建多波段关键在于写入数据:
- 创建新数据集
driver.Create
,数据集的创建不同于单波段,不需要指定eType
,但是需要标注options
,两个选择options=["INTERLEAVE=PIXEL"]
或者options=["INTERLEAVE=BAND"]
。 - 写入读取的
datas
,读取采用ReadAsArray
,WriteRaster
函数具有以下参数:
WriteRaster(xoff, yoff, xsize, ysize, buf_string, buf_xsize=None, buf_ysize=None, buf_type=None, band_list=None, buf_pixel_space=None, buf_line_space=None, buf_band_space=None)
buf_string
代表写入的所有波段数据,band_list
需要指定写入的波段列表,因为ReadAsArray
读取的结果是numpy
的数组。
分段读取创建多光谱影像
这里和上面直接读取的区别在于,写入形成新影像的时候,先获取DataSet
的波段,然后对波段写入对应波段的数据数组。
from osgeo import gdal
from osgeo.gdalconst import *
import numpy as np
# 获取原始数据集
dataset = gdal.Open('I:\\MasterLife\\资源与环境遥感\\实验作业一\\caijian2.tif')
# 获取必要数据信息
projection = dataset.GetProjection()
geotransform = dataset.GetGeoTransform()
width = dataset.RasterXSize
height = dataset.RasterYSize
bands = dataset.RasterCount
# 读取整个影像数据
datas = dataset.ReadAsArray(0, 0, width, height)
driver = gdal.GetDriverByName('gtiff')
filename = "temp/demo11_create.tif"
# 读取数据集不需要eType吧,有默认的值
# 新建波段
out_dataset = driver.Create(filename, width, height, bands=bands, eType=gdal.GDT_Byte, options=["INTERLEAVE=PIXEL"])
# 设定数据集的投影和GT
out_dataset.SetProjection(projection)
out_dataset.SetGeoTransform(geotransform)
for i in range(bands):
floop_band = out_dataset.GetRasterBand(i + 1)
# floop_band.WriteRaster(0, 0, width, height, datas[i])
floop_band.WriteArray(datas[i], 0, 0)
总结这两种方法的区别:
两者使用的写入函数有点差异。new_dataset.WriteRaster
和floop_band.WriteArray
使用help
查看一下两个函数的参数。
WriteArray(array, xoff=0, yoff=0, resample_alg=0, callback=None, callback_data=None) method of osgeo.gdal.Band instance
WriteRaster(xoff, yoff, xsize, ysize, buf_string, buf_xsize=None, buf_ysize=None, buf_type=None, buf_pixel_space=None, buf_line_space=None) method of osgeo.gdal.Band instance
首先:在Band
和RasterSet
中各有一个WriteRaster
和WriteArray
函数。
Band类中的WriteArray和WriteRaster
先在Band
类中进行分析。
WriteArray(array, xoff=0, yoff=0)
这三个参数比较重要,建议都需要写入。Array的重点是写入的数据需要是Array
格式,所以用ReadAsArray
读入的数据可以放在array
参数进行写入。
WriteRaster(xoff, yoff, xsize, ysize, buf_string)
这五个参数比较重要,一般情况下的写入都是xoff=0, yoff=0, xsize=width, ysize=height
,而buf_string
则是需要写入的数据。
所以对demo11来说,下面这两句话是等价的,都可以实现相同的效果。
floop_band.WriteRaster(0, 0, width, height, datas[i])
floop_band.WriteArray(datas[i], 0, 0)
假设,我们读取数据集的方式是ReadRaster
,能不能用WriteRaster
进行写入?
不能,读取的结果是个ByteArray
,需要进行转换;那么使用decode
或者使用str
变成字符串格式,结果也是报错,所以这个读取方式暂时不考虑。
Dataset类中的WriteArray和WriteRaster
有了以上的经验,按道理说在Dataset
类中,两种方法也都可以使用。
因为是写入多个波段,所以band_list
参数必须存在。
WriteRaster(xoff, yoff, xsize, ysize, buf_string, buf_xsize=None, buf_ysize=None,band_list=None)
这八个参数需要全写。若是不想全写,那要把前五个参数必须写上,同时把band_list
用命名写上。
WriteArray(array, xoff=0, yoff=0, band_list=None, interleave='band')
这五个参数需要写上。所以下面这两句话是等价的。
new_dataset.WriteRaster(xoff=0, yoff=0, xsize=width, ysize=height, buf_string=datas, band_list=[1, 2, 3])
new_dataset.WriteArray(datas, 0, 0, band_list=[1, 2, 3], interleave='band')
那么,有了个新的问题,针对Dataset
类中,有个参数是interleave
或者在使用driver.Create
的一个可选参数options=["INTERLEAVE=PIXEL"]
。
首先在驱动创建时的可选参数是PIXEL
或BAND
对结果并没有什么影响;
其次在WriteArray
时的interleave
必须是band
,若是pixel
则会报错:
ValueError: array larger than output file, or offset off edge
这个错误可以理解为原数据集是Band排列,那么输入过来的结果就一定是Band排列。
那么为什么驱动创建时的排列方式是P或B都是无所谓的?
这个问题,暂时不清楚。
所以,无论Band
类和Dataset
类的多光谱图像创建使用WriteArray
或WriteRaster
都可以,但是如果不愿意多想,那么WriteArray
总是可以实现的。数据的读取用ReadAsArray
也是如此,正好可以方便numpy
的处理。
影像的排列方式最好是BSQ,在处理大气校正可能是会选择BIL/BIP。上下影像的排列方式最好保持一致~~(因为不一致的暂时我不会实现)~~。
创建影像金字塔
使用代码直接创建影像金字塔。
这里的列表中有6级,加上原图级别一共有7级。
new_dataset.BuildOverviews(overviewlist=[2, 4, 8, 16, 32, 64])
打开影像,左下角有级别预览,能够看见6级的影像金字塔。
设定Imagine风格的pyramids。
gdal.SetConfigOption('HFA_USE_RRD', 'YES')
这句代码不知道有什么用。
所以先写第二句,再在代码中引入第一句。
使用CreateCopy创建影像
GDAL
创建影像可以使用Create
方式直接创建,但是对于有些数据格式,Create
方法并不支持(JPEG,PNG等),这些格式可以在Raster Driver
中有标注。
所以就用CreateCopy
方法来创建这些格式。
CreateCopy
其实概念很简单,当我们使用Create
方法创建好一个GTIFF
格式的影像后,需要将他变成PNG/JPEG
格式怎么办?使用CreateCopy
完成这一步。
CreateCopy
其实就是将参考文件(TIFF)的内容信息,全部赋值给目标文件(JPEG/PNG)。
CreateCopy
赋值的目标文件可以存在也可以不存在。不存在会新建一个文件,存在会覆盖原有文件的全部内容。
from osgeo import gdal
from osgeo.gdalconst import *
# 需要变换的目标文件
target_filename = 'temp/demo13.png'
# 参考源文件
ref_filename = 'temp/demo11_create.tif'
# 源文件驱动
driver = gdal.GetDriverByName('GTIFF')
ref_dataset = gdal.Open(ref_filename)
sr_ds = driver.CreateCopy(target_filename, ref_dataset, 1)
help(driver.CreateCopy)
我们查看一下CreateCopy
函数。
CreateCopy(Driver self, char const * utf8_path, Dataset src, int strict=1, char ** options=None, GDALProgressFunc callback=0, void * callback_data=None) -> Dataset
char const * utf8_path
表示输出结果路径;
Dataset src
表示原有数据集,必须gdal.Open
的数据集;
int strict=1
表示约束限制,参数取值为0(False)或1(True), 取值为非的时候说明即使不能精确的由原数据转化为目标数据, 程序也照样执行CreateCopy方法,不会产生致命错误。 这种错误有可能是输出格式不支持输入数据格式象元的数据类型, 或者是目标数据不支持写入空间参考等等;
char ** options=None
表示可选参数,是指在格式转换中所要用到的一些特殊的参数;比如['TILED=YES', 'COMPRESS=PACKBITS']
表示输出过程中使用瓦片存储,压缩方式采用PACKBITS
,具体参数在哪里查找?
GDALProgressFunc callback=0
是一个挂钩函数,表示程序运行的进度;
void * callback_data=None
是一个回调函数,表示当前运行的数据。
像素存储顺序
通过Create
和CreateCopy
创建的影像,默认都是['INTERLEAVE=BAND']
,BAND(tags[284]=2)
,在导出时是的284号域(PlanarConfiguration域)
默认为2
,排列方式是 RRRR……,GGGG……,BBBB……
,专业的一些软件是可以支持这种格式的。
而大部分看图软件支持的影像查看方式都是RGBRGBRGBRGB……
,所以,在CreateCopy
成PNG/JPEG
这样的格式时,可以在char ** options=None
参数中添加'INTERLEAVE=PIXEL'
,毕竟我们生成的PNG/JPEG
本身也不是作为影像处理使用的吧。