【PyGIS】GDAL矢量裁剪栅格,设置背景值为空白

本文介绍了使用Python的GDAL库进行矢量裁剪栅格的方法,重点关注如何设置背景值为空白,并讨论了使用gdal.Warp、PIL结合GDAL以及ArcTools裁剪的差异。在使用gdal.Warp时,通过参数cropToCutline和dstNodata可以精确控制裁剪范围和背景值。此外,文章还提到在PIL与GDAL交互中遇到的中文属性表报错问题,以及裁剪结果与ArcTool相比存在的行数差异问题。
摘要由CSDN通过智能技术生成

一、使用gdal.Warp

gdalwarp 实用程序是一种图像拼接、重投影和扭曲实用程序。该程序可以重新投影到任何支持的投影。如果图像是带有控制信息的“原始”图像,也可以存储原始图像的投影信息。
gdal.warp官网链接:
https://gdal.org/api/python/osgeo.gdal.html#osgeo.gdal.Warp

def extractTiff(input_shape,input_raster,output_raster):
    #裁剪
    input_raster = gdal.Open(input_raster)
    # 开始裁剪
    result = gdal.Warp(output_raster,
              input_raster,
              format='GTiff',
              cutlineDSName=input_shape,
              cropToCutline=True)
    result.FlushCache()
    del result

gdal.Warp主要参数如下:

gdal.Warp(options = [], format = 'GTiff', outputBounds = None, 
                 outputBoundsSRS = one, xRes = None, yRes = None, 
                 targetAlignedPixels = False, width = 0, height = 0, srcSRS = None,
                 dstSRS = None, srcAlpha = False, dstAlpha = False, warpOptions = None,
                 errorThreshold = None, warpMemoryLimit = None, creationOptions = None,
                 outputType = GDT_Unknown, workingType = GDT_Unknown, resampleAlg = None,
                 srcNodata = None, dstNodata = None, multithread = False, tps = False, 
                 rpc = False, geoloc = False, polynomialOrder = None, 
                 transformerOptions = None, cutlineDSName = None, cutlineLayer = None,
                 cutlineWhere = None, cutlineSQL = None, cutlineBlend = None, 
                 ropToCutline = False, copyMetadata = True, metadataConflictValue = None,
                 setColorInterpretation = False, callback = None, callback_data = None):
其中:
options — 可以是一个字符串数组,一个字符串或者令其为空值,但是使用后面其他的参数来定义。
format — 输出的格式 (例如"GTiff"等)。
outputBounds — 在目标空间参考系统的输出数据集的范围,形式为 (minX,minY, maxX, maxY) 。
outputBoundsSRS — 如果在dstSRS中没有定义的话,使用这个关键字定义输出数据集的边界的空间参考系统。
xRes, yRes — 在目标参考系统中的像元大小。
targetAlignedPixels —是否强制输出边界为输出分辨率的倍数。
width — 输出栅格的像素列数。
height — 输出栅格的像素行数。
srcSRS —源空间参考系统。
dstSRS — 输出空间参考系统。
srcAlpha — 是否强制将输入数据集的最后一个波段作为alpha波段。
dstAlpha — 是否强制创建一个输出数据集的alpha波段。
outputType — 输出类型 (例如gdal.GDT_Byte等)
workingType — working type (gdal.GDT_Byte, etc…)
warpOptions —变形选项列表。
errorThreshold --近似转换的误差阈值(用像素表示) 。
warpMemoryLimit — 工作缓存大小,单位是bytes。
resampleAlg — 重采样模式。
creationOptions — 创建选项列表。
srcNodata — 源数据的nodata值。
dstNodata — 输出数据的nodata值。
multithread — 是否多线程计算和输入输出操作。
tps— 是否使用Thin Plate Spline GCP 转换器。
rpc— 是否使用RPC转换器。
geoloc — 是否使用GeoLocation数组转换器。
polynomialOrder — 多项式GCP插值的阶数。
transformerOptions — 转换参数
cutlineDSName — 剪切线数据集名称。这里的剪切线是指对影像进行剪切的时候所使用的矢量图层。
cutlineLayer — 剪切线图层名称。
cutlineWhere — 剪切线的WHERE语句。
cutlineSQL — 剪切线的SQL 语句。
cutlineBlend — 以像素为单位的剪切线混合距离。
cropToCutline — 是否使用剪切线的extent作为输出的界线。
copyMetadata — 是否拷贝源数据的元数据。
metadataConflictValue — 元数据冲突值。
setColorInterpretation — 是否强制将输入波段的颜色解释赋予输出波段。
callback — 回调函数。
callback_data — 回调函数数据

cropToCutline为True时,使用剪切线的extent作为输出的界线;dstNodata='NULL’时,可将背景值设置为空。此方法非常好用,强烈推荐。
注意cropToCutline默认为False,默认界限为输入矢量图的界限,使用默认设置会导致裁剪后的图片非常大(见下图设置不同的dstNodata后的文件大小)。另外还可以参考这篇文章的说明:python批量遥感影像裁剪的三种方法
在这里插入图片描述
在这里插入图片描述

二、使用PIL结合GDAL

基本原理可以参考:GDAL和 Pillow 的互操作
网上公开的代码是通过PIL与GDAL栅格数据互操作,将栅格数据转换为PIL中的对象,使用PyShp读取矢量边界,裁剪PIL,最后通过数组,转回栅格数据。以下是完整代码

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
@File    :   k_factor.py
@Time    :   2022/09/26 08:45:09
@Author  :   Junlai
@Version :   1.0
@Desc    :   None
'''
import os,shapefile
from osgeo import gdal, gdal_array, ogr
from PIL import Image, ImageDraw

def image2Array(i):
    """
    将一个Python图像库的数组转换为一个gdal_array图片
    """
    a = gdal_array.numpy.frombuffer(i.tobytes(), 'b')
    a.shape = i.im.size[1], i.im.size[0]
    return a

def world2Pixel(geoMatrix, x, y):
    """
    使用GDAL库的geomatrix对象((gdal.GetGeoTransform()))计算地理坐标的像素位置
    """
    ulx = geoMatrix[0]
    uly = geoMatrix[3]
    xDist = geoMatrix[1]
    yDist = geoMatrix[5]
    rtnX = geoMatrix[2]
    rtnY = geoMatrix[4]
    pixel = int((x - ulx) / xDist)
    line = int((uly - y) / abs(yDist))
    return (pixel, line)

def extractFromShp(input_raster,input_shape,output_raster):
    # 用于裁剪的栅格数据
    raster = input_raster
    # 裁剪后的栅格数据
    output = output_raster
    # 将数据源作为gdal_array载入
    srcArray = gdal_array.LoadFile(raster)
    # 同时载入gdal库的图片从而获取geotransform
    srcImage = gdal.Open(raster)
    geoTrans = srcImage.GetGeoTransform()
    geoProj = srcImage.GetProjection()
    # # 使用PyShp库打开shp文件
    shp = input_shape
    r = shapefile.Reader("{}.shp".format(shp))
    # # 将图层扩展转换为图片像素坐标
    minX, minY, maxX, maxY = r.bbox
    ulX, ulY = world2Pixel(geoTrans, minX, maxY)
    lrX, lrY = world2Pixel(geoTrans, maxX, minY)
    # 计算新图片的尺寸
    pxWidth = int(lrX - ulX)
    pxHeight = int(lrY - ulY)
    clip = srcArray[ulY:lrY, ulX:lrX]
    # 为图片创建一个新的geomatrix对象以便附加地理参照数据
    geoTrans = list(geoTrans)
    geoTrans[0] = minX
    geoTrans[3] = maxY
    # 在一个空白的8字节黑白掩膜图片上把点映射为像元
    # 边界线
    pixels = []
    for p in r.shape(0).points:
        pixels.append(world2Pixel(geoTrans, p[0], p[1]))
    rasterPoly = Image.new("L", (pxWidth, pxHeight), 1)
    # 使用PIL创建一个空白图片用于绘制多边形
    rasterize = ImageDraw.Draw(rasterPoly)
    rasterize.polygon(pixels, 0)
    # 使用PIL图片转换为Numpy掩膜数组
    mask = image2Array(rasterPoly)
    # 根据掩膜图层对图像进行裁剪
    clip = gdal_array.numpy.choose(mask, (clip, 0)).astype(gdal_array.numpy.uint8)
    print(clip.max())
    # 保存为tiff文件
     gdal_array.SaveArray(clip, "{}.tif".format(output),format="GTiff")

if __name__ == '__main__':
    pj_path = os.path.abspath(
        os.path.dirname(os.path.abspath(os.path.dirname(__file__))))
    fpath = os.path.join(pj_path, 'data')
    input_shape = f'{fpath}\\boundary\\input'
    output_raster = f'{fpath}\\out'
    # tif输入路径,打开文件
    input_raster = f'{fpath}\\lu\\input.tif'
    # 矢量裁剪栅格
    extractFromShp(input_raster,input_shape,output_raster)

该方法存在一个小bug,当矢量文件属性表存在中文,会出现以下报错,
在这里插入图片描述
解决方法可以简单粗暴,直接修改PyShp源码:
self.encoding = kwargs.pop('encoding', 'utf-8')修改为self.encoding = kwargs.pop('encoding', 'gbk')
笔者尝试直接用ogr替代PyShp,完成上述操作,完全可行,结果一致,在此不表,感兴趣的可以自行尝试。

三、使用ArcTools裁剪

过于简单粗暴,在此不表

四、BUG

有个小的BUG,暂时还没摸清楚原因,MARK一下:
使用GDAL结合PIL裁剪后的栅格图,会比ArcTool少一行,GDAL.WRAP裁剪时会少两行一列。

  • 使用ArcTool裁剪
    在这里插入图片描述
  • 使用PIL结合GDAL裁剪时
    在这里插入图片描述
  • 使用GDAL.WRAP裁剪时
    在这里插入图片描述
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

KmBase

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值