Python利用GDAL读写遥感影像

Jun 13,2018   10330 words   37 min


之前在这篇博客中简单介绍了GDAL的使用,但并不是很完整。本文给出一个相对规范、比较完整,具有一定通用性的GDAL读写tif影像的代码,以便以后使用。代码以图像裁剪和波段融合/分离两个小功能为例进行介绍。

1.影像裁剪代码

# coding=utf-8
from osgeo import gdal
from gdalconst import *
import numpy as np


def writeTif(bands, path):
    if bands is None or bands.__len__() == 0:
        return
    else:
        # 认为各波段大小相等,所以以第一波段信息作为保存
        band1 = bands[0]
        # 设置影像保存大小、波段数
        img_width = band1.shape[1]
        img_height = band1.shape[0]
        num_bands = bands.__len__()

        # 设置保存影像的数据类型
        if 'int8' in band1.dtype.name:
            datatype = gdal.GDT_Byte
        elif 'int16' in band1.dtype.name:
            datatype = gdal.GDT_UInt16
        else:
            datatype = gdal.GDT_Float32

        # 创建文件
        driver = gdal.GetDriverByName("GTiff")
        dataset = driver.Create(path, img_width, img_height, num_bands, datatype)
        if dataset is not None:
            for i in range(bands.__len__()):
                dataset.GetRasterBand(i + 1).WriteArray(bands[i])
        print("save image success.")


def readTifImage(img_path):
    data = []
    # 以只读方式打开遥感影像
    dataset = gdal.Open(img_path, GA_ReadOnly)
    if dataset is None:
        print("Unable to open image file.")
        return data
    else:
        print("Open image file success.")
        bands_num = dataset.RasterCount
        print("Image height:" + dataset.RasterYSize.__str__() + " Image width:" + dataset.RasterXSize.__str__())
        print(bands_num.__str__() + " bands in total.")
        for i in range(bands_num):
            # 获取影像的第i+1个波段
            band_i = dataset.GetRasterBand(i + 1)
            # 读取第i+1个波段数据
            band_data = band_i.ReadAsArray(0, 0, band_i.XSize, band_i.YSize)
            data.append(band_data)
            print("band " + (i + 1).__str__() + " read success.")
        return data


def readTifImageWithWindow(img_path, start_x, start_y, x_range, y_range):
    data = []
    # 以只读方式打开遥感影像
    dataset = gdal.Open(img_path, GA_ReadOnly)
    if dataset is None:
        print("Unable to open image file.")
        return data
    else:
        print("Open image file success.")
        bands_num = dataset.RasterCount
        print("Image height:" + dataset.RasterYSize.__str__() + " Image width:" + dataset.RasterXSize.__str__())
        print(bands_num.__str__() + " bands in total.")
        for i in range(bands_num):
            # 获取影像的第i+1个波段
            band_i = dataset.GetRasterBand(i + 1)
            # 读取第i+1个波段数据
            band_data = band_i.ReadAsArray(start_x, start_y, x_range, y_range)
            data.append(band_data)
            print("band " + (i + 1).__str__() + " read success.")
        return data


bands_data = readTifImageWithWindow("D1923-561-32_rc.tif", 0, 8000, 5000, 4000)
writeTif(bands_data, "cut_img.tif")

代码比较简单,读写功能包装成了函数方便调用。整个脚本的功能是对tif影像进行裁剪提取ROI。裁剪后的影像如下。 这样便实现了单波段遥感影像的读取与存储。

2.多波段融合代码

# coding=utf-8
from osgeo import gdal
from gdalconst import *
import numpy as np


def writeTif(bands, path):
    if bands is None or bands.__len__() == 0:
        return
    else:
        # 认为各波段大小相等,所以以第一波段信息作为保存
        band1 = bands[0]
        # 设置影像保存大小、波段数
        img_width = band1.shape[1]
        img_height = band1.shape[0]
        num_bands = bands.__len__()

        # 设置保存影像的数据类型
        if 'int8' in band1.dtype.name:
            datatype = gdal.GDT_Byte
        elif 'int16' in band1.dtype.name:
            datatype = gdal.GDT_UInt16
        else:
            datatype = gdal.GDT_Float32

        # 创建文件
        driver = gdal.GetDriverByName("GTiff")
        dataset = driver.Create(path, img_width, img_height, num_bands, datatype)
        if dataset is not None:
            for i in range(bands.__len__()):
                dataset.GetRasterBand(i + 1).WriteArray(bands[i])
        print("save image success.")


def readTifImage(img_path):
    data = []
    # 以只读方式打开遥感影像
    dataset = gdal.Open(img_path, GA_ReadOnly)
    if dataset is None:
        print("Unable to open image file.")
        return data
    else:
        print("Open image file success.")
        bands_num = dataset.RasterCount
        print("Image height:" + dataset.RasterYSize.__str__() + " Image width:" + dataset.RasterXSize.__str__())
        print(bands_num.__str__() + " bands in total.")
        for i in range(bands_num):
            # 获取影像的第i+1个波段
            band_i = dataset.GetRasterBand(i + 1)
            # 读取第i+1个波段数据
            band_data = band_i.ReadAsArray(0, 0, band_i.XSize, band_i.YSize)
            data.append(band_data)
            print("band " + (i + 1).__str__() + " read success.")
        return data


def readTifImageWithWindow(img_path, start_x, start_y, x_range, y_range):
    data = []
    # 以只读方式打开遥感影像
    dataset = gdal.Open(img_path, GA_ReadOnly)
    if dataset is None:
        print("Unable to open image file.")
        return data
    else:
        print("Open image file success.")
        bands_num = dataset.RasterCount
        print("Image height:" + dataset.RasterYSize.__str__() + " Image width:" + dataset.RasterXSize.__str__())
        print(bands_num.__str__() + " bands in total.")
        for i in range(bands_num):
            # 获取影像的第i+1个波段
            band_i = dataset.GetRasterBand(i + 1)
            # 读取第i+1个波段数据
            band_data = band_i.ReadAsArray(start_x, start_y, x_range, y_range)
            data.append(band_data)
            print("band " + (i + 1).__str__() + " read success.")
        return data


# 蓝波段
band4_data = readTifImageWithWindow("D1923-561-04_rc_out.tif", 1300, 13000, 3000, 4000)
# 绿波段
band11_data = readTifImageWithWindow("D1923-561-11_rc_out.tif", 1300, 13000, 3000, 4000)
# 红波段
band22_data = readTifImageWithWindow("D1923-561-22_rc_out.tif", 1300, 13000, 3000, 4000)
# 近红外波段
band32_data = readTifImageWithWindow("D1923-561-32_rc.tif", 1300, 13000, 3000, 4000)

# 构造一个list用于存放各波段,存放顺序为B、G、R、NIR
join_data = []
join_data.extend(band4_data)
join_data.extend(band11_data)
join_data.extend(band22_data)
join_data.extend(band32_data)

writeTif(join_data, "join.tif")

利用波段配准后的数据,波段融合成一个包含B、G、R、NIR,4个波段的tif文件。合成后用ENVI打开如下。波段列表中一共有4个波段,说明合并成功了。 依次点击,装载RGB波段效果如下。 NIR、G、B波段效果如下。 在近红外波段下植被呈现红色。

3.波段拆分

# coding=utf-8
from osgeo import gdal
from gdalconst import *
import numpy as np


def writeTif(bands, path):
    if bands is None or bands.__len__() == 0:
        return
    else:
        # 认为各波段大小相等,所以以第一波段信息作为保存
        band1 = bands[0]
        # 设置影像保存大小、波段数
        img_width = band1.shape[1]
        img_height = band1.shape[0]
        num_bands = bands.__len__()

        # 设置保存影像的数据类型
        if 'int8' in band1.dtype.name:
            datatype = gdal.GDT_Byte
        elif 'int16' in band1.dtype.name:
            datatype = gdal.GDT_UInt16
        else:
            datatype = gdal.GDT_Float32

        # 创建文件
        driver = gdal.GetDriverByName("GTiff")
        dataset = driver.Create(path, img_width, img_height, num_bands, datatype)
        if dataset is not None:
            for i in range(bands.__len__()):
                dataset.GetRasterBand(i + 1).WriteArray(bands[i])
        print("save image success.")


def readTifImage(img_path):
    data = []
    # 以只读方式打开遥感影像
    dataset = gdal.Open(img_path, GA_ReadOnly)
    if dataset is None:
        print("Unable to open image file.")
        return data
    else:
        print("Open image file success.")
        bands_num = dataset.RasterCount
        print("Image height:" + dataset.RasterYSize.__str__() + " Image width:" + dataset.RasterXSize.__str__())
        print(bands_num.__str__() + " bands in total.")
        for i in range(bands_num):
            # 获取影像的第i+1个波段
            band_i = dataset.GetRasterBand(i + 1)
            # 读取第i+1个波段数据
            band_data = band_i.ReadAsArray(0, 0, band_i.XSize, band_i.YSize)
            data.append(band_data)
            print("band " + (i + 1).__str__() + " read success.")
        return data


def readTifImageWithWindow(img_path, start_x, start_y, x_range, y_range):
    data = []
    # 以只读方式打开遥感影像
    dataset = gdal.Open(img_path, GA_ReadOnly)
    if dataset is None:
        print("Unable to open image file.")
        return data
    else:
        print("Open image file success.")
        bands_num = dataset.RasterCount
        print("Image height:" + dataset.RasterYSize.__str__() + " Image width:" + dataset.RasterXSize.__str__())
        print(bands_num.__str__() + " bands in total.")
        for i in range(bands_num):
            # 获取影像的第i+1个波段
            band_i = dataset.GetRasterBand(i + 1)
            # 读取第i+1个波段数据
            band_data = band_i.ReadAsArray(start_x, start_y, x_range, y_range)
            data.append(band_data)
            print("band " + (i + 1).__str__() + " read success.")
        return data


# 装载数据
bands_data = readTifImage("join.tif")
# 数据拆分
# 注意虽然是一个波段,但是依然要以list的形式传入
band_b = [bands_data[0]]
band_g = [bands_data[1]]
band_r = [bands_data[2]]
band_nir = [bands_data[3]]

writeTif(band_b, "band_b.tif")
writeTif(band_g, "band_g.tif")
writeTif(band_r, "band_r.tif")
writeTif(band_nir, "band_nir.tif")

运行效果如下,下图是G波段单独拆分出来的结果。

4.功能整合脚本

最后将这几个功能写到了一块变成了一个脚本,方便使用。对于日常使用,就不用再打开很大的ENVI了,直接运行脚本即可,可以节约点时间。

# coding=utf-8
from osgeo import gdal
from gdalconst import *
import sys
import os


def writeTif(bands, path):
    if bands is None or bands.__len__() == 0:
        return
    else:
        # 认为各波段大小相等,所以以第一波段信息作为保存
        band1 = bands[0]
        # 设置影像保存大小、波段数
        img_width = band1.shape[1]
        img_height = band1.shape[0]
        num_bands = bands.__len__()

        # 设置保存影像的数据类型
        if 'int8' in band1.dtype.name:
            datatype = gdal.GDT_Byte
        elif 'int16' in band1.dtype.name:
            datatype = gdal.GDT_UInt16
        else:
            datatype = gdal.GDT_Float32

        # 创建文件
        driver = gdal.GetDriverByName("GTiff")
        dataset = driver.Create(path, img_width, img_height, num_bands, datatype)
        if dataset is not None:
            for i in range(bands.__len__()):
                dataset.GetRasterBand(i + 1).WriteArray(bands[i])
        print("save image success.")


def readTifImage(img_path):
    data = []
    # 以只读方式打开遥感影像
    dataset = gdal.Open(img_path, GA_ReadOnly)
    if dataset is None:
        print("Unable to open image file.")
        return data
    else:
        print("Open image file success.")
        bands_num = dataset.RasterCount
        print("Image height:" + dataset.RasterYSize.__str__() + " Image width:" + dataset.RasterXSize.__str__())
        print(bands_num.__str__() + " bands in total.")
        for i in range(bands_num):
            # 获取影像的第i+1个波段
            band_i = dataset.GetRasterBand(i + 1)
            # 读取第i+1个波段数据
            band_data = band_i.ReadAsArray(0, 0, band_i.XSize, band_i.YSize)
            data.append(band_data)
            print("band " + (i + 1).__str__() + " read success.")
        return data


def readTifImageWithWindow(img_path, start_x, start_y, x_range, y_range):
    data = []
    # 以只读方式打开遥感影像
    dataset = gdal.Open(img_path, GA_ReadOnly)
    if dataset is None:
        print("Unable to open image file.")
        return data
    else:
        print("Open image file success.")
        bands_num = dataset.RasterCount
        print("Image height:" + dataset.RasterYSize.__str__() + " Image width:" + dataset.RasterXSize.__str__())
        print(bands_num.__str__() + " bands in total.")
        for i in range(bands_num):
            # 获取影像的第i+1个波段
            band_i = dataset.GetRasterBand(i + 1)
            # 读取第i+1个波段数据
            band_data = band_i.ReadAsArray(start_x, start_y, x_range, y_range)
            data.append(band_data)
            print("band " + (i + 1).__str__() + " read success.")
        return data


def findAllFiles(root_dir, filter):
    print("Finding files ends with \'" + filter + "\' ...")
    separator = os.path.sep
    paths = []
    names = []
    files = []
    # 遍历
    for parent, dirname, filenames in os.walk(root_dir):
        for filename in filenames:
            if filename.endswith(filter):
                paths.append(parent + separator)
                names.append(filename)
    for i in range(paths.__len__()):
        files.append(paths[i] + names[i])
    print (names.__len__().__str__() + " files have been found.")
    paths.sort()
    names.sort()
    files.sort()
    return paths, names, files


if __name__ == '__main__':
    if sys.argv.__len__() >= 2:
        if sys.argv[1] == 'help' or 'HELP':
            print("Help Info:")
            print("Resize Mode:[img_path] [start_x] [start_y] [x_range] [y_range]")
            print("Join Mode:[img_dir] [img_type]")
            print("Separate Mode:[img_path] [output_dir]")
            pass
        elif sys.argv[1] == 'resize' or 'RESIZE':
            img_path = sys.argv[2]
            start_x = int(sys.argv[3])
            start_y = int(sys.argv[4])
            x_range = int(sys.argv[5])
            y_range = int(sys.argv[6])
            bands_data = readTifImageWithWindow(img_path, start_x, start_y, x_range, y_range)
            writeTif(bands_data, "cut_img.tif")
        elif sys.argv[1] == 'join' or 'JOIN':
            img_dir = sys.argv[2]
            img_type = sys.argv[3]
            paths, names, files = findAllFiles(img_dir, img_type)
            bands_data = []
            for i in range(files.__len__()):
                band_data = readTifImage(files[i])
                bands_data.extend(band_data)
                print("joined " + (i + 1).__str__() + " bands.")
            print(bands_data.__len__().__str__() + " bands in total.")
            writeTif(bands_data, "join.tif")
        elif sys.argv[1] == 'separate' or 'SEPARATE':
            img_path = sys.argv[2]
            output_dir = sys.argv[3]
            bands_data = readTifImage(img_path)
            for i in range(bands_data.__len__()):
                writeTif([bands_data[i]], output_dir + os.path.sep + "band_" + i.__str__().zfill(2) + ".tif")
                print("saved " + (i + 1).__str__() + "/" + bands_data.__len__().__str__())
        else:
            print("Unknown mode, input scriptname.py help to get help infomation.")
    else:
        print("Unknown mode, input scriptname.py help to get help infomation.")

以上便介绍了GDAL对遥感影像的读取和写入,并基于此实现了遥感影像的裁剪和波段融合/拆分。

本文作者原创,未经许可不得转载,谢谢配合

返回顶部