最近在处理Landsat的遥感影像。.tif格式的图像,与之前接触的.jpg,.png不太一样,在读写上面有更多的要求和规范。学习了好久,尤其是在显示上。由于之前并不是很懂数据类型的转换(后续已解决),在自己的电脑上怎么样都点不开,会显示:
所以经常因为,看不到图像长什么样,所以会陷入一种麻烦,不够直接,每次看还得打开ArcGIS等软件才能,太难顶了。再加上懒得整理笔记和做过的东西,走过的坑,就发奋来记录一下吧。
这一部分现在看来还是比较简单的,方便以后自己查阅,所以基本上写了很多乱七八糟的注释,就直接放代码了。
import os
import numpy as np
from PIL import Image
from osgeo import gdal
import cv2
# 不一定所有库都用上了,只是方便
# 读取tif图像的数据(只是读取数据,不是显示)
def readTiff(img_path):
dataset = gdal.Open(img_path)
if dataset == None:
print(img_path + "文件无法打开")
return
im_width = dataset.RasterXSize # 栅格矩阵的列数
im_height = dataset.RasterYSize # 栅格矩阵的行数
im_bands = dataset.RasterCount # 波段数
band1 = dataset.GetRasterBand(1)
# print(im_bands)
# print('Band Type=', gdal.GetDataTypeName(band1.DataType)) # 输出band的类型
im_data = dataset.ReadAsArray(0, 0, im_width, im_height) # 获取数据,将数据写成数组,对应栅格矩阵,前两个参数是偏移量
im_geotrans = dataset.GetGeoTransform() # 获取仿射矩阵信息
im_proj = dataset.GetProjection() # 获取地图投影信息
# print(im_data.shape)
return im_data, im_geotrans, im_proj
# 将数据写成tif格式图像保存
def writeTiff(im_data, im_geotrans, im_proj, img_path):
# 判断数据类型
if 'int8' in im_data.dtype.name:
datatype = gdal.GDT_Byte
elif 'int16' in im_data.dtype.name:
datatype = gdal.GDT_UInt16
else:
datatype = gdal.GDT_Float32
# 判断数据的波段数
if len(im_data.shape) == 3: # 三个参数,波段数,H,W
im_bands, im_height, im_width = im_data.shape
# elif len(im_data.shape) == 2: # 两个参数,一般只有灰度图,band为1,不显示,只有H,W,相当于直接是二维矩阵了
# im_data = np.array([im_data])
else:
im_bands, (im_height, im_width) = 1, im_data.shape # 同上,没有band值的返回值,就手动给band赋值
# 创建tif文件
driver = gdal.GetDriverByName("GTiff") # 读取某一类型的数据,需要先载入数据驱动,初始化一个对象
dataset = driver.Create(img_path, im_width, im_height, im_bands, datatype) # 在路径处创建空文件,并确定开辟多大内存;每个像素都有一个对应的值,这个值得类型用数据类型指定。这里的数据类型是gdal数据类型。
# 设置头文件信息
if (dataset != None):
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): # i从0开始,数组是从0开始,但是获取波段是从1开始
dataset.GetRasterBand(i + 1).WriteArray(im_data[i]) # 写入数组数据
# 释放内存空间
del dataset
# 使用CV2,显示tif图像
def showTiff(img_path):
img = cv2.imread(img_path,4)
# 第二个参数是通道数和位深的参数,
# IMREAD_UNCHANGED = -1 # 不进行转化,比如保存为了16位的图片,读取出来仍然为16位。
# IMREAD_GRAYSCALE = 0 # 进行转化为灰度图,比如保存为了16位的图片,读取出来为8位,类型为CV_8UC1。
# IMREAD_COLOR = 1 # 进行转化为RGB三通道图像,图像深度转为8位
# IMREAD_ANYDEPTH = 2 # 保持图像深度不变,进行转化为灰度图。
# IMREAD_ANYCOLOR = 4 # 若图像通道数小于等于3,则保持原通道数不变;若通道数大于3则只取取前三个通道。图像深度转为8位
# print(img) # 输出图像的数据
# print(img.shape) # 输出图像的三维
# print(img.dtype) # 输出图像的编码类型
# print(img.min()) # 输出图像的最小值
# print(img.max()) # 输出图像的最大值
# 创建窗口并显示图像
cv2.namedWindow("Image") # 创建一个窗口
cv2.imshow("Image", img) # 显示图像
cv2.waitKey(0) # 设置显示图像的延迟
# 释放窗口
cv2.destroyAllWindows()
使用的GDAL版本是:
GDAL-3.1.3-cp38-cp38-win_amd64.whl
在实验室的电脑上好像安装别的版本,cmd里用pip安装会报错,pycharm里install也会报错(command error),应该是要找一个最符合的才行。下载GDAL的whl链接:
https://www.lfd.uci.edu/~gohlke/pythonlibs/#gdal
最初接触tif图像,带地理坐标和投影信息,不是很懂。以为打不开的原因是有地理坐标和投影信息等其他参数,所以写代码去show。但其实这个show的条件是,图像本身就是能打开的。我之前使用的GID数据集,那个本来就是能直接点开的,所以用这个代码没问题。后面找到了更好的办法,就没有测试这个show的代码是否也能适用,大概率是不适用的。图像能不能在计算机中直接显示,取决于的是图像本身的数据类型,如果是uint8的类型,就是能直接双击打开的,其他比如float32或者别的,可能就会出现打不开的提示。