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
英文地址,請注明出處:http://en.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…