OpenCV HDF 模块实战指南:在 opencv_contrib 中用 HDF5 读写组、数据集与属性
2026/9/24 20:52:39 网站建设 项目流程
  • 计算机视觉
  • 图像处理
  • 深度学习
  • 机器学习

【免费下载链接】opencv_contrib

Repository for OpenCV's extra modules

项目地址:https://gitcode.com/gh_mirrors/op/opencv_contrib
点击查看免费下载

本指南围绕 opencv_contrib 仓库中modules/hdf模块的官方教程总览(table_of_content_hdf.markdown)展开,系统讲解如何借助 OpenCV 的 HDF5 I/O 接口完成三件事:创建组与子组、创建并读写数据集、读写属性。读完本文,你将掌握cv::hdf::HDF5的核心 API 用法、压缩与分块(chunking)等进阶特性,并能结合 HDFView / h5dump 工具验证写入结果。

背景:什么是 OpenCV 的 hdf 模块

hdf 模块是 opencv_contrib 中提供分层数据格式(Hierarchical Data Format)存储例程的扩展模块,模块入口声明位于 modules/hdf/include/opencv2/hdf.hpp。它封装了 HDF5(版本 5)的读写能力,让开发者可以直接用cv::Matcv::KeyPoint等 OpenCV 原生类型与.h5文件交互,而无需直接操作 HDF5 C API。

从 CMakeLists.txt 可以看到该模块的编译前提:

  • 系统必须已安装 HDF5 库,CMake 通过find_package(HDF5)完成探测;
  • Windows 上则通过HDF_DIR环境变量查找头文件与动态库;
  • 若未找到 HDF5,会调用ocv_module_disable(hdf)直接禁用该模块;
  • 模块最终链接opencv_core并以 Python 包装形式(WRAP python)构建。

因此,使用本模块前必须先在你的系统上安装 HDF5 开发库(例如 Ubuntu 下sudo apt-get install libhdf5-dev),这也是官方教程在开头特别提示的前提条件。

教程总览:三个子教程对应三类核心能力

总览页将模块能力拆分为三个循序渐进的主题,每个主题都有独立的教程文档与可运行示例:

子教程兼容版本作者核心能力示例源码
创建组OpenCV > 3.0Fangjun Kuang创建组与子组create_groups.cpp
创建、读写数据集OpenCV > 3.0Fangjun Kuang创建数据集并读写cv::Matcreate_read_write_datasets.cpp
读写属性OpenCV > 3.4Fangjun Kuang读写根组属性read_write_attributes.cpp

下面分别展开讲解,并补充来自 hdf5.hpp 接口定义的进阶细节。

创建组与子组

对应教程 how_to_create_groups.markdown,目标是创建 HDF5 文件、创建组、判断组是否存在、创建子组。

打开(或自动创建)HDF5 文件

一切操作从cv::hdf::open()开始。如果文件不存在会自动创建,否则以读写模式打开:

Ptr<hdf::HDF5> h5io = hdf::open("mytest.h5");

从接口文档(hdf5.hpp)可知:该函数返回Ptr<HDF5>对象指针,对象使用完毕后必须调用close()释放。"/"表示根组(root group),它始终存在。

创建组并做存在性检查

// "/" means the root group, which is always present if (!h5io->hlexists("/Group1")) h5io->grcreate("/Group1"); else std::cout << "/Group1 has already been created, skip it.\n";

关键要点:

  • hlexists(label)用于检查任意 hdf5 链接(组、数据集或其他对象)是否存在,接口注释明确它是线程安全的;
  • grcreate(grlabel)以默认属性创建组,创建后自动关闭句柄;不允许创建同名的组,否则会报错,所以创建前必须用hlexists检查;
  • 组可以嵌套,标签形如Group1/SubGroup1,其中SubGroup1位于根组下的Group1之内。

创建子组:父组必须先存在

// Note that Group1 has been created above, otherwise exception will occur if (!h5io->hlexists("/Group1/SubGroup1")) h5io->grcreate("/Group1/SubGroup1"); else std::cout << "/Group1/SubGroup1 has already been created, skip it.\n";

创建子组前必须确保父组已存在,否则抛出异常。完整示例见 create_groups.cpp,其执行流程为:open()hlexists()grcreate()close()

验证结果

h5dump转储文件内容,可以看到清晰的层级结构:

$ h5dump mytest.h5 HDF5 "mytest.h5" { GROUP "/" { GROUP "Group1" { GROUP "SubGroup1" { } } } }

下图是教程中给出的 HDFView 可视化结果,SubGroup1作为Group1的子组嵌套显示:

创建、写入与读取数据集

对应教程 create_read_write_dataset.markdown。数据集的载体是cv::Mat,教程明确说明:当前仅支持读写连续内存(continuous)的cv::Mat,其他数据类型尚未实现。

直接写入根组下的数据集(自动创建)

若数据集位于根组(/)之下,可以直接调用dswrite(),数据集会被自动创建

// write data to the given dataset // the dataset "/single" is created automatically, since it is a child of the root h5io->dswrite(data, dataset_name);

⚠️ 此自动创建行为仅适用于根组的直接子数据集

手动创建非根组数据集

对于非根组(如/data/single)中的数据集,需要先创建父组,再显式创建数据集:

// first we need to create the parent group if (!h5io->hlexists(parent_name)) h5io->grcreate(parent_name); // create the dataset if it not exists if (!h5io->hlexists(dataset_name)) h5io->dscreate(data.rows, data.cols, data.type(), dataset_name);

dscreate(rows, cols, type, dslabel)是最基本的二维数据集创建接口,type可以是CV_8UC3CV_32FC1CV_64FC2等任意 OpenCV 类型。如果数据集已存在,dscreate会抛出异常,因此同样需要先hlexists检查。单通道与多通道矩阵的读写方式完全一致,例如示例中的CV_32SC2双通道矩阵:

Mat data(2, 3, CV_32SC2); for (size_t i = 0; i < data.total()*data.channels(); i++) ((int*) data.data)[i] = (int)i;

读取并校验

Mat expected; h5io->dsread(expected, dataset_name); double diff = norm(data - expected); CV_Assert(abs(diff) < 1e-10);

dsread()按标签读取整个数据集到Mat;若目标文件不存在会抛出异常。示例通过计算norm(data - expected)验证读回数据与原数据一致。

进阶:压缩、分块与维度信息

接口定义(hdf5.hpp)为数据集创建提供了大量可选能力:

  • 压缩级别compresslevel取值 0–9,H5_NONE(-1)或 0 表示不压缩,9 表示最佳压缩比,但压缩级别越高计算开销越大;压缩依赖 GNU gzip;
  • 分块(chunking)dims_chunks指定块 I/O 的大小。启用压缩必须配合内部分块;若未指定,默认以整个数据集为单个大块。合理的分块能显著提升窗口化读写的速度;
  • 无限维度:将 rows 或 cols 设为H5_UNLIMITED(-1)可使该维度无限增长,但要求必须自定义分块,且写入必须用dsinsert()而不是dswrite()
  • 多维数据集dscreate(n_dims, sizes, type, ...)支持创建 n 维存储空间;
  • 元数据查询dsgetsize(dslabel, dims_flag)可获取实际维度(H5_GETDIMS)、最大维度(H5_GETMAXDIMS)与分块尺寸(H5_GETCHUNKDIMS);dsgettype()返回与CvMat类型系统兼容的存储类型,可用CV_MAT_CN()CV_MAT_DEPTH()解析通道数与数据类型;
  • 偏移写入dswrite(Array, dslabel, dims_offset, dims_counts)支持写入数据集中的指定窗口区域(offset 指定起始位置,counts 指定各维写入量)。

一个无限行数据集配合dsinsert()自动扩展的典型用法:

int chunks[2] = { 100, 100 }; // create Unlimited x 100 CV_64FC2 space h5io->dscreate(cv::hdf::HDF5::H5_UNLIMITED, 100, CV_64FC2, "hilbert", cv::hdf::HDF5::H5_NONE, chunks); int offset[2] = { 0, 0 }; for (int t = 0; t < 5; t++) { offset[0] += 100 * t; h5io->dsinsert(H, "hilbert", offset); }

该场景下最终数据集的行数会随每次dsinsert自动扩展。下图展示了单通道矩阵写入根组数据集后的 HDFView 可视化结果:

关键点(KeyPoint)数据集的专用接口

cv::Mat外,模块还提供了cv::KeyPoint的专用存取接口:kpcreate()kpwrite()kpread()kpinsert()kpgetsize()(见 hdf5.hpp)。它们同样支持压缩级别、分块大小与H5_UNLIMITED无限容量模式;kpwrite()在数据集不存在时会自动创建,而kpinsert()需要预创建且仅能在无限维度上扩展。

读写属性

对应教程 read_write_attributes.markdown。

支持范围与类型限制

属性(attribute)通常可挂载到组或数据集上,但当前 OpenCV 仅实现了根组属性的读写。支持的类型有四种:

  • int
  • double
  • cv::String
  • cv::InputArray(仅限连续数组,如cv::Mat

写入属性:先检查再写入

String attr_mat_name = "array attribute"; Mat attr_mat; attr_mat = cv::Mat_<float>({2, 3}, {0, 1, 2, 3, 4, 5, 6}); if (!h5io->atexists(attr_mat_name)) h5io->atwrite(attr_mat, attr_mat_name);

写入前必须用atexists()确认属性不存在——若属性已存在,atwrite()会调用CV_Error()抛错。同理,删除属性用atdelete(),删除不存在的属性同样会报错。

字符串、整型与浮点型属性的写入方式完全一致:

String attr_str_name = "string attribute"; String attr_str = "Hello HDF5 from OpenCV!"; if (!h5io->atexists(attr_str_name)) h5io->atwrite(attr_str, attr_str_name);

读取属性

String expected_attr_str; h5io->atread(&expected_attr_str, attr_str_name);

atread()要求属性必须存在,否则抛异常;建议先atexists()检查。完整示例 read_write_attributes.cpp 中,写入了MatStringintdouble四类属性,并通过断言逐一校验读回结果与写入值一致(例如CV_Assert(norm(attr_mat - expected_attr_mat) < 1e-10))。

查看属性

用 HDFView 打开生成的attributes.h5,即可在根组下看到这些属性及其值;下图分别为根组属性列表和属性详细信息:

测试与更多参考

仓库内对应的单元测试位于 modules/hdf/test/test_hdf5.cpp,覆盖了组创建、数据集读写、属性读写、压缩、分块、无限维度扩展等场景,是验证 API 行为与编写自己代码时最直接的参考。三个示例程序均可作为可运行模板:

  • create_groups.cpp —— 组与子组创建
  • create_read_write_datasets.cpp —— 数据集创建与cv::Mat读写
  • read_write_attributes.cpp —— 根组属性读写

常见注意事项小结

  1. 依赖前置:使用 hdf 模块前必须在系统中安装 HDF5 开发库,且 CMake 能通过find_package(HDF5)找到它,否则模块会被自动禁用(见 CMakeLists.txt)。
  2. 命名冲突grcreate()dscreate()atwrite()都不允许覆盖已存在的对象/属性,务必先用hlexists()/atexists()检查。
  3. 父组优先:创建子组或非根组数据集前,父组必须已经存在。
  4. 连续内存要求:数据集读写仅支持连续的cv::Mat;属性数组同样要求连续。
  5. 线程安全边界hlexists()dsread()dswrite()等接口是线程安全的,但grcreate()dscreate()dsinsert()不保证线程安全;多线程写入建议限定在互不重叠的区域。
  6. 无限维度必须分块:任何维度使用H5_UNLIMITED时都必须显式指定 chunk 大小,且写入需使用dsinsert()/kpinsert()以便自动扩展。
  7. 验证工具:Ubuntu 下可安装sudo apt-get install hdf5-tools hdfview,用 HDFView 图形化查看或用h5dump命令行转储文件结构,是调试 HDF5 输出的高效手段。
  • 计算机视觉
  • 图像处理
  • 深度学习
  • 机器学习

【免费下载链接】opencv_contrib

Repository for OpenCV's extra modules

项目地址:https://gitcode.com/gh_mirrors/op/opencv_contrib
点击查看免费下载

相关推荐

上一篇:从入门到精通:CalcBinding的终极WPF表达式绑定指南
下一篇:moOde Audio Player常见问题解答:解决你的播放与配置难题

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询