Bootstrap

基于Python的GDAL库的分割、裁剪、合并。

前言

在地理信息系统(GIS)和遥感领域中,数据处理是一个核心环节,其中数据的分割、裁剪和拼接是常见的需求。这些操作对于分析局部地区的变化、合成大范围图像或是处理大型数据集时尤为重要。Python作为一种强大的编程语言,结合GDAL库,可以高效地实现这些数据处理任务。本文将详细介绍如何使用Python和GDAL库来进行数据的分割、裁剪与拼接。

分割

这段代码定义了一个名为 split_tiff 的函数,旨在将一个大的 TIFF 文件分割成多个较小的 TIFF 文件块,每个块的大小由参数 n 决定。这里使用了 GDAL 库来处理地理空间数据。

def split_tiff(input_tiff, output_folder, n):
    # 打开大的TIFF文件
    dataset = gdal.Open(input_tiff)
    if dataset is None:
        print("无法打开TIFF文件")
        return

    # 获取TIFF文件的基本信息
    width = dataset.RasterXSize
    height = dataset.RasterYSize
    band_count = dataset.RasterCount
    geotransform = dataset.GetGeoTransform()
    projection = dataset.GetProjection()

    # 计算每个小块的大小
    block_width = width // n
    block_height = height // n

    # 创建输出文件夹
    os.makedirs(output_folder, exist_ok=True)

    # 分割TIFF文件为n×n个小块
    for i in range(n):
        for j in range(n):
            # 计算当前小块的范围
            x_offset = j * block_width
            y_offset = i * block_height  # 翻转i以从左下角开始

            # 创建小块的文件名
            output_filename = os.path.join(output_folder, f"block_{i}_{j}.tif")
            print(output_filename)
            # 写入小块数据到文件
            driver = gdal.GetDriverByName("GTiff")
            output_dataset = driver.Create(output_filename, block_width, block_height, band_count, gdal.GDT_Float32)

            # 设置小块的地理转换和投影信息
            output_dataset.SetGeoTransform((geotransform[0] + x_offset * geotransform[1], geotransform[1], 0,
                                            geotransform[3] + y_offset * geotransform[5], 0, geotransform[5]))
            output_dataset.SetProjection(projection)

            # 写入小块的数据
            for band in range(band_count):
                band_data = dataset.GetRasterBand(band + 1).ReadAsArray(x_offset, y_offset, block_width, block_height)
                output_dataset.GetRasterBand(band + 1).WriteArray(band_data)

            # 关闭输出文件
            output_dataset = None

    # 关闭输入文件
    dataset = None

函数解释

  1. 打开 TIFF 文件:使用 gdal.Open(input_tiff) 打开输入的 TIFF 文件,并检查是否成功打开。
  2. 获取文件信息:获取 TIFF 文件的宽度、高度、波段数、地理变换和投影信息。
  3. 计算小块大小:将大文件的宽度和高度分别除以 n,得到每个小块的宽度和高度。
  4. 创建输出文件夹:确保输出文件夹存在,如果不存在则创建。
  5. 分割文件:通过两层循环遍历每个小块的位置,计算每个小块的偏移量,并创建对应的输出文件名。
  6. 创建输出文件:为每个小块创建一个新的 TIFF 文件,并设置其地理变换和投影信息。
  7. 写入数据:从原 TIFF 文件中读取每个波段的数据块,并写入到对应的输出文件中。
  8. 关闭文件:循环结束后,关闭所有打开的文件。

合并

这段代码定义了一个 merge_tiff_blocks 函数,用于将分割成多个块的 TIFF 文件重新拼接成一个完整的 TIFF 文件。

def merge_tiff_blocks(block_folder, output_path, n):
    """
    将分割后的多个 TIFF 文件重新拼接成一个单独的 TIFF 文件。

    :param block_folder: 存储分割后的 TIFF 文件的文件夹路径。
    :param output_path: 输出的单个 TIFF 文件路径。
    :param n: 分块的行数或列数。
    """
    try:
        # 获取第一个分块 TIFF 文件的基本信息
        first_block_path = os.path.join(block_folder, f"block_0_0.tif")
        first_block_ds = gdal.Open(first_block_path)
        output_width = first_block_ds.RasterXSize * n
        output_height = first_block_ds.RasterYSize * n
        output_projection = first_block_ds.GetProjection()
        output_geotransform = first_block_ds.GetGeoTransform()
        band_count = first_block_ds.RasterCount

        # 创建输出的数据集
        output_driver = gdal.GetDriverByName("GTiff")
        output_dataset = output_driver.Create(output_path, output_width, output_height, band_count, gdal.GDT_Float32)
        output_dataset.SetProjection(output_projection)
        output_dataset.SetGeoTransform(output_geotransform)

        # 将分块 TIFF 文件写入到输出数据集中
        for i in range(n):
            for j in range(n):
                block_path = os.path.join(block_folder, f"block_{i}_{j}.tif")  # 从左上角开始拼接
                block_ds = gdal.Open(block_path)
                block_data = block_ds.ReadAsArray()
                output_dataset.GetRasterBand(1).WriteArray(block_data, j * first_block_ds.RasterXSize,
                                                           i * first_block_ds.RasterYSize)
                block_ds = None

        # 关闭输出数据集和第一个分块 TIFF 文件
        output_dataset = None
        first_block_ds = None

        print(f"成功拼接并保存为 {output_path}")
    except Exception as e:
        print(f"拼接 TIFF 文件时出现异常:{e}")

裁剪

这段代码定义了一个名为 cutShp 的函数,用于根据给定的 Shapefile (SHP) 文件裁剪一个 GeoTIFF (TIF) 图像。

def cutShp(input_path, shp_path, out_path):
    """
    对单个tif图像进行Shp裁剪
    :param input_path:文件输入地址
    :param shp_path:shp图层输入地址
    :param out_path:文件输出地址
    :return:
    """
    # 打开栅格数据集
    input_raster = gdal.Open(input_path)
    # 获取输入栅格数据的地理信息
    x_origin, x_pixel_size, _, y_origin, _, y_pixel_size = input_raster.GetGeoTransform()
    # 打开矢量多边形数据集(研究区域)
    clip_polygon_path = shp_path
    clip_polygon_ds = ogr.Open(clip_polygon_path)
    clip_polygon_layer = clip_polygon_ds.GetLayer()
    # 获取裁剪多边形的边界框
    clip_extent = clip_polygon_layer.GetExtent()
    a = abs(input_raster.GetGeoTransform()[5])
    b = input_raster.GetGeoTransform()[1]
    xmin, xmax, ymin, ymax = clip_extent

    # 获取裁剪多边形的几何
    # 创建输出栅格数据集
    driver = gdal.GetDriverByName("GTiff")
    output_raster = driver.Create(out_path, int((xmax - xmin) / b) + 2,
                                   int((ymax - ymin) / a) + 2, input_raster.RasterCount, gdal.GDT_Float32)

    output_raster.SetProjection(input_raster.GetProjection())
    output_raster.SetGeoTransform(
        (xmin, x_pixel_size, 0, ymax, 0, y_pixel_size))
    logging.info("开始裁剪")
    # 执行裁剪操作
    gdal.Warp(output_raster, input_raster, cutlineDSName=clip_polygon_path, cropToCutline=True)
    logging.info("裁剪完成")
    # 关闭数据集
    input_raster = None
    output_raster = None
    clip_polygon_ds = None
    return out_path

函数流程

  1. 打开栅格数据集:使用 GDAL 库打开输入的 TIFF 图像文件,并存储在 input_raster 变量中。
  2. 获取栅格数据的地理信息:通过 input_raster.GetGeoTransform() 方法获取栅格数据的地理变换信息,包括左上角的 x 坐标 (x_origin)、x 方向的像素大小 (x_pixel_size)、y 方向的像素大小 (y_pixel_size) 以及左上角的 y 坐标 (y_origin,但这里只获取了部分参数,因为 GetGeoTransform() 返回的元组包含六个元素)。
  3. 打开矢量多边形数据集:使用 OGR 库打开提供的 Shapefile,并获取其图层。这个图层包含了用于裁剪的多边形。
  4. 获取裁剪多边形的边界框:通过 clip_polygon_layer.GetExtent() 获取裁剪多边形的边界框,即其在地理空间中的最小 x 坐标、最大 x 坐标、最小 y 坐标和最大 y 坐标。
  5. 计算输出栅格的大小:基于裁剪多边形的边界框和输入栅格的像素大小,计算输出栅格的大小(即宽度和高度)。这里使用 int((xmax - xmin) / b) + 2 和 int((ymax - ymin) / a) + 2 来计算宽度和高度,并加上 2 以确保裁剪区域完全包含在内(虽然这种做法在大多数情况下可能不是必需的,因为 GDAL 的 Warp 函数会处理边界情况)。
  6. 创建输出栅格数据集:使用 GDAL 的 GTiff 驱动程序创建一个新的 TIFF 文件,并设置其投影、地理变换和波段数等信息。
  7. 执行裁剪操作:使用 GDAL 的 Warp 函数执行裁剪操作。cutlineDSName 参数指定了裁剪多边形的数据集,cropToCutline 设置为 True 以确保裁剪结果严格符合裁剪多边形的边界。
  8. 关闭数据集:裁剪完成后,关闭输入栅格、输出栅格和裁剪多边形数据集,以释放资源。
  9. 返回输出路径:虽然函数在内部已经完成了裁剪操作,但它仍然返回了输出路径,这在某些情况下可能是有用的,尽管这个返回值在函数外部可能不是必需的。

补充 :分割时添加缓冲区

def split_tiff(input_tiff, output_folder, n, buffer_size):
    # 打开大的TIFF文件
    dataset = gdal.Open(input_tiff)
    if dataset is None:
        print("无法打开TIFF文件")
        return

    # 获取TIFF文件的基本信息
    width = dataset.RasterXSize
    height = dataset.RasterYSize
    band_count = dataset.RasterCount
    geotransform = dataset.GetGeoTransform()
    projection = dataset.GetProjection()

    # 计算每个小块的大小
    block_width = width // n
    block_height = height // n

    os.makedirs(output_folder, exist_ok=True)

    # 分割TIFF文件为n×n个小块
    for i in range(n):
        for j in range(n):
            # 计算当前小块的范围,考虑缓冲区
            x_offset = j * block_width
            y_offset = i * block_height

            x_start = max(x_offset - buffer_size, 0)
            y_start = max(y_offset - buffer_size, 0)
            x_end = min(x_offset + block_width + buffer_size, width)
            y_end = min(y_offset + block_height + buffer_size, height)

            actual_block_width = x_end - x_start
            actual_block_height = y_end - y_start

            # 创建小块的文件名
            output_filename = os.path.join(output_folder, f"block_{i}_{j}.tif")
            print(output_filename)

            # 写入小块数据到文件
            driver = gdal.GetDriverByName("GTiff")
            output_dataset = driver.Create(output_filename, actual_block_width, actual_block_height, band_count,
                                           gdal.GDT_Float32)

            # 设置小块的地理转换和投影信息
            output_dataset.SetGeoTransform((geotransform[0] + x_start * geotransform[1], geotransform[1], 0,
                                            geotransform[3] + y_start * geotransform[5], 0, geotransform[5]))
            output_dataset.SetProjection(projection)

            # 写入小块的数据
            for band in range(band_count):
                band_data = dataset.GetRasterBand(band + 1).ReadAsArray(x_start, y_start, actual_block_width,
                                                                        actual_block_height)
                output_dataset.GetRasterBand(band + 1).WriteArray(band_data)

            # 关闭输出文件
            output_dataset = None

    # 关闭输入文件
    dataset = None


# 示例用法
input_tiff = r"F:\20240522-ResultMerge\ResultMerge.tiff"
output_folder = r"data1/高程1"
n = 10  # 分割为4×4个小块
buffer_size = 10  # 缓冲区大小为10像素
split_tiff(input_tiff, output_folder, n, buffer_size)

结论

通过使用Python和GDAL/rasterio库,我们可以高效地处理栅格数据,包括分割、裁剪和拼接等操作。这些操作对于GIS和遥感分析至关重要,能够帮助我们更好地理解和利用地理空间数据。希望本文的示例代码能为你的数据处理工作提供一些帮助。

;