• Google Earth Engine城市水体提取


    Google Earth Engine城市水体提取

    大家都知道城市水体提取相比较于山区,丘陵的地区,肯定是比较难的,为什么呢,因为城市水体有很多高层建筑导致的阴影,这个就非常复杂了,而且现在很多高分影像只有可见光和近红外波段,那么我们如何准确提取城市水体呢?

    Remoe Sensing2018年刊发了一篇城市水体高分影像自动提取算法(Two-Step Urban Water Index (TSUWI): A New Technique for High-Resolution Mapping of Urban SurfaceWater [J]Remote Sensing,2018),初步看来,效果还行,在高分二号上面效果不错,我再想,如果对于开源的哨兵、Landsat如何?这些是中等分辨率影像,能做到吗?

    话不多说,利用GEE,直接编码,实验结果如下(以2018年10月的北京某景Sentinel2影像为例):

    (a) 这是原始影像

    (b) 这是城市水体指数

    (c) 这是城市阴影指数

    (d) 这是城市水体提取结果,蓝色为水体

    其中城市水体指数和城市阴影指数计算公式如下所示:

     

    我把最终成果发布成了APPengine(https://wang749195.users.earthengine.app/view/urbanwaterextraction),大家可以直接在web上看,总的来说,实验结果还是不错的,去掉了阴影现象,这篇文章出自中科院遥感所,在此申明,值得一读,后续我会发布C++软件版本,Matlab版本,以及Python版本。我个人的开发思路是,首先用GEE实现,如果GEE不好实现,就用matlab或者python实现第一遍,效果可以,能工程应用,立马就用GDAL+C++打包成工程源代码,我感觉这样会节省时间,且不会造成时间浪费。

    接着上面讲,我们用c++来实现一遍,使用GDAL读写影像,先把这两个函数写上来:

    /*栅格影像读取,返回数据指针
    * imgPath:图像位置
    * 返回float类型的数据指针
    */
    void readImage(char *imgpath, imgData *IMG, int bandindex) {
    
        GDALDataset *img = (GDALDataset*)GDALOpen(imgpath, GA_ReadOnly);
        if (img != NULL) {
    
            int imgWidth = img->GetRasterXSize();   //图像宽度,特别注意:对应matlab中的行
            int imgHeight = img->GetRasterYSize();  //图像高度,特别注意:对应matlab中的列
            int bandNum = img->GetRasterCount();    //波段数
    
            IMG->imgH = imgHeight;
            IMG->imgW = imgWidth;
    
            GDALRasterBand *poBand;
            
            poBand = img->GetRasterBand(bandindex);  //灰度一个波段
    
            img->GetGeoTransform(IMG->adfGeoTransform); // 变换参数
    
            int size = imgWidth*imgHeight;
            IMG->pData = new float[size]; //分配缓冲区空间
    
            //读取
            poBand->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, IMG->pData,
                imgWidth, imgHeight, GDT_Float32, 0, 0);
    
            GDALClose(img); // 释放内存
        }
    }
    
    /*写出栅格影像
    * imgPath:输出影像位置
    * adfGeoTransform:变换参数
    * IMG:导出的影像数组
    */
    void writeImage(char *imgPath, float *Img, int nImgSizeX, int  nImgSizeY, int nBandCount, double *adfGeoTransform) {
        GDALDataset *poDataset2; //待创建的GDAL数据集 
        GDALDriver *poDriver;    //驱动,用于创建新的文件
    
                                 //创建新文件
        poDriver = GetGDALDriverManager()->GetDriverByName("GTiff");
    
        //获取格式类型
        char **papszMetadata = poDriver->GetMetadata();
    
        //特别注意,数据类型要与后面的写出类型要保持一致
        poDataset2 = poDriver->Create(imgPath, nImgSizeX, nImgSizeY, nBandCount, GDT_Float32, papszMetadata);
        //坐标赋值
        poDataset2->SetGeoTransform(adfGeoTransform);
    
        //将图像数据写入新图像中
        poDataset2->RasterIO(GF_Write, 0, 0, nImgSizeX, nImgSizeY,
            Img, nImgSizeX, nImgSizeY, GDT_Float32, nBandCount, 0, 0, 0, 0);
    
        GDALClose(poDataset2);
        delete poDriver;
    }

    然后就是我们的USI,UWI计算公式,贴上来:

    // 计算UWI指数
    void UWI_cal(float *rband, float *gband, float *nirband,float *UWI,int width,int length) {
        int Length = width*length;
        
        for (int i = 0; i < Length; i++) {
            UWI[i] = (gband[i] - 1.1*rband[i] - 5.2*nirband[i] + 0.4) /
                abs(gband[i] - 1.1*rband[i] - 5.2*(nirband[i]));
        }
    }
    
    // 计算USI指数
    void USI_cal(float *rband, float *gband, float *bband, float *nirband, float *USI, int width, int length) {
        int Length = width*length;
    
        for (int i = 0; i < Length; i++) {
            USI[i] = 0.25*gband[i] / rband[i] - 0.57*nirband[i] / 
                gband[i] - 0.83*bband[i] / gband[i] + 1.0;
    
        }
    }

    然后就是我们的影像数据结构:

    /*可见光与近红外波段数据结构
    */
    struct imgData {
        float *pData;
    
        int imgH;
        int imgW;
        double adfGeoTransform[6];
    };

    last but not least,就是我们的main函数:

    int main()
    {
        //必须先注册一个!
        GDALAllRegister();
        CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO");
    
        char *ImgPath = "C:\Users\Administrator\Desktop\UrbanWater\SentinelImg.tif";
    
        // 读取蓝波段
        imgData *B = new imgData;
        readImage(ImgPath, B, 1);
    
        // 读取绿波段
        imgData *G = new imgData;
        readImage(ImgPath, G, 2);
    
        // 读取红波段
        imgData *R = new imgData;
        readImage(ImgPath, R, 3);
    
        // 读取近红外波段
        imgData *NIR = new imgData;
        readImage(ImgPath, NIR, 4);
    
        printf("读取影像成功!
    ");
    
        int width = B->imgW;
        int height = B->imgH;
    
        float *USI = new float[width*height];
        float *UWI = new float[width*height];
    
        UWI_cal(R->pData, G->pData, NIR->pData, UWI, width, height);
        USI_cal(R->pData, G->pData, B->pData, NIR->pData, USI, width, height);
    
        float T1 = -0.1;
        float T2 = -0.2;
        float *UrbanWater = new float[width*height];
        UrbanWaterExtraction(T1, T2, UWI, USI, UrbanWater, width, height);
    
        char *savePath = "C:\Users\Administrator\Desktop\UrbanWater\urbanwater.tif";
        writeImage(savePath, UrbanWater, width, height, 1, R->adfGeoTransform);
        printf("提取水体成功!
    ");
    
        // 清空内存
        delete []NIR->pData;
        delete []R->pData;
        delete []G->pData;
        delete []B->pData;
        delete []UrbanWater;
        delete []USI;
        delete []UWI;
        delete NIR, R, G, B;
        system("pause");
    
    }

    还是上一张c++搞出来的城市水体图吧:

    可以看到,GEE与c++效果几乎一样,但是GEE的栅格渲染,还是非常值得国产软件学习!

    (打个小广告,本文兼职软件开发,qq1044625113)。

  • 相关阅读:
    【科技】扩展Lucas随想
    【NOI 2018】屠龙勇士(扩欧)
    【NOI 2018】冒泡排序(组合数学)
    【NOI 2018】归程(Kruskal重构树)
    【APIO 2018】铁人两项(圆方树)
    【科技】KD-tree随想
    UOJ#207. 共价大爷游长沙 LCT
    UOJ#23. 【UR #1】跳蚤国王下江南 仙人掌 Tarjan 点双 圆方树 点分治 多项式 FFT
    UOJ#33. 【UR #2】树上GCD 点分治 莫比乌斯反演
    UOJ#191. 【集训队互测2016】Unknown 点分治 分治 整体二分 凸包 计算几何
  • 原文地址:https://www.cnblogs.com/wzp-749195/p/9962498.html
Copyright © 2020-2023  润新知