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

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中通过Pythonh5pygdal等库直接打开了。

  那么在这里,我们就介绍一下基于C++ 语言的hdf5库,打开.h5格式图像(包括那些用到szip压缩程序的HDF5图像)的方法。不过需要注意,我这里是在LinuxUbuntu系统中操作的,至少可以保证这个代码在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_pathtif_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库&#xff0c;将.h5格式的多波段HDF5图像批量转换为.tif格式的方法&#xff1b;其中&#xff0c;本方法支持对szip压缩的HDF5图像&#xff08;例如高分一号卫星遥感影像&#xff09;加以转换。 将HDF5图像批量转换为.tif格式&#xff0c;在部…...

【JAVA】Java第十三节:String类(String相关方法,以及StrinBuftrer , StringBulder相关方法)

本文详细介绍了String类以及常用的String相关方法&#xff0c;以及StrinBuftrer , StringBulder相关方法的使用&#xff0c;建议有印象即可&#xff0c;不需要都记住&#xff0c;使用时去查取即可 一、创建一个String类型的变量 我们平时创建String类型的变量一般是第一种形式…...

WordPress安装或访问时出现数据库连接错误的处理方式

一、在安装时出现数据库连接错误 1、如果数据库名称、用户名或密码错误&#xff0c;或者主机设置不正确&#xff08;如数据库服务器不是在本地localhost&#xff0c;而是在远程服务器&#xff0c;需要正确填写远程服务器的 IP 地址或域名&#xff09;&#xff0c;就会导致连接错…...

JAVA-面向对象基础

文章目录 概要封装多态抽象类接口内部类为什么需要内部类 概要 面向对象是一种编程范式或设计哲学&#xff0c;它将软件系统设计为由多个对象组成&#xff0c;这些对象通过特定的方式相互作用 封装 将数据和操作数据的方法封装在一个类中&#xff0c;并通过访问修饰符控制对…...

[Java]项目入门

这篇简单介绍一些入门的有关项目和行业的知识&#xff0c;并带着实现一个小项目。便于已经编程入门的各位准备进阶到下一个阶段。 先大致地介绍&#xff0c;一个完整的项目(不看客户端、服务端的分类)基本可以划分为三部分&#xff1a; 1.前端。比如你现在看到的CSDN页面就是一…...

opencv Mat To Heif

高效率图像文件格式&#xff08;英语&#xff1a;High Efficiency Image File Format, HEIF&#xff1b;也称高效图像文件格式&#xff09;是一个用于单张图像或图像序列的文件格式。它由运动图像专家组&#xff08;MPEG&#xff09;开发&#xff0c;并在MPEG-H Part 12&#x…...

二刷代码随想录第24天

93. 复原 IP 地址 确定函数is_ip的实现细节&#xff0c;start不能超过end&#xff0c;没有0开头的非0数字&#xff0c;每个字符都在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

什么是张量&#xff1f; 张量&#xff08;Tensor&#xff09;是深度学习和机器学习中一个非常基础且重要的概念。在数学上&#xff0c;张量可以被看作是向量和矩阵的泛化。简单来说&#xff0c;张量是一种多维数组&#xff0c;它可以表示标量&#xff08;0维&#xff09;、向量…...

SaaS财务软件:赋能企业数字化转型

在数字化浪潮的推动下&#xff0c;企业财务管理正逐步迈向智能化、高效化的新阶段。在这个过程中&#xff0c;SaaS财务软件应运而生&#xff0c;成为许多企业的首选。以易舟云财务软件为例&#xff0c;这款软件不仅集成了众多先进的财务管理功能&#xff0c;而且在用户体验上做…...

FPGA实战篇(按键控制LDE实验)

1.按键简介 按键开关是一种电子开关&#xff0c;属于电子元器件类。我们的开发板上有两种按键开关&#xff1a;第一种是本实验所使用的轻触式按键开关&#xff0c;简称轻触开关。使用时以向开关的操作方向施加压力使内部电路闭合接通&#xff0c;当撤销压力时开关断开&#xff…...

在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:均分纸牌

主要思路 整体思路概述&#xff1a; 本题旨在解决给定N堆纸牌&#xff08;纸牌总数是N的倍数&#xff09;&#xff0c;通过按照特定移牌规则移动纸牌&#xff0c;找出用最少移动次数使每堆纸牌数量相等的方法。程序采用了一种逐步调整的思路&#xff0c;先计算出每堆纸牌应有的…...

ThreadFactory

ThreadFactory 是 Java 中一个用于创建线程的接口&#xff0c;它可以自定义线程的创建过程&#xff0c;例如线程的名称、优先级、是否为守护线程等。它是 java.util.concurrent 包的一部分&#xff0c;通常与线程池&#xff08;ThreadPoolExecutor&#xff09;一起使用。 线程…...

WEB开发: Node.js路由之由浅入深(一) - 全栈工程师入门

作为一个使用Node.js多年的开发者&#xff0c;我已经习惯于用Node.js写一些web应用来为工作服务&#xff0c;因为实现快速、部署简单、自定义强。今天我们一起来学习一个全栈工程师必备技能&#xff1a;web路由。&#xff08;观看此文的前提是默认你已经装好nonde.js了&#xf…...

NES游戏机项目制作笔记(未完成)

24年12月1日晚记——在网上找项目学习的时候发现一个有意思的项目&#xff0c;准备靠这个应用一些STM32的高级功能。值得提醒的是——目的在于学习不可贪杯&#xff0c;注意效率 01 根据项目需求分析 为确保充分考虑每一个细节&#xff0c;并且让自己高效的完成项目制作&#…...

云服务器部署upload-labs-docker(文件上传靶场)环境 以及相关报错问题

环境的搭建 准备&#xff1a;云服务器&#xff08;本地的linux服务器&#xff08;版本最好不要是老的不然不兼容docker&#xff09;&#xff09; f8x配置docker环境&#xff1a; https://github.com/ffffffff0x/f8x 一键配置 docker拉取file-labs靶场 https://github.com…...

Elasticsearch入门之HTTP基础操作

RESTful REST 指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。Web 应用程序最重要的 REST 原则是&#xff0c;客户端和服务器之间的交互在请求之间是无状态的。从客户端到服务器的每个请求都必须包含理解请求所必需的信息。如果服务器在…...

maven聚合项目部署到tomcat上

目录 一.聚合项目 1.检查无误后将项目打包 2.将这四个拷贝到tomcat的webapp下 二.启动tomcat 1.双击startup.bat 2.页面访问http://localhost:8080 3.打开webapp文件夹&#xff0c;发现多了三个文件夹 4.点进去才有要访问的index页面 5.再进行访问 解决&#xff1a; …...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?

在建筑行业&#xff0c;项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升&#xff0c;传统的管理模式已经难以满足现代工程的需求。过去&#xff0c;许多企业依赖手工记录、口头沟通和分散的信息管理&#xff0c;导致效率低下、成本失控、风险频发。例如&#…...

ServerTrust 并非唯一

NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...

Python如何给视频添加音频和字幕

在Python中&#xff0c;给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加&#xff0c;包括必要的代码示例和详细解释。 环境准备 在开始之前&#xff0c;需要安装以下Python库&#xff1a;…...

【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看

文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...

Qt 事件处理中 return 的深入解析

Qt 事件处理中 return 的深入解析 在 Qt 事件处理中&#xff0c;return 语句的使用是另一个关键概念&#xff0c;它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别&#xff1a;不同层级的事件处理 方…...

Pydantic + Function Calling的结合

1、Pydantic Pydantic 是一个 Python 库&#xff0c;用于数据验证和设置管理&#xff0c;通过 Python 类型注解强制执行数据类型。它广泛用于 API 开发&#xff08;如 FastAPI&#xff09;、配置管理和数据解析&#xff0c;核心功能包括&#xff1a; 数据验证&#xff1a;通过…...

Monorepo架构: Nx Cloud 扩展能力与缓存加速

借助 Nx Cloud 实现项目协同与加速构建 1 &#xff09; 缓存工作原理分析 在了解了本地缓存和远程缓存之后&#xff0c;我们来探究缓存是如何工作的。以计算文件的哈希串为例&#xff0c;若后续运行任务时文件哈希串未变&#xff0c;系统会直接使用对应的输出和制品文件。 2 …...

倒装芯片凸点成型工艺

UBM&#xff08;Under Bump Metallization&#xff09;与Bump&#xff08;焊球&#xff09;形成工艺流程。我们可以将整张流程图分为三大阶段来理解&#xff1a; &#x1f527; 一、UBM&#xff08;Under Bump Metallization&#xff09;工艺流程&#xff08;黄色区域&#xff…...

【把数组变成一棵树】有序数组秒变平衡BST,原来可以这么优雅!

【把数组变成一棵树】有序数组秒变平衡BST,原来可以这么优雅! 🌱 前言:一棵树的浪漫,从数组开始说起 程序员的世界里,数组是最常见的基本结构之一,几乎每种语言、每种算法都少不了它。可你有没有想过,一组看似“线性排列”的有序数组,竟然可以**“长”成一棵平衡的二…...

WEB3全栈开发——面试专业技能点P4数据库

一、mysql2 原生驱动及其连接机制 概念介绍 mysql2 是 Node.js 环境中广泛使用的 MySQL 客户端库&#xff0c;基于 mysql 库改进而来&#xff0c;具有更好的性能、Promise 支持、流式查询、二进制数据处理能力等。 主要特点&#xff1a; 支持 Promise / async-await&#xf…...