Python 创建地形图

原始地 DEM。

没有任何

火山口湖 (OR) 区域的起始 DEM。数据来自 NASA

DEM 本身非常美丽,但我们先进行分层。

将自定义色彩图应用于 DEM

对于我在 ArcGIS Pro 版本中所做的初始高程样式着色,我使用了“高程 #7”。在 matplotlib 中可用的标准颜色图中,我没有看到任何接近此方案的内容,因此我决定基于它创建自定义颜色图。

首先,我截取了 ArcGIS Pro 色彩图的屏幕截图并将其保存为 PNG 格式。

没有任何

ArcGIS Pro 中海拔 #7 的屏幕截图。

接下来我编写了以下代码来从中提取颜色并返回颜色图。

from PIL import Image
from matplotlib.colors import LinearSegmentedColormap
import matplotlib.pyplot as pltdef extract_colors_from_image(image_path: str) -> np.ndarray:"""Extract colors from the center line of an image."""image = Image.open(image_path).convert('RGB')width, height = image.sizecolors = [image.getpixel((x, int(height / 2))) for x in range(width)]return np.array(colors) / 255.0colors = extract_colors_from_image('input/ramp.png')
color_ramp = LinearSegmentedColormap.from_list("custom_ramp", colors)

为了检查它,我们可以使用 matplotlib 显示颜色图。

plt.imshow([colors], aspect='auto')
plt.title('Custom Color Ramp')
plt.axis('off')
plt.show()

没有任何

自定义地形色彩图。

为了查看该区域的外观,让我们加载 DEM 并预览结果。

from osgeo import gdal
import numpy as npdef normalize_array(array: np.ndarray, lower_percentile: float = 0.0, upper_percentile: float = 100.0) -> np.ndarray:"""Normalize array values to a specified percentile range."""lower_val = np.percentile(array, lower_percentile)upper_val = np.percentile(array, upper_percentile)clipped = np.clip(array, lower_val, upper_val)return (clipped - lower_val) / (upper_val - lower_val)dem_path = 'input/oblique-clip.tif'
dem_data = gdal.Open(dem_path).ReadAsArray()
normalized_dem = normalize_array(dem_data)plt.imshow(normalized_dem, cmap=color_ramp)
plt.colorbar()
plt.title('DEM with Custom Color Ramp')
plt.show()

没有任何

这看起来很棒。它与 ArcGIS Pro 中的外观几乎完全相同。让我们开始吧。

def apply_colormap(array: np.ndarray, cmap: LinearSegmentedColormap) -> np.ndarray:"""Apply a matplotlib colormap to an array."""colormap = plt.get_cmap(cmap) if isinstance(cmap, str) else cmapnormed_data = (array - array.min()) / (array.max() - array.min())colored = colormap(normed_data)return (colored[:, :, :3] * 255).astype('uint8')terrain_dem = apply_colormap(normalized_dem, color_ramp)
terrain_image = Image.fromarray(terrain_dem)
terrain_image.save('output/1-terrain.png')

没有任何

应用了自定义颜色图的地形。

地形变暖

在 John Nelson 的教程中,他使用 ArcGIS Pro 中的“inferno”色图作为他的变暖层。matplotlib 中的等效色图是“plasma”色图。我们可以使用它的名称将其应用于 DEM。

warm_dem = apply_colormap(normalized_dem, 'plasma')
warm_image = Image.fromarray(warm_dem)
warm_image.save('output/2-warming.png')

没有任何

血浆中的 DEM。

现在我们得到了两个版本的 DEM:自定义颜色图和等离子版本。在 ArcGIS Pro 中,使用柔光混合模式将它们组合在一起。柔光结合了图层的亮度值并增强了对比度。在 Python 中,我们可以使用 PIL 库来执行此操作。

from PIL import ImageChopswarming_blended = ImageChops.soft_light(terrain_image, warm_image)
warming_blended.save('output/3-warming_blended.png')

没有任何

使用柔和的光用等离子体加热海拔。

现在是时候去一些山影了。

应用传统山体阴影

我编写了两个函数来处理山体阴影生成。第一个函数生成单个山体阴影,第二个函数使用该函数生成并组合多个山体阴影(下一步将用于多方向山体阴影层)。我不确定这是否接近 ArcGIS Pro 内部发生的事情,但结果非常接近。

def generate_hillshade(dem_path: str, azimuth: float, altitude: float, zFactor: float = 1.0) -> np.ndarray:"""Generate a hillshade for a specific azimuth and altitude."""options = gdal.DEMProcessingOptions(format='GTiff', computeEdges=True, zFactor=zFactor, azimuth=azimuth, altitude=altitude)hillshade_path = f'output/temp_hillshade_{azimuth}_{altitude}.tif'gdal.DEMProcessing(hillshade_path, dem_path, 'hillshade', options=options)return gdal.Open(hillshade_path).ReadAsArray()def combine_hillshades(dem_path: str, azimuths: List[float], altitudes: List[float], weights: Optional[List[float]] = None) -> np.ndarray:"""Combine hillshades from multiple directions."""hillshades = [generate_hillshade(dem_path, az, alt) for az, alt in zip(azimuths, altitudes)]if weights is None:weights = np.ones(len(azimuths))weights = np.array(weights) / np.sum(weights)combined_hillshade = np.average(hillshades, axis=0, weights=weights)combined_hillshade = np.clip(combined_hillshade, 0, 255)return combined_hillshade

我没有费心清理中间的山体阴影,因为我想在玩输出时检查它们。

以下生成传统(单一方位角和高度)山体阴影,然后使用叠加混合模式将其混合到变暖的地形中。叠加会使暗区变暗,亮区变亮。

def rgb_image_from_array(array: np.ndarray) -> Image.Image:"""Create an RGB image from an array."""return Image.fromarray((array * 255 / array.max()).astype('uint8')).convert("RGB")traditional_hillshade = combine_hillshades(dem_path, [315], [45])
traditional_hillshade_image = rgb_image_from_array(traditional_hillshade)
traditional_hillshade_image.save('output/4-traditional_hillshade.png')
traditional_hillshade_blended = ImageChops.overlay(warming_blended, traditional_hillshade_image)
traditional_hillshade_blended.save('output/5-hillshade_blended.png')

没有任何

传统的山体阴影。

没有任何

使用叠加混合将传统的山体阴影融入到暖色图像中。

看起来不错并且仍然跟踪 ArcGIS Pro 版本。

应用多方向山体阴影

此步骤使用与上一步相同的函数,只是这次我们输入了四个不同的方位角和高度,然后一起取平均值。多方向山体阴影可以更好地模拟现实世界,从而产生更逼真的效果。

azimuths = [45, 135, 225, 315]
altitudes = [45, 45, 45, 45]
multidirectional_hillshade = combine_hillshades(dem_path, azimuths, altitudes)
multidirectional_hillshade_image = rgb_image_from_array(multidirectional_hillshade)
multidirectional_hillshade_image.save('output/6-multidirectional_hillshade.png')
multidirectional_hillshade_blended = ImageChops.multiply(traditional_hillshade_blended, multidirectional_hillshade_image)
multidirectional_hillshade_blended.save('output/7-blended-multidirectional_hillshade.png')

没有任何

多方向的山体阴影。真漂亮。

没有任何

使用乘法混合的多方向山体阴影。这是我最喜欢的中间步骤之一。

乘法将混合层的颜色值与基础层的颜色值相乘,从而产生颜色组合后的整体图像较暗。这会稍微放大阴影。

低光山体阴影

最后一个山体阴影是另一种传统的山体阴影,但光线角度较低。这个是用柔和的光线混合而成的。

low_light_hillshade = combine_hillshades(dem_path, [315], [25])
low_light_hillshade_image = rgb_image_from_array(low_light_hillshade)
low_light_hillshade_image.save('output/8-low_light_hillshade.png')
low_light_hillshade_blended = ImageChops.soft_light(multidirectional_hillshade_blended, low_light_hillshade_image)
low_light_hillshade_blended.save('output/9-low_light_hillshade_blended.png')

没有任何

低光山体阴影。

没有任何

低光山体阴影混合。

灯光

下一层是照明层,用于使山峰变亮并使山谷变暗。为此,我们可以使用 matplotlib 中现有的白色到黑色(二进制反转)颜色图。

lighting_dem = apply_colormap(normalized_dem, 'binary_r')
lighting_image = Image.fromarray(lighting_dem)
lighting_image.save('output/10-lighting.png')
lighting_blended = ImageChops.soft_light(low_light_hillshade_blended, lighting_image)
lighting_blended.save('output/11-lighting_blended.png')

没有任何

照明层。黑暗的山谷,明亮的山峰。就像在现实生活中一样。

没有任何

灯光与柔和的灯光融为一体。

雾时!

接下来的两个步骤是我最喜欢的。首先,喷雾。

雾层需要另一个自定义颜色图。它需要在低海拔处呈半透明白色,在海拔值过高之前迅速转变为完全透明的白色。要获得恰到好处的过渡需要进行一些实验。

colors = [(1.0, 1.0, 1.0, 0.85),(1.0, 1.0, 1.0, 0.45),(1.0, 1.0, 1.0, 0.15),(1.0, 1.0, 1.0, 0.0),(1.0, 1.0, 1.0, 0.0)
]
positions = [0.0, 0.15, 0.25, .35, 1.0]
mist_custom_cmap = LinearSegmentedColormap.from_list("custom_cmap", list(zip(positions, colors)))

colors 列表中的每个元素都是一个元组,包含 RGBA(红色、绿色、蓝色、Alpha)颜色分量的值。所有颜色都有三个 1,即白色。唯一的变化是 alpha 分量,它决定透明度。第一个记录 0.85 表示 85% 不透明(轻微透明度)。

位置列表指定了颜色列表中每种颜色在颜色图中的确切位置,范围从 0 到 1。每个位置值对应于渐变中的特定点,确定颜色之间的过渡发生的位置。在这个自定义颜色图中,我们逐渐将白色的不透明度从开始时的 85% 更改为完全透明,每次更改 35%,当我们从低海拔到高海拔范围的三分之一时,它实际上就完全透明了。

让我们应用它。

mist_dem = mist_custom_cmap(normalized_dem)
mist_image = Image.fromarray((mist_dem * 255).astype(np.uint8), 'RGBA')
mist_image.save("output/12-dem_with_transparent_mist.png")terrain_image = Image.open('output/11-lighting_blended.png').convert('RGBA')
combined_terrain_and_mist = Image.alpha_composite(terrain_image, mist_image).convert('RGB')
combined_terrain_and_mist.save('output/13-combined_terrain_and_mist.png')

没有任何

黑暗背景上弥漫着薄雾。

没有任何

地形上空雾气弥漫。

最后…山体阴影的坡度

最后一步,我们生成坡度栅格并应用“cividis”色彩图来突出显示陡峭区域。

def generate_slope_array(dem_path: str) -> Tuple[np.ndarray, Optional[float]]:"""Generate a slope array from a DEM."""dem_data = gdal.Open(dem_path, gdal.GA_ReadOnly)slope_ds = gdal.DEMProcessing('', dem_data, 'slope', format='MEM', scale=1)slope_band = slope_ds.GetRasterBand(1)slope_array = slope_band.ReadAsArray()no_data_value = slope_band.GetNoDataValue()if no_data_value is not None:mask = slope_array == no_data_valueslope_array = np.ma.masked_where(mask, slope_array)min_val = np.min(slope_array)max_val = np.max(slope_array)norm_slope_array = (slope_array - min_val) / (max_val - min_val)norm_slope_array = np.ma.filled(norm_slope_array, 0)return norm_slope_arraycividis_cmap = plt.get_cmap('cividis')
slope_dem = cividis_cmap(generate_slope_array(dem_path))
slope_image = rgb_image_from_array(slope_dem)
slope_image.save('output/14-slope.png', 'png')

没有任何

应用了 cividis 色彩图的斜率。

最终结果

最后一步是用柔和的光线将斜坡融入雾蒙蒙的地形。

slope_blended = ImageChops.soft_light(combined_terrain_and_mist, slope_image)
slope_blended.save('output/15-slope_blended.png')

没有任何

最终结果。

我对结果非常满意。仅使用几个 Python 库,我就能获得非常接近 ArcGIS Pro 版本的结果。

以下是一些特写镜头。

没有任何

雾霭!金脊从坡层而来。

没有任何

高地。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/27926.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《Operating System Concepts》阅读笔记:p180-p187

《Operating System Concepts》学习第 20 天,p180-p187 总结,总计 8 页。 一、技术总结 1.forke-join A strategy for thread creation in which the main parent thread creates (forks) one or more child threads and then waits for the children…

文心4.5,大模型下半场的野心之作

2025年开年,全球大模型竞赛进入白热化阶段。2月28日,百度宣布其文心大模型4.5将于3月16日正式上线,强调其原生多模态与深度思考能力,并计划于6月30日开源。这一动作不仅标志着百度技术路线的重大转向,更被视为中国大模…

transformer架构解析{前馈全连接层,规范化层,子层(残差)连接结构}(含代码)-4

目录 前言 前馈全连接层 学习目标 什么是前馈全连接层 前馈全连接层的作用 前馈全连接层代码实现 规范化层 学习目标 规范化层的作用 规范化层的代码实现 子层(残差)连接结构 学习目标 什么是子层(残差)连接结构 子层连…

Django视图与URLs路由详解

在Django Web框架中,视图(Views)和URLs路由(URL routing)是Web应用开发的核心概念。它们共同负责将用户的请求映射到相应的Python函数,并返回适当的响应。本篇博客将深入探讨Django的视图和URLs路由系统&am…

串口通讯基础

第1章 串口的发送和接收过程 1.1 串口接收过程 当上位机给串口发送(0x55)数据时,MCU的RX引脚接受到(0x55)数据,数据(0x55)首先进入移位寄存器。数据全部进入移位寄存器后,一次将(0x55)全部搬运…

kakfa-3:ISR机制、HWLEO、生产者、消费者、核心参数负载均衡

1. kafka内核原理 1.1 ISR机制 光是依靠多副本机制能保证Kafka的高可用性,但是能保证数据不丢失吗?不行,因为如果leader宕机,但是leader的数据还没同步到follower上去,此时即使选举了follower作为新的leader&#xff…

基于Linux系统的物联网智能终端

背景 产品研发和项目研发有什么区别?一个令人发指的问题,刚开始工作时项目开发居多,认为项目开发和产品开发区别不大,待后来随着自身能力的提升,逐步感到要开发一个好产品还是比较难的,我认为项目开发的目的…

STM32——DMA详解

目录 一:DMA简介 二:DMA基本结构 三:DMA实现过程 1.框图 2.DMA进行转运的条件 四:函数 一:DMA简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设存储器或者存储器和存储器之间的高速数据传输&…

告别卡顿,拥抱流畅!MemReduct——内存清理工具

先给安装包下载地址:MemReduct.exe下载,无脑下一步安装即可。 MemReduct 是一款出色的内存清理工具,以下是对它的详细介绍: 功能特点 高效内存清理:采用先进算法及系统底层 API,能智能清理系统缓存、应用…

告别GitHub连不上!一分钟快速访问方案

一、当GitHub抽风时,你是否也这样崩溃过? 😡 npm install卡在node-sass半小时不动😭 git clone到90%突然fatal: early EOF🤬 改了半天hosts文件,第二天又失效了... 根本原因:传统代理需要复杂…

指纹细节提取(Matlab实现)

指纹细节提取概述指纹作为人体生物特征识别领域中应用最为广泛的特征之一,具有独特性、稳定性和便利性。指纹细节特征对于指纹识别的准确性和可靠性起着关键作用。指纹细节提取,即从指纹图像中精确地提取出能够表征指纹唯一性的关键特征点,是…

【对话推荐系统综述】A Survey on Conversational Recommender Systems

文章信息: 发表于:ACM Computing Surveys 2021 原文链接:https://arxiv.org/abs/2004.00646 Abstract 推荐系统是一类软件应用程序,旨在帮助用户在信息过载的情况下找到感兴趣的项目。当前的研究通常假设一种一次性交互范式&am…

【0001】初识Java

Java是世界上最好的语言,没有之一!!! Java是世界上最好的语言,没有之一!!! Java是世界上最好的语言,没有之一!!! 重要的事情说三遍&am…

全向广播扬声器在油气田中的关键应用 全方位守护安全

油气田作为高风险作业场所,安全生产始终是重中之重。在紧急情况下,如何快速、有效地传达信息,确保人员安全撤离,是油气田安全管理的关键环节。全向广播扬声器凭借其全方位覆盖、高音质输出和强大的环境适应性,成为油气…

显式 GC 的使用:留与去,如何选择?

目录 一、什么是显式 GC? (一) 垃圾回收的基本原理 (二)显式 GC 方法和行为 1. System.gc() 方法 2. 显式 GC 的行为 (三)显式 GC 的使用场景与风险 1. JVM 如何处理显式 GC 2. 显式 GC…

基于vue框架的游戏商城系统cq070(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。

系统程序文件列表 项目功能:用户,分类,商品信息,游戏高手,游戏代练 开题报告内容 基于Vue框架的游戏商城系统开题报告 一、研究背景与意义 随着互联网技术的飞速发展和游戏产业的蓬勃兴起,游戏商城作为游戏产业链中的重要一环,迎来了前所…

【OpenCV】OpenCV指南:图像处理基础及实例演示

OpenCV 是一个功能强大且易于使用的库,广泛应用于图像处理和计算机视觉领域。从读取和显示图像,到颜色空间转换、图像缩放、翻转、边缘检测、高斯模糊、形态学操作以及图像平滑和绘制,本文详细介绍了 OpenCV 的基础使用方法,附带了…

网络安全数据富化 网络数据安全处理规范

本文件规定了网络运营者开展网络数据收集、存储、使用、加工、传输、提供、公开等数据处理的安全 技术与管理要求。 本文件适用于网络运营者规范网络数据处理,以及监管部门、第三方评估机构对网络数据处理进行 监督管理和评估。 部分术语和定义 数据(data&#x…

蓝桥杯备考:动态规划线性dp之下楼梯问题进阶版

老规矩,按照dp题的顺序 step1 定义状态表达 f[i]表示到第i个台阶的方案数 step2:推导状态方程 step3:初始化 初始化要保证 1.数组不越界 2.推导结果正确 如图这种情况就越界了,我们如果把1到k的值全初始化也不现实,会增加程序的时间复杂度…

springboot + mybatis-plus + druid

目录架构 config MyMetaObjectHandler.java package com.example.config;import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import org.apache.ibatis.reflection.MetaObject; import org.springframework.stereotype.Component;import java.util.Date;Com…