使用GDAL(C++库)从末尾行读取图像数据
OpenCV等图像库默认的读取方式都是从第一行开始,逐行读取数据(自顶向下),填充到内存缓冲区;对于某些特殊应用,需要反行序读取(从末尾行读到起始行)的图像数据结果。GDAL提供了灵活的栅格数据读取方式RasterIO
,下面介绍RasterIO
的调用方式,以及如何利用它自底向上读取图像数据。
关键函数RasterIO
此函数的参数列表与实现与C语言APIGDALDatasetRasterIO()
或GDALDatasetRasterIOEx()
函数基本相同。
参数意义
RasterIO提供了读写多波段的图像数据的统一接口,功能强大。其函数定义如下:
CPLErr RasterIO(GDALRWFlag eRWflag, // 取值GF_Read/GF_Write 读/写数据
int nDSXOff, // 读取区域的起始列号
int nDSYOff, // 读取区域的起始行号
int nDSXSize, // 读取区域的宽(列数)
int nDSYSize, // 读取区域的高(行数)
void* pBuffer, // 存放将读取数据的内存指针
int nBXSize, // 目标内存的宽(列数)
int nBYSize, // 目标内存的高(行数)
GDALDataType eBDataType, // 目标缓存的数据类型
int nBandCount, // 需要读取的波段数
int* panBandMap, // 存放波段序列数组的指针
GSpacing nPixelSpace, // 一个波段中的数据在pBuffer中的列间隔
GSpacing nLineSpace, // 一个波段中的数据在pBuffer中的行间隔
GSpacing nBandSpace, // 波段之间的在pBuffer中的间隔
GDALRasterIOExtraArg *psExtraArg)
参数的详细含义如下:
参数 | 含义 |
---|---|
eRWFlag | GF_Read读取数据区域,或GF_Write写入数据区域。 |
nXOff | 到要访问的频带区域左上角的像素偏移。从左侧开始为零。 |
nYOff | 到要访问的频带区域左上角的线偏移。从顶部开始,这将为零。 |
nXSize | 要访问的频带区域的宽度,以像素为单位。 |
nYSize | 要以直线方式访问的带区区域的高度。 |
pData | 应将数据读取或写入的缓冲区。此缓冲区必须至少包含eBufType类型的nBufXSizenBufYSizenBandCount字。它按从左到右、从上到下的像素顺序组织。间距由nPixelSpace和nLineSpace参数控制。 |
nBufXSize | 将要读取或从中写入所需区域的缓冲区图像的宽度。 |
nBufYSize | 将要读取或从中写入所需区域的缓冲区图像的高度。 |
eBufType | pData数据缓冲区中像素值的类型。根据需要,像素值将自动转换为GDALRasterBand数据类型/从GDALRaster Band转换为数据类型。 |
nBandCount | 正在读取或写入的频带数。 |
panBandMap | 要读/写的nBandCount个波段的编号数组指针(注意,波段编号从1开始)比如{1,2,3}表示RGB,{3,2,1}表示BGR。若设为NULL,则取前nBandCount个波段。 |
nPixelSpace | 从pData中一个像素值的开始到扫描行中下一个像素数值的开始的字节偏移量。默认数据按BSQ组织,该参数取sizeof(eBufType) 。 |
nLineSpace | 从pData中一条扫描线的开始到下一条扫描行的开始的字节偏移量。默认数据按BSQ组织,该参数取sizeof(eBufType)*nBufXSize 。 |
nBandSpace | 一个波段数据的起始处到下一个波段数据起始处的字节偏移量。默认数据按BSQ组织,该参数取nLineSpace*nBufYSize 。 |
psExtraArg | 默认为NULL。(GDAL 2.0后添加的新参数)GDALRasterIOExtraArg 类型的指针,该结构体可用于指定重采样方法和进度回调函数等详见官方文档。 |
如果指定的内存缓冲区的数据类型(eBufType)不同于原图的数据类型,则会自动完成数据类型转换。如果缓冲区大小(nBufXSize,nBufYSize)与要读写的目标区域的大小(nXSize,nYSze)不同,则该方法还会自动重采样图像。
nPixelSpace
、nLineSpace
和nBandSpace
对应的读取方式
nPixelSpace
、nLineSpace
和nBandSpace
这三个参数控制将图像数据写到内存或从内存读取的方式。对于三通道的jpg格式图像而言,t图像按BIP方式组织,每个像素的色彩值由3个byte类型数据构成.。设其行数为r,列数为c,读取时开辟的内存起始指针记作pImgData
,设置:
nPixelSpace为3,表示每3个byte是下一个像素;
nLineSpace为3*列数,即每隔3*c
个字节,就是下一行;
nBandSpace为1:RGB三个波段都是紧挨着的。
如图所示,为实现反行序读取(从末尾行读取),需要将nLineSpace参数设置为-3*c
,并且将传入RasterIO的内存缓冲区指针设置为最后一行的起始处pImgData+(r-1)*c*3
。
示例:使用RasterIO反行序(自底向上)读取图像块数据
在GDAL中,图像中的像素的坐标以左上角为原点,向右为x轴正方向,向下为y轴正方向。
以一张三通道的jpg格式图像MerchDataTest.JPG
为例,以图片中坐标(100,100)为起点,读取30列、50行的图块数据,代码示例如下:
#include"gdal_priv.h"
#include<vector>
int main(int argc, char** argv)
{
GDALAllRegister();
int nCs = 30;
int nRs = 50;
std::vector<BYTE> vData1(nCs * nRs * 3);
std::vector<BYTE> vData2(nCs * nRs * 3);
GDALDataset* pdata = (GDALDataset*)GDALOpen("MerchDataTest.JPG", GDALAccess::GA_ReadOnly);
int panBandMap[] = { 1,2,3 };
pdata->RasterIO(GDALRWFlag::GF_Read, 100, 400, nCs, nRs, &vData1[0], nCs, nRs, GDALDataType::GDT_Byte, 3, panBandMap, 3, 3 * nCs, 1);
pdata->RasterIO(GDALRWFlag::GF_Read, 100, 400, nCs, nRs, &vData2[0]+nCs*(nRs-1)*3, nCs, nRs, GDALDataType::GDT_Byte, 3, panBandMap, 3, -3 * nCs, 1);
GDALClose(pdata);
/// 将读取到的内容保存为新的tif图像
GDALDriver* pDriver = (GDALDriver*)GDALGetDriverByName("GTIFF");
GDALDataset* pOutImg1= pDriver->Create("E:\\Output1.tif",nCs,nRs,3,GDALDataType::GDT_Byte, NULL);
GDALDataset* pOutImg2= pDriver->Create("E:\\Output2.tif",nCs,nRs,3,GDALDataType::GDT_Byte, NULL);
GDALDataset* pOutImg3= pDriver->Create("E:\\Output2_row_reversed.tif",nCs,nRs,3,GDALDataType::GDT_Byte, NULL);
pOutImg1->RasterIO(GDALRWFlag::GF_Write, 0, 0, nCs, nRs, &vData1[0], nCs, nRs, GDALDataType::GDT_Byte, 3, panBandMap, 3, 3 * nCs, 1);
pOutImg2->RasterIO(GDALRWFlag::GF_Write, 0, 0, nCs, nRs, &vData2[0]+nCs*(nRs-1)*3, nCs, nRs, GDALDataType::GDT_Byte, 3, panBandMap, 3, -3 * nCs, 1);
pOutImg3->RasterIO(GDALRWFlag::GF_Write, 0, 0, nCs, nRs, &vData2[0], nCs, nRs, GDALDataType::GDT_Byte, 3, panBandMap, 3, 3 * nCs, 1);
GDALClose(pOutImg1);
GDALClose(pOutImg2);
GDALClose(pOutImg3);
}
所得三张输出图像效果如下:
Output1是按常规方式读取一块区域并保存的结果;
Output2是按反行序读取,并按反行序方式保存的结果;
Output2_row_reversed是按反行序读取、正常方式保存的结果。
可见,第二种读取方法所读数据在内存中的行序与第一种读取方法所读的是相反的。