引言
本人研究的方向是遥感,研究了2年也搞没清楚普通图像和遥感影像的区别,只知道到了多了地理坐标信息,但是经纬度信息映射到每个图像像素点的底层逻辑我还不太理解。因为现在需要使用python将图像转化为栅格影像,所以在此仔细研究一下。
地理坐标系和投影坐标系
做遥感首先要理解地理坐标系(GeographicCoordinate System)和投影坐标系(ProjectedCoordinate System)2个概念。
-
地理坐标系是球面坐标,参考平面是椭球面,坐标单位是经纬度;
-
投影坐标系是平面坐标系,参考平面是水平面,坐标单位是米、千米等。
-
地理坐标系转换到投影坐标系的过程理解为投影,即将不规则的地球曲面转换为平面。
以一个具体示例来初识ArcGIS中的坐标系,其全部参数拷贝在下面。这一示例是一个“投影坐标系(Projected Coordinate System)”。其名称是“WGS_1984_UTM_Zone_50N”。“WKID”是坐标系的编号,“ESPG”是“European Petroleum Survey Group”的缩写,表示其由“欧洲石油调查组织”发布。
可知,“WGS_1984_UTM_Zone_50N”这个投影坐标系由两部分组成:名为“Transverse_Mercator”的“投影(Projection)”和名为“GCS_WGS_1984”的“地理坐标系(GeographicCoordinate System)”。
WGS_1984_UTM_Zone_50N
WKID: 32650Authority: EPSG
Projection:Transverse_Mercator
False_Easting:500000.0
False_Northing:0.0
Central_Meridian:117.0
Scale_Factor:0.9996
Latitude_Of_Origin:0.0
Linear Unit: Meter(1.0)
GeographicCoordinate System: GCS_WGS_1984
Angular Unit:Degree (0.0174532925199433)
Prime Meridian:Greenwich (0.0)
Datum: D_WGS_1984
Spheroid: WGS_1984
Semimajor Axis: 6378137.0
Semiminor Axis: 6356752.314245179
Inverse Flattening: 298.257223563
地理坐标系
地理坐标系由三个参数来定义:角度单位(Angular Unit)、本初子午线(Prime Meridian)和大地测量系统(Datum)。
地理坐标系“GCS_WGS_1984”使用的角度单位为“度(Degree)”,0.0174532925199433这个数字等于“π/180”,使用的本初子午线为0.0度经线,即格林威治皇家天文台(Greenwich)所在位置的经线,使用的大地测量系统则为“D_WGS_1984”。
**地理坐标系的最重要的参数是“大地测量系统(Datum)”,而大地测量系统的最重要的参数是“椭球(Spheroid)”。**椭球相同,大地测量系统不一定相同,因为原点(origin)和方位(orientation)可以不同。想象一下,同一个椭球,首先可以固定在三维空间中的任意一个点,并且在固定于某点后还能以三个自由度任意地旋转其方位(朝向)。
WGS_1984”椭球的“长半轴(Semimajor Axis)”和“短半轴(Semiminor Axis)”分别为6378137.0和6356752.314245179,其“反扁率(Inverse Flattening)”为298.257223563,等于Semimajor Axis/( Semimajor Axis - Semiminor Axis)。
投影坐标系“WGS_1984_UTM_Zone_50N”使用的“投影(Projection)”名为“横轴墨卡托(Transverse_Mercator)”,然而这个名称并不能完全准确概括其投影。
投影坐标系
事实上,投影坐标系“WGS_1984_UTM_Zone_50N”这个名称中的“WGS_1984”指出了其地理坐标系为“GCS_WGS_1984”,而“UTM_Zone_50N”则指出了其投影。
“UTM_Zone_50N”这个名称指出,其投影方法是“通用横轴墨卡托(Universal Transverse Mercator,UTM)”,其投影带为北半球第50带,这个“Zone_50N”的“中央经线(Central Meridian)”正是117.0度,在“Transverse_Mercator”的参数中得到了体现。举一反三,“Xian_1980_GK_CM_117E”这个坐标系使用的地理坐标系为“GCS_Xian_1980”,而投影名称“GK_CM_117E”指出其使用以东经117度为中央经线的“高斯-克吕格(Gauss-Kruger,GK)”投影。投影的另一个重要参数是“东偏(False Easting)”。
有些投影会在X坐标值前加上投影带号,比如:“Xian_1980_GK_Zone_20”的“false_easting”参数为20500000.0,其中20为投影带号,而“Xian_1980_GK_CM_117E”的“false_easting”参数为500000.0,尽管它们的中央经线都为东经117度。
地理坐标系和投影坐标区别及常用操作: https://zhuanlan.zhihu.com/p/484585670
GeoTIFF数据格式
GeoTIFF是一种常用的地理信息系统(GIS)数据格式,它结合了TIFF(Tagged Image File Format)图像格式和地理空间信息。GeoTIFF文件可以包含地理坐标系、投影信息、地理位置和其他与地理空间数据相关的元数据。
GeoTIFF文件可以存储各种类型的地理空间数据,包括栅格数据(如卫星影像、数字高程模型等)和矢量数据(如点、线、面等地理要素)。它支持不同的地理坐标系和投影系统,可以准确地表示地球上的位置和形状。
GeoTIFF文件的优点包括:
- 与TIFF格式兼容:GeoTIFF文件可以使用标准的TIFF查看器和编辑器打开和编辑。
- 地理空间信息:GeoTIFF文件可以包含地理坐标系、投影信息和其他与地理空间数据相关的元数据,使得数据可以准确地在地球上的位置上显示和分析。
- 多波段支持:GeoTIFF文件可以存储多个波段的数据,例如多光谱卫星影像,方便进行多光谱分析。
- 跨平台兼容性:GeoTIFF文件可以在不同的GIS软件和平台上使用,包括ArcGIS、QGIS、ENVI等。
使用GeoTIFF文件,可以进行地理空间数据的获取、处理、分析和可视化,是GIS领域中常用的数据格式之一。
以下是一些常见的地理空间信息在GeoTIFF文件中的保存方式:
- 坐标系信息:GeoTIFF文件中保存了坐标系的相关信息,包括坐标系的名称、投影信息、椭球体参数等。这些信息可以通过GeoTIFF文件的元数据标签来获取。
- 地理范围:GeoTIFF文件可以保存地理范围的信息,即图像所覆盖的地理区域的边界。通常使用左上角和右下角的经纬度坐标来表示地理范围。
- 分辨率:GeoTIFF文件可以保存图像的分辨率信息,即每个像素代表的地理距离。通常以米或度为单位。
- 像素对应的地理坐标:GeoTIFF文件可以保存每个像素对应的地理坐标信息。这样可以通过像素坐标来获取对应的地理位置。
这些地理空间信息是通过GeoTIFF文件的元数据标签来保存的,常见的元数据标签包括"GeoKeyDirectoryTag"、“ModelPixelScaleTag”、"ModelTiepointTag"等。可以使用专门的GIS软件或者Python库(如GDAL)来读取和处理GeoTIFF文件的地理空间信息。
GDAL库
GDAL 官方文档 (osgeo.cn)
GDAL(Geospatial Data Abstraction Library)是一个开源的地理空间数据处理库,它提供了对各种格式的地理空间数据进行读取、写入和转换的功能。GDAL库支持包括GeoTIFF、Shapefile、NetCDF、HDF等在内的多种地理空间数据格式。
GDAL库的主要功能包括:
-
数据读取和写入:GDAL可以读取和写入各种地理空间数据格式,包括栅格数据和矢量数据。它可以从文件、数据库或内存中读取数据,并将数据写入到文件或其他数据源中。
-
数据转换和投影:GDAL可以进行地理空间数据的投影转换、坐标系转换和数据格式转换。它提供了丰富的投影和坐标系操作功能,可以将数据从一个坐标系转换到另一个坐标系,并进行投影变换。
-
数据操作和分析:GDAL提供了一些基本的数据操作和分析功能,如裁剪、重采样、统计等。它可以对栅格数据进行像素级别的操作,如计算均值、最大值、最小值等。
-
数据集信息获取:GDAL可以获取地理空间数据集的元数据信息,包括坐标系信息、地理范围、分辨率等。它还可以获取数据集中的波段信息、属性表信息等。
GDAL是一个功能强大且广泛应用的地理空间数据处理库,它提供了丰富的功能和灵活的接口,适用于各种地理空间数据处理需求。在Python中,可以使用GDAL库的Python绑定(即GDAL Python)来进行地理空间数据处理。
基础操作
from osgeo import gdal# 打开影像文件
dataset = gdal.Open('data/other/guoyang2019.tif')
# 获取影像宽度和高度
width = dataset.RasterXSize
height = dataset.RasterYSize
# 获取影像波段数
band_count = dataset.RasterCount
# 获取投影信息
projection = dataset.GetProjection()
# 获取地理转换信息
geotransform = dataset.GetGeoTransform()
# 打印影像基本信息
print("影像宽度:", width)
print("影像高度:", height)
print("波段数:", band_count)
print("投影信息:", projection)
print("地理转换信息:", geotransform)
# 关闭影像文件
dataset = None
输出:
影像宽度: 7471
影像高度: 4896
波段数: 1
投影信息: GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AXIS["Latitude",NORTH],AXIS["Longitude",EAST],AUTHORITY["EPSG","4326"]]
地理转换信息: (115.878317255137, 8.982254799999953e-05, 0.0, 33.785346925021, 0.0, -8.982254799999988e-05)
其中地理转换信息即仿射矩阵,实现了从像素点到经纬度坐标的变换,其含义为
GeoTransform = [115.878317255137, 8.982254799999953e-05, 0.0, 33.785346925021, 0.0, -8.982254799999988e-05]
'''
0:图像左上角的X坐标;
1:图像东西方向分辨率;
2:旋转角度,如果图像北方朝上,该值为0;
3:图像左上角的Y坐标;
4:旋转角度,如果图像北方朝上,该值为0;
5:图像南北方向分辨率;
'''
得到这六个参数之后就可以进行图像像素坐标(即行列号)和地理坐标之间的变换:
#像素坐标和地理坐标仿射变换
XGeo = GeoTransform[0]+GeoTransform[1]*Xpixel+Ypixel*GeoTransform[2];
YGeo = GeoTransform[3]+GeoTransform[4]*Xpixel+Ypixel*GeoTransform[5];
读取和写入文件的函数:
def read_img(filename):dataset = gdal.Open(filename)im_width = dataset.RasterXSize #栅格矩阵的列数im_height = dataset.RasterYSize #栅格矩阵的行数im_geotrans = dataset.GetGeoTransform() #仿射矩阵im_proj = dataset.GetProjection() #地图投影im_data = dataset.ReadAsArray(0,0,im_width,im_height)del datasetreturn im_proj,im_geotrans,im_data
def write_img(filename,im_proj,im_geotrans,im_data):if "int8" in im_data.dtype.name:datatype = gdal.GDT_Byteif "int16" in im_data.dtype.name:datatype = gdal.GDT_UInt16else:datatype = gdal.GDT_Float32if len(im_data.shape) == 3:im_bands, im_height, im_width = im_data.shapeelse:im_bands, (im_height, im_width) = 1, im_data.shapedriver = gdal.GetDriverByName("GTiff") # 数据类型必须有,因为要计算需要多大内存空间dataset = driver.Create(filename, im_width, im_height, im_bands, datatype)dataset.SetGeoTransform(im_geotrans) # 写入仿射变换参数dataset.SetProjection(im_proj) # 写入投影if im_bands == 1:dataset.GetRasterBand(1).WriteArray(im_data) # 写入数组数据else:for i in range(im_bands):dataset.GetRasterBand(i + 1).WriteArray(im_data[i])del dataset
实战
写这篇文章的目的是因为我目前深度学习拼接上的作物分布图只是一张PNG图片,没有地理坐标信息,现在需要通过代码添加上:
from osgeo import gdal, osrpath = f"img/predict2020/SoyaMap.png"
# 读取图像
image = Image.open(path)
# 转换为numpy数组
image_array = (1-np.array(image)/255).astype(np.int8)
# 创建一个空间参考对象 ,将空间坐标系设置为EPSG:4326
srs = osr.SpatialReference()
srs.ImportFromEPSG(4326)
# 地理转换信息,图像左上角的经纬度坐标点和像素大小
geotransform = [115.86991557438377,8.983152841195215E-5, 0.0, 33.789500591456914, 0.0, -8.983152841195215E-5]
write_img("img/predict2020/SoyaMap.tif",srs.ExportToWkt(),geotransform,image_array)
导出成功后,使用Arcgis打开制图,简直不要太酷啦。