当前位置: 首页 > news >正文

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.dtcms.com/a/344925.html

相关文章:

  • 爬楼梯变式
  • 24小时变2小时:RFQ系统如何重构电子元器件询价生态链
  • 在飞牛 NAS 上部署 PanSou:图文指南
  • Java后端学习路线
  • Java RESTful API 构建从入门到精通:一步步打造高效后端服务
  • DataStream实现WordCount
  • 世界模型一种能够对现实世界环境进行仿真,并基于文本、图像、视频和运动等输入数据来生成视频、预测未来状态的生成式 AI 模型
  • LeetCode第1695题 - 删除子数组的最大得分
  • 数字经济浪潮下的刑事法律风险与辩护新路径
  • k8s 简介及部署方法以及各方面应用
  • STM32F1 GPIO介绍及应用
  • Vue2.x核心技术与实战(三)
  • 掌握DRF的serializer_class:高效API开发
  • [激光原理与应用-318]:光学设计 - Solidworks - 草图中常见的操作
  • PCIe 5.0 SSD的发热量到底有多大?如何避免?
  • ubuntu - 终端工具 KConsole安装
  • DL00433-基于深度学习的无人机红外成像系统可视化含数据集
  • 【数据结构】选择排序:直接选择与堆排序详解
  • 【小白笔记】 MNN 移动端大模型部署
  • Java试题-选择题(14)
  • 新能源知识库(83)新能源行业的标准制定机构介绍
  • 期权买沽是什么意思?
  • python3GUI--Joy音乐播放器 在线播放器 播放器 By:PyQt5(附下载地址)
  • DAY01:【DL 第一弹】深度学习的概述
  • 什么是哈希值(hash value)???
  • FFmpeg03:多媒体文件处理基础
  • ffmpeg 中 crc32 源码分析及调试
  • vagrant怎么在宿主机操作虚拟机里面的系统管理和软件安装
  • xilinx的oddr原语是否可以直接使用verilog实现?
  • ingress和service区别