如何使用GDAL进行AOI裁剪

来源:互联网 发布:代步滑板哪种好 知乎 编辑:程序博客网 时间:2024/04/30 09:14

    在工作中,会经常使用一个行政区的矢量图去裁剪一个遥感影像图,得到该行政区的影像图,一般的商业遥感软件都具有这个功能。今天就是用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 voidLoadCutline( 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 voidTransformCutlineToSource( 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;}}

原创粉丝点击