GDAL关于读写图像的简明总结
读写影像可以说是图像处理最基础的一步。关于使用GDAL读写影像,平时也在网上查了很多资料,就想结合自己的使用心得,做做简单的总结。
在这里写一个例子:裁剪lena图像的某部分内容,将其放入到新创建的.tif文。以此来说明GDAL读写影像的具体实现。

1.打开图像
用GDAL打开lena.bmp,实现如下。注意这里打开图像,指的是获取图像的头文件,以此得到图像的一些信息,没有涉及到读取像素操作。
GDALAllRegister(); //GDAL所有操作都需要先注册格式
CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO"); //支持中文路径
const char* imgPath = "E:\\Data\\lena.bmp";
GDALDataset* img = (GDALDataset *)GDALOpen(imgPath, GA_ReadOnly);
if (img == nullptr)
{
cout << "Can't Open Image!" << endl;
return 1;
}
图像需要关注的信息很多,可以重点关注以下四个值。图像宽、高总所周知了,而波段数就是通道,如RGB图像的波段数为3。深度标识的就是图像的存储单位,比如一般图像就是8位,用无字节字符型unsigned char来表达0~255的像素值;而除以8标识1个字节,方便读取像素buf。
int imgWidth = img->GetRasterXSize(); //图像宽度
int imgHeight = img->GetRasterYSize(); //图像高度
int bandNum = img->GetRasterCount(); //波段数
int depth = GDALGetDataTypeSize(img->GetRasterBand(1)->GetRasterDataType()) / 8; //图像深度
如果已经读取完毕或者不需要这张图像的相关操作了,最后要关闭打开的文件,否则会内存泄漏。
GDALClose(img);
2.创建图像
用GDAL创建一个新的图像,例如这里创建了一个256X256大小,被读取图像波段,深度8位的tif。
GDALDriver *pDriver = GetGDALDriverManager()->GetDriverByName("GTIFF"); //图像驱动
char** ppszOptions = NULL;
ppszOptions = CSLSetNameValue(ppszOptions, "BIGTIFF", "IF_NEEDED"); //配置图像信息
const char* dstPath = "E:\\Data\\dst.tif";
int bufWidth = 256;
int bufHeight = 256;
GDALDataset* dst = pDriver->Create(dstPath, bufWidth, bufHeight, bandNum, GDT_Byte, ppszOptions);
if (dst == nullptr)
{
printf("Can't Write Image!");
return false;
}
需要注意的是创建图像可能需要一些特别的设置信息,是需要到GDAL对应格式的文档中去查看的,也可以什么都不设置用默认值。我这里设置的是如果需要的话,就创建支持大小超过4G的bigtiff。
如果已经写入完毕或者不需要这张图像的相关操作了,最后一定要注意关闭关闭打开的文件,之前只会内存泄漏,而这里还会可能创建失败。
GDALClose(dst);
如果创建后什么都不做,关闭后GDAL会自动写入0像素值,打开后就是纯黑色图像。

3.图像读写
GDAL读写图像是通过RasterIO()这个函数实现的,这个函数提供了非常强大的功能,目前笔者也只总结了这以下方面的内容。
3.1.一般情况下读写
GDAL读取图像是以左上角为起点的,读取起点位置开始的256X256的内容,写入dst.tif中的实现如下:
//申请buf
size_t imgBufNum = (size_t) bufWidth * bufHeight * bandNum * depth;
GByte *imgBuf = new GByte[imgBufNum];
//读取
img->RasterIO(GF_Read, 0, 0, bufWidth, bufHeight, imgBuf, bufWidth, bufHeight,
GDT_Byte, bandNum, nullptr, bandNum*depth, bufWidth*bandNum*depth, depth);
//写入
dst->RasterIO(GF_Write, 0, 0, bufWidth, bufHeight, imgBuf, bufWidth, bufHeight,
GDT_Byte, bandNum, nullptr, bandNum*depth, bufWidth*bandNum*depth, depth);
//释放
delete[] imgBuf;
imgBuf = nullptr;
逐个说明RasterIO()参数的含义:
- 参数1:读写标记。如果为GF_Read,则是将影像内容写入内存,如果为GF_Write,则是将内存中内容写入文件。
- 参数2、3:读写开始位置。相对于图像左上角顶点(从零开始)的行列偏移量。
- 参数4、5:要读写的块在x方向的象素个数和y方向的象素列数。
- 参数6:指向目标缓冲区的指针,由用户分配。
- 参数7、8:目标块在x方向上和y方向上的大小。
- 参数9:目标缓冲区的数据类型,原类型会自动转换为目标类型。
- 参数10:要处理的波段数。
- 参数11:记录要操作的波段的索引(波段索引从1开始)的数组,若为空则数组中存放的是前nBandCount个波段的索引。
- 参数12:X方向上两个相邻象素之间的字节偏移,默认为0,则列间的实际字节偏移由目标数据类型eBufType确定。
- 参数13:y方向上相邻两行之间的字节偏移, 默认为0,则行间的实际字节偏移为eBufType * nBufXSize。
- 参数14:相邻两波段之间的字节偏移,默认为0,则意味着波段是顺序结构的,其间字节偏移为nLineSpace * nBufYSize。
有的参数推荐使用上面的标准写法而不是采用默认值0,可以更好地理解图像buf的存放排布。最后得到的dst.tif如下:

3.2.16位影像读写
上述RasterIO()的写法可以兼容16为图像的读写,只不过要注意的是buf中是用2个Gbyte来表达1个16像素值的。当然为了更方便图像处理,也可以采用16位整型来读取buf:
//申请buf
size_t imgBufNum = (size_t)bufWidth * bufHeight * bandNum;
GUInt16 *imgBuf = new GUInt16[imgBufNum];
//读取
img->RasterIO(GF_Read, 0, 0, bufWidth, bufHeight, imgBuf, bufWidth, bufHeight,
GDT_UInt16, bandNum, nullptr, bandNum*depth, bufWidth*bandNum*depth, depth);
//写入
dst->RasterIO(GF_Write, 0, 0, bufWidth, bufHeight, imgBuf, bufWidth, bufHeight,
GDT_UInt16, bandNum, nullptr, bandNum*depth, bufWidth*bandNum*depth, depth);
//释放
delete[] imgBuf;
imgBuf = nullptr;
可以发现,除了要更改buf的容量和RasterIO()的第九个参数GDT_UInt16,其余什么都不需要更改。注意创建16位图像时参数也需要更改成16位:
GDALDataset* dst = pDriver->Create(dstPath, bufWidth, bufHeight, bandNum, GDT_UInt16, ppszOptions);
3.3.读取特定波段
某些情况下需要读取特定波段,或者需要重组波段顺序。例如VC中显示图像往往需要将buf按照BGR传递给BITMAP,再显示BITMAP。这时只需要修改第11个参数就行了:
//波段索引
int panBandMap[3] = { 3,2,1 };
//申请buf
size_t imgBufNum = (size_t) bufWidth * bufHeight * bandNum * depth;
GByte *imgBuf = new GByte[imgBufNum];
//读取
img->RasterIO(GF_Read, 0, 0, bufWidth, bufHeight, imgBuf, bufWidth, bufHeight,
GDT_Byte, bandNum, panBandMap, bandNum*depth, bufWidth*bandNum*depth, depth);
//写入
dst->RasterIO(GF_Write, 0, 0, bufWidth, bufHeight, imgBuf, bufWidth, bufHeight,
GDT_Byte, bandNum, nullptr, bandNum*depth, bufWidth*bandNum*depth, depth);
//释放
delete[] imgBuf;
imgBuf = nullptr;
这时得到的dst.tif为:

3.4.左下角起点读写
默认情况RasterIO()是以左上角起点读写的,不过也是可以以左下角为起点读写,只需要重新设置排布buf的位置。这里读写lena图像上同一块位置:
//申请buf
size_t imgBufNum = (size_t) bufWidth * bufHeight * bandNum * depth;
size_t imgBufOffset = (size_t) bufWidth * (bufHeight-1) * bandNum * depth;
GByte *imgBuf = new GByte[imgBufNum];
//读取
img->RasterIO(GF_Read, 0, 0, bufWidth, bufHeight, imgBuf + imgBufOffset, bufWidth, bufHeight,
GDT_Byte, bandNum, nullptr, bandNum*depth, -bufWidth*bandNum*depth, depth);
//写入
dst->RasterIO(GF_Write, 0, 0, bufWidth, bufHeight, imgBuf + imgBufOffset, bufWidth, bufHeight,
GDT_Byte, bandNum, nullptr, bandNum*depth, -bufWidth*bandNum*depth, depth);
//释放
delete[] imgBuf;
imgBuf = nullptr;
注意这里Y方向起点位置,也就是第三个参数仍然要用左上角起算,但是buf已经是左下角起点了。
3.5.重采样读写
RasterIO()另外一个用法是可以自动缩放,重采样读写影像,例如这里将512X512大小的lena图像重采样成256X256大小:
//申请buf
size_t imgBufNum = (size_t) bufWidth * bufHeight * bandNum * depth;
size_t imgBufOffset = (size_t) bufWidth * (bufHeight-1) * bandNum * depth;
GByte *imgBuf = new GByte[imgBufNum];
//读取
img->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, imgBuf + imgBufOffset, bufWidth, bufHeight,
GDT_Byte, bandNum, nullptr, bandNum*depth, -bufWidth*bandNum*depth, depth);
//写入
dst->RasterIO(GF_Write, 0, 0, bufWidth, bufHeight, imgBuf + imgBufOffset, bufWidth, bufHeight,
GDT_Byte, bandNum, nullptr, bandNum*depth, -bufWidth*bandNum*depth, depth);
//释放
delete[] imgBuf;
imgBuf = nullptr;
可以看到重采样读写只需要修改参数4,参数5就行了。查阅网上资料得知,RasterIO()重采样方式默认是最临近的方法,只有建立金字塔时可以设置重采样方式,但也仅限于缩小。最后得到的dst.tif结果:

GDAL功能非常丰富,本文仅仅做了一点关于图像读写的总结,自认为算的上“简明”了。当然也希望大家批评指正。
GDAL关于读写图像的简明总结的更多相关文章
- gdal读写图像分块处理
转自赵文原文 gdal读写图像分块处理(精华版) Review: 用gdal,感觉还不如直接用C++底层函数对遥感数据进行处理.因为gdal进行太多封装,如果你仅仅只是Geotif等格式进行处理,IO ...
- Java 读写图像
Java中进行图像I/O(即读图片和写图片,不涉及到复杂图像处理)有三个方法:1. Java Image I/O API,支持常见图片,从Java 2 version 1.4.0开始就内置了.主页:h ...
- 使用GDAL/OGR读写矢量文件
感觉GIS中矢量相关内容还是挺庞杂的,并且由于版本迭代的关系,使用GDAL/OGR读写矢量的资料也有点不太一样.这里总结了一个读写矢量的示例,实现代码如下: #include <iostream ...
- gdal读写图像分块处理(精华版)
一.gdal进行数据操作在安装好gdal后,即可调用gdal库中的函数.(需要包含的头文件:gdal_priv.h)1.打开数据集使用gdal库进行数据(影像)操作的第一步就是打开一个数据集.对于“数 ...
- 使用C#版本GDAL读取复数图像
GDAL的C#版本虽然在很多算法接口没有导出,但是在读写数据中的接口基本上都是完全导出了.使用ReadRaster和WriteRaster方法来进行读写,同时对这两个方法进行了重载,对于常用的数据类型 ...
- GDAL库——读取图像并提取基本信息
GDAL库是一个跨平台的栅格地理数据格式库,包括读取.写入.转换.处理各种栅格数据格式(有些特定的格式对一些操作如写入等不支持).它使用了一个单一的抽象数据模型就支持了大多数的栅格数据.这里有GDAL ...
- GDAL切割重采样遥感图像
一个小测试程序开发全过程实录,完全新手入门级的实例,如果你还在为处理大影像而发愁,来试试这个称手的工具吧. Imagec 开发日记 2013-6-25 需求: 影像数据切割,重采样 数据切割的要求是简 ...
- ArcEngine和GDAL读写栅格数据机制对比(一)
最近应用AE开发插值和栅格转等值线的程序,涉及到栅格读写的有关内容.联想到ArcGIS利用了GDAL的某些东西,从AE的OMD中也发现RasterDataset和RasterBand这些命名和GDAL ...
- python中的Matplot库和Gdal库绘制富士山三维地形图-参考了虾神的喜马拉雅山
首先请大家读一下面这篇文章了解什么是Gdal http://blog.csdn.net/grllery/article/details/77822595 剩下的我要公布绘制富士山的代码了,虽然基本co ...
随机推荐
- 12个新潮的 HTML5 & CSS3 网站设计欣赏
响应式设计和基于 HTML5 & CSS3 编码的网站是为网站制作的理想解决方案. HTML5 & CSS3 制作出来的网站结构良好,有很多惊人的效果,并能够在任何设备上浏览. 今天, ...
- 前端开发教程:使用 CSS3 Transforms 构建圆形导航
在本教程中我将告诉你如何使用 CSS 变换来创建圆形导航.教程逐一讲解实现这个样式将要涉及一些基本的数学知识并配合 CSS 变换来创建这些样式.不过不用担心,这里用到的数学知识真的是很简单的.教程使用 ...
- nodejs 调试 node-inspector包
nodejs 调试调试比较麻烦,让习惯了用chrome浏览器调试的前端同学来说有点不适用 node-inspector这个包让我们可以在chrome上像调试前端代码一样来调试nodejs 1.全局 ...
- JDK8 的 Lambda 表达式原理
JDK8 使用一行 Lambda 表达式可以代替先前用匿名类五六行代码所做的事情,那么它是怎么实现的呢?从所周知,匿名类会在编译的时候生成与宿主类带上 $1, $2 的类文件,如写在 TestLamb ...
- Azure ARM (5) ARM Template初探 - 本地JSON Template文件(1)
<Windows Azure Platform 系列文章目录> Azure ARM (1) 概览 Azure ARM (2) 概览 Azure ARM (3) ...
- installshield生成时提示6003错误的一种可能
因为项目需要,2014年写过的一个老项目需要重新打包.开发电脑换了,原来开发的系统是win7,现在已经升到了win10.而且原来使用installshield limited 2013开发的打包项目已 ...
- Coreseek + Sphinx + Mysql + PHP构建中文检索引擎
首先明确几个概念 Sphinx是开源的搜索引擎,它支持英文的全文检索.所以如果单独搭建Sphinx,你就已经可以使用全文索引了.但是往往我们要求的是中文索引,怎么做呢?国人提供了一个可供企业使用的,基 ...
- 网络基础:NetBIOS
网络基础小补. 利用 NetBIOS 名称与其他计算机通信 网络中的计算机之间必须知道IP地址后才能相互通信.但对人来说IP难以记忆,NetBIOS计算机名称比较容易记忆.当计算机使用 NetBIOS ...
- Topcoder SRM 627 div1 HappyLettersDiv1 : 字符串
Problem Statement The Happy Letter game is played as follows: At the beginning, several players ...
- CodeSnippet.info 开源说明 和 环境搭建 (第一版)
Github开源声明 本网站的代码开源,开源的目的如下 技术分享 希望业内同行贡献代码 希望能够让网站更加安全 开源地址: CodeSnippet开源地址 关于代码贡献 任何人都可以贡献代码,一般在 ...