緣起
對於一般的遙感影像文件,金字塔文件默認都是與影像文件放在同一個目錄下,金字塔文件名一般與源影像文件名相同,但后綴名不同。或者金字塔內建於影像內部,但這不是這里所涉及的。
在使用ArcGIS桌面版或者Erdas遙感影像處理軟件打開遙感影像文件的時候,如果影像不含(帶有)金字塔,則會提示是否創建金字塔。

我們碰到這么一種情況,就是原始影像是不帶金字塔的,並且所在的目錄不允許修改,也就是不能將金字塔創建在其中,原始影像文件更是不允許修改,所以也不能更新內建金字塔。
解決
對於這個問題,為了加速影像的瀏覽,只能將金字塔建立在外部的目錄,而讀取的時候根據讀取輸出的分辨率,來確認是使用源影像讀取還是使用金字塔文件讀取,這都需要自己控制。
因為自己控制會將一些控制流程變復雜,所以我想讓GDAL直接支持索引到這個外部的金字塔文件。
在GDAL中,金字塔稱之為Overviews(概覽視圖),所以與之相關的接口都帶有Overview單詞。
我看了GDAL的RasterIO流程相關的代碼,簡單的制作了如下的流程圖:

GDALDefaultOverviews::OverviewScan()函數
與金字塔路徑查找的關鍵代碼在函數GDALDefaultOverviews::OverviewScan()中。
通過查看其實現代碼,和被調用時候的參數(參數在GDALDefaultOverviews::Initialize函數被調用的時候確定,可以搜索poDS->oOvManager.Initialize查看,都是在對應格式的XXXDataset::Open()函數中被調用,不同格式影像調用時候傳的參數可能不同),可以知道GDAL搜尋金字塔的方式與順序。
這里不詳細說這里面的流程結構了,一些判斷條件也略過,只大概說一下搜索順序(如果有內建金字塔則不會讀取外部金字塔):
- 1、
.ovr后綴金字塔文件 - 2、
.aux后綴金字塔文件 - 3、
.rrd后綴金字塔文件(如果配置有USE_RRD=YES) - 4、影像元數據Metadata中的
OVERVIEW_FILE指定的金字塔路徑
如果需要使用到自定義的金字塔文件,可以在此處修改,添加一段搜索代碼,示例如下:
if( poODS == nullptr )
{
osOvrFilename = MyOverviewFileSearch(pszInitName);
poODS = GDALDataset::Open(osOvrFilename,
GDAL_OF_RASTER | (poDS->GetAccess() == GA_Update ? GDAL_OF_UPDATE: 0));
}
MyOverviewFileSearch是用於搜索自定義路徑下金字塔的函數。
關於 GDALRasterBand::GetOverview
上面說如果有內建金字塔則不會讀取外部金字塔,這個實際上是各個影像文件格式的相關代碼里面自己定義的。
因為virtual GDALRasterBand* GDALRasterBand::GetOverview(int);本來就是一個虛函數,而GDAL中又基本都是依賴指針進行操作,所以實際上會調用各自的實現。
以GeoTiff格式為例,在它的Overview相關函數中就是先搜索Tiff目錄內的金字塔,如果找不到才調用GDALRasterBand中對應的接口,進而訪問外部金字塔文件。
例如以下代碼:
/************************************************************************/
/* GetOverviewCount() */
/************************************************************************/
int GTiffRasterBand::GetOverviewCount()
{
poGDS->ScanDirectories();
if( poGDS->nOverviewCount > 0 )
{
return poGDS->nOverviewCount;
}
const int nOverviewCount = GDALRasterBand::GetOverviewCount();
if( nOverviewCount > 0 )
return nOverviewCount;
// Implicit JPEG overviews are normally hidden, except when doing
// IRasterIO() operations.
if( poGDS->nJPEGOverviewVisibilityCounter )
return poGDS->GetJPEGOverviewCount();
return 0;
}
/************************************************************************/
/* GetOverview() */
/************************************************************************/
GDALRasterBand *GTiffRasterBand::GetOverview( int i )
{
poGDS->ScanDirectories();
if( poGDS->nOverviewCount > 0 )
{
// Do we have internal overviews?
if( i < 0 || i >= poGDS->nOverviewCount )
return nullptr;
return poGDS->papoOverviewDS[i]->GetRasterBand(nBand);
}
GDALRasterBand* const poOvrBand = GDALRasterBand::GetOverview( i );
if( poOvrBand != nullptr )
return poOvrBand;
// For consistency with GetOverviewCount(), we should also test
// nJPEGOverviewVisibilityCounter, but it is also convenient to be able
// to query them for testing purposes.
if( i >= 0 && i < poGDS->GetJPEGOverviewCount() )
return poGDS->papoJPEGOverviewDS[i]->GetRasterBand(nBand);
return nullptr;
}
上面代碼中的poGDS->ScanDirectories();語句,內部調用去遍歷Tiff目錄,找到TIFFTAG_SUBFILETYPE(子文件類型)字段為FILETYPE_REDUCEDIMAGE(縮小圖像)的目錄,然后解析出需要的金字塔信息(僅第一次調用時做,后面調用會判斷bScanDeferred直接跳過)。
如果沒有找到才調用基類GDALRasterBand對應的函數去訪問外部金字塔。
關於構建外部金字塔等
構建也是差不多的過程,這里就簡單的寫一下調用過程:GDALDataset::BuildOverviews ---> GDALDataset::IBuildOverviews ---> GDALDefaultOverviews::BuildOverviews

所以設置構建外部金字塔的路徑,也可以修改GDALDefaultOverviews::BuildOverviews。
同樣,對於不同的影像格式,可以有自己的GDALDataset::IBuildOverviews實現,例如GeoTiff的就是GTiffDataset::IBuildOverviews,如果需要創建內建的金字塔,則做另外的實現。
