利用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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章