C++的HDF5库将h5图像转为tif格式:szip压缩的图像也可转换
本文介绍基于C++ 语言的hdf5库与gdal库,将.h5格式的多波段HDF5图像批量转换为.tif格式的方法;其中,本方法支持对szip压缩的HDF5图像(例如高分一号卫星遥感影像)加以转换。
将HDF5图像批量转换为.tif格式,在部分场景下操作并不难——在我们之前的文章Python中ArcPy实现栅格图像文件由HDF格式批量转换为TIFF格式(https://blog.csdn.net/zhebushibiaoshifu/article/details/124200816)中,就介绍过基于Python中的arcpy模块实现这一需求的方法。但是,正如我们在文章Windows打开HDF5图像:HDFView软件的下载、安装(https://blog.csdn.net/zhebushibiaoshifu/article/details/142642691)中提到的那样,由于szip这个压缩模块不再受到hdf5库的支持,导致用szip程序压缩的HDF5图像,比如高分系列遥感影像的.h5文件,就没办法在Windows中通过Python的h5py、gdal等库直接打开了。
那么在这里,我们就介绍一下基于C++ 语言的hdf5库,打开.h5格式图像(包括那些用到szip压缩程序的HDF5图像)的方法。不过需要注意,我这里是在Linux的Ubuntu系统中操作的,至少可以保证这个代码在Linux下可以正常运行;但能否在Windows中的C++ 环境下也正常运行,我暂时还没试过——按道理应该也是可行的,大家如果有需要的话可以尝试一下。
本文所用代码如下。
#include <iostream>
#include <sstream>
#include <vector>
#include <filesystem>
#include <gdal.h>
#include <gdal_priv.h>
#include "hdf5.h"
#include "ogr_spatialref.h" int main(int argc, char *argv[]) {const std::string h5_path = "/home/ctj/data/H5/";const std::string tif_path = "/home/ctj/data/TIFF_48SUB/";// const std::string h5_path = argv[1];// const std::string tif_path = argv[2];const char *dataset_0 = "/Cloud_Mask/cloudmask";const char *dataset_1 = "/GeometricCorrection/DataSet_16_1";const char *dataset_2 = "/GeometricCorrection/DataSet_16_2";const char *dataset_3 = "/GeometricCorrection/DataSet_16_3";const char *dataset_4 = "/GeometricCorrection/DataSet_16_4";const char *projection_para = "ProjectionPara";const char *projection_str = "ProjectionStr";hid_t file_id;hid_t dataset_id;hid_t attr_id;hid_t attr_dtype;herr_t status;hid_t mem_type_id = H5T_NATIVE_UINT16;int size = 6863;int band_num = 5;// namespace fs = filesystem;status = H5open();GDALAllRegister();for (const auto& entry : std::filesystem::directory_iterator(h5_path)) {if (entry.path().extension() == ".h5") {std::string filename = entry.path().filename().string();std::cout << filename << std::endl;std::string baseName = filename.substr(0, filename.find_last_of('.'));const std::string output_filename = tif_path + baseName + ".tif";file_id = H5Fopen((h5_path + filename).c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);attr_id = H5Aopen(file_id, projection_para, H5P_DEFAULT);attr_dtype = H5Aget_type(attr_id);size_t string_length = H5Tget_size(attr_dtype);char *attr_data = new char[1000];status = H5Aread(attr_id, attr_dtype, attr_data);std::istringstream iss(attr_data);std::vector<double> transform(6);int i = 0;std::string str;while (getline(iss, str, ',')) {transform[i] = stod(str);++i;}attr_id = H5Aopen(file_id, projection_str, H5P_DEFAULT);attr_dtype = H5Aget_type(attr_id);char *attr_data_str = new char[1000];status = H5Aread(attr_id, attr_dtype, attr_data_str);dataset_id = H5Dopen1(file_id, dataset_0);std::vector<u_int16_t> data_0(size * size);status = H5Dread(dataset_id, mem_type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, data_0.data());dataset_id = H5Dopen1(file_id, dataset_1);std::vector<u_int16_t> data_1(size * size);status = H5Dread(dataset_id, mem_type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, data_1.data());dataset_id = H5Dopen1(file_id, dataset_2);std::vector<u_int16_t> data_2(size * size);status = H5Dread(dataset_id, mem_type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, data_2.data());dataset_id = H5Dopen1(file_id, dataset_3);std::vector<u_int16_t> data_3(size * size);status = H5Dread(dataset_id, mem_type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, data_3.data());dataset_id = H5Dopen1(file_id, dataset_4);std::vector<u_int16_t> data_4(size * size);status = H5Dread(dataset_id, mem_type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, data_4.data());status = H5Fclose(file_id);GDALDriver *poDriver = GetGDALDriverManager()->GetDriverByName("GTiff");GDALDataset *poDstDS = poDriver->Create(output_filename.c_str(), size, size, band_num, GDT_UInt16, nullptr);u_int16_t *band_data_0 = &data_0[0];poDstDS->GetRasterBand(1)->RasterIO(GF_Write, 0, 0, size, size, band_data_0, size, size, GDT_UInt16, 0, 0);u_int16_t *band_data_1 = &data_1[0];poDstDS->GetRasterBand(2)->RasterIO(GF_Write, 0, 0, size, size, band_data_1, size, size, GDT_UInt16, 0, 0);u_int16_t *band_data_2 = &data_2[0];poDstDS->GetRasterBand(3)->RasterIO(GF_Write, 0, 0, size, size, band_data_2, size, size, GDT_UInt16, 0, 0);u_int16_t *band_data_3 = &data_3[0];poDstDS->GetRasterBand(4)->RasterIO(GF_Write, 0, 0, size, size, band_data_3, size, size, GDT_UInt16, 0, 0);u_int16_t *band_data_4 = &data_4[0];poDstDS->GetRasterBand(5)->RasterIO(GF_Write, 0, 0, size, size, band_data_4, size, size, GDT_UInt16, 0, 0);for (int i = 1; i <= band_num; ++i) {GDALRasterBand *poBand = poDstDS->GetRasterBand(i);if (poBand != nullptr) {poBand->SetNoDataValue(0);}}poDstDS->SetGeoTransform(transform.data());poDstDS->SetProjection(attr_data_str);GDALClose(poDstDS);}}status = H5close();return 0;
}
上述是本文完整代码。接下来,就分段介绍一下每段代码的具体含义。
首先,需要包含必要的头文件。在这里,包括标准输入输出、字符串流、向量、文件系统等功能,以及hdf5库与gdal库。同时,定义了两个常量字符串h5_path与tif_path,分别指向转换前的HDF5图像和转换后的TIFF图像的目录。
#include <iostream>
#include <sstream>
#include <vector>
#include <filesystem>
#include <gdal.h>
#include <gdal_priv.h>
#include "hdf5.h"
#include "ogr_spatialref.h" int main(int argc, char *argv[]) {const std::string h5_path = "/home/ctj/data/H5/";const std::string tif_path = "/home/ctj/data/TIFF_48SUB/";
随后,设定要读取的HDF5图像的数据集(波段)的路径,以及空间参考信息的属性名称;这些参数大家就按照自己HDF5图像的实际情况来修改即可。
接下来,初始化hdf5库的状态变量,这些变量是hdf5库操作需要的。同时,用size表示图像的宽度和高度,因为我这里HDF5图像是正方形,所以只需指定1个值。此外,band_num表示待转换遥感影像的波段数。
const char *dataset_0 = "/Cloud_Mask/cloudmask";
const char *dataset_1 = "/GeometricCorrection/DataSet_16_1";
// ... 省略部分代码 ...
const char *projection_para = "ProjectionPara";
const char *projection_str = "ProjectionStr";hid_t file_id;
hid_t dataset_id;
hid_t attr_id;
hid_t attr_dtype;
herr_t status;
hid_t mem_type_id = H5T_NATIVE_UINT16;
int size = 6863;
int band_num = 5;
紧接着,初始化hdf5库,注册所有可用的GDAL驱动程序。
status = H5open();
GDALAllRegister();
随后,使用std::filesystem::directory_iterator遍历指定目录中的所有文件,并只处理扩展名为.h5的文件;对于这些文件,构建输出文件名——基于原始文件名,去掉扩展名并添加.tif扩展名。
for (const auto& entry : std::filesystem::directory_iterator(h5_path)) {if (entry.path().extension() == ".h5") {std::string filename = entry.path().filename().string();std::cout << filename << std::endl;std::string baseName = filename.substr(0, filename.find_last_of('.'));const std::string output_filename = tif_path + baseName + ".tif";
随后,使用H5Fopen打开HDF5图像,在这里选择以只读模式访问。
file_id = H5Fopen((h5_path + filename).c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
随后,需要读取原本HDF5图像的空间参考信息。在这里,首先打开名为projection_para的属性,读取其内容到attr_data中;随后,解析attr_data为一个包含6个元素的double向量transform——这些元素用于地理变换。
attr_id = H5Aopen(file_id, projection_para, H5P_DEFAULT);
attr_dtype = H5Aget_type(attr_id);
size_t string_length = H5Tget_size(attr_dtype);
char *attr_data = new char[1000];
status = H5Aread(attr_id, attr_dtype, attr_data);std::istringstream iss(attr_data);
std::vector<double> transform(6);
int i = 0;
std::string str;
while (getline(iss, str, ',')) {transform[i] = stod(str);++i;
}
类似地,读取名为projection_str的属性,该属性包含投影信息的WKT字符串。
attr_id = H5Aopen(file_id, projection_str, H5P_DEFAULT);
attr_dtype = H5Aget_type(attr_id);
char *attr_data_str = new char[1000];
status = H5Aread(attr_id, attr_dtype, attr_data_str);
到这里,我们就可以对每个数据集调用H5Dopen1将其打开,并使用H5Dread将数据读入向量中
dataset_id = H5Dopen1(file_id, dataset_0);
std::vector<u_int16_t> data_0(size * size);
status = H5Dread(dataset_id, mem_type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, data_0.data());// ... 重复上述步骤读取其他数据集 ...
随后,记得关闭HDF5图像以释放资源。
status = H5Fclose(file_id);
接下来,就该gdal库登场了。使用gdal库创建一个新的TIFF文件,并使用RasterIO方法将每个波段的数据写入到TIFF文件中。
GDALDriver *poDriver = GetGDALDriverManager()->GetDriverByName("GTiff");GDALDataset *poDstDS = poDriver->Create(output_filename.c_str(), size, size, band_num, GDT_UInt16, nullptr);
u_int16_t *band_data_0 = &data_0[0];
poDstDS->GetRasterBand(1)->RasterIO(GF_Write, 0, 0, size, size, band_data_0, size, size, GDT_UInt16, 0, 0);
// ... 写入其他波段 ...
同时,设置每个波段的NoData值为0,同时按照前述从HDF5图像中读取到的信息,设置TIFF图像的地理变换参数和投影信息。
for (int i = 1; i <= band_num; ++i) {GDALRasterBand *poBand = poDstDS->GetRasterBand(i);if (poBand != nullptr) {poBand->SetNoDataValue(0);}
}poDstDS->SetGeoTransform(transform.data());
poDstDS->SetProjection(attr_data_str);GDALClose(poDstDS);
最后,不要忘记关闭hdf5库以释放资源。
status = H5close();
至此,大功告成。
欢迎关注:疯狂学习GIS
相关文章:
C++的HDF5库将h5图像转为tif格式:szip压缩的图像也可转换
本文介绍基于C 语言的hdf5库与gdal库,将.h5格式的多波段HDF5图像批量转换为.tif格式的方法;其中,本方法支持对szip压缩的HDF5图像(例如高分一号卫星遥感影像)加以转换。 将HDF5图像批量转换为.tif格式,在部…...
【JAVA】Java第十三节:String类(String相关方法,以及StrinBuftrer , StringBulder相关方法)
本文详细介绍了String类以及常用的String相关方法,以及StrinBuftrer , StringBulder相关方法的使用,建议有印象即可,不需要都记住,使用时去查取即可 一、创建一个String类型的变量 我们平时创建String类型的变量一般是第一种形式…...
WordPress安装或访问时出现数据库连接错误的处理方式
一、在安装时出现数据库连接错误 1、如果数据库名称、用户名或密码错误,或者主机设置不正确(如数据库服务器不是在本地localhost,而是在远程服务器,需要正确填写远程服务器的 IP 地址或域名),就会导致连接错…...
JAVA-面向对象基础
文章目录 概要封装多态抽象类接口内部类为什么需要内部类 概要 面向对象是一种编程范式或设计哲学,它将软件系统设计为由多个对象组成,这些对象通过特定的方式相互作用 封装 将数据和操作数据的方法封装在一个类中,并通过访问修饰符控制对…...
[Java]项目入门
这篇简单介绍一些入门的有关项目和行业的知识,并带着实现一个小项目。便于已经编程入门的各位准备进阶到下一个阶段。 先大致地介绍,一个完整的项目(不看客户端、服务端的分类)基本可以划分为三部分: 1.前端。比如你现在看到的CSDN页面就是一…...
opencv Mat To Heif
高效率图像文件格式(英语:High Efficiency Image File Format, HEIF;也称高效图像文件格式)是一个用于单张图像或图像序列的文件格式。它由运动图像专家组(MPEG)开发,并在MPEG-H Part 12&#x…...
二刷代码随想录第24天
93. 复原 IP 地址 确定函数is_ip的实现细节,start不能超过end,没有0开头的非0数字,每个字符都在0-9之间,每段字符小于255在原字符串s上做操作会更简单一些 class Solution { public:vector<string> result;vector<string> rest…...
Java设计模式之状态模式架构高扩展的订单状态管理
🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c=1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编程,高并发设计,Springboot和微服务,熟悉Linux,ESXI虚拟化以及云原生Docker和K8s…...
Yagmail邮件发送库:如何用Python实现自动化邮件营销?
目录 一、Yagmail简介 二、安装Yagmail 三、基本使用示例 1. 发送简单文本邮件 2. 发送HTML邮件 3. 发送带有附件的邮件 4. 多收件人处理 5. 自定义邮件头 四、高级功能 1. SMTP配置 2. 邮件模板 3. OAuth2认证 五、自动化邮件营销案例 六、错误处理和调试 七、…...
李宏毅深度学习-Pytorch Tutorial2
什么是张量? 张量(Tensor)是深度学习和机器学习中一个非常基础且重要的概念。在数学上,张量可以被看作是向量和矩阵的泛化。简单来说,张量是一种多维数组,它可以表示标量(0维)、向量…...
SaaS财务软件:赋能企业数字化转型
在数字化浪潮的推动下,企业财务管理正逐步迈向智能化、高效化的新阶段。在这个过程中,SaaS财务软件应运而生,成为许多企业的首选。以易舟云财务软件为例,这款软件不仅集成了众多先进的财务管理功能,而且在用户体验上做…...
FPGA实战篇(按键控制LDE实验)
1.按键简介 按键开关是一种电子开关,属于电子元器件类。我们的开发板上有两种按键开关:第一种是本实验所使用的轻触式按键开关,简称轻触开关。使用时以向开关的操作方向施加压力使内部电路闭合接通,当撤销压力时开关断开ÿ…...
在Ubuntu-22.04 [WSL2]中配置Docker
文章目录 0. 进入Ubuntu-22.041. 更新系统软件包2. 安装Docker相关依赖包3. 添加Docker官方GPG密钥4. 添加Docker软件源5. 安装Docker Engine5.1 更新软件包列表5.2 安装Docker相关软件包 6. 验证Docker安装是否成功6.1 查看Docker版本信息6.2 启动Docker6.3 配置镜像加速器6.4…...
ACM:均分纸牌
主要思路 整体思路概述: 本题旨在解决给定N堆纸牌(纸牌总数是N的倍数),通过按照特定移牌规则移动纸牌,找出用最少移动次数使每堆纸牌数量相等的方法。程序采用了一种逐步调整的思路,先计算出每堆纸牌应有的…...
ThreadFactory
ThreadFactory 是 Java 中一个用于创建线程的接口,它可以自定义线程的创建过程,例如线程的名称、优先级、是否为守护线程等。它是 java.util.concurrent 包的一部分,通常与线程池(ThreadPoolExecutor)一起使用。 线程…...
WEB开发: Node.js路由之由浅入深(一) - 全栈工程师入门
作为一个使用Node.js多年的开发者,我已经习惯于用Node.js写一些web应用来为工作服务,因为实现快速、部署简单、自定义强。今天我们一起来学习一个全栈工程师必备技能:web路由。(观看此文的前提是默认你已经装好nonde.js了…...
NES游戏机项目制作笔记(未完成)
24年12月1日晚记——在网上找项目学习的时候发现一个有意思的项目,准备靠这个应用一些STM32的高级功能。值得提醒的是——目的在于学习不可贪杯,注意效率 01 根据项目需求分析 为确保充分考虑每一个细节,并且让自己高效的完成项目制作&#…...
云服务器部署upload-labs-docker(文件上传靶场)环境 以及相关报错问题
环境的搭建 准备:云服务器(本地的linux服务器(版本最好不要是老的不然不兼容docker)) f8x配置docker环境: https://github.com/ffffffff0x/f8x 一键配置 docker拉取file-labs靶场 https://github.com…...
Elasticsearch入门之HTTP基础操作
RESTful REST 指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。Web 应用程序最重要的 REST 原则是,客户端和服务器之间的交互在请求之间是无状态的。从客户端到服务器的每个请求都必须包含理解请求所必需的信息。如果服务器在…...
maven聚合项目部署到tomcat上
目录 一.聚合项目 1.检查无误后将项目打包 2.将这四个拷贝到tomcat的webapp下 二.启动tomcat 1.双击startup.bat 2.页面访问http://localhost:8080 3.打开webapp文件夹,发现多了三个文件夹 4.点进去才有要访问的index页面 5.再进行访问 解决: …...
渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...
JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...
