GDALWarp設置GDALWarpOptions::dfWarpMemoryLimit過大時處理失敗

使用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出錯時的各個變量的信息。
GDAL調試時出錯各個變量的值

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