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.再进行访问 解决: …...
CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...
UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...
什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...
【Go语言基础【12】】指针:声明、取地址、解引用
文章目录 零、概述:指针 vs. 引用(类比其他语言)一、指针基础概念二、指针声明与初始化三、指针操作符1. &:取地址(拿到内存地址)2. *:解引用(拿到值) 四、空指针&am…...
2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)
安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...
uniapp 小程序 学习(一)
利用Hbuilder 创建项目 运行到内置浏览器看效果 下载微信小程序 安装到Hbuilder 下载地址 :开发者工具默认安装 设置服务端口号 在Hbuilder中设置微信小程序 配置 找到运行设置,将微信开发者工具放入到Hbuilder中, 打开后出现 如下 bug 解…...
MyBatis中关于缓存的理解
MyBatis缓存 MyBatis系统当中默认定义两级缓存:一级缓存、二级缓存 默认情况下,只有一级缓存开启(sqlSession级别的缓存)二级缓存需要手动开启配置,需要局域namespace级别的缓存 一级缓存(本地缓存&#…...
