osgEarth 图像融合正片叠底

 * 需求:
* 高程渲染图 RGB.tif、 山体阴影图 AMP.tif
*
* 高程渲染图 rgb波段分别  乘以 山体阴影图r波段, 然后除以255(AI说  读取的纹理就已经归一化到了 0~1 范围,不用除以 255)。


本人遥感知识匮乏。

问了AI,以上 需求在许多商业软件上已实现。在 ArcGIS 和 QGIS 中制作 Hillshade Overlay 的核心逻辑是通过数字高程模型(DEM)生成地形阴影,再将其与其他图层叠加以增强立体感。

环境:Win10 64bit,  ARM R9 3900X ,Qt 5.15.2, C++,osgEarth3.7.0

1、C++ 结合 OpenCV实现。适合静态展示;

2、glsl实现。效率高。glsl 我也是抄、描、问,  零零碎碎 拼凑出来的,这里就想抛砖引玉,希望openGL大佬们来指导哈。

这完全是个笔记,借助AI拼凑出来的代码,希望遥感数据处理大牛们多多指教,

特别是osgEarth中的着色器编程。


无图无真相。先图后码。

原 彩色浮雕图:

山体阴影

在Qt 里面使用 C++  OpenCV运行出来的效果,源码在后面。

osgearth着色器, minLumFactor = 0.125时的效果,明显偏暗。

minLumFactor = 0.618

好,把之前实现的代码 和效果做个笔记。

代码如下:

#include "GenerateColorReliefThread.h"
#include "Global.h"
#include "MyFile/MyRaster.h"
#include "gdal_utils.h"
#include "osg/BlendFunc"
#include "qcoreapplication.h"#include "SettingsManager.h"#include "ProjectManage/MyProject.h"GenerateColorReliefThread::GenerateColorReliefThread( const QString &filePath, GDALDataset *dataset, const std::string &colorFilename, QObject *parent): QThread(parent), m_filePath(filePath), m_colorFilename(colorFilename), m_dataset(dataset)
{}GenerateColorReliefThread::~GenerateColorReliefThread()
{wait();
}bool GenerateColorReliefThread::createColorRelief( std::string &filePathColorRelief )
{CPLSetConfigOption("GDAL_NUM_THREADS", "ALL_CPUS"); // 启用所有CPU核心并行处理// 3) 调 GDALDEMProcessing 做 color-relief(或 hillshade)const char* dArgvStr = "-alpha -of GTiff";char**papszArgv = CSLTokenizeString(dArgvStr);// 处理选项GDALDEMProcessingOptions *psOptions = GDALDEMProcessingOptionsNew(papszArgv, nullptr);if (psOptions == nullptr){qDebugV5() <<"Failed to create processing options.";return false;}static std::atomic<int> tempFileCounter(0); // 避免内存文件名冲突filePathColorRelief = "/vsimem/color_relief_" + std::to_string(tempFileCounter++) + ".tif";// std::cout<<"memFileName: "<<filePathColorRelief<<std::endl;int pbUsageError = 0;GDALDatasetH hColorRelief = GDALDEMProcessing(filePathColorRelief.c_str(), m_dataset, "color-relief", m_colorFilename.c_str(), psOptions, &pbUsageError);if (!hColorRelief || pbUsageError != 0){qDebugV5() << "Processing failed with error code: " << pbUsageError;GDALDEMProcessingOptionsFree(psOptions);return false;}cv::Mat matHillShade;FileCategory fileCategory = Global::getFileCategory(m_filePath);switch (fileCategory.rasterSubCategory) {case RASTER_OPT:case RASTER_AMP:break;case RASTER_DEM:{switch (fileCategory.demSubSubCategory) {case DEM_COPERNICUS:matHillShade = MyProject::instance()->matHillShadeCopernicus().clone();break;case DEM_RAW:matHillShade = MyProject::instance()->matHillShadeRaw().clone();break;case DEM_CACHE:{double zFactor  = SettingsManager::instance().value("HILL_SHADE_ZFACTOR", 0.00001).toDouble();double scale    = SettingsManager::instance().value("HILL_SHADE_SCALE", 1.0).toDouble();double azimuth  = SettingsManager::instance().value("HILL_SHADE_AZIMUTH", 315.0).toDouble();double altitude = SettingsManager::instance().value("HILL_SHADE_ALTITUDE", 45.0).toDouble();bool   combined = SettingsManager::instance().value("HILL_SHADE_COMBINED", false).toBool();Global::generateHillShade(matHillShade, m_filePath, zFactor, scale, azimuth, altitude, combined);if(fileCategory.demSubSubCategory == DEM_CACHE){MyProject::instance()->setMatHillShadeCache( matHillShade );}}break;            }}break;case RASTER_MASK:matHillShade = MyProject::instance()->matHillShadeCache().clone();break;}// 获取图像尺寸int width  = m_dataset->GetRasterXSize();int height = m_dataset->GetRasterYSize();int panBandMap [3]= {1, 2, 3};// 转换为C++对象GDALDataset* poColorRelief = (GDALDataset*)hColorRelief;if(!poColorRelief){return false;}// 创建三通道矩阵一次性读取RGB数据cv::Mat matRGB(height, width, CV_8UC3);// 一次性读取三个波段CPLErr readErr = poColorRelief->RasterIO(GF_Read,0, 0,width, height,(void*)matRGB.data,width, height,GDT_Byte, 3, panBandMap,3, width*3, 1);if (readErr != CE_None) {qDebugV5() << "Failed to read RGB bands!";GDALClose(hColorRelief);GDALDEMProcessingOptionsFree(psOptions);return false;}// 将单通道hillshade转换为三通道以匹配RGBcv::Mat matHillShade3C;cv::cvtColor(matHillShade, matHillShade3C, cv::COLOR_GRAY2BGR);// 2. 分别对每个通道做 multiply(与你原来的分通道逻辑完全一致)cv::multiply(matRGB, matHillShade3C, matRGB, 1.0 / 255.0, CV_8UC3); // R通道// 一次性写入三个波段CPLErr writeErr = poColorRelief->RasterIO(GF_Write,0, 0,width, height,(void*)matRGB.data,width, height,GDT_Byte, 3, panBandMap,3, width*3, 1);if (writeErr != CE_None) {std::cerr << "GDAL 写入失败: " << CPLGetLastErrorMsg() << std::endl;GDALClose(hColorRelief);GDALDEMProcessingOptionsFree(psOptions);return false;}GDALClose(hColorRelief);GDALDEMProcessingOptionsFree(psOptions);return true;
}void GenerateColorReliefThread::run()
{// 创建并启动计时器QElapsedTimer timer;timer.start();std::string filePathColorRelief;bool ret = createColorRelief(filePathColorRelief);// qDebugV0() <<m_filePath<< "  createColorRelief() 代码执行时间: " << timer.elapsed() << " 毫秒";  // 计算执行时间(毫秒)if(!ret){return;}osg::ref_ptr<GDALImageLayer> layer = nullptr;if (!filePathColorRelief.empty()) {timer.restart();Global::buildOverviews(filePathColorRelief, -1); // 减少金字塔级数// qDebugV0() <<QString::fromStdString(filePathColorRelief)<< "  buildOverviewsTime() 代码执行时间: " << timer.elapsed() << " 毫秒";  // 计算执行时间(毫秒)osgEarth::GDALImageLayer::Options options;options.set_url(osgEarth::URI(filePathColorRelief));// 基础优化options.set_async( true );  // 异步加载layer = new GDALImageLayer(options);layer->options().set_name(m_filePath.toStdString());layer->setAsyncLoading(true);}emit sigProcessingFinished(layer);
}

C++ 里面嵌套着色器代码实现:


/******************************************************************************* 需求:* 高程渲染图 RGB.tif、 山体阴影图 AMP.tif** 高程渲染图 rgb波段分别  乘以 山体阴影图r波段, 然后除以255(AI说  读取的纹理就已经归一化到了 0~1 范围,不用除以 255)。*/void MyWidget::testGlsl()
{osgEarth::GDALImageLayer::Options ampOpt;// 2. AMP 阴影图层,开启 sharedampOpt.url() = "D:/Demo/glsl/AMP.tif";ampOpt.shared() = true;                   // 关键点:让它暴露共享采样器和矩阵ampOpt.shareTexUniformName() = "ampTex";        // Uniform 采样器名ampOpt.shareTexMatUniformName() = "ampTexMatrix";  // Uniform 矩阵名osg::ref_ptr<GDALImageLayer> ampLayer = new GDALImageLayer(ampOpt);ampLayer->setOpacity(0.00f);_map->addLayer(ampLayer.get());Global::buildOverviews("D:/Demo/glsl/RGB.tif", 20, "AVERAGE"); // 减少金字塔级数osgEarth::GDALImageLayer::Options rgbOpt;rgbOpt.tileSize() = 256;rgbOpt.maxLevel() = 20;rgbOpt.url() = "D:/Demo/glsl/RGB.tif";osg::ref_ptr<GDALImageLayer> rgbLayer = new GDALImageLayer(rgbOpt);_map->addLayer(rgbLayer.get());osg::ref_ptr<osg::StateSet> ss = rgbLayer->getOrCreateStateSet();ss->addUniform(new osg::Uniform("minLumFactor", 0.125f));//虚拟程序设置auto vp = osgEarth::VirtualProgram::getOrCreate(ss);// 获取可执行文件目录QString appDirQt = QCoreApplication::applicationDirPath();// GLSL 文件路径(同级 glsl 文件夹)QString glslPathQt = appDirQt + "/glsl/colorRelief_hillShade.glsl";std::string glslFile = glslPathQt.toStdString();// 打印检查std::cout << "glslFile: " << glslFile << std::endl;// 从文件里加载一个 GLSL 源码到 osg::Shader 对象osg::ref_ptr<osg::Shader> fragShader = osgDB::readRefShaderFile(osg::Shader::FRAGMENT, glslFile);if (fragShader.valid()) {std::string src = fragShader->getShaderSource();vp->setFunction("customFragment", src , osgEarth::ShaderComp::LOCATION_FRAGMENT_COLORING);}else{qDebugV5()<<"fragShader ×";}
}

以下是着色器glsl代码:

uniform sampler2D ampTex;
uniform mat4 ampTexMatrix;uniform float minLumFactor;// 新增 uniform,用滑条控制in vec2 oe_layer_texc;void customFragment(inout vec4 color)
{// RGB 原始颜色vec3 baseColor=color.rgb;// 转到 AMP 图层坐标vec2 uv_amp=(ampTexMatrix*vec4(oe_layer_texc,0.,1.)).st;// Hillshade 灰度float amp=texture(ampTex,uv_amp).r;// --- 关键:用 hillshade 调制亮度 ---// 1. 先算彩色图的亮度(感知加权公式)float lum=dot(baseColor,vec3(.299,.587,.114));// 2. 用 hillshade 去调制亮度// float lumMod = lum * amp;float lumMod=lum*(minLumFactor+amp*(1.-minLumFactor));// 把 hillshade 映射到 minLumFactor~1.0// 3. 保留原色相和饱和度:缩放 baseColor,使亮度变成 lumModfloat lumBase=max(lum,1e-4);// 防止除零vec3 resultColor=baseColor*(lumMod/lumBase);color.rgb=clamp(resultColor,0.,1.);
}

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

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

相关文章

Java接口响应速度优化

在 Java 开发中&#xff0c;接口响应速度直接影响用户体验和系统吞吐量。优化接口性能需要从代码、数据库、缓存、架构等多个维度综合考量&#xff0c;以下是具体方案及详细解析&#xff1a;一、代码层面优化代码是接口性能的基础&#xff0c;低效的代码会直接导致响应缓慢。1.…

A Large Scale Synthetic Graph Dataset Generation Framework的学习笔记

文章的简介 作者提出了一个可扩展的合成图生成框架&#xff0c;能够从真实图中学习结构和特征分布&#xff0c;并生成任意规模的图数据集&#xff0c;支持&#xff1a; 节点和边的结构生成节点和边的特征生成特征与结构的对齐&#xff08;Aligner&#xff09; 它区别于GraphWor…

Android12 Framework读写prop属性selinux报错解决

文章目录问题描述解决过程相关文章问题描述 Android读prop值时&#xff0c;就算是system应用&#xff0c; 也需要selinux权限&#xff0c;否则会报错。 java代码如下 SystemProperties.get("ro.input.resampling", "")selinux报错如下 2025-06-28 17:57:…

【图文版】AIOT 小智 AI 聊天机器人 ESP32 项目源码图解

前言 小智 AI 聊天机器人是最近一个很火的开源项目&#xff0c;它借助LLM大模型以及TTS等AI的能力&#xff0c;通过自然语言来与其对话实现交互。它可以回答任何问题、播放音乐、背诵古诗&#xff0c;颇有未来AI机器人的雏形。 因为最近工作上的需要对其进行了研究&#xff0c;…

250821-RHEL9.4上Docker及Docker-Compose的离线安装

在 离线环境下 在 RHEL (Red Hat Enterprise Linux) 系统上安装 Docker 和 Docker Compose&#xff0c;需要提前在有网络的环境中下载相关 RPM 包及依赖&#xff0c;然后在目标机器上进行安装。以下是比较完整的步骤&#xff1a; 1. Docker及Docker-Compose离线安装 在 RHEL 9.…

react相关知识

1.类组件和函数组件&#xff08;1&#xff09;类组件import React, { Component } from react;class UserProfile extends Component {constructor(props) {super(props);this.state {userData: null,isLoading: true,};this.timerId null;}componentDidMount() {// 模拟 API…

算法第五十五天:图论part05(第十一章)

并查集理论基础并查集主要有两个功能&#xff1a;将两个元素添加到一个集合中。判断两个元素在不在同一个集合class UnionFind:def __init__(self, n):"""初始化并查集"""self.n nself.father list(range(n)) # 每个节点自己是根self.rank […

雨雾天气漏检率骤降80%!陌讯多模态车牌识别方案实战解析

一、行业痛点&#xff1a;车牌识别的天气敏感性据《智慧交通系统检测白皮书》统计&#xff0c;雨雾环境下传统车牌识别漏检率高达42.7%&#xff08;2024年数据&#xff09;。主要存在三大技术瓶颈&#xff1a;1.​​水膜干扰​​&#xff1a;挡风玻璃水渍导致车牌区域纹理模糊2…

PostgreSQL15——查询详解

PostgreSQL15查询详解一、简单查询1.1、单表查询1.2、无表查询1.3、消除重复结果1.4、使用注释二、查询条件2.1、WHERE子句2.2、模式匹配2.3、空值判断2.4、复杂条件三、排序显示3.1、单列排序3.2、多列排序3.3、空值排序四、限定结果数量4.1、Top-N查询4.2、分页查询4.3、注意…

03-容器数据卷

卷就是目录或文件&#xff0c;存在于一个或多个容器中&#xff0c;由 docker 挂载到容器&#xff0c;但不属于联合文件系统&#xff0c;因此能够绕过 UnionFS&#xff0c;提供一些用于持续存储或共享数据。 特性&#xff1a;卷设计的目的就是数据的持久化&#xff0c;完全独立于…

Linux内核进程管理子系统有什么第三十三回 —— 进程主结构详解(29)

接前一篇文章&#xff1a;Linux内核进程管理子系统有什么第三十二回 —— 进程主结构详解&#xff08;28&#xff09; 本文内容参考&#xff1a; Linux内核进程管理专题报告_linux rseq-CSDN博客 《趣谈Linux操作系统 核心原理篇&#xff1a;第三部分 进程管理》—— 刘超 《…

从代码学习深度强化学习 - 目标导向的强化学习-HER算法 PyTorch版

文章目录 1. 前言:当一个任务有多个目标 2. 目标导向的强化学习 (GoRL) 简介 3. HER算法:化失败为成功的智慧 4. 代码实践:用PyTorch实现HER+DDPG 4.1 自定义环境 (WorldEnv) 4.2 智能体与算法 (DDPG) 4.3 HER的核心:轨迹经验回放 4.4 主流程与训练 5. 训练结果与分析 6. 总…

前端 H5分片上传 vue实现大文件

用uniapp开发APP上传视频文件&#xff0c;大文件可以上传成功&#xff0c;但是一旦打包为H5的代码&#xff0c;就会一提示链接超时&#xff0c;我的代码中是实现的上传到阿里云 如果需要看全文的私信我 官方开发文档地址 前端&#xff1a;使用分片上传的方式上传大文件_对象…

Linux服务器Systemctl命令详细使用指南

目录 1. 基本语法 2. 基础命令速查表 3. 常用示例 3.1 部署新服务后&#xff0c;设置开机自启并启动 3.2 检查系统中所有失败的服务并尝试修复 3.3 查看系统中所有开机自启的服务 4. 总结 以下是 systemctl 使用指南&#xff0c;涵盖服务管理、单元操作、运行级别控制、…

【JVM内存结构系列】二、线程私有区域详解:程序计数器、虚拟机栈、本地方法栈——搞懂栈溢出与线程隔离

上一篇文章我们搭建了JVM内存结构的整体框架,知道程序计数器、虚拟机栈、本地方法栈属于“线程私有区域”——每个线程启动时会单独分配内存,线程结束后内存直接释放,无需GC参与。这三个区域看似“小众”,却是理解线程执行逻辑、排查栈溢出异常的关键,也是面试中高频被问的…

红帽认证升级华为openEuler证书活动!

如果您有红帽证书&#xff0c;可以升级以下相应的证书&#xff1a;&#x1f447; 有RHCSA证书&#xff0c;可以99元升级openEuler HCIA 有RHCE证书&#xff0c;可以99元升级openEuler HCIP 有RHCA证书&#xff0c;可以2100元升级openEuler HCIE 现金激励&#xff1a;&#x1f4…

迭代器模式与几个经典的C++实现

迭代器模式详解1. 定义与意图迭代器模式&#xff08;Iterator Pattern&#xff09; 是一种行为设计模式&#xff0c;它提供一种方法顺序访问一个聚合对象中的各个元素&#xff0c;而又不暴露该对象的内部表示。主要意图&#xff1a;为不同的聚合结构提供统一的遍历接口。将遍历…

epoll 陷阱:隧道中的高级负担

上周提到了 tun/tap 转发框架的数据通道结构和优化 tun/tap 转发性能优化&#xff0c;涉及 RingBuffer&#xff0c;packetization 等核心话题。我也给出了一定的数据结构以及处理逻辑&#xff0c;但竟然没有高尚的 epoll&#xff0c;本文说说它&#xff0c;因为它不适合。 epo…

微前端架构常见框架

1. iframe 这里指的是每个微应用独立开发部署,通过 iframe 的方式将这些应用嵌入到父应用系统中,几乎所有微前端的框架最开始都考虑过 iframe,但最后都放弃,或者使用部分功能,原因主要有: url 不同步。浏览器刷新 iframe url 状态丢失、后退前进按钮无法使用。 UI 不同…

SQL Server更改日志模式:操作指南与最佳实践!

全文目录&#xff1a;开篇语**前言****摘要****概述&#xff1a;SQL Server 的日志模式****日志模式的作用****三种日志模式**1. **简单恢复模式&#xff08;Simple&#xff09;**2. **完整恢复模式&#xff08;Full&#xff09;**3. **大容量日志恢复模式&#xff08;Bulk-Log…