GDALWarp設定GDALWarpOptions::dfWarpMemoryLimit過大時處理失敗
阿新 • • 發佈:2018-12-29
使用GDALWarp寫了一個裁切影象的演算法,在小記憶體的電腦沒事,大記憶體的電腦就處理失敗(32位也沒問題),檢視GDAL的日誌發現下面的錯誤資訊:
Fri Apr 08 17:39:02 2016: GDAL: GDALOpen(E:/Out/TRIPLESAT_1_PAN_L1_20160330024710_000315VI_005.tif, this=000000000508EB40) succeeds as GTiff.
Fri Apr 08 17:39:02 2016: GDAL: GDALDriver::Create(GTiff,E:\xxx/TRIPLESAT_1_PAN_L1_20160330024710_000315VI_005_20160406003001_001.tif,31260 ,24550,1,UInt16,0000000000000000)
Fri Apr 08 17:39:04 2016: WARP_TIMING: Output buffer read: 2s
Fri Apr 08 17:39:07 2016: WARP_TIMING: Input buffer read: 3s
Fri Apr 08 17:39:07 2016: CPLError: Out of memory allocating -1225110252 bytes for UnifiedSrcDensity mask.
ERROR 2: Out of memory allocating -1225110252 bytes for UnifiedSrcDensity mask.
從上面的日誌中可以看出,居然申請記憶體失敗了,見過小記憶體的機器申請記憶體失敗的,沒見過大記憶體的機器申請記憶體失敗,很鬱悶。
對比了小記憶體機器和大記憶體機器輸入的資料,所有的引數都一樣,除了GDALWarpOptions::dfWarpMemoryLimit這個。由於程式需要處理大量的資料,而且機器的記憶體也很大(128GB),所以就在程式啟動之後先獲取了系統的空閒記憶體,然後將空閒記憶體的四分之一設定給了dfWarpMemoryLimit這個引數(大記憶體機器這個值大概25GB左右)。將這個值手動改小,程式正確處理了,看來就是這個引數的原因。
通過除錯GDAL的程式碼,在檔案gdalwarpoperation.cpp中的函式GDALWarpOperation::CreateKernelMask中有下面的程式碼(SVN版本庫中的程式碼行數大致在2059行,其他所有的版本都有這個問題,已經提交gdal的bug庫)
if( *ppMask == NULL )
{
int nBytes; //this line should be GIntBig nBytes;
if( nBitsPerPixel == 32 )
nBytes = (nXSize * nYSize + nExtraElts) * 4;
else
nBytes = (nXSize * nYSize + nExtraElts + 31) / 8;
*ppMask = VSI_MALLOC_VERBOSE( nBytes );
if( *ppMask == NULL )
{
return CE_Failure;
}
memset( *ppMask, nDefault, nBytes );
}
從上面的程式碼中看的話,其實一般情況下沒有任何問題,除了一種情況,如果nXSize和nYSize很大,比如我日誌中的(31260*24550+nExtraElts)*4,即使nExtraElts=0,結果也會超過int的儲存範圍導致資料溢位,從而nBytes的值就會變成負數,進而分配記憶體的時候就失敗了。下面是修改後的程式碼:
if( *ppMask == NULL )
{
GIntBig nBytes;
if( nBitsPerPixel == 32 )
nBytes = ((GIntBig )nXSize * nYSize + nExtraElts) * 4;
else
nBytes = ((GIntBig )nXSize * nYSize + nExtraElts + 31) / 8;
*ppMask = VSI_MALLOC_VERBOSE( nBytes );
if( *ppMask == NULL )
{
return CE_Failure;
}
memset( *ppMask, nDefault, nBytes );
}
下面的圖是GDAL出錯時的各個變數的資訊。