Commit 7f7b0bc1 authored by KUANG Fangjun's avatar KUANG Fangjun Committed by Fangjun Kuang

Add attribute read/write supports to the HDF module.

The following data types for attributes are implemented:
 - int
 - double
 - cv::String
 - cv::InputArray
parent f03e415e
......@@ -103,13 +103,109 @@ public:
*/
CV_WRAP virtual bool hlexists( const String& label ) const = 0;
/* @overload */
/**
* Check whether a given attribute exits or not in the root group.
*
* @param atlabel the attribute name to be checked.
* @return true if the attribute exists, false otherwise.
*
* @sa atdelete, atwrite, atread
*/
CV_WRAP virtual bool atexists(const String& atlabel) const = 0;
/**
* Delete an attribute from the root group.
*
* @param atlabel the attribute to be deleted.
*
* @note CV_Error() is called if the given attribute does not exist. Use atexists()
* to check whether it exists or not beforehand.
*
* @sa atexists, atwrite, atread
*/
CV_WRAP virtual void atdelete(const String& atlabel) = 0;
/**
* Write an attribute inside the root group.
*
* @param value attribute value.
* @param atlabel attribute name.
*
* The following example demonstrates how to write an attribute of type cv::String:
*
* @snippet samples/read_write_attributes.cpp snippets_write_str
*
* @note CV_Error() is called if the given attribute already exists. Use atexists()
* to check whether it exists or not beforehand. And use atdelete() to delete
* it if it already exists.
*
* @sa atexists, atdelete, atread
*/
CV_WRAP virtual void atwrite(const int value, const String& atlabel) = 0;
/**
* Read an attribute from the root group.
*
* @param value address where the attribute is read into
* @param atlabel attribute name
*
* The following example demonstrates how to read an attribute of type cv::String:
*
* @snippet samples/read_write_attributes.cpp snippets_read_str
*
* @note The attribute MUST exist, otherwise CV_Error() is called. Use atexists()
* to check if it exists beforehand.
*
* @sa atexists, atdelete, atwrite
*/
CV_WRAP virtual void atread(int* value, const String& atlabel) = 0;
/** @overload */
CV_WRAP virtual void atwrite(const double value, const String& atlabel) = 0;
/** @overload */
CV_WRAP virtual void atread(double* value, const String& atlabel) = 0;
/** @overload */
CV_WRAP virtual void atwrite(const String& value, const String& atlabel) = 0;
/** @overload */
CV_WRAP virtual void atread(String* value, const String& atlabel) = 0;
/**
* Write an attribute into the root group.
*
* @param value attribute value. Currently, only n-d continuous multi-channel arrays are supported.
* @param atlabel attribute name.
*
* @note CV_Error() is called if the given attribute already exists. Use atexists()
* to check whether it exists or not beforehand. And use atdelete() to delete
* it if it already exists.
*
* @sa atexists, atdelete, atread.
*/
CV_WRAP virtual void atwrite(InputArray value, const String& atlabel) = 0;
/**
* Read an attribute from the root group.
*
* @param value attribute value. Currently, only n-d continuous multi-channel arrays are supported.
* @param atlabel attribute name.
*
* @note The attribute MUST exist, otherwise CV_Error() is called. Use atexists()
* to check if it exists beforehand.
*
* @sa atexists, atdelete, atwrite
*/
CV_WRAP virtual void atread(OutputArray value, const String& atlabel) = 0;
/** @overload */
CV_WRAP virtual void dscreate( const int rows, const int cols, const int type,
const String& dslabel ) const = 0;
/* @overload */
/** @overload */
CV_WRAP virtual void dscreate( const int rows, const int cols, const int type,
const String& dslabel, const int compresslevel ) const = 0;
/* @overload */
/** @overload */
CV_WRAP virtual void dscreate( const int rows, const int cols, const int type,
const String& dslabel, const int compresslevel, const vector<int>& dims_chunks ) const = 0;
/** @brief Create and allocate storage for two dimensional single or multi channel dataset.
......
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
/**
* @file create_groups.cpp
* @author Fangjun Kuang <csukuangfj dot at gmail dot com>
......
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
/**
* @file create_read_write.cpp
* @file create_read_write_datasets.cpp
* @author Fangjun Kuang <csukuangfj dot at gmail dot com>
* @date December 2017
*
......@@ -13,14 +9,6 @@
*
*/
#ifdef __GNUC__
# pragma GCC diagnostic ignored "-Wmissing-declarations"
# if defined __clang__ || defined __APPLE__
# pragma GCC diagnostic ignored "-Wmissing-prototypes"
# pragma GCC diagnostic ignored "-Wextra"
# endif
#endif
//! [tutorial]
#include <iostream>
......@@ -29,7 +17,7 @@
using namespace cv;
void write_root_group_single_channel()
static void write_root_group_single_channel()
{
String filename = "root_group_single_channel.h5";
String dataset_name = "/single"; // Note that it is a child of the root group /
......@@ -61,7 +49,7 @@ void write_root_group_single_channel()
h5io->close();
}
void write_single_channel()
static void write_single_channel()
{
String filename = "single_channel.h5";
String parent_name = "/data";
......@@ -98,7 +86,7 @@ void write_single_channel()
* creating, reading and writing multiple-channel matrices
* are the same with single channel matrices
*/
void write_multiple_channels()
static void write_multiple_channels()
{
String filename = "two_channels.h5";
String parent_name = "/data";
......
/**
* @file read_write_attributes.cpp
* @author Fangjun Kuang <csukuangfj dot at gmail dot com>
* @date December 2017
*
* @brief It demonstrates how to read and write attributes inside the
* root group.
*
* Currently, only the following datatypes can be used as attributes:
* - cv::String
* - int
* - double
* - cv::InputArray (n-d continuous multichannel arrays)
*
* Although HDF supports associating attributes with both datasets and groups,
* only support for the root group is implemented by OpenCV at present.
*/
//! [tutorial]
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/hdf.hpp>
using namespace cv;
static void read_write_attributes()
{
String filename = "attributes.h5";
//! [tutorial_open_file]
Ptr<hdf::HDF5> h5io = hdf::open(filename);
//! [tutorial_open_file]
//! [tutorial_write_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);
//! [tutorial_write_mat]
//! [snippets_write_str]
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);
//! [snippets_write_str]
String attr_int_name = "int attribute";
int attr_int = 123456;
if (!h5io->atexists(attr_int_name))
h5io->atwrite(attr_int, attr_int_name);
String attr_double_name = "double attribute";
double attr_double = 45678.123;
if (!h5io->atexists(attr_double_name))
h5io->atwrite(attr_double, attr_double_name);
// read attributes
Mat expected_attr_mat;
int expected_attr_int;
double expected_attr_double;
//! [snippets_read_str]
String expected_attr_str;
h5io->atread(&expected_attr_str, attr_str_name);
//! [snippets_read_str]
//! [tutorial_read_mat]
h5io->atread(expected_attr_mat, attr_mat_name);
//! [tutorial_read_mat]
h5io->atread(&expected_attr_int, attr_int_name);
h5io->atread(&expected_attr_double, attr_double_name);
// check results
CV_Assert(norm(attr_mat - expected_attr_mat) < 1e-10);
CV_Assert(attr_str.compare(expected_attr_str) == 0);
CV_Assert(attr_int == expected_attr_int);
CV_Assert(fabs(attr_double - expected_attr_double) < 1e-10);
//! [tutorial_close_file]
h5io->close();
//! [tutorial_close_file]
}
int main()
{
read_write_attributes();
return 0;
}
//! [tutorial]
This diff is collapsed.
......@@ -61,6 +61,9 @@ TEST_F(HDF5_Test, create_a_single_group)
EXPECT_EQ(m_hdf_io->hlexists(group_name), true);
EXPECT_EQ(m_hdf_io->hlexists("child"), false);
// It should fail since it creates a group with an existing name
EXPECT_ANY_THROW(m_hdf_io->grcreate(group_name));
m_hdf_io->close();
}
......@@ -210,3 +213,146 @@ TEST_F(HDF5_Test, write_read_dataset_2)
m_hdf_io->close();
}
TEST_F(HDF5_Test, test_attribute)
{
reset();
String attr_name = "test attribute name";
int attr_value = 0x12345678;
m_hdf_io = hdf::open(m_filename);
EXPECT_EQ(m_hdf_io->atexists(attr_name), false);
m_hdf_io->atwrite(attr_value, attr_name);
EXPECT_ANY_THROW(m_hdf_io->atwrite(attr_value, attr_name)); // error! it already exists
EXPECT_EQ(m_hdf_io->atexists(attr_name), true);
int expected_attr_value;
m_hdf_io->atread(&expected_attr_value, attr_name);
EXPECT_EQ(attr_value, expected_attr_value);
m_hdf_io->atdelete(attr_name);
EXPECT_ANY_THROW(m_hdf_io->atdelete(attr_name)); // error! Delete non-existed attribute
EXPECT_EQ(m_hdf_io->atexists(attr_name), false);
m_hdf_io->close();
}
TEST_F(HDF5_Test, test_attribute_int)
{
reset();
String attr_name = "test int";
int attr_value = 0x12345678;
m_hdf_io = hdf::open(m_filename);
m_hdf_io->atwrite(attr_value, attr_name);
int expected_attr_value;
m_hdf_io->atread(&expected_attr_value, attr_name);
EXPECT_EQ(attr_value, expected_attr_value);
m_hdf_io->close();
}
TEST_F(HDF5_Test, test_attribute_double)
{
reset();
String attr_name = "test double";
double attr_value = 123.456789;
m_hdf_io = hdf::open(m_filename);
m_hdf_io->atwrite(attr_value, attr_name);
double expected_attr_value;
m_hdf_io->atread(&expected_attr_value, attr_name);
EXPECT_NEAR(attr_value, expected_attr_value, 1e-9);
m_hdf_io->close();
}
TEST_F(HDF5_Test, test_attribute_String)
{
reset();
String attr_name = "test-String";
String attr_value = "----_______----Hello HDF5----_______----\n";
m_hdf_io = hdf::open(m_filename);
m_hdf_io->atwrite(attr_value, attr_name);
String expected_attr_value;
m_hdf_io->atread(&expected_attr_value, attr_name);
EXPECT_EQ(attr_value.compare(expected_attr_value), 0);
m_hdf_io->close();
}
TEST_F(HDF5_Test, test_attribute_InutArray_OutputArray_2d)
{
reset();
String attr_name = "test-InputArray-OutputArray-2d";
cv::Mat attr_value;
std::vector<int> depth_vec;
depth_vec.push_back(CV_8U); depth_vec.push_back(CV_8S);
depth_vec.push_back(CV_16U); depth_vec.push_back(CV_16S);
depth_vec.push_back(CV_32S); depth_vec.push_back(CV_32F);
depth_vec.push_back(CV_64F);
std::vector<int> channel_vec;
channel_vec.push_back(1); channel_vec.push_back(2);
channel_vec.push_back(3); channel_vec.push_back(4);
channel_vec.push_back(5); channel_vec.push_back(6);
channel_vec.push_back(7); channel_vec.push_back(8);
channel_vec.push_back(9); channel_vec.push_back(10);
std::vector<std::vector<int> > dim_vec;
std::vector<int> dim_2d;
dim_2d.push_back(2); dim_2d.push_back(3);
dim_vec.push_back(dim_2d);
std::vector<int> dim_3d;
dim_3d.push_back(2);
dim_3d.push_back(3);
dim_3d.push_back(4);
dim_vec.push_back(dim_3d);
std::vector<int> dim_4d;
dim_4d.push_back(2); dim_4d.push_back(3);
dim_4d.push_back(4); dim_4d.push_back(5);
dim_vec.push_back(dim_4d);
Mat expected_attr_value;
m_hdf_io = hdf::open(m_filename);
for (size_t i = 0; i < depth_vec.size(); i++)
for (size_t j = 0; j < channel_vec.size(); j++)
for (size_t k = 0; k < dim_vec.size(); k++)
{
if (m_hdf_io->atexists(attr_name))
m_hdf_io->atdelete(attr_name);
attr_value.create(dim_vec[k], CV_MAKETYPE(depth_vec[i], channel_vec[j]));
randu(attr_value, 0, 255);
m_hdf_io->atwrite(attr_value, attr_name);
m_hdf_io->atread(expected_attr_value, attr_name);
double diff = norm(attr_value - expected_attr_value);
EXPECT_NEAR(diff, 0, 1e-6);
EXPECT_EQ(attr_value.size(), expected_attr_value.size());
EXPECT_EQ(attr_value.type(), expected_attr_value.type());
}
m_hdf_io->close();
}
......@@ -3,13 +3,13 @@ Creating Groups {#tutorial_hdf_create_groups}
Goal
----
This tutorial will show you:
- How to create a HDF5 file?
- How to create a group?
- How to check whether a given group exists or not?
- How to create a subgroup?
Source Code
----
......@@ -35,7 +35,7 @@ Next, we create the group `Group1`
@snippet samples/create_groups.cpp tutorial_create_group
Note that we have to check whether `/Group1` exists or not using
the function `hlexists` before creating it. You can not create
the function cv::hdf::HDF5::hlexists() before creating it. You can not create
a group with an existing name. Otherwise, an error will occur.
Then, we create the subgroup named `Subgroup1`. In order to
......
......@@ -3,12 +3,13 @@ Creating, Writing and Reading Datasets {#tutorial_hdf_create_read_write_datasets
Goal
----
This tutorial shows you:
- How to create a dataset?
- How to write a `cv::Mat` to a dataset?
- How to read a `cv::Mat` from a dataset?
@note Currently, it supports only reading and writing `cv::Mat` and the matrix should be continuous
@note Currently, it supports only reading and writing cv::Mat and the matrix should be continuous
in memory. Supports for other data types have not been implemented yet.
Source Code
......@@ -36,7 +37,7 @@ the dataset name is `/single`, which is inside the root group, we can use
@snippet samples/create_read_write_datasets.cpp tutorial_write_root_single_channel
to write the data directly to the dataset without the need of creating
it beforehand. Because it is created inside `HDF5::dswrite()`
it beforehand. Because it is created inside cv::hdf::HDF5::dswrite()
automatically.
@warning This applies only to datasets that reside inside the root group.
......@@ -59,7 +60,7 @@ Results
----
Figure 1 shows the result visualized using the tool HDFView for the file
`root_group_sinle_channel`. The results
`root_group_single_channel`. The results
of matrices for datasets that are not the direct children of the root group
are given in Figure 2 and Figure 3, respectively.
......
Reading and Writing Attributes{#tutorial_hdf_read_write_attributes}
===============================
Goal
----
This tutorial shows you:
- How to write attributes?
- How to read attributes?
@note Although attributes can be associated with groups and datasets, only attributes
with the root group are implemented in OpenCV. Supported attribute types are
`int`, `double`, `cv::String` and `cv::InputArray` (only for continuous arrays).
Source Code
----
The following code demonstrates reading and writing attributes
inside the root group with data types `cv::Mat`, `cv::String`, `int`
and `double`.
You can download the code from [here][1] or find it in the file
`modules/hdf/samples/read_write_attributes.cpp` of the opencv_contrib source code library.
@snippet samples/read_write_attributes.cpp tutorial
Explanation
----
The first step is to open the HDF5 file:
@snippet samples/read_write_attributes.cpp tutorial_open_file
Then we use cv::hdf::HDF5::atwrite() to write attributes by specifying its value and name:
@snippet samples/read_write_attributes.cpp tutorial_write_mat
@warning Before writing an attribute, we have to make sure that
the attribute does not exist using cv::hdf::HDF5::atexists().
To read an attribute, we use cv::hdf::HDF5::atread() by specifying the attribute name
@snippet samples/read_write_attributes.cpp tutorial_read_mat
In the end, we have to close the HDF file
@snippet samples/read_write_attributes.cpp tutorial_close_file
Results
----
Figure 1 and Figure 2 give the results visualized using the tool HDFView.
![Figure 1: Attributes of the root group](pics/attributes-file.png)
![Figure 2: Detailed attribute information](pics/attributes-details.png)
[1]: https://github.com/opencv/opencv_contrib/tree/master/modules/hdf/samples/read_write_attributes.cpp
......@@ -2,9 +2,9 @@ The Hierarchical Data Format (hdf) I/O {#tutorial_table_of_content_hdf}
=====================================
Here you will know how to read and write a HDF5 file using OpenCV.
Currently, only `cv::Mat` is supported.
Specifically, it shows you how to read/write groups, datasets and attributes.
Note that the HDF5 library has to be installed in your system
@note The HDF5 library has to be installed in your system
to use this module.
- @subpage tutorial_hdf_create_groups
......@@ -22,3 +22,11 @@ to use this module.
*Author:* Fangjun Kuang
You will learn how to create, read and write datasets.
- @subpage tutorial_hdf_read_write_attributes
*Compatibility:* \> OpenCV 3.4
*Author:* Fangjun Kuang
You will learn how to read and write attributes.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment