• 【Python&RS】基于GDAL修改栅格数据的DN值


            遥感工作者离不开栅格数据,有时候我们可能需要修改栅格数据的值,但ENVI和ArcGIS中并没有直接修改DN值的工具,只有栅格计算器、Band math这些工具去计算整个波段的值,或者Edit Classification Image工具可以修改ENVI分类后的像元值,但这个工具只对分类格式有效,博主整不明白怎么把单波段数据转为分类格式,所以这些工具都无法满足我们的需求。

            今天跟大家分享一下如何使用Python的GDAL库将栅格数据中特定的DN值修改成我们想要的。

    一、安装库

    1. import os
    2. import numpy as np
    3. from osgeo import gdal

    二、读取栅格数据信息

            这一步可有可无,只是让你了解一下栅格数据的基本信息,如投影信息、长度、宽度等。

    1. def Get_data(filepath):
    2. ds = gdal.Open(filepath) # 打开数据集dataset
    3. ds_width = ds.RasterXSize # 获取数据宽度
    4. ds_height = ds.RasterYSize # 获取数据高度
    5. ds_bands = ds.RasterCount # 获取波段数
    6. ds_geo = ds.GetGeoTransform() # 获取仿射地理变换参数
    7. ds_prj = ds.GetProjection() # 获取投影信息
    8. print("影像的宽度为:" + str(ds_width))
    9. print("影像的高度为:" + str(ds_height))
    10. print("仿射地理变换参数为:" + str(ds_geo))
    11. print("投影坐标系为:" + str(ds_prj))
    12. # data = ds.ReadAsArray(0, 0, ds_width, ds_height) # 以数组的形式读取整个数据集

    三、修改DN值

            我这里写的函数,只适用于修改单波段的栅格影像,如果需要修改多波段就自己加一个循环。其实原理都一样,就是将波段读成数组,然后通过索引读取第几行第几列像元的值,然后判断这个值是否为你想要修改的值,如果是,就将其赋予新的值。

    1. def Modify_value(filepath, out_path, original, target):
    2. print("-----正在进行DN值的修改-----")
    3. ds = gdal.Open(filepath) # 打开数据集dataset
    4. ds_width = ds.RasterXSize # 获取数据宽度
    5. ds_height = ds.RasterYSize # 获取数据高度
    6. ds_geo = ds.GetGeoTransform() # 获取仿射地理变换参数
    7. ds_prj = ds.GetProjection() # 获取投影信息
    8. array_band = ds.GetRasterBand(1).ReadAsArray(0, 0, ds_width, ds_height).astype(np.float64)
    9. # 读取第一个波段全部
    10. for row in range(0, ds_height):
    11. # 循环当前波段的行
    12. for col in range(0, ds_width):
    13. # 循环当前波段的列
    14. if array_band[row][col] == original:
    15. # 判断第row行第col列的DN值是否为需要修改的值
    16. array_band[row][col] = target
    17. # 修改该值
    18. else:
    19. continue
    20. driver = gdal.GetDriverByName('GTiff') # 载入数据驱动,用于存储内存中的数组
    21. ds_result = driver.Create(out_path, ds_width, ds_height, bands=1, eType=gdal.GDT_Float64)
    22. # 创建一个数组,宽高为原始尺寸
    23. ds_result.SetGeoTransform(ds_geo) # 导入仿射地理变换参数
    24. ds_result.SetProjection(ds_prj) # 导入投影信息
    25. ds_result.GetRasterBand(1).SetNoDataValue(0) # 将无效值设为0
    26. ds_result.GetRasterBand(1).WriteArray(array_band) # 将结果写入数组
    27. del ds_result
    28. # 删除内存中的结果,否则结果不会写入图像中
    29. print("计算完成")

    四、完整代码

    1. # -*- coding: utf-8 -*-
    2. """
    3. @Time : 2023/9/8 8:51
    4. @Auth : RS迷途小书童
    5. @File :Modifying Raster Data DN Values.py
    6. @IDE :PyCharm
    7. @Purpose:修改栅格数据DN值
    8. """
    9. import os
    10. import numpy as np
    11. from osgeo import gdal
    12. def Get_data(filepath):
    13. ds = gdal.Open(filepath) # 打开数据集dataset
    14. ds_width = ds.RasterXSize # 获取数据宽度
    15. ds_height = ds.RasterYSize # 获取数据高度
    16. ds_bands = ds.RasterCount # 获取波段数
    17. ds_geo = ds.GetGeoTransform() # 获取仿射地理变换参数
    18. ds_prj = ds.GetProjection() # 获取投影信息
    19. print("影像的宽度为:" + str(ds_width))
    20. print("影像的高度为:" + str(ds_height))
    21. print("仿射地理变换参数为:" + str(ds_geo))
    22. print("投影坐标系为:" + str(ds_prj))
    23. # data = ds.ReadAsArray(0, 0, ds_width, ds_height) # 以数组的形式读取整个数据集
    24. def Modify_value(filepath, out_path, original, target):
    25. print("-----正在进行DN值的修改-----")
    26. ds = gdal.Open(filepath) # 打开数据集dataset
    27. ds_width = ds.RasterXSize # 获取数据宽度
    28. ds_height = ds.RasterYSize # 获取数据高度
    29. ds_geo = ds.GetGeoTransform() # 获取仿射地理变换参数
    30. ds_prj = ds.GetProjection() # 获取投影信息
    31. array_band = ds.GetRasterBand(1).ReadAsArray(0, 0, ds_width, ds_height).astype(np.float64)
    32. # 读取第一个波段全部
    33. for row in range(0, ds_height):
    34. # 循环当前波段的行
    35. for col in range(0, ds_width):
    36. # 循环当前波段的列
    37. if array_band[row][col] == original:
    38. # 判断第row行第col列的DN值是否为需要修改的值
    39. array_band[row][col] = target
    40. # 修改该值
    41. else:
    42. continue
    43. driver = gdal.GetDriverByName('GTiff') # 载入数据驱动,用于存储内存中的数组
    44. ds_result = driver.Create(out_path, ds_width, ds_height, bands=1, eType=gdal.GDT_Float64)
    45. # 创建一个数组,宽高为原始尺寸
    46. ds_result.SetGeoTransform(ds_geo) # 导入仿射地理变换参数
    47. ds_result.SetProjection(ds_prj) # 导入投影信息
    48. ds_result.GetRasterBand(1).SetNoDataValue(0) # 将无效值设为0
    49. ds_result.GetRasterBand(1).WriteArray(array_band) # 将结果写入数组
    50. del ds_result
    51. # 删除内存中的结果,否则结果不会写入图像中
    52. print("计算完成")
    53. if __name__ == "__main__":
    54. os.environ['PROJ_LIB'] = 'G:/Anaconda/envs/pyDL/Lib/site-packages/osgeo/data/proj'
    55. os.environ['GDAL_DATA'] = 'G:/Anaconda/envs/pyDL/Lib/site-packages/osgeo/data'
    56. # 添加PROJ至环境变量,消除警告
    57. file_path = r"B:\1m_xiugai.tif" # 输入的栅格数据路径
    58. out_path1 = r"B:\1m_xiugai1.tif" # 导出的文件路径
    59. data1 = int(input("请输入需要修改的DN值:"))
    60. data2 = int(input("请输入目标DN值 :"))
    61. Get_data(file_path) # 执行函数,获取影像基本信息
    62. print("\n")
    63. print("--------------DN值修改--------------")
    64. Modify_value(file_path, out_path1, data1, data2) # 执行函数,修改DN值

            今天的分享就到这里了,大家需要注意的是,我这段代码只适用于单波段数据且想要修改的值只有一种时,如你想要将所有DN值等于1的像元全部改成0,就可以直接使用我的点吗改数据路径,然后再输入1和0就可以了(因为我的任务就是将分类数据(DN值为0,1,2,3,4)中分错的部分改成正确的)。不要问为什么不使用工具,因为我手里的分类数据不是ENVI支持的分类格式(泪目)。

            如果大家在学习Python或者RS时有什么问题,可以随时留言交流!如果大家对批量处理有兴趣同样可以留言给博主,博主会分享相关代码以供学习!

  • 相关阅读:
    网络原理~初识
    LeetCode-828. 统计子串中的唯一字符【哈希表,字符串,动态规划】
    基于微信小程序的校园活动平台的设计与实现
    “比特币市场风起云涌:第三季度报告揭示表现和未来趋势“
    北斗导航 | 最小二乘模糊度估计方法(LSAR:Least Squares Ambiguity Resolution)
    指针和数组笔试题的透析
    SpringIoC之Bean生命周期源码主要流程解析
    计算sum=1+2...+n,要求number和sum的类型都是int,且sum在32位以内~
    渗透测试与攻防对抗-信息收集与社工技巧
    前端入门--JavaScript篇
  • 原文地址:https://blog.csdn.net/m0_56729804/article/details/132756340