QGIS中的金字塔機制優化

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 );  

重點就是紅色部分的兩個變量修改,希望能對大家有所幫助。

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