#include "gdal_priv.h"
#include "ogrsf_frmts.h"
#include "gdalwarper.h"
/***
* 遙感影像重採樣 (要求影像必須有投影,否則走不通)
* @param pszSrcFile 輸入文件的路徑
* @param pszOutFile 寫入的結果圖像的路徑
* @param eResample 採樣模式,有五種,具體參見GDALResampleAlg定義,默認爲雙線性內插
* @param fResX X轉換採樣比,默認大小爲1.0,大於1圖像變大,小於1表示圖像縮小。數值上等於採樣後圖像的寬度和採樣前圖像寬度的比
* @param fResY Y轉換採樣比,默認大小爲1.0,大於1圖像變大,小於1表示圖像縮小。數值上等於採樣後圖像的高度和採樣前圖像高度的比
* @retrieve 0 成功
* @retrieve -1 打開源文件失敗
* @retrieve -2 創建新文件失敗
* @retrieve -3 處理過程中出錯
*/
int ResampleGDAL(const char* pszSrcFile, const char* pszOutFile, float fResX = 1.0 ,float fResY = 1.0,GDALResampleAlg eResample = GRA_Bilinear)
{
GDALAllRegister();
CPLSetConfigOption("GDAL_FILENAME_IS_UTF8","NO");
GDALDataset *pDSrc = (GDALDataset *)GDALOpen(pszSrcFile, GA_ReadOnly);
if (pDSrc == NULL)
{
return -1;
}
GDALDriver *pDriver = GetGDALDriverManager()->GetDriverByName("GTiff");
if (pDriver == NULL)
{
GDALClose((GDALDatasetH) pDSrc);
return -2;
}
int width=pDSrc->GetRasterXSize();
int height=pDSrc->GetRasterYSize();
int nBandCount = pDSrc->GetRasterCount();
GDALDataType dataType = pDSrc->GetRasterBand(1)->GetRasterDataType();
char *pszSrcWKT = NULL;
pszSrcWKT = const_cast<char *>(pDSrc->GetProjectionRef());
//如果沒有投影,人爲設置一個
if(strlen(pszSrcWKT)<=0)
{
OGRSpatialReference oSRS;
oSRS.SetUTM(50,true); //北半球 東經120度
oSRS.SetWellKnownGeogCS("WGS84");
oSRS.exportToWkt(&pszSrcWKT);
}
void *hTransformArg;
hTransformArg = GDALCreateGenImgProjTransformer((GDALDatasetH) pDSrc,pszSrcWKT,NULL,pszSrcWKT,FALSE,0.0,1);
//(沒有投影的影像到這裏就走不通了)
if (hTransformArg == NULL)
{
GDALClose((GDALDatasetH) pDSrc);
return -3;
}
double dGeoTrans[6] = {0};
int nNewWidth=0,nNewHeight=0;
if(GDALSuggestedWarpOutput((GDALDatasetH)pDSrc,GDALGenImgProjTransform,hTransformArg,dGeoTrans,&nNewWidth,&nNewHeight)!=CE_None)
{
GDALClose((GDALDatasetH) pDSrc);
return -3;
}
GDALDestroyGenImgProjTransformer(hTransformArg);
//adfGeoTransform[0] /* top left x */
//adfGeoTransform[1] /* w-e pixel resolution */
//adfGeoTransform[2] /* rotation, 0 if image is "north up" */
//adfGeoTransform[3] /* top left y */
//adfGeoTransform[4] /* rotation, 0 if image is "north up" */
//adfGeoTransform[5] /* n-s pixel resolution */
dGeoTrans[1] = dGeoTrans[1] / fResX;
dGeoTrans[5] = dGeoTrans[5] / fResY;
nNewWidth = static_cast<int>(nNewWidth*fResX+0.5);
nNewHeight = static_cast<int>(nNewHeight*fResY+0.5);
//創建結果數據集
GDALDataset *pDDst = pDriver->Create(pszOutFile, nNewWidth, nNewHeight, nBandCount, dataType, NULL);
if (pDDst == NULL)
{
GDALClose((GDALDatasetH) pDSrc);
return -2;
}
pDDst->SetProjection(pszSrcWKT);
pDDst->SetGeoTransform(dGeoTrans);
GDALWarpOptions *psWo = GDALCreateWarpOptions();
//psWo->papszWarpOptions = CSLDuplicate(NULL);
psWo->eWorkingDataType = dataType;
psWo->eResampleAlg = eResample;
psWo->hSrcDS = (GDALDatasetH) pDSrc;
psWo->hDstDS = (GDALDatasetH) pDDst;
psWo->pfnTransformer = GDALGenImgProjTransform;
psWo->pTransformerArg = GDALCreateGenImgProjTransformer((GDALDatasetH) pDSrc,pszSrcWKT,(GDALDatasetH) pDDst,pszSrcWKT,FALSE,0.0,1);;
psWo->nBandCount = nBandCount;
psWo->panSrcBands = (int *) CPLMalloc(nBandCount*sizeof(int));
psWo->panDstBands = (int *) CPLMalloc(nBandCount*sizeof(int));
for (int i=0; i<nBandCount; i++)
{
psWo->panSrcBands[i] = i+1;
psWo->panDstBands[i] = i+1;
}
GDALWarpOperation oWo;
if (oWo.Initialize(psWo) != CE_None)
{
GDALClose((GDALDatasetH) pDSrc);
GDALClose((GDALDatasetH) pDDst);
return -3;
}
oWo.ChunkAndWarpImage(0, 0, nNewWidth, nNewHeight);
GDALFlushCache( pDDst );
GDALDestroyGenImgProjTransformer(psWo->pTransformerArg);
GDALDestroyWarpOptions( psWo );
GDALClose((GDALDatasetH) pDSrc);
GDALClose((GDALDatasetH) pDDst);
return 0;
}
用法示例: ResampleGDAL("F:\\Work\\\\數據\\spline.tif",
"F:\\Work\\數據\\Resample_Lanczos.tif",GRA_Lanczos,10,10);
ResampleGDAL("F:\\Work\\數據\\test.tif",
"F:\\Work\\數據\\Resample3.tif",GRA_Lanczos,3,3);
缺陷:沒有投影就不能重採樣,關鍵點在於:GDALCreateGenImgProjTransformer取到的是null,根據gdal源代碼,如果沒有投影,dGeoTrans[] = {0,1,0,0,0,1},這個時候GDALCreateGenImgProjTransformer就返回null,因此可以將dGeoTrans[0]或dGeoTrans[3]設置爲非0,這樣GDALCreateGenImgProjTransformer就不返回null了。改進:
/***
* 遙感影像重採樣
* @param pszSrcFile 輸入文件的路徑
* @param pszOutFile 寫入的結果圖像的路徑
* @param eResample 採樣模式,有五種,具體參見GDALResampleAlg定義,默認爲雙線性內插
GRA_NearestNeighbour=0 最近鄰法,算法簡單並能保持原光譜信息不變;缺點是幾何精度差,灰度不連續,邊緣會出現鋸齒狀
GRA_Bilinear=1 雙線性法,計算簡單,圖像灰度具有連續性且採樣精度比較精確;缺點是會喪失細節;
GRA_Cubic=2 三次卷積法,計算量大,圖像灰度具有連續性且採樣精度高;
GRA_CubicSpline=3 三次樣條法,灰度連續性和採樣精度最佳;
GRA_Lanczos=4 分塊蘭索斯法,由匈牙利數學家、物理學家蘭索斯法創立,實驗發現效果和雙線性接近;
* @param fResX X轉換採樣比,默認大小爲1.0,大於1圖像變大,小於1表示圖像縮小。數值上等於採樣後圖像的寬度和採樣前圖像寬度的比
* @param fResY Y轉換採樣比,默認大小爲1.0,大於1圖像變大,小於1表示圖像縮小。數值上等於採樣後圖像的高度和採樣前圖像高度的比
* @retrieve 0 成功
* @retrieve -1 打開源文件失敗
* @retrieve -2 創建新文件失敗
* @retrieve -3 處理過程中出錯
*/
int ResampleGDAL(const char* pszSrcFile, const char* pszOutFile, float fResX = 1.0 ,float fResY = 1.0,GDALResampleAlg eResample = GRA_Bilinear)
{
GDALAllRegister();
CPLSetConfigOption("GDAL_FILENAME_IS_UTF8","NO");
GDALDataset *pDSrc = (GDALDataset *)GDALOpen(pszSrcFile, GA_ReadOnly);
if (pDSrc == NULL)
{
return -1;
}
GDALDriver *pDriver = GetGDALDriverManager()->GetDriverByName("GTiff");
if (pDriver == NULL)
{
GDALClose((GDALDatasetH) pDSrc);
return -2;
}
int width=pDSrc->GetRasterXSize();
int height=pDSrc->GetRasterYSize();
int nBandCount = pDSrc->GetRasterCount();
GDALDataType dataType = pDSrc->GetRasterBand(1)->GetRasterDataType();
char *pszSrcWKT = NULL;
pszSrcWKT = const_cast<char *>(pDSrc->GetProjectionRef());
double dGeoTrans[6] = {0};
int nNewWidth=width,nNewHeight=height;
pDSrc->GetGeoTransform(dGeoTrans);
bool bNoGeoRef = false;
double dOldGeoTrans0 = dGeoTrans[0];
//如果沒有投影,人爲設置一個
if(strlen(pszSrcWKT)<=0)
{
//OGRSpatialReference oSRS;
//oSRS.SetUTM(50,true); //北半球 東經120度
//oSRS.SetWellKnownGeogCS("WGS84");
//oSRS.exportToWkt(&pszSrcWKT);
//pDSrc->SetProjection(pszSrcWKT);
//////////////////////////////////////////////////////////////////////////
dGeoTrans[0]=1.0;
pDSrc->SetGeoTransform(dGeoTrans);
//////////////////////////////////////////////////////////////////////////
bNoGeoRef = true;
}
//adfGeoTransform[0] /* top left x */
//adfGeoTransform[1] /* w-e pixel resolution */
//adfGeoTransform[2] /* rotation, 0 if image is "north up" */
//adfGeoTransform[3] /* top left y */
//adfGeoTransform[4] /* rotation, 0 if image is "north up" */
//adfGeoTransform[5] /* n-s pixel resolution */
dGeoTrans[1] = dGeoTrans[1] / fResX;
dGeoTrans[5] = dGeoTrans[5] / fResY;
nNewWidth = static_cast<int>(nNewWidth*fResX+0.5);
nNewHeight = static_cast<int>(nNewHeight*fResY+0.5);
//創建結果數據集
GDALDataset *pDDst = pDriver->Create(pszOutFile, nNewWidth, nNewHeight, nBandCount, dataType, NULL);
if (pDDst == NULL)
{
GDALClose((GDALDatasetH) pDSrc);
return -2;
}
pDDst->SetProjection(pszSrcWKT);
pDDst->SetGeoTransform(dGeoTrans);
void *hTransformArg = NULL;
hTransformArg = GDALCreateGenImgProjTransformer2((GDALDatasetH) pDSrc, (GDALDatasetH) pDDst, NULL); //GDALCreateGenImgProjTransformer((GDALDatasetH) pDSrc,pszSrcWKT,(GDALDatasetH) pDDst,pszSrcWKT,FALSE,0.0,1);
if (hTransformArg == NULL)
{
GDALClose((GDALDatasetH) pDSrc);
GDALClose((GDALDatasetH) pDDst);
return -3;
}
GDALWarpOptions *psWo = GDALCreateWarpOptions();
psWo->papszWarpOptions = CSLDuplicate(NULL);
psWo->eWorkingDataType = dataType;
psWo->eResampleAlg = eResample;
psWo->hSrcDS = (GDALDatasetH) pDSrc;
psWo->hDstDS = (GDALDatasetH) pDDst;
psWo->pfnTransformer = GDALGenImgProjTransform;
psWo->pTransformerArg = hTransformArg;
psWo->nBandCount = nBandCount;
psWo->panSrcBands = (int *) CPLMalloc(nBandCount*sizeof(int));
psWo->panDstBands = (int *) CPLMalloc(nBandCount*sizeof(int));
for (int i=0; i<nBandCount; i++)
{
psWo->panSrcBands[i] = i+1;
psWo->panDstBands[i] = i+1;
}
GDALWarpOperation oWo;
if (oWo.Initialize(psWo) != CE_None)
{
GDALClose((GDALDatasetH) pDSrc);
GDALClose((GDALDatasetH) pDDst);
return -3;
}
oWo.ChunkAndWarpImage(0, 0, nNewWidth, nNewHeight);
GDALDestroyGenImgProjTransformer(hTransformArg);
GDALDestroyWarpOptions( psWo );
if(bNoGeoRef)
{
dGeoTrans[0]=dOldGeoTrans0;
pDDst->SetGeoTransform(dGeoTrans);
//pDDst->SetProjection("");
}
GDALFlushCache( pDDst );
GDALClose((GDALDatasetH) pDSrc);
GDALClose((GDALDatasetH) pDDst);
return 0;
}
改進後:沒有投影也可以過去,但是如果沒有投影,輸出結果會自動帶上一個投影,並且這個投影信息去不掉。