QGIS 中可以通過圖層的屬性窗口來給柵格數據創建金字塔,如下圖:
右側的 Pyramid resolutions 表示可以建立的金字塔的級別,數字前面的符號有個小叉就表示該級別的金字塔還沒有被建立。可以通過點擊右下方的 Build pyramid 按鈕來爲影像建立金字塔。
QGIS 內部也是調用了 GDAL 的 GDALBuildOverviews 方法來爲影像建立金字塔。 但在使用 Build pyramid 建立金字塔的過程中發現速度非常慢,與使用 gdaladdo.exe 命令來建立金字塔相比,要耗費它幾倍的時間,對此感到非常疑惑,於是看了一下內部的實現機制。
建立金字塔的實現函數位於 QgsRasterLayer 中,函數名爲buildPyramids ,定義如下:
/** /brief Create GDAL pyramid overviews */
QString buildPyramids ( const RasterPyramidList &,
const QString & theResamplingMethod = "NEAREST" ,
bool theTryInternalFlag = false );
RasterPyramidList 參數表示要建立金字塔的級別。
theResamplingMethod 參數表示重採樣方法。
theTryInternalFlag 參數表示是否將金字塔嵌入影像內部,該操作好像只支持TIFF 格式。
找到了函數,查看其內部實現,很快就找到了原因所在。
以下代碼是 QGIS 建立金字塔的核心代碼
RasterPyramidList ::const_iterator myRasterPyramidIterator ;
for ( myRasterPyramidIterator = theRasterPyramidList .begin ();
myRasterPyramidIterator != theRasterPyramidList .end ();
++myRasterPyramidIterator )
{
if (( *myRasterPyramidIterator ).build )
{
QgsDebugMsg ( "Building....." );
emit drawingProgress ( myCount , myTotal );
int myOverviewLevelsArray [1] = {( *myRasterPyramidIterator ).level };
/* From : http://remotesensing.org/gdal/classGDALDataset.html#a23
* pszResampling : one of "NEAREST", "AVERAGE" or "MODE" controlling the downsampling method applied.
* nOverviews : number of overviews to build.
* panOverviewList : the list of overview decimation factors to build.
* nBand : number of bands to build overviews for in panBandList. Build for all bands if this is 0.
* panBandList : list of band numbers.
* pfnProgress : a function to call to report progress, or NULL.
* pProgressData : application data to pass to the progress function.
*/
//build the pyramid and show progress to console
try
{
//build the pyramid and show progress to console
//NOTE this (magphase) is disabled in teh gui since it tends
//to create corrupted images. The images can be repaired
//by running one of the other resampling strategies below.
//see ticket #284
if ( theResamplingMethod == tr ( "Average Magphase" ) )
{
myError = GDALBuildOverviews ( mGdalBaseDataset , "MODE" , 1, myOverviewLevelsArray , 0, NULL ,
progressCallback , this ); //this is the arg for the gdal progress callback
}
else if ( theResamplingMethod == tr ( "Average" ) )
{
myError = GDALBuildOverviews ( mGdalBaseDataset , "AVERAGE" , 1, myOverviewLevelsArray , 0, NULL ,
progressCallback , this ); //this is the arg for the gdal progress callback
}
else // fall back to nearest neighbor
{
myError = GDALBuildOverviews ( mGdalBaseDataset , "NEAREST" , 1, myOverviewLevelsArray , 0, NULL ,
progressCallback , this ); //this is the arg for the gdal progress callback
}
if ( myError == CE_Failure || CPLGetLastErrorNo () == CPLE_NotSupported )
{
//something bad happenend
//QString myString = QString (CPLGetLastError());
GDALClose ( mGdalBaseDataset );
mGdalBaseDataset = GDALOpen ( QFile ::encodeName ( mDataSource ).constData (), GA_ReadOnly );
//Since we are not a virtual warped dataset, mGdalDataSet and mGdalBaseDataset are supposed to be the same
mGdalDataset = mGdalBaseDataset ;
emit drawingProgress ( 0, 0 );
return "FAILED_NOT_SUPPORTED" ;
}
else
{
//make sure the raster knows it has pyramids
mHasPyramids = true ;
}
myCount ++;
}
catch ( CPLErr )
{
QgsLogger ::warning ( "Pyramid overview building failed!" );
}
}
可以看到,QGIS 將建立金字塔的關鍵函數GDALBuildOverviews 放到了一個for 循環中。也就是說每建立一級金字塔,都會調用一次GDALBuildOverviews 函數。
對此我使用gdaladdo 命令做了一個驗證,使用GDALBuildOverviews 函數一次建立4 級金字塔和分四次建立一個金字塔耗費的時間差不多是1:4 。這就是速度慢的問題所在,不清楚QGIS 的開發團隊爲什麼要用這種方法來實現。
我對代碼進行了一些修改,將GDALBuildOverviews 函數移到了for 循環之外。然後重新在屬性面板中建立金字塔,速度提高很多。
修改的時侯要注意的核心代碼如下:
原代碼:
int myOverviewLevelsArray [1] = {( *myRasterPyramidIterator ).level };
myError = GDALBuildOverviews ( mGdalBaseDataset , "NEAREST" , 1, myOverviewLevelsArray , 0, NULL , progressCallback , this );
修改後:假如5 級金字塔
Int iLevels = 5;
int myOverviewLevelsArray [5];
在循環中對myOverviewLevelsArray 分別賦值: ( *myRasterPyramidIterator ).level
myError = GDALBuildOverviews ( mGdalBaseDataset , "NEAREST" , iLevels , myOverviewLevelsArray , 0, NULL ,progressCallback , this );
重點就是紅色部分的兩個變量修改,希望能對大家有所幫助。