首页 > 编程语言 >C# 读取四波段遥感影像生成植被覆盖度栅格(TIF)

C# 读取四波段遥感影像生成植被覆盖度栅格(TIF)

时间:2024-11-15 17:56:16浏览次数:1  
标签:actualHeight C# double 栅格 blockSize var TIF actualWidth Math

GDAL使用

使用gdal.netcore来读取和生成栅格文件。
优点:自带gdal运行时相关文件,不用额外再安装gdal库
缺点:导致发布的文件变大很多,比如Win+Linux的运行时加起来就超过了400M,所以最好是按需加载对应的运行时。比如DEBUG是运行在win的,就添加MaxRev.Gdal.WindowsRuntime.Minimal包,然后Release环境是运行在linux x64的,就添加MaxRev.Gdal.LinuxRuntime.Minimal.x64

系统 运行时
windows MaxRev.Gdal.WindowsRuntime.Minimal
linux-x64 MaxRev.Gdal.WindowsRuntime.Minimal
linux-arm64 MaxRev.Gdal.WindowsRuntime.Minimal
mac-x64 MaxRev.Gdal.WindowsRuntime.Minimal
mac-arm64 MaxRev.Gdal.WindowsRuntime.Minimal

如图所示,仅支持64位系统。
还有就是太老的系统不支持,比如centOS7,那就只能是源码编译安装Gdal(比较折腾的过程),然后再单独修改gdal注册相关的代码。不过老系统更推荐的做法是使用docker,基础容器镜像使用较新的系统版本,比如debian 12之类的。

可以通过msbuild condition比较编译时的提供的环境变量来引用不同的运行时包

  <ItemGroup Condition="'$(TargetRuntime)'=='win'">
    <PackageReference Include="MaxRev.Gdal.WindowsRuntime.Minimal" />
  </ItemGroup>
  <ItemGroup Condition="'$(TargetRuntime)'=='lin-x64'">
    <PackageReference Include="MaxRev.Gdal.LinuxRuntime.Minimal.x64" />
  </ItemGroup>

然后在dotnet build或publish时传入对应的参数 -p:TargetRuntime=win-p:TargetRuntime=lin-x64

计算FVC

计算方法如下,读取卫星遥感影像的3、4波段,计算NDVI值中的5%和95%值作为极值,然后再根据极值计算FVC。
由于单个tif文件可能极大,所以第一次计算NDVI极值时不存计算出的NDVI值(一个省的10m分辨率影像就近100G了)
后续计算FVC时每次只读取缓冲区大小的数据进行计算。
缺点是串行计算十分缓慢,直接使用Task.WhenAll又会导致读取栅格报错protected memory。可能在读取和写入栅格数据时进行加锁处理是可行的。

参数是遥感影像栅格和FVC结果栅格的路径,返回值是遥感影像栅格的大小、坐标系、地理变换等信息。

    /// <summary>
    /// 基于哨兵四波段遥感卫星影像(B\G\R\NIR),计算植被归一化指数(NDVI) 基于NDVI,计算植被覆盖度FVC
    /// </summary>
    /// <param name="tifPath">遥感影像路径</param>
    /// <param name="resultTifPath">FVC栅格路径</param>
    private (double[] transform, int xSize, int ySize, SpatialReference srs, string projection) Fvc(string tifPath,
        string resultTifPath)
    {
        Console.WriteLine($"{DateTime.Now: mm:ss} 开始计算FVC");
        using var ds = Gdal.Open(tifPath, Access.GA_ReadOnly);
        using var driver = Gdal.GetDriverByName("GTiff");
        using var bandR = ds.GetRasterBand(3);
        using var bandNIR = ds.GetRasterBand(4);
        //NDVI=float(band4-band3)/(band4+band3)
        //FVC=(NDVI-bandmin)/(bandmax-bandmin) bandmin取NDVI中累计值为最接近5%的值,bandmax取NDVI中累计值为最接近95%的值,FVC计算结果应为0~1区间,计算结果中存在小于0的值归为0,存在大于1的值归为1
        //设置缓冲区2048*2048,每次只从4个波段中读取对应缓冲区的数据进行计算
        var xSize = ds.RasterXSize;
        var ySize = ds.RasterYSize;
        var blockSize = 2048;
        var xCount = (int)Math.Ceiling((float)xSize / blockSize);
        var yCount = (int)Math.Ceiling((float)ySize / blockSize);
        bandR.GetNoDataValue(out var noDataValue, out _);
        if (File.Exists(resultTifPath)) File.Delete(resultTifPath);
        var transform = new double[6];
        ds.GetGeoTransform(transform);
        ConcurrentDictionary<double, int> nvdiSet = new();
        for (int y = 0; y < yCount; y++)
        {
            var bufferR = new List<double[]>(xCount);
            var bufferNIR = new List<double[]>(xCount);
            for (int x = 0; x < xCount; x++)
            {
                var offsetX = x * blockSize;
                var offsetY = y * blockSize;
                var actualWidth = Math.Min(blockSize, Math.Max(xSize - offsetX, 0));
                var actualHeight = Math.Min(blockSize, Math.Max(ySize - offsetY, 0));
                bufferR.Add(new double[actualWidth * actualHeight]);
                bandR.ReadRaster(offsetX, offsetY, actualWidth, actualHeight, bufferR[x], actualWidth, actualHeight, 0,
                    0);
                bufferNIR.Add(new double[actualWidth * actualHeight]);
                bandNIR.ReadRaster(offsetX, offsetY, actualWidth, actualHeight, bufferNIR[x], actualWidth, actualHeight,
                    0, 0);
            }

            //计算NDVI
            for (var i = 0; i < xCount; i++)
            {
                var index = i;
                for (var j = 0; j < bufferR[index].Length; j++)
                {
                    var r = bufferR[index][j];
                    var nir = bufferNIR[index][j];
                    if (double.IsNaN(r) || double.IsNaN(nir)) continue;
                    var ndvi = (nir - r) / (nir + r);
                    if (double.IsNaN(ndvi)) continue;
                    nvdiSet.AddOrUpdate(Math.Round(ndvi, 5), 1, (key, oldValue) => oldValue + 1);
                }
            }
        }

        //计算5%和95%对应的Count
        var totalCount = nvdiSet.Sum(d => d.Value);
        var ndviMin = GetPercentCount(totalCount, 0.05, nvdiSet.OrderBy(d => d.Key));
        var ndviMax = GetPercentCount(totalCount, 0.05, nvdiSet.OrderByDescending(d => d.Key));

        using var dsResult = driver.Create(resultTifPath, xSize, ySize, 1, DataType.GDT_Float32, null);
        var projection = ds.GetProjection();
        dsResult.SetProjection(projection);
        dsResult.SetGeoTransform(transform);
        var srs = ds.GetSpatialRef();
        dsResult.SetSpatialRef(srs);
        using var bandResult = dsResult.GetRasterBand(1);
        bandResult.SetNoDataValue(noDataValue);

        var ndviDiff = ndviMax - ndviMin;
        for (int y = 0; y < yCount; y++)
        {
            //计算NDVI
            for (var i = 0; i < xCount; i++)
            {
                var index = i;
                var y1 = y;
                var offsetX = index * blockSize;
                var offsetY = y1 * blockSize;
                var actualWidth = Math.Min(blockSize, Math.Max(xSize - offsetX, 0));
                var actualHeight = Math.Min(blockSize, Math.Max(ySize - offsetY, 0));
                var bufferR = new double[actualWidth * actualHeight];
                bandR.ReadRaster(offsetX, offsetY, actualWidth, actualHeight, bufferR, actualWidth, actualHeight, 0,
                    0);
                var bufferNIR = new double[actualWidth * actualHeight];
                bandNIR.ReadRaster(offsetX, offsetY, actualWidth, actualHeight, bufferNIR, actualWidth,
                    actualHeight,
                    0, 0);
                var dataResult = new double[bufferR.Length];
                for (var j = 0; j < bufferR.Length; j++)
                {
                    var r = bufferR[j];
                    var nir = bufferNIR[j];
                    if (double.IsNaN(r) || double.IsNaN(nir)) continue;
                    var ndvi = (nir - r) / (nir + r);
                    if (double.IsNaN(ndvi)) continue;
                    var fvc = (ndvi - ndviMin) / ndviDiff;
                    dataResult[j] =
                        fvc < 0 ? NoDataValue :
                        fvc > 1 ? 1 :
                        fvc;
                }

                bandResult.WriteRaster(offsetX, offsetY, actualWidth, actualHeight, dataResult, actualWidth,
                    actualHeight, 0, 0);
            }
        }

        return (transform, xSize, ySize, srs, projection);
    }

标签:actualHeight,C#,double,栅格,blockSize,var,TIF,actualWidth,Math
From: https://www.cnblogs.com/turingguo/p/18547683

相关文章

  • 【SpringBoot每日学习 - 第二天】SpringApplication 启动类:方法篇一
    SpringApplication类是SpringBoot应用程序的核心类之一,负责启动和初始化整个SpringBoot应用。通过调用SpringApplication.run()方法,SpringBoot会启动嵌入式的Web服务器(如Tomcat)并创建Spring容器。SpringApplication类具有一系列方法和配置项,允许开发者自定......
  • 【SpringBoot每日学习 - 第一天】SpringApplication 启动类:属性篇
    SpringApplication类是SpringBoot应用启动的核心类之一,包含了大量的属性,控制着应用启动的各个方面。这些属性涵盖了从配置环境、应用上下文类型、Banner显示、启动日志、事件监听等多个方面。以下是SpringApplication类中重要属性的详细说明及其用途:静态属性DEFAUL......
  • 数据结构程序设计(C语言)校园导游系统
    使用队列以及深度搜索算法,加上dos命令调用图片的校园导游系统#define_CRT_SECURE_NO_WARNINGS#include<stdio.h>#include<stdlib.h>#include<string.h>#include<Windows.h>structgraph{ intnode_num;//顶点数 intedge_num;//边数 charnode_name[20][50......
  • Your last search took too long to run. This is probably a combination of at leas
    Yourlastsearchtooktoolongtorun.Thisisprobablyacombinationofatleasttwothings:1.Thesizeofthecorpus(largercorporalikeiWeborNOWareslowerthansmallercorpora),or2.Thefrequencyofthewordsorstringsinyoursearch(atleasto......
  • C语言经典100题 学习笔记(更新中)
    第一题:有1、2、3、4四个数字,能组成多少互不相同且无重复数字的三位数?都是多少?#include<stdio.h>//有1、2、3、4四个数字//能组成多少互不相同且无重复数字的三位数?都是多少?intmain01(){ inta=0; intb=0; intc=0; intcount=0; for(a=1;a<5;a++) {......
  • Dictionnaire de l’Académie française, 9e édition (actuelle)
     Dictionnairedel’AcadémieFrançaisehttps://www.dictionnaire-academie.fr/article/A9B1826  France'snewdictionarystrugglestokeepupwiththetimeshttps://www.bbc.com/news/articles/cly03ve799go  L’Académiefrançaisemetunpointfinal......
  • 基于米尔NXP i.MX93开发板OpenCV的相机捕捉视频进行人脸检测
    本篇测评由优秀测评者“eefocus_3914144”提供。 本文将介绍基于米尔电子MYD-LMX93开发板(米尔基于NXPi.MX93开发板)的基于OpenCV的人脸检测方案测试。OpenCV提供了一个非常简单的接口,用于相机捕捉一个视频(我用的电脑内置摄像头)1、安装python3-opencvaptinstallpython3-......
  • handycontrol NotifyIcon ContextMenu
    <hc:NotifyIconText="xxxx程序"Visibility="Collapsed"Name="notifyIcon"Icon="/Assets/Ico/title.ico"Click="NotifyIcon_Click"MouseRightButtonDown="notifyIcon_MouseRightButtonDown"......
  • 仓颉_Cangjie-函数式编程
    函数定义CC语言中,函数的声明告诉编译器函数的名称、返回类型和参数列表。函数的定义则提供了函数的实际体C++返回类型函数名(参数列表){//函数体//执行的操作//返回返回类型的值}Java函数的定义分为函数的声明和函数的实现Rust使用fn关键字定义函数。函......
  • css制作消息框
     css如下:        .msg{            width:200px;height:50px;            margin-bottom:20px;            background:#20B2AA;color:#fff;            text-align:center;line-height:50px;......