在遙感領域,許多圖片的大小動輒上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