一、简介

本文原文地址:http://www.gdal.org/warptut.html

GDAL Warp API(在文件gdalwarper.h中定义)是一个高效的进行图像变换的接口。主要由几何变换函数(GDALTransformerFunc),多种图像重采样方式,掩码操作选项等组成。这个接口可以对很大的图像进行处理。

下面说明示例让你如何在程序中使用变换API。首先假定你已经熟悉了GDAL的抽象数据模型,以及GDAL的API。

在程序中,首先要初始化一个GDALWarpOptions 结构体的对象,然后使用GDALWarpOptions 的对象来初始化GDALWarpOperation 的对象,最后通过调用GDALWarpKernel 类里面的GDALWarpOperation::ChunkAndWarpImage() 函数来完成图像的变换。

二、简单的影像重投影示例

首先我们以一个图像重投影的例子来入手,需要注意的是,这里假设输出结果文件已经存在,同时这里没有对错误信息进行检查,仅仅演示的最正常的处理流程。

#include "gdalwarper.h"
int main()
{GDALDatasetH  hSrcDS, hDstDS;// 打开输入输出图像GDALAllRegister();hSrcDS = GDALOpen("in.tif", GA_ReadOnly );hDstDS = GDALOpen("out.tif", GA_Update );// 建立变换选项GDALWarpOptions*psWarpOptions = GDALCreateWarpOptions();psWarpOptions->hSrcDS =hSrcDS;psWarpOptions->hDstDS =hDstDS;psWarpOptions->nBandCount = 1;psWarpOptions->panSrcBands =(int *) CPLMalloc(sizeof(int) * psWarpOptions->nBandCount );psWarpOptions->panSrcBands[0] = 1;psWarpOptions->panDstBands =(int *) CPLMalloc(sizeof(int) * psWarpOptions->nBandCount );psWarpOptions->panDstBands[0] = 1;psWarpOptions->pfnProgress = GDALTermProgress;  // 创建重投影变换函数psWarpOptions->pTransformerArg =GDALCreateGenImgProjTransformer( hSrcDS,GDALGetProjectionRef(hSrcDS),hDstDS,GDALGetProjectionRef(hDstDS),FALSE,0.0, 1 );psWarpOptions->pfnTransformer = GDALGenImgProjTransform;// 初始化并且执行变换操作GDALWarpOperationoOperation;oOperation.Initialize(psWarpOptions );oOperation.ChunkAndWarpImage( 0, 0,GDALGetRasterXSize( hDstDS),GDALGetRasterYSize( hDstDS) );GDALDestroyGenImgProjTransformer(psWarpOptions->pTransformerArg );GDALDestroyWarpOptions( psWarpOptions );GDALClose( hDstDS );GDALClose( hSrcDS );return 0;
}

这个例子中首先打开已经存在的输入文件和输出文件(in.tif和out.tif)。使用GDALCreateWarpOptions()函数创建一个GDALWarpOptions结构体对象(结构体中的参数会指定一个比较合理的默认值),然后对这个结构体对象设置输入输出文件的句柄(就是文件指针)和要处理的波段。需要注意的是panSrcBands和panDstBands数组需要在外面动态申请,然后在调用GDALDestroyWarpOptions()函数的时候会自动释放,所以在后面就不需要我们对这两个数组进行释放了。GDALTermProgress是一个简单的控制台进度信息函数,用来显示处理进度。

GDALCreateGenImgProjTransformer()函数是用来创建一个从原始图像到结果图像的投影变换参数。我们假设这两个图像都有合适的四至范围和坐标系统,不使用GCP点。

一旦GDALWarpOptions结构体创建好了,可以使用这个GDALWarpOptions对象来初始化一个GDALWarpOperation的对象,然后再调用函数GDALWarpOperation::ChunkAndWarpImage()进行转换。在转换完成之后,转换选项,数据集等都需要进行释放。

通常应该在打开图像之后进行一系列的检查,然后在建立投影变换参数是要对返回的参数进行检查(返回值为NULL表示失败),最后还要对建立的变换操作进行检查。上面的例子为了方便,没有对这些信息进行检查,在我们自己的程序中需要对这些进行检查。

三、其他变换选项

GDALWarpOptions结构体中包含了很多参数来对变换进行设置。下面对一些比较重要的进行列举说明:

1.  GDALWarpOptions::dfWarpMemoryLimit:设置GDALWarpOperation在处理图像中使用的最大内存数。单位为比特,默认值比较保守(比较小),可以根据自己的内存大小来进行调整这个值。增加这个值可以帮助提高程序的运行效率,但是需要注意内存的大小。这个大小、GDAL的缓存大小,还有你的应用程序以及系统所需要的内存加起来要小于系统的内存,否则会导致错误。一般来说,比如一个内存为256MB的系统,这个值最少设置为64MB比较合理。注意,这个值不包括GDAL读取数据使用的内存。

2.  GDALWarpOpations::eResampleAlg:重采样方式,可用的值有 GRA_NearestNeighbour (最邻近采样,默认值,处理速度最快)、GRA_Bilinear(2x2双线性内插采样)和 GRA_Cubic(三次立方卷积采样)。GRA_NearestNeighbour采样方式一般用在专题图或者彩色图像中,其他的重采样方式效果比较好,尤其是在计算中改变图像分辨率的算法中。

3.  GDALWarpOptions::padfSrcNoDataReal:这个数组(每个波段一个值),可以用来指定输入图像波段的NODATA值,比如图像的背景值,对于这样的值,算法不会参与运算,直接将这个值复制到结果图像中。

4.  GDALWarpOptions::papszWarpOptions:这个是一个字符串列表,用来设置图像变换过程中的一些选项,样子为NAME=VALUE。更多详细的内容可以参考 GDALWarpOptions::papszWarpOptions的文档,里面含有全部的选项,支持的值包括:

  • INIT_DEST=[value]或者INIT_DEST=NO_DATA:这个选项用来强制设置结果图像的初始值(所有的波段),初始值为指定的value,或者NODATA值。NODATA值从参数padfDstNoDataReal或者参数padfDstNoDataImag中获取。如果这个值没有设置,那么将会使用原始图像的NODATA值来覆盖。
  • WRITE_FLUSH=YES/NO:这个选项用来强制设置在处理完每一块后将数据写入磁盘中。在某些时候,这个选项可以更加安全的写入结果数据,但是同时会增加更多的磁盘操作。目前这个默认值为NO。

四、创建输出文件

在前面的例子中,结果图像是已经存在的。选择我们将通过预测输出文件的范围和坐标系统来创建新的图像。这个操作不是图像变换的特殊API,这个仅仅是变换的一个API。

#include "gdalwarper.h"
#include"ogr_spatialref.h"...GDALDriverH hDriver;GDALDataType eDT;GDALDatasetH hDstDS;GDALDatasetH hSrcDS;// 打开源文件hSrcDS = GDALOpen("in.tif", GA_ReadOnly );CPLAssert( hSrcDS != NULL );// 创建输出图像的数据类型和输入图像第一个波段类型一致eDT = GDALGetRasterDataType(GDALGetRasterBand(hSrcDS,1));// 获取输出图像的驱动(GeoTIFF格式)hDriver = GDALGetDriverByName("GTiff" );CPLAssert( hDriver != NULL );// 获取源图像的坐标系统const char *pszSrcWKT, *pszDstWKT = NULL;pszSrcWKT = GDALGetProjectionRef( hSrcDS);CPLAssert( pszSrcWKT != NULL &&strlen(pszSrcWKT) > 0 );// 设置输出图像的坐标系统为UTM 11 WGS84OGRSpatialReference oSRS;oSRS.SetUTM( 11, TRUE );oSRS.SetWellKnownGeogCS( "WGS84");oSRS.exportToWkt( &pszDstWKT );// 创建一个变换参数,从源图像的行列号坐标到结果图像的地理坐标(没有//结果行列)的句柄,初始值设置为NULL。void *hTransformArg;hTransformArg =GDALCreateGenImgProjTransformer( hSrcDS,pszSrcWKT, NULL, pszDstWKT,FALSE,0, 1 );CPLAssert( hTransformArg != NULL );// 获取输出文件的近似地理范围和分辨率double adfDstGeoTransform[6];int nPixels=0, nLines=0;CPLErr eErr;eErr = GDALSuggestedWarpOutput( hSrcDS,GDALGenImgProjTransform, hTransformArg,adfDstGeoTransform, &nPixels, &nLines );CPLAssert( eErr == CE_None );GDALDestroyGenImgProjTransformer(hTransformArg );// 创建输出文件hDstDS = GDALCreate(hDriver, "out.tif", nPixels, nLines,GDALGetRasterCount(hSrcDS),eDT, NULL );CPLAssert( hDstDS != NULL );// 写入投影GDALSetProjection( hDstDS,pszDstWKT );GDALSetGeoTransform( hDstDS,adfDstGeoTransform );// 复制颜色表,如果有的话GDALColorTableH hCT;hCT = GDALGetRasterColorTable( GDALGetRasterBand(hSrcDS,1));if( hCT != NULL )GDALSetRasterColorTable( GDALGetRasterBand(hDstDS,1),hCT );... 变换处理之前做的工作...

这里需要注意的一些逻辑关系:

  • 我们需要创建的输出坐标是使用地理坐标表示的,而不是行列号坐标。同时输出的坐标是在结果坐标系统之下的坐标,不是原始坐标系统下的。
  • GDALSuggestedWarpOutput()函数返回值adfDstGeoTransform、nPixels 和nLines这三个值是描述输出图像的大小和四至范围,这个四至范围包含了源图像的所有的像素点。里面的分辨率是根据原始图像估算出来的,输出图像的分辨率一致是横向和纵向保持一致,不受输入图像限制。
  • 变化要求输出文件格式是可以“随机”写入的。一般使用Create()(不能用CreateCopy()函数)函数创建非压缩的图像格式。如果要创建压缩的图像格式,或者使用CreateCopy()函数创建的话,系统内部会创建一个临时文件,然后使用CreateCopy()函数写入到结果图像中。
  • 变换API仅仅处理图像的象元值。所有的颜色表,地理参考以及其他元数据信息必须使用程序自行设置到结果图像中去,变换是不会设置这些信息的。

五、性能优化

下面几点可以在使用变换API的时候提高处理效率。

  1. 增加变换API使用的内存,可以使程序执行的更快。这个参数叫GDALWarpOptions::dfWarpMemoryLimit。理论上,越大的块对于数据I/O效率越高,并且变换的效率也会越高。然而,变换所需的内存和GDAL缓存应该小于机器的内存,最多是内存的2/3左右。
  2. 增加GDAL的缓存。这个尤其对于在处理非常大的输入图像很有用。如果所有的输入输出图像的数据频繁的读取会极大的降低运行效率。使用函数GDALSetCacheMax()来设置GDAL使用的最大缓存。
  3. 使用近似的变换代替精确的变换(精确的变换是每个象元都会计算一次)。下面的代码用来说明近似变换的使用方式,近似变换要求指定一个变换的误差dfErrorThreshold,这个误差单位是输出图像的象元个数。
  4. hTransformArg =GDALCreateApproxTransformer(GDALGenImgProjTransform,
    hGenImgProjArg, dfErrorThreshold );
    pfnTransformer = GDALApproxTransform;
    
  5. 当写入一个空的输出文件,在GDALWarpOptions::papszWarpOptions 对象中使用INIT_DEST选项来进行设置。这样可以很大的减少磁盘的IO操作。
  6. 输入和输出文件使用分块存储的文件格式。分块文件格式可以快速的访问一块数据,相比来说,普通的大数据量的顺序存储文件格式在IO操作中要花费更多的时间。
  7. 在一次调用中处理所有的波段。一次处理所有的波段比每次处理一个波段要保险。
  8. 使用GDALWarpOperation::ChunkAndWarpMulti()方法来代替GDALWarpOperation::ChunkAndWarpImage()方法。这个使用多个独立的线程来进行IO操作和变换影像处理,能够提高CPU和IO的效率。执行这个操作,GDAL需要多线程的支持(从GDAL1.8.0开始,默认在Win32平台、Unix平台是支持的,对于之前的版本,需要在配置中进行修改才行)。
  9. 重采样方式,最邻近象元执行速度最快,双线性内插次之,三次立方卷次最慢。不要使用根据复杂的重采样函数。
  10. 避免使用复杂的掩码选项。比如,最邻近采样在处理没有掩码的8bit数据要比一般的效率高很多。

六、关于掩码选项

GDALWarpOptions包含了一些处理复杂的掩码的能力,比如掩码的有效性,对输入和输出数据的掩码。这些功能还没有做足够的测试。其他每个波段的有效的掩码在使用的时候要小心。

GDAL源码剖析(十二)之GDAL Warp API使用说明相关推荐

  1. GDAL源码剖析(二)之编译说明

    一.简单的编译 1.使用VisualStudio IDE编译 首先进入GDAL的源代码目录,可以看到有几个sln为后缀的文件名,比如makegdal10.sln,makegdal80.sln,make ...

  2. grpc-go源码剖析十二之平衡器基本原理介绍

      本小节主要介绍平衡器的相关原理: 例如:   如何实现一个平衡器?   知道如何实现平衡器后,那么如何将平衡器注册到grpc框架里呢?   将平衡器注册到grpc框架内部后,grpc框架是如何创建 ...

  3. 《GDAL源码剖析与开发指南》导读

    前言 GDAL源码剖析与开发指南 GDAL全称是Geospatial Data Abstraction Library(地理空间数据抽象库),是一个在X/MIT许可协议下读写空间数据(包括栅格数据和矢 ...

  4. GDAL源码剖析(四)之命令行程序说明二

    接博客GDAL源码剖析(四)之命令行程序说明一http://blog.csdn.net/liminlu0314/article/details/6978589 其中有个nearblack,gdalbu ...

  5. GDAL源码剖析(一)

    前言:一直在使用和研究GDAL的相关东西,发现网上对GDAL的内容倒是不少,但是很少有系统的介绍说明,以及内部的一些结构说明,基于这些原因,将本人的一些粗浅的理解放在此处,形成一个系列,暂时名为< ...

  6. 《GDAL源码剖析与开发指南》一一1.9 简单的调用

    本节书摘来自异步社区出版社<GDAL源码剖析与开发指南>一书中的第1章,第1.9节,作者:李民录 更多章节内容可以访问云栖社区"异步社区"公众号查看. 1.9 简单的调 ...

  7. java实现gdal栅格矢量化,《GDAL源码剖析与开发指南》一一1.5 GDAL源码目录

    本节书摘来自异步社区出版社<GDAL源码剖析与开发指南>一书中的第1章,第1.5节,作者:李民录 更多章节内容可以访问云栖社区"异步社区"公众号查看. 1.5 GDAL ...

  8. STL源码剖析学习二:空间配置器(allocator)

    STL源码剖析学习二:空间配置器(allocator) 标准接口: vlaue_type pointer const_pointer reference const_reference size_ty ...

  9. 价值4500的国际版多语言点赞抖音分享点赞任务平台源码(十二种语言)

    介绍: 平台会员分享给我的,他自己搭建成功了,测试可用!我就不测试了,需要的拿! 九种语言 :西班牙语,泰语.日语,印度尼西亚语言.越南语言.英文.繁体中文,简体中文,印度语 前台支持更换5种颜色风格 ...

  10. 【vue-router源码】十二、useRoute、useRouter、useLink源码分析

    [vue-rouer源码]系列文章 [vue-router源码]一.router.install解析 [vue-router源码]二.createWebHistory.createWebHashHis ...

最新文章

  1. Redis 为什么这么快?
  2. LINQ to Entities 不识别方法“System.String ToString()”,因此该方法无法转换为存储表达式。...
  3. [转]Apache Commons IO入门教程
  4. php账号密码备忘,WordPress使用备忘
  5. 在Centos 7中开放80端口
  6. JavaScript时间日期函数
  7. 处理Java异常的10种最佳实践
  8. 保留凸性的方式:一个凸函数在一个随机变量上的期望仍然是凸函数
  9. python画一条水平直线(matplotlib)
  10. python二分查找算法_如何使用python的二分查找算法
  11. 27 PP配置-生产车间控制-工序-定义确认参数
  12. 【C语言深入】[002] valotile 关键字:
  13. okHttp3 源码分析
  14. 吴恩达课程及视频笔记汇总
  15. 好程序员web前端分享逻辑运算
  16. bat命令 延迟执行
  17. win7 计算机 地址栏扫描,Win7系统添加地址栏的两种方法
  18. 用友NC总账辅助余额表与应收应付模块余额表对账技巧
  19. TLS完美前向保密(perfect forward secrecy)翻译
  20. 这次,大数据工程师赢了!

热门文章

  1. html表示主题内容的标签是,HTML 基本标签
  2. python vtk实时更新点云_Python-VTK:点云和颜色b
  3. 重启iis与mysql服务器吗_每晚定时重启IIS和数据库服务可节省服务器资源
  4. 问答| 为什么car-like robot运动中存在最小转弯半径?
  5. 语言可以编辑系统软件吗_你知道吗?你本来也可以精通多国语言
  6. 微信小程序API之request
  7. 倒数第N个字符串 (15 分)
  8. input 限制只能输入数字,且保留小数后两位
  9. 计算机组成原理运算器设计,计算机组成原理2_5教学计算机运算器设计.ppt
  10. Springboot邮箱接口(使用个人邮箱发送邮件)