zoukankan      html  css  js  c++  java
  • GDAL源码剖析(七)之GDAL RasterIO使用说明

    一、关于RasterIO

    在GDAL中读写图像是最基本的操作,那么RasterIO也就是最基本的函数了,关于RasterIO有很多方式,这个函数的功能相当强大,下面慢慢说明。RasterIO一共有两个,一个是GDALRasterBand::RasterIO,另一个是GDALDataset::RasterIO,这两个RasterIO都可以对图像数据来进行读写,大多数情况下是一样的,但是还是有一些区别的。

    二、RasterIO参数说明

    下面对两个RasterIO的参数进行一个简单的说明:首先是GDALRasterBand::RasterIO  ,该函数的声明如下,具体可以参考下面网址:http://gdal.org/classGDALRasterBand.html#5497e8d29e743ee9177202cb3f61c3c7

    CPLErr GDALRasterBand::RasterIO (   GDALRWFlag eRWFlag,
    int     nXOff,
    int     nYOff,
    int     nXSize,
    int     nYSize,
    void * pData,
    int     nBufXSize,
    int     nBufYSize,
    GDALDataType    eBufType,
    int     nPixelSpace,
    int     nLineSpace 
    )

    接下来是GDALDataset::RasterIO ,该函数的什么如下,具体形式可以参考下面的网址:http://gdal.org/classGDALDataset.html#e077c53268d2272eebed10b891a05743

    CPLErr GDALDataset::RasterIO    (   GDALRWFlag eRWFlag,
    int     nXOff,
    int     nYOff,
    int     nXSize,
    int     nYSize,
    void * pData,
    int     nBufXSize,
    int     nBufYSize,
    GDALDataType    eBufType,
    int     nBandCount,
    int *   panBandMap,
    int     nPixelSpace,
    int     nLineSpace,
    int     nBandSpace 
    )

    由于这两个函数的参数基本一致,大多数参数都是一样的,下面就一起进行说明:

    第一个参数RWFlag来指定是读数据还是写入数据,其值只能有两个即:GF_Read 和GF_Write,分别表示读取数据和写入数据。

    第二个和第三个参数nXOff, nYOff表示读取或者写入图像数据的起始坐标图像的左上角坐标为(0,0)。

    第四个和第五个参数 nXSize, nYSize表示读取或者写入图像数据的窗口大小,nXSize表示宽度,nYSize表示高度,均使用像素为单位,该宽度和高度是从第二个和第三个参数处开始计算。这两个参数和第二第三个参数一起表示就是,读取和写入图像的窗口位置和大小。

    第六个参数pData是指向存储数据的一个指针。如果是写入数据,那么会将pData中的数据写入到栅格图像中去;如果是读取数据,那么会将栅格数据中的数据读入到pData中。pData的真实数据类型是通过后面的eBufType参数来指定的。如GDT_Byte就是代表的是一个8U的数据类型,如果是GDT_Float32就表示的是一个32F(float)的数据类型。RasterIO会自动将读入的数据按照参数eBufType指定的数据类型进行转换,需要注意的是,将浮点数转换为整数时,将对数据进行四舍五入处理;而且从一个大的存储单位转换到一个较小的存储单位是所进行的操作是截断操作而不是按照比例缩小操作,比如原来的实际数据中float的存取数据范围超过了缓冲数据指定的数据byte类型的最大存储范围,操作将把超过byte存储的范围外的数据进行丢弃处理,而不是将float缩小到0~255。

    第七个和第八个参数nBufXSize和nBufYSize参数指定缓冲区的大小。注意pData的大小应当是nBufXSize×nBufYSize。当读取的数据是完整分辨率的数据(原始数据,没有进行缩放操作),他们应该设置和取值窗口的大小相同,也就是与第四个和第五个参数相等,但是在读取时使用了缩小或者放大系数,那么他们需要根据这个缩放系数进行调整。在这种情况下,RasterIO将会使用缩略图组overviews(金字塔)中某个合适的缩略图来进行读取数据。

    第九个和第十个参数(对于GDALDataset来说还有第十一个参数nBandSpace)nPixelSpace和nLineSpace(以及nBandSpace)参数一般情况下是将0作为缺省值。但是,他们可以用于控制存取的内存数据的排列顺序,可以使用这两个参数将图像数据按照另一种组织形式读取内存缓冲区中。也就是说这两个(三个)参数可以读取或者写入非常规组织的缓冲数据。这个首先可以用于在一个缓冲区中包含多个波段数据,并且各个数据之间是交叉排列的,比如一个图像中的数据组织是RGBRGBRGB…,而普通的数据可能是RRR…GGG…BBB…,我们一般读取到的数据就是RGBRGBRGB…这种排列,现在需要使用RRR…GGG…BBB…这样的排列,一般想法就是自己写个for循环之类的,重新组织一次,其实完全没有必要,只要设置这两个(三个)参数就可以达到这个目的。

    nPixelSpace表示的是在一个扫描行中一个像元的字节偏移起始点到下一个像元字节偏移起始点之间的字节间隔,如果默认使用0,那么将使用eBufType作为实际的两个像元之间的字节间隔。

    nLineSpace表示在一行数据和下一行数据之间的起始字节见的间隔,如果使用0,那么将会使用eBufType*nBufXSize来表示实际间隔。

    此外如果使用的GDALDataset::RasterIO函数,最后还有一个参数叫nBandSpace,同上,这个参数的意思就是一个波段与下一个波段之间的起始字节间的间隔,如果使用0,实际将使用eBufType*nBufXSize*nBufYSize来表示。

    对于GDALDataset::RasterIO函数还有两个参数nBandCount和panBandMap,分别表示要读取的波段个数和波段序号,尤其是后一个参数波段序号,可以自定义先读取那一个波段,后读取那一个。具体使用方法见下。

    三、RasterIO使用方法示例

    使用示例一,在Windsow位图数据颜色排列是BGR,但是图像存储的可能是按照RGB来存储的,一般的做法是将数据按照每个波段读出来,然后再认为的按照BGR来进行组织,其实完全可以使用后面三个参数来将读出来的数据自动按照BGR的方式组织好。只要将参数设置为:nBandCount=3;panBandMap =new int[]{3,2,1}即可。     

       int panBandMap [3]= {3,2,1};   //按照BGR BGR BGR ... 来读取数据组织
            DT_8U *pData = new DT_8U[iWidth*iHeight*3];
            poDataset ->RasterIO(GF_Read, 0, 0, iWidth,iHeight, pData,iWidth, iHeight,        (GDALDataType)iDataType, 3, panBandMap,iDataType*3, iDataType*iMthWidth*3, iDataType);

    使用示例二,实现的是将7波段图像中的第2 3 4波段按照3 42的顺序读入内存中,逐像素存储: 

      intbandmap[7];
      bandmap[0]=3;
      bandmap[1]=4;
      bandmap[2]=2;
      Scanline=(nImagSizex*8+31)/32*4;
     BYTE   pafScan=( BYTE )CPLMalloc(sizeof(byte)*nImgSizeX*nImgSizeY*3)
     poDataset->RasterIO(GF_Read, 0, 0,nImgSizeX,nImgSizeY, pafScan,
    nImgSizeX,nImgSizeY,GDT_Byte,3,bandmap,3,Scanline*3,1 );

    以这种方式读取之后,直接可构建位图进行显示。这里可以按照自己的需要进行其他方式读取。以上读取方式仅仅为了显示方便,如进行图像处理相关运算,则按波段全部读出会比较方便,即按照常规的方式读取处理:

    poDataset->RasterIO( GF_Read, 0,0,nImgSizeX,nImgSizeY, pafScan,
    nImgSizeX,nImgSizeY,GDT_Byte,bandcount,0,0,0,0);

    之前申请的内存改为:

    BYTE  pafScan=new byte[nImgSizeX*nImgSizeY*bandcount];

    将图像数据读入内存后,即可通过指针pafScan对图像进行你想要进行的操作了。

    以下内容更新于2012年5月25日

    通过上面的介绍,发现很多人还是不太理解,下面举六个例子,基本上涵盖了RasterIO的所有用法:

    1、第一种方式

    常规的读图方式,这里只读取图像的第二波段的第二行数据,代码如下:

    //获取第二波段的波段指针,参数就是表示第几波段的意思
    GDALRasterBand *pBand =poDataset->GetRasterBand(2);   
    //获取该波段的数据类型,如8U,16U等
    GDALDataType dataType =pBand->GetRasterDataType();
    //分配存储一行数据的空间,按照8U的数据类型
    DT_8U *pBuf = newDT_8U[iWidth];   
    pBand->RasterIO(GF_Read,0, 1, iWidth, 1, pBuf, iWidth, 1, GDT_Byte, 0, 0);
    delete []pBuf;
    pBuf = NULL;   

    2、第二种方式

    使用数据集读取,按照8bit的数据来进行处理,读取整幅图像,按照BGRBGR…BGR的顺序来组织,代码如下:

    DT_8U *pBuf = newDT_8U[iWidth*iWidth*iBandCount];  //分配存储空间
    int panBandMap [3]={3,2,1};   //如果想读取为RGB,那么将数据换成1,2,3
    poDataset->RasterIO(GF_Read, 0, 0, iWidth,iHeight, pBuf,iWidth, iHeight,     
                    GDT_Byte, 3, panBandMap, 3, iWidth*3, 1);
    delete []pBuf;
    pBuf = NULL;

    3、第三种方式

    读取一个矩形区域的像元,这里读取第三波段的数据,读取范围为从100行,200列开始,到400行,400列结束的一个矩形区域,代码如下:

    //获取第三波段的波段指针,参数就是表示第几波段的意思
    GDALRasterBand *pBand =poDataset->GetRasterBand(3);
    //获取该波段的数据类型,如8U,16U等
    GDALDataType dataType =pBand->GetRasterDataType();
    //分配存储数据的空间,按照8U的数据类型,大小按照矩形区域来确定
    DT_8U *pBuf = newDT_8U[200*300];  
    pBand->RasterIO(GF_Read,200, 100, 200, 300, pBuf, 200, 300, GDT_Byte, 0, 0);
    delete []pBuf;
    pBuf = NULL;

    4、第四种方式

    读取缩放区域,即读取一个区域的数据,然后把这个区域进行缩放,放大或者缩小,这个非常有用,比如在显示大图像的时候,就可以用这个方式来进行读取数据,这里依旧读取第三波段的数据,读取范围为从100行,200列开始,到400行,400列结束的一个矩形区域,按照0.5的缩放比例对区域进行缩放,所以原始图像的区域大小为200*300,缩放后就是100*150。代码如下:

    //为了举例,这里读取第三波段的数据,读取范围为从100行,200列开始,到400行,400列结束的一个矩形区域
    GDALRasterBand *pBand =poDataset->GetRasterBand(3);   
    //获取该波段的数据类型,如8U,16U等
    GDALDataType dataType =pBand->GetRasterDataType();
    //按照0.5的区域进行缩放,所以原始图像的区域大小为200*300,缩放后就是100*150
    //分配存储数据的空间,按照8U的数据类型,大小按照矩形区域来确定
    DT_8U *pBuf = newDT_8U[100*150];  
    pBand->RasterIO(GF_Read,200, 100, 200, 300, pBuf, 100, 150, GDT_Byte, 0, 0);
    delete []pBuf;
    pBuf = NULL;   

    5、第五种方式

    将8bit读取为16bit,为了方便说明,这里读取第一波段的第一行数据,这里假设打开的数据是一个8bit的数据。代码如下:

    GDALRasterBand *pBand =poDataset->GetRasterBand(1);
    //获取该波段的数据类型,如8U,16U等
    GDALDataType dataType =pBand->GetRasterDataType();
    //分配存储一行数据的空间,按照16U的数据类型
    DT_16U *pBuf = newDT_16U[iWidth]; 
    pBand->RasterIO(GF_Read,0, 0, iWidth, 1, pBuf, iWidth, 1, GDT_UInt16, 0, 0);
    delete []pBuf;
    pBuf = NULL;   

    6、第六种方式

    将16bit读取为8bit,为了方便说明,这里依旧读取第一波段的第一行数据,这里假设打开的数据是一个16bit的数据。在这里需要特别的注意,虽然GDAL提供这种可以把16bit的数据读取到8bit的存储空间中,GDAL会自动进行转换,但是这种转换是有损的,和把int值转为unsigned char类似,超过存储范围的就会被截取掉,这里也是一样,比如,16bit中的数据有个超过了255,那么使用8bit的数据存储来只剩下255了,所以一般不建议从这里转换/最好自己写个算法来进行转换,比如线性拉伸等,代码如下:

    //!!!!!!!!!!!!!!!!!!
    //强烈注意,虽然GDAL提供这种可以把16bit的数据读取到8bit的存储空间中,但是会自动进行转换这种转换是有损的,和把int值转为unsigned char类似,超过存储范围的就会被截取掉,这里也是一样。比如,16bit中的数据有个超过了255,那么使用8bit的数据存储来只剩下255了,所以一般不建议从这里转换。最好自己写个算法来进行转换,比如线性拉伸等。
    //!!!!!!!!!!!!!!!!!!
    GDALRasterBand *pBand =poDataset->GetRasterBand(1);
    GDALDataType dataType =pBand->GetRasterDataType();
     
    DT_8U *pBuf = new DT_8U[iWidth];
    pBand->RasterIO(GF_Read,0, 0, iWidth, 1, pBuf, iWidth, 1, GDT_Byte, 0, 0);
    …
    delete []pBuf;
    pBuf = NULL;

    如果还有疑问,请留言,或者发邮件。

  • 相关阅读:
    uniapp解决图形验证码问题及arraybuffer二进制转base64格式图片
    uni-app图片上传接口联调
    Redis与Mysql双写一致性方案解析(转载)
    python 3 for循环倒序、一组数据参数解包
    python使用for循环打印直角三角形、菱形、乘法口诀,1至100的和、奇数和、偶数和
    使用jmeter做接口测试,简单实例
    python发送无参数get请求
    python的第三方库unittestreport 详细功能使用文档(V1.1.1)-转载
    python 3 发送邮件(转载)
    python使用apscheduler执行定时任务时报错:Run time of job "pr (trigger: cron[minute='25'], next run at: 2021-05-05 22:25:00 CST)" was missed by 0:00:01.185258
  • 原文地址:https://www.cnblogs.com/xiaowangba/p/6314037.html
Copyright © 2011-2022 走看看