wenzhaofei的个人博客分享 http://blog.sciencenet.cn/u/wenzhaofei

博文

如何利用python和GDAL实现影像剪切并获得剪切区域的最小范围

已有 12110 次阅读 2018-3-26 10:53 |个人分类:科研笔记|系统分类:科研笔记|关键词:学者| gdal, 最小范围, 有效值, 剪切, gdal

虽然python和GDAL配合可以很容易实现影像剪切,据我了解其对于剪切后输出范围有三个:按照原影像范围输出、按照剪切数据(如一个polygon shapefile)的范围输出和按照自定义的范围输出。这看来是比较完美的,能够满足绝大多数对输出范围的要求。但我在实际工作中遇到一种情况,貌似这三种方式不能直接或者完美的解决。先举个例子(图1):我要剪切出某一现状地物(如河流)的影像,但是这个现状地物很长,超过多幅影像的覆盖范围,另如果要处理的影像非常多,这个时候怎么办。

blob.png

                              图1. 剪切文件和被剪切影像的叠加示意。

如图1所示,如果我们直接去剪切:若按照影像范围输出,那么输出文件的大小同影像的大小(占据的磁盘空间),当影像非常多的时候,对电脑的硬件要求就很高,而且不利于后续进一步处理;若按照剪切文件的范围输出的话,那么输出影像更大,更不利后续处理;若按照自定义范围去输出的话,问题就是如何自动获取overlap部分的范围呢?


获取利用arcpy可以方便的解决,我也想啊。问题是我的python是64位的,而arcpy是32位的,不能调用,也不能重装符合要求的版本(各种原因),总之只能用64位python去实现。下面是我折腾出来的解决办法,不一定高效,但能用。


思路是分三步解决:

1、首先按照影像范围输出一个剪切文件

2、只提取该剪切文件中有效值部分,无效值(nodata,白色部分)删除,形成掩膜文件

3、将掩膜文件转换成polygon。


# -*- coding: utf-8 -*-
"""
Created on Sun Mar 25 14:21:46 2018
@author: wenzf
"""
import subprocess
from osgeo import ogr
def clip_rst(inraster, inshape, outraster, crop = 0):
    if crop == 0:
        cmd = ['gdalwarp', '-dstnodata', '0', inraster, outraster, '-cutline', inshape, '-overwrite']
    else:
        cmd = ['gdalwarp', '-dstnodata', '0', '-crop_to_cutline', inraster, outraster, '-cutline', inshape, '-overwrite']
    subprocess.call(cmd)
    
#   or:    
#    from osgeo import gdal
#    gdal.Warp(outraster,
#              inraster,
#              cutlineDSName = inshape,
#              dstNodata = 0)
    
def reclass_rst(inraster, outraster):
    exe = 'C:\Users\wenzf\Anaconda2\Lib\site-packages\osgeo\scripts\gdal_calc.py'
    cmd = ['python', exe, '-A', inraster, '--outfile='+outraster, '--calc=(A>0)', \
           '--NoDataValue=0','--overwrite']
    subprocess.call(cmd)
    
def rst2polyg(inraster, outshape):
    driver = ogr.GetDriverByName("ESRI Shapefile")
    driver.DeleteDataSource(outshape)
    exe = 'C:\Users\wenzf\Anaconda2\Scripts\gdal_polygonize.py'
    cmd = ['python', exe, inraster, '-f', 'ESRI Shapefile', outshape]
    subprocess.call(cmd)

#---- clipping raster -----
inshape = folder+'river_buffer.shp'
# ----------生成最小剪切范围 --------------
tmp = fns[0]    # fns是包含需要进行剪切的影像文件路径的数组
inraster = tmp
outraster = inraster.replace('.TIF', '_clip.TIF')
core.clip_rst(inraster, inshape, outraster)
outr_tmp = inraster.replace('.TIF', '_tmp.TIF')
core.reclass_rst(outraster, outr_tmp)
driver = gdal.GetDriverByName('GTiff')
driver.Delete(outraster)
tmp_shape = folder+f_name.replace('.tar.gz','.shp')
core.rst2polyg(outr_tmp, tmp_shape)
driver.Delete(outr_tmp)
# ----------生成最小剪切范围------------
# ----------根据最小范围剪切----------
outfns = []
for fn in fns:
    inraster = fn
    outraster = inraster.replace('.TIF', '_clip.TIF')
    outfns = np.append(outfns, outraster)
    core.clip_rst(inraster, tmp_shape, outraster, 1)


补充说明:

上面的方法也有一些缺陷,就是当得到的新shapefile存在多个feature时,就不能成功运行。对此,有两种方案来解决:一是直接获得新生成的shapefile文件的extent,然后将该extent输入gdalwarp参数中进行剪切;一是直接绕过前面所列三个步骤中的二、三步,即不通过生成新shapefile。在获得初始剪切的raster后,直接获得该raster有效值部分的最小extent,然后将该extent输入gdalwarp参数中进行剪切。

def rst_extent(inraster, nodata):
    inds = gdal.Open(inraster)
    trans = inds.GetGeoTransform()
    upl_x = trans[0]
    upl_y = trans[3]
    rsx = trans[1]
    rsy = trans[5]
    img = inds.ReadAsArray()
    msk = np.where(img != nodata)
    row_min = msk[0].min()
    col_min = msk[1].min()
    row_max = msk[0].max()
    col_max = msk[1].max()
    x_min = upl_x + (col_min+1)*rsx
    y_max = upl_y + (row_min+1)*rsy
    x_max = upl_x + (col_max+1)*rsx
    y_min = upl_y + (row_max+1)*rsy
    
    inds = img = None    
    return (x_min, y_min, x_max, y_max)
#---- clipping raster -----
# -----生成最小剪切范围 -----
inshape = folder+'river_buffer.shp'
tmp = fns[0]
inraster = tmp
outraster = inraster.replace('.TIF', '_clip.TIF')
cmd = ['gdalwarp', '-dstnodata', '0', inraster, outraster, '-cutline', inshape, '-overwrite']
subprocess.call(cmd)
min_extent = rst_extent(outraster, 0)
driver = gdal.GetDriverByName('GTiff')
driver.Delete(outraster)
# ----------生成最小剪切范围------------
# ----------根据最小范围剪切----------
outfns = []
extent = '-te '+ str(min_extent[0]) +' '+str(min_extent[1]) +' '+str(min_extent[2]) +' '+str(min_extent[3])
for fn in fns:
    inraster = fn
    outraster = inraster.replace('.TIF', '_clip.TIF')
    outfns = np.append(outfns, outraster)
    cmd = ['gdalwarp', '-dstnodata', '0', '-te', str(int(min_extent[0])), str(int(min_extent[1])), \
           str(int(min_extent[2])), str(int(min_extent[3])), '-cutline', inshape, inraster, outraster, '-overwrite']
    subprocess.call(cmd)




https://m.sciencenet.cn/blog-365459-1105761.html

上一篇:python处理Landsat系列影像的一些总结(3)
下一篇:如何让arcpy能在spyder环境下使用

0

该博文允许注册用户评论 请点击登录 评论 (0 个评论)

数据加载中...

Archiver|手机版|科学网 ( 京ICP备07017567号-12 )

GMT+8, 2024-6-3 19:28

Powered by ScienceNet.cn

Copyright © 2007- 中国科学报社

返回顶部