利用gdal给影像添加金字塔

在遥感领域,许多图片的大小动辄上G。读取、显示这样的图片极为耗时,影响用户体验。金字塔技术在几乎不降低显示效果的前提下,大大降低了图片处理的耗时,改善了用户体验

目录

原理

操作方法

示例

解释

生成金字塔

读取金字塔

效果



原理

考虑一幅1024乘以1024的图片。假如每个像素占据1字节,则整个文件占据1MB(不算文件辅助信息之类的话)。假如显示这幅图片的窗口尺寸只有256*256,则我们完全可以抽样,每4行抽一行,每4列抽一列。抽取出来的字节数仅为原有字节数的1/16.假如读取的字节数变少了,耗时自然会降低。但是这要求我们在读取之前,就要把抽样完成,并且将抽样结果存放到一段连续的磁盘或内存里供使用(有人可能提出来,RasterIO函数具备抽样功能,可否每次读取时都利用RasterIO来抽样一次?不行,假如对若干段不连续的地址进行读取,RasterIO的耗时也会增加,不会比读取全部数据快多少。所以抽样结果一定要存入一段连续的内存或者磁盘里)。

操作方法

gdal提供了函数BuildOverviews()完成抽样并保存。

CPLErr BuildOverviews(const char *pszResampling, int nOverviews, 
int *panOverviewList, int nListBands, int *panBandList, 
GDALProgressFunc pfnProgress, void *pProgressData)

1)pszResampling可以是如下字符串的一个:

"NEAREST" 选取最近邻的原始像素值

"AVERAGE"取平均

"BILINEAR"双线性插值

"CUBIC"立方插值

"GAUSS"高斯插值

"LANZCOS"兰佐斯插值(高斯与兰佐斯都是数学-物理学家)

"AVERAGE_MAGPHASE"

"CUBICSPLINE"

"MODE"

"NONE"

2) 考虑到许多显示界面都具备缩放功能,一旦图像发生缩放,抽样的比率将发生变化,所以BuildOverviews()函数提供了nOverviews和panOverviewList。前者表示你可以做几次抽样;后者是一个数组,表示每次抽样的采样率是多少。

3) nListBands表示被采样的波段号。

4) 最后两个变量一般不用,写NULL即可

以下面的代码为例,nOverviews=3,panOverviewList={2,4,8},说明采样三次。第一次采样率为2,也就是每两行取一行,每2列取一列;第二次采样率为4,也就是每4行取一行,每4列取1列;...三次采样的结果都要保存下来。对于tif文件,抽样结果存入源文件;对于bmp文件,所有抽样结果存入一个统一的新文件,文件名是源文件名加后缀.ovr

采样率越高,抽样的结果占用字节数就越少。以原文件占1024kB为例,采样率为2,抽样结果占256kB;采样率4,则占据64kB;采样率为8,则占据16kB.采样率从高到低,占用空间越来越大,形成金字塔型,这也正是“影像金字塔”命名的由来。

示例

下面的代码利用buildOverviews函数建立金字塔,然后按照不同分辨率来读取图片,并记录不同分辨率下读取的花费时间。关于如何记录读取时间,可以参看我的另外一篇博客《Visual Studio C++精确计时》

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "gdal_priv.h"
#include <QDebug>

#pragma comment(lib, "winmm.lib")//不可少


MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    ui->lineEdit->setText("C:\\code\\QT\\pyramid\\2-1.tif");
    ui->lineEdit_2->setText("C:\\code\\QT\\pyramid\\");
    QueryPerformanceFrequency(&m_nFreq);//获取频率
    QueryPerformanceCounter(&m_nBegin);//获取起始时间
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_pushButton_clicked()
{
    QString qstrName = ui->lineEdit->text();
    GDALDataset * pSet = (GDALDataset *)GDALOpen(qstrName.toLatin1().data(), GA_Update);
    int       anOverviewList[3] = { 2, 4, 8};

    if(CE_None == pSet->BuildOverviews( "NEAREST", 3, anOverviewList, 0, NULL,
                               NULL, NULL ))
    {
        ui->label->setText("OK");
    }
    else
    {
        ui->label->setText("fail");
    }
    GDALClose(pSet);
}

void MainWindow::on_pushButton_2_clicked()
{
    int iRatio = ui->spinBox->value();
    QString qstrName = ui->lineEdit->text();
    GDALDataset * pSet = (GDALDataset *)GDALOpen(qstrName.toLatin1().data(), GA_ReadOnly);

    GDALRasterBand *pBand =pSet->GetRasterBand(1);
    int iX = pSet->GetRasterXSize();
    int iY = pSet->GetRasterYSize();
    unsigned char * pData = new unsigned char[iX * iY];

    LARGE_INTEGER nTime1, nTime2;
    QueryPerformanceCounter(&nTime1);//获取时间
    pBand->RasterIO(GF_Read,0, 0, iX, iY, pData, iX/iRatio, iY/iRatio, GDT_Byte, 0, 0);

    QueryPerformanceCounter(&nTime2);
    int iInterval = (nTime2.QuadPart - nTime1.QuadPart) / (double)m_nFreq.QuadPart * 1000;
    ui->label_2->setText(QString("%1ms").arg(iInterval));

    GDALClose(pSet);


    //save
    static int iIndex = 0;
    QString qstrSavePath = ui->lineEdit_2->text();
    QString qstrSaveFile = QString("%1new%2.tif").arg(qstrSavePath).arg(iIndex++);
    GDALDriver * pDriver = GetGDALDriverManager()->GetDriverByName("GTiff");
    GDALDataset * pSetWrite = pDriver->Create(qstrSaveFile.toLatin1().data(),
                                   iX/iRatio, iY/iRatio,1,GDT_Byte,NULL);
    pBand =pSetWrite->GetRasterBand(1);
    pBand->RasterIO(GF_Write,0, 0, iX/iRatio, iY/iRatio, pData, iX/iRatio, iY/iRatio, GDT_Byte, 0, 0);
    GDALClose(pSetWrite);

    delete [] pData;
}

解释

生成金字塔

函数 on_pushButton_clicked()用于产生金字塔

语句

int       anOverviewList[3] = { 2, 4, 8};

意味着抽样三次,抽样率分别为2,4,8

读取金字塔

函数 on_pushButton_2_clicked()用于读取金字塔,然后把读取的结果保存成单独的文件

语句

pBand->RasterIO(GF_Read,0, 0, iX, iY, pData, iX/iRatio, iY/iRatio, GDT_Byte, 0, 0);

中各个参数意义如下:(按从左到右的次序)

GF_Read 表示读取

第一对 0,0表示读取的开始位置是原始图片(也就是抽样之前的图片)的左上角

iX,iY表示读取的范围:原始图片横向iX个像素,纵向iY个像素

pData表示读取后,像素存储的首地址

iRatio代表抽样率,iX/iRatio代表抽样后,抽样结果在X方向占多少像素,iY/iRatio代表抽样结果在Y方向占多少像素

GDT_Byte代表原始文件的像素占一个字节

RasterIO前后的两个QueryPerformanceCounter函数记下了读取前后的时间,两者相减便是RasterIO的耗时。这个耗时将显示在界面上。

效果

读取不抽样的图(微缩比例1)耗时48ms。结果文件new0.tif占据2967KB

读取抽样率为2(微缩比例2)的图耗时12ms。结果文件new1.tif占据742KB

读取抽样率为4的图耗时16ms。当图变小以后,读取耗时就存在随机性了,但是总的趋势不变。16ms也正常。结果文件new2.tif占据186KB

读取抽样率为8的图耗时0.结果文件new3.tif占据47KB

我们从图片的字节数也可以看出抽样确实减少了文件大小。从new0.tif到new3.tif

发布了148 篇原创文章 · 获赞 46 · 访问量 28万+

猜你喜欢

转载自blog.csdn.net/liji_digital/article/details/104248253