如何使用GDAL进行AOI裁剪
在工作中,会经常使用一个行政区的矢量图去裁剪一个遥感影像图,得到该行政区的影像图,一般的商业遥感软件都具有这个功能。今天就是用GDAL来实现这一个很实用的功能。首先用到的是GDAL中的gdalwarp,又是warp,呵呵,上一篇就是使用warp进行重采样的。
首先需要用到gdal源码目录里面的app文件夹下的gdalwarp.cpp文件中的几个函数,大概行数是1651行,直到文件结尾,代码如下:
/************************************************************************/ /* GeoTransform_Transformer() */ /* */ /* Convert points from georef coordinates to pixel/line based */ /* on a geotransform. */ /************************************************************************/ class CutlineTransformer : public OGRCoordinateTransformation { public: void *hSrcImageTransformer; virtual OGRSpatialReference *GetSourceCS() { return NULL; } virtual OGRSpatialReference *GetTargetCS() { return NULL; } virtual int Transform( int nCount, double *x, double *y, double *z = NULL ) { int nResult; int *pabSuccess = (int *) CPLCalloc(sizeof(int),nCount); nResult = TransformEx( nCount, x, y, z, pabSuccess ); CPLFree( pabSuccess ); return nResult; } virtual int TransformEx( int nCount, double *x, double *y, double *z = NULL, int *pabSuccess = NULL ) { return GDALGenImgProjTransform( hSrcImageTransformer, TRUE, nCount, x, y, z, pabSuccess ); } }; /************************************************************************/ /* LoadCutline() */ /* */ /* Load blend cutline from OGR datasource. */ /************************************************************************/ static void LoadCutline( const char *pszCutlineDSName, const char *pszCLayer, const char *pszCWHERE, const char *pszCSQL, void **phCutlineRet ) { #ifndef OGR_ENABLED CPLError( CE_Failure, CPLE_AppDefined, "Request to load a cutline failed, this build does not support OGR features./n" ); exit( 1 ); #else // def OGR_ENABLED OGRRegisterAll(); /* -------------------------------------------------------------------- */ /* Open source vector dataset. */ /* -------------------------------------------------------------------- */ OGRDataSourceH hSrcDS; hSrcDS = OGROpen( pszCutlineDSName, FALSE, NULL ); if( hSrcDS == NULL ) exit( 1 ); /* -------------------------------------------------------------------- */ /* Get the source layer */ /* -------------------------------------------------------------------- */ OGRLayerH hLayer = NULL; if( pszCSQL != NULL ) hLayer = OGR_DS_ExecuteSQL( hSrcDS, pszCSQL, NULL, NULL ); else if( pszCLayer != NULL ) hLayer = OGR_DS_GetLayerByName( hSrcDS, pszCLayer ); else hLayer = OGR_DS_GetLayer( hSrcDS, 0 ); if( hLayer == NULL ) { fprintf( stderr, "Failed to identify source layer from datasource./n" ); exit( 1 ); } /* -------------------------------------------------------------------- */ /* Apply WHERE clause if there is one. */ /* -------------------------------------------------------------------- */ if( pszCWHERE != NULL ) OGR_L_SetAttributeFilter( hLayer, pszCWHERE ); /* -------------------------------------------------------------------- */ /* Collect the geometries from this layer, and build list of */ /* burn values. */ /* -------------------------------------------------------------------- */ OGRFeatureH hFeat; OGRGeometryH hMultiPolygon = OGR_G_CreateGeometry( wkbMultiPolygon ); OGR_L_ResetReading( hLayer ); while( (hFeat = OGR_L_GetNextFeature( hLayer )) != NULL ) { OGRGeometryH hGeom = OGR_F_GetGeometryRef(hFeat); if( hGeom == NULL ) { fprintf( stderr, "ERROR: Cutline feature without a geometry./n" ); exit( 1 ); } OGRwkbGeometryType eType = wkbFlatten(OGR_G_GetGeometryType( hGeom )); if( eType == wkbPolygon ) OGR_G_AddGeometry( hMultiPolygon, hGeom ); else if( eType == wkbMultiPolygon ) { int iGeom; for( iGeom = 0; iGeom < OGR_G_GetGeometryCount( hGeom ); iGeom++ ) { OGR_G_AddGeometry( hMultiPolygon, OGR_G_GetGeometryRef(hGeom,iGeom) ); } } else { fprintf( stderr, "ERROR: Cutline not of polygon type./n" ); exit( 1 ); } OGR_F_Destroy( hFeat ); } if( OGR_G_GetGeometryCount( hMultiPolygon ) == 0 ) { fprintf( stderr, "ERROR: Did not get any cutline features./n" ); exit( 1 ); } /* -------------------------------------------------------------------- */ /* Ensure the coordinate system gets set on the geometry. */ /* -------------------------------------------------------------------- */ OGR_G_AssignSpatialReference( hMultiPolygon, OGR_L_GetSpatialRef(hLayer) ); *phCutlineRet = (void *) hMultiPolygon; /* -------------------------------------------------------------------- */ /* Cleanup */ /* -------------------------------------------------------------------- */ if( pszCSQL != NULL ) OGR_DS_ReleaseResultSet( hSrcDS, hLayer ); OGR_DS_Destroy( hSrcDS ); #endif } /************************************************************************/ /* TransformCutlineToSource() */ /* */ /* Transform cutline from its SRS to source pixel/line coordinates.*/ /************************************************************************/ static void TransformCutlineToSource( GDALDatasetH hSrcDS, void *hCutline, char ***ppapszWarpOptions, char **papszTO_In ) { #ifdef OGR_ENABLED OGRGeometryH hMultiPolygon = OGR_G_Clone( (OGRGeometryH) hCutline ); char **papszTO = CSLDuplicate( papszTO_In ); /* -------------------------------------------------------------------- */ /* Checkout that SRS are the same. */ /* -------------------------------------------------------------------- */ OGRSpatialReferenceH hRasterSRS = NULL; const char *pszProjection = NULL; if( GDALGetProjectionRef( hSrcDS ) != NULL && strlen(GDALGetProjectionRef( hSrcDS )) > 0 ) pszProjection = GDALGetProjectionRef( hSrcDS ); else if( GDALGetGCPProjection( hSrcDS ) != NULL ) pszProjection = GDALGetGCPProjection( hSrcDS ); if( pszProjection != NULL ) { hRasterSRS = OSRNewSpatialReference(NULL); if( OSRImportFromWkt( hRasterSRS, (char **)&pszProjection ) != CE_None ) { OSRDestroySpatialReference(hRasterSRS); hRasterSRS = NULL; } } OGRSpatialReferenceH hCutlineSRS = OGR_G_GetSpatialReference( hMultiPolygon ); if( hRasterSRS != NULL && hCutlineSRS != NULL ) { /* ok, we will reproject */ } else if( hRasterSRS != NULL && hCutlineSRS == NULL ) { fprintf(stderr, "Warning : the source raster dataset has a SRS, but the cutline features/n" "not. We assume that the cutline coordinates are expressed in the destination SRS./n" "If not, cutline results may be incorrect./n"); } else if( hRasterSRS == NULL && hCutlineSRS != NULL ) { fprintf(stderr, "Warning : the input vector layer has a SRS, but the source raster dataset does not./n" "Cutline results may be incorrect./n"); } if( hRasterSRS != NULL ) OSRDestroySpatialReference(hRasterSRS); /* -------------------------------------------------------------------- */ /* Extract the cutline SRS WKT. */ /* -------------------------------------------------------------------- */ if( hCutlineSRS != NULL ) { char *pszCutlineSRS_WKT = NULL; OSRExportToWkt( hCutlineSRS, &pszCutlineSRS_WKT ); papszTO = CSLSetNameValue( papszTO, "DST_SRS", pszCutlineSRS_WKT ); CPLFree( pszCutlineSRS_WKT ); } /* -------------------------------------------------------------------- */ /* Transform the geometry to pixel/line coordinates. */ /* -------------------------------------------------------------------- */ CutlineTransformer oTransformer; /* The cutline transformer will *invert* the hSrcImageTransformer */ /* so it will convert from the cutline SRS to the source pixel/line */ /* coordinates */ oTransformer.hSrcImageTransformer = GDALCreateGenImgProjTransformer2( hSrcDS, NULL, papszTO ); CSLDestroy( papszTO ); if( oTransformer.hSrcImageTransformer == NULL ) exit( 1 ); OGR_G_Transform( hMultiPolygon, (OGRCoordinateTransformationH) &oTransformer ); GDALDestroyGenImgProjTransformer( oTransformer.hSrcImageTransformer ); /* -------------------------------------------------------------------- */ /* Convert aggregate geometry into WKT. */ /* -------------------------------------------------------------------- */ char *pszWKT = NULL; OGR_G_ExportToWkt( hMultiPolygon, &pszWKT ); OGR_G_DestroyGeometry( hMultiPolygon ); *ppapszWarpOptions = CSLSetNameValue( *ppapszWarpOptions, "CUTLINE", pszWKT ); CPLFree( pszWKT ); #endif }
需要将上面的代码稍微进行改造,也就是把exit(1)之类的去掉,换成返回错误代码,具体修改过的代码就不贴了,就那么几个,还有就是记得把函数的返回值void改成int,用于获取错误代码。下面就是我写的函数代码:
/** * @brief AOI截图(GDAL) * @param pszInFile 输入文件的路径 * @param pszOutFile 截图后输出文件的路径 * @param pszAOIFile AOI文件路径 * @param pszSQL 指定AOI文件中的属性字段值来裁剪 * @param pBandInex 指定裁剪的波段,默认为NULL,表示裁剪所有波段 * @param piBandCount 指定裁剪波段的个数,同上一个参数同时使用 * @param iBuffer 指定AOI文件外扩范围,默认为0,表示不外扩 * @param pszFormat 截图后输出文件的格式 * @param pProgress 进度条指针 * @return 成功返回 */ int CutImageByAOIGDAL(const char* pszInFile, const char* pszOutFile, const char* pszAOIFile, const char* pszSQL, int *pBandInex, int *piBandCount, int iBuffer, const char* pszFormat, LT_Progress *pProgress) { if(pProgress != NULL) { pProgress->SetProgressCaption("AOI裁剪"); pProgress->SetProgressTip("开始裁剪图像..."); } GDALAllRegister(); void *hCutLine = NULL; int iRev = LoadCutline( pszAOIFile, "", "", pszSQL, &hCutline ); GDALDataset * pSrcDS = (GDALDataset*) GDALOpen(pszInFile, GA_ReadOnly); if (pSrcDS == NULL) { if (pProgress != NULL) pProgress->SetProgressTip("输入的栅格文件不能打开,请检查文件是否存在!"); return RE_NOFILE; } int iBandCount = pSrcDS->GetRasterCount(); const char* pszWkt = pSrcDS->GetProjectionRef(); GDALDataType dT = pSrcDS->GetRasterBand(1)->GetRasterDataType(); double adfGeoTransform[6] = {0}; double newGeoTransform[6] = {0}; pSrcDS->GetGeoTransform(adfGeoTransform); memcpy(newGeoTransform, adfGeoTransform, sizeof(double)*6); int *pSrcBand = NULL; int iNewBandCount = iBandCount; if (pBandInex != NULL && piBandCount != NULL) { int iMaxBandIndex = pBandInex[0]; for (int i=1; i<*piBandCount; i++) { if (iMaxBandIndex < pBandInex[i]) iMaxBandIndex = pBandInex[i]; } if (iMaxBandIndex > iBandCount) { if (pProgress != NULL) pProgress->SetProgressTip("输入的波段序号没有指定的波段数!"); GDALClose((GDALDatasetH) pSrcDS); return RE_PARAMERROR; } iNewBandCount = *piBandCount; pSrcBand = new int[iNewBandCount]; for (int i=0; i<iNewBandCount; i++) pSrcBand[i] = pBandInex[i]; } else { pSrcBand = new int[iNewBandCount]; for (int i=0; i<iNewBandCount; i++) pSrcBand[i] = i+1; } OGRGeometryH hGeometry = (OGRGeometryH) hCutline; OGRGeometryH hMultiPoly = NULL; if (iBuffer > 0) { double dDistance = ABS(adfGeoTransform[1]*iBuffer); hMultiPoly = OGR_G_Buffer(hGeometry, dDistance, 30); OGR_G_AssignSpatialReference(hMultiPoly, OGR_G_GetSpatialReference(hGeometry)); OGR_G_DestroyGeometry(hGeometry); } else hMultiPoly = hGeometry; OGREnvelope eRect; OGR_G_GetEnvelope(hMultiPoly, &eRect); int iNewWidth = 0, iNewHeigh = 0; int iBeginRow = 0, iBeginCol = 0; newGeoTransform[0] = eRect.MinX; newGeoTransform[3] = eRect.MaxY; iNewWidth = static_cast<int>((eRect.MaxX -eRect.MinX) / ABS(adfGeoTransform[1])); iNewHeigh = static_cast<int>((eRect.MaxY -eRect.MinY) / ABS(adfGeoTransform[5])); Projection2ImageRowCol(adfGeoTransform, newGeoTransform[0], newGeoTransform[3], iBeginCol, iBeginRow); ImageRowCol2Projection(adfGeoTransform, iBeginCol, iBeginRow, newGeoTransform[0], newGeoTransform[3]); GDALDriver *pDriver = GetGDALDriverManager()->GetDriverByName(pszFormat); if (pDriver == NULL) { if (pProgress != NULL) pProgress->SetProgressTip("不能创建指定类型的文件!"); GDALClose((GDALDatasetH) pSrcDS); return RE_NOFILE; } GDALDataset* pDstDS = pDriver->Create(pszOutFile, iNewWidth, iNewHeigh, iNewBandCount, dT, NULL); if (pDstDS == NULL) { if (pProgress != NULL) pProgress->SetProgressTip("创建文件失败!"); GDALClose((GDALDatasetH) pSrcDS); return RE_CREATEFILE; } pDstDS->SetGeoTransform(newGeoTransform); pDstDS->SetProjection(pszWkt); void *hTransformArg, *hGenImgProjArg=NULL; char **papszTO = NULL; /* -------------------------------------------------------------------- */ /* Create a transformation object from the source to */ /* destination coordinate system. */ /* -------------------------------------------------------------------- */ hTransformArg = hGenImgProjArg = GDALCreateGenImgProjTransformer2( hSrcDS, (GDALDatasetH)pDstDS, papszTO ); if( hTransformArg == NULL ) { if (pProgress != NULL) pProgress->SetProgressTip("创建投影失败,请检查输入参数!"); GDALClose((GDALDatasetH) pSrcDS); GDALClose((GDALDatasetH) pDstDS); RELEASE(pSrcBand); return RE_PARAMERROR; } GDALTransformerFunc pfnTransformer = GDALGenImgProjTransform; GDALWarpOptions *psWO = GDALCreateWarpOptions(); psWO->papszWarpOptions = CSLDuplicate(NULL); psWO->eWorkingDataType = dT; psWO->eResampleAlg = GRA_Bilinear ; psWO->hSrcDS = (GDALDatasetH) pSrcDS; psWO->hDstDS = (GDALDatasetH) pDstDS; psWO->pfnTransformer = pfnTransformer; psWO->pTransformerArg = hTransformArg; psWO->pfnProgress = GDALProgress; psWO->pProgressArg = pProgress; psWO->nBandCount = iNewBandCount; psWO->panSrcBands = (int *) CPLMalloc(iNewBandCount*sizeof(int)); psWO->panDstBands = (int *) CPLMalloc(iNewBandCount*sizeof(int)); for (int i=0; i<iNewBandCount; i++) { psWO->panSrcBands[i] = pSrcBand[i]; psWO->panDstBands[i] = i+1; } RELEASE(pSrcBand); psWO->hCutline = (void*) hMultiPoly; TransformCutlineToSource((GDALDatasetH) pSrcDS, (void*)hMultiPoly, &(psWO->papszWarpOptions), papszTO ); GDALWarpOperation oWO; if (oWO.Initialize(psWO) != CE_None) { if(pProgress != NULL) pProgress->SetProgressTip("转换参数错误!"); GDALClose((GDALDatasetH) pSrcDS); GDALClose((GDALDatasetH) pDstDS); return RE_PARAMERROR; } oWO.ChunkAndWarpImage(0, 0, iNewWidth, iNewHeigh); GDALDestroyGenImgProjTransformer(psWO->pTransformerArg); GDALDestroyWarpOptions( psWO ); GDALClose((GDALDatasetH) pSrcDS); GDALClose((GDALDatasetH) pDstDS); if(pProgress != NULL) pProgress->SetProgressTip("图像裁剪完成!"); return RE_SUCCESS; }
裁剪后的效果图就不贴了,希望对大家有用。
………………………………………………分割线 下面更新于2013-5-2…………………………………………………………
上面的代码中有两处粘贴错误,已经修改,同时有人问代码中有两个坐标转换的函数,就是行列号和投影坐标相互转换的函数,其实很简单,就是根据六参数计算各仿射变换就OK,下面把代码贴出来。
bool Projection2ImageRowCol(double *adfGeoTransform, double dProjX, double dProjY, int &iCol, int &iRow) { try { double dTemp = adfGeoTransform[1]*adfGeoTransform[5] - adfGeoTransform[2]*adfGeoTransform[4]; double dCol = 0.0, dRow = 0.0; dCol = (adfGeoTransform[5]*(dProjX - adfGeoTransform[0]) - adfGeoTransform[2]*(dProjY - adfGeoTransform[3])) / dTemp + 0.5; dRow = (adfGeoTransform[1]*(dProjY - adfGeoTransform[3]) - adfGeoTransform[4]*(dProjX - adfGeoTransform[0])) / dTemp + 0.5; iCol = static_cast<int>(dCol); iRow = static_cast<int>(dRow); return true; } catch(...) { return false; } } bool ImageRowCol2Projection(double *adfGeoTransform, int iCol, int iRow, double &dProjX, double &dProjY) { //adfGeoTransform[6] 数组adfGeoTransform保存的是仿射变换中的一些参数,分别含义见下 //adfGeoTransform[0] 左上角x坐标 //adfGeoTransform[1] 东西方向分辨率 //adfGeoTransform[2] 旋转角度, 0表示图像 "北方朝上" //adfGeoTransform[3] 左上角y坐标 //adfGeoTransform[4] 旋转角度, 0表示图像 "北方朝上" //adfGeoTransform[5] 南北方向分辨率 try { dProjX = adfGeoTransform[0] + adfGeoTransform[1] * iCol + adfGeoTransform[2] * iRow; dProjY = adfGeoTransform[3] + adfGeoTransform[4] * iCol + adfGeoTransform[5] * iRow; return true; } catch(...) { return false; } }