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: ...@@ -103,13 +103,109 @@ public:
*/ */
CV_WRAP virtual bool hlexists( const String& label ) const = 0; 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, CV_WRAP virtual void dscreate( const int rows, const int cols, const int type,
const String& dslabel ) const = 0; const String& dslabel ) const = 0;
/* @overload */ /** @overload */
CV_WRAP virtual void dscreate( const int rows, const int cols, const int type, CV_WRAP virtual void dscreate( const int rows, const int cols, const int type,
const String& dslabel, const int compresslevel ) const = 0; const String& dslabel, const int compresslevel ) const = 0;
/* @overload */ /** @overload */
CV_WRAP virtual void dscreate( const int rows, const int cols, const int type, 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; 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. /** @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 * @file create_groups.cpp
* @author Fangjun Kuang <csukuangfj dot at gmail dot com> * @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> * @author Fangjun Kuang <csukuangfj dot at gmail dot com>
* @date December 2017 * @date December 2017
* *
...@@ -13,14 +9,6 @@ ...@@ -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] //! [tutorial]
#include <iostream> #include <iostream>
...@@ -29,7 +17,7 @@ ...@@ -29,7 +17,7 @@
using namespace cv; using namespace cv;
void write_root_group_single_channel() static void write_root_group_single_channel()
{ {
String filename = "root_group_single_channel.h5"; String filename = "root_group_single_channel.h5";
String dataset_name = "/single"; // Note that it is a child of the root group / String dataset_name = "/single"; // Note that it is a child of the root group /
...@@ -61,7 +49,7 @@ void write_root_group_single_channel() ...@@ -61,7 +49,7 @@ void write_root_group_single_channel()
h5io->close(); h5io->close();
} }
void write_single_channel() static void write_single_channel()
{ {
String filename = "single_channel.h5"; String filename = "single_channel.h5";
String parent_name = "/data"; String parent_name = "/data";
...@@ -98,7 +86,7 @@ void write_single_channel() ...@@ -98,7 +86,7 @@ void write_single_channel()
* creating, reading and writing multiple-channel matrices * creating, reading and writing multiple-channel matrices
* are the same with single channel matrices * are the same with single channel matrices
*/ */
void write_multiple_channels() static void write_multiple_channels()
{ {
String filename = "two_channels.h5"; String filename = "two_channels.h5";
String parent_name = "/data"; 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]
...@@ -61,6 +61,21 @@ public: ...@@ -61,6 +61,21 @@ public:
// check if object / link exists // check if object / link exists
virtual bool hlexists( const String& label ) const; virtual bool hlexists( const String& label ) const;
virtual bool atexists(const String& atlabel) const;
virtual void atdelete(const String& atlabel);
virtual void atwrite(const int value, const String& atlabel);
virtual void atread(int* value, const String& atlabel);
virtual void atwrite(const double value, const String& atlabel);
virtual void atread(double* value, const String& atlabel);
virtual void atwrite(const String& value, const String& atlabel);
virtual void atread(String* value, const String& atlabel);
virtual void atwrite(InputArray value, const String& atlabel);
virtual void atread(OutputArray value, const String& atlabel);
/* /*
* h5 group * h5 group
*/ */
...@@ -218,7 +233,7 @@ inline hid_t HDF5Impl::GetH5type( int cvType ) const ...@@ -218,7 +233,7 @@ inline hid_t HDF5Impl::GetH5type( int cvType ) const
h5Type = H5T_NATIVE_INT; h5Type = H5T_NATIVE_INT;
break; break;
default: default:
CV_Error( Error::StsInternal, "Unknown cvType." ); CV_Error_(Error::StsInternal, ("Unknown cvType: %d.", cvType));
} }
return h5Type; return h5Type;
} }
...@@ -242,7 +257,7 @@ inline int HDF5Impl::GetCVtype( hid_t h5Type ) const ...@@ -242,7 +257,7 @@ inline int HDF5Impl::GetCVtype( hid_t h5Type ) const
else if ( H5Tequal( h5Type, H5T_NATIVE_INT ) ) else if ( H5Tequal( h5Type, H5T_NATIVE_INT ) )
cvType = CV_32S; cvType = CV_32S;
else else
CV_Error( Error::StsInternal, "Unknown H5Type." ); CV_Error_(Error::StsInternal, ("Unknown H5Type: %d.", h5Type));
return cvType; return cvType;
} }
...@@ -302,15 +317,229 @@ bool HDF5Impl::hlexists( const String& label ) const ...@@ -302,15 +317,229 @@ bool HDF5Impl::hlexists( const String& label ) const
return exists; return exists;
} }
bool HDF5Impl::atexists(const String& atlabel) const
{
bool res = false;
// save old error handler
void *errdata;
H5E_auto2_t errfunc;
hid_t stackid = H5E_DEFAULT;
H5Eget_auto(stackid, &errfunc, &errdata);
// turn off error handling
H5Eset_auto(stackid, NULL, NULL);
hid_t attr = H5Aopen_name(m_h5_file_id, atlabel.c_str());
if (attr >= 0)
{
res = true;
H5Aclose(attr);
}
// restore previous error handler
H5Eset_auto(stackid, errfunc, errdata);
return res;
}
void HDF5Impl::atdelete(const String& atlabel)
{
if (!atexists(atlabel))
CV_Error_(Error::StsInternal,("The attribute '%s' does not exist!", atlabel.c_str()));
H5Adelete(m_h5_file_id, atlabel.c_str());
}
void HDF5Impl::atwrite(const int value, const String& atlabel)
{
if (atexists(atlabel))
CV_Error_(Error::StsInternal,("The attribute '%s' already exists!", atlabel.c_str()));
hid_t aid = H5Screate(H5S_SCALAR);;
hid_t attr = H5Acreate2(m_h5_file_id, atlabel.c_str(), H5T_NATIVE_INT, aid,
H5P_DEFAULT, H5P_DEFAULT);
H5Awrite(attr, H5T_NATIVE_INT, &value);
H5Sclose(aid);
H5Aclose(attr);
}
void HDF5Impl::atread(int* value, const String& atlabel)
{
if (!value)
CV_Error(Error::StsBadArg, "NULL pointer");
if (!atexists(atlabel))
CV_Error_(Error::StsInternal, ("Attribute '%s' does not exist!", atlabel.c_str()));
hid_t attr = H5Aopen(m_h5_file_id, atlabel.c_str(), H5P_DEFAULT);
H5Aread(attr, H5T_NATIVE_INT, value);
H5Aclose(attr);
}
void HDF5Impl::atwrite(const double value, const String& atlabel)
{
if (atexists(atlabel))
CV_Error_(Error::StsInternal,("The attribute '%s' already exists!", atlabel.c_str()));
hid_t aid = H5Screate(H5S_SCALAR);;
hid_t attr = H5Acreate2(m_h5_file_id, atlabel.c_str(), H5T_NATIVE_DOUBLE, aid,
H5P_DEFAULT, H5P_DEFAULT);
H5Awrite(attr, H5T_NATIVE_DOUBLE, &value);
H5Sclose(aid);
H5Aclose(attr);
}
void HDF5Impl::atread(double* value, const String& atlabel)
{
if (!value)
CV_Error(Error::StsBadArg, "NULL pointer");
if (!atexists(atlabel))
CV_Error_(Error::StsInternal, ("Attribute '%s' does not exist!", atlabel.c_str()));
hid_t attr = H5Aopen(m_h5_file_id, atlabel.c_str(), H5P_DEFAULT);
H5Aread(attr, H5T_NATIVE_DOUBLE, value);
H5Aclose(attr);
}
void HDF5Impl::atwrite(const String& value, const String& atlabel)
{
if (atexists(atlabel))
CV_Error_(Error::StsInternal,("The attribute '%s' already exists!", atlabel.c_str()));
hid_t aid = H5Screate(H5S_SCALAR);
hid_t atype = H5Tcopy(H5T_C_S1);
H5Tset_size(atype, value.size()+1);
H5Tset_strpad(atype, H5T_STR_NULLTERM);
hid_t attr = H5Acreate2(m_h5_file_id, atlabel.c_str(), atype, aid, H5P_DEFAULT, H5P_DEFAULT);
H5Awrite(attr, atype, value.c_str());
H5Sclose(aid);
H5Tclose(atype);
H5Aclose(attr);
}
void HDF5Impl::atread(String* value, const String& atlabel)
{
if (!value)
CV_Error(Error::StsBadArg, "NULL pointer");
if (!atexists(atlabel))
CV_Error_(Error::StsInternal, ("Attribute '%s' does not exist!", atlabel.c_str()));
hid_t attr = H5Aopen(m_h5_file_id, atlabel.c_str(), H5P_DEFAULT);
hid_t atype = H5Aget_type(attr);
H5T_class_t type_class = H5Tget_class(atype);
if (type_class != H5T_STRING)
{
H5Tclose(atype);
H5Aclose(attr);
CV_Error_(Error::StsInternal, ("Attribute '%s' is not of string type!", atlabel.c_str()));
}
size_t size = H5Tget_size(atype);
*value = String(size, 0); // allocate space
hid_t atype_mem = H5Tget_native_type(atype, H5T_DIR_ASCEND);
H5Aread(attr, atype_mem, const_cast<char*>(value->c_str()));
H5Tclose(atype_mem);
H5Tclose(atype);
H5Aclose(attr);
}
void HDF5Impl::atwrite(InputArray value, const String& atlabel)
{
if (atexists(atlabel))
CV_Error_(Error::StsInternal,("The attribute '%s' already exists!", atlabel.c_str()));
Mat value_ = value.getMat();
if (!value_.isContinuous())
CV_Error(Error::StsInternal, "Only continuous array are implemented. Current array is not continuous!");
int ndims = value_.dims;
vector<hsize_t> dim_vec(ndims);
for (int i = 0; i < ndims; i++)
dim_vec[i] = value_.size[i];
hid_t dtype = GetH5type(value_.type());
if (value_.channels() > 1)
{
hsize_t dims[1] = { (hsize_t)value_.channels()};
dtype = H5Tarray_create(dtype, 1, dims);
}
hid_t aid = H5Screate(H5S_SIMPLE);
H5Sset_extent_simple(aid, ndims, dim_vec.data(), NULL);
hid_t attr = H5Acreate2(m_h5_file_id, atlabel.c_str(), dtype,
aid, H5P_DEFAULT, H5P_DEFAULT);
H5Awrite(attr, dtype, value_.data);
if (value_.channels() > 1)
H5Tclose(dtype);
H5Sclose(aid);
H5Aclose(attr);
}
void HDF5Impl::atread(OutputArray value, const String& atlabel)
{
if (!atexists(atlabel))
CV_Error_(Error::StsInternal, ("Attribute '%s' does not exist!", atlabel.c_str()));
hid_t attr = H5Aopen(m_h5_file_id, atlabel.c_str(), H5P_DEFAULT);
hid_t atype = H5Aget_type(attr);
hid_t aspace = H5Aget_space(attr);
int rank = H5Sget_simple_extent_ndims(aspace);
vector<hsize_t> dim_vec_(rank);
H5Sget_simple_extent_dims(aspace, dim_vec_.data(), NULL);
vector<int> dim_vec(dim_vec_.begin(), dim_vec_.end());
int nchannels = 1;
hid_t h5type;
if (H5Tget_class(atype) == H5T_ARRAY)
{
hsize_t dims;
H5Tget_array_dims(atype, &dims);
nchannels = (int) dims;
hid_t super_type = H5Tget_super(atype);
h5type = H5Tget_native_type(super_type, H5T_DIR_ASCEND);
H5Tclose(super_type);
}
else
h5type = H5Tget_native_type(atype, H5T_DIR_ASCEND);
int dtype = GetCVtype(h5type);
value.create(rank, dim_vec.data(), CV_MAKETYPE(dtype, nchannels));
H5Aread(attr, atype, value.getMat().data);
H5Sclose(aspace);
H5Tclose(atype);
H5Aclose(attr);
}
/* /*
* h5 group * h5 group
*/ */
void HDF5Impl::grcreate( const String& grlabel ) void HDF5Impl::grcreate( const String& grlabel )
{ {
hid_t gid = H5Gcreate( m_h5_file_id, grlabel.c_str(), if (hlexists(grlabel))
H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); CV_Error_(Error::StsInternal, ("Requested group '%s' already exists.", grlabel.c_str()));
H5Gclose( gid );
hid_t gid = H5Gcreate(m_h5_file_id, grlabel.c_str(),
H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
H5Gclose(gid);
} }
/* /*
...@@ -358,7 +587,7 @@ vector<int> HDF5Impl::dsgetsize( const String& dslabel, int dims_flag ) const ...@@ -358,7 +587,7 @@ vector<int> HDF5Impl::dsgetsize( const String& dslabel, int dims_flag ) const
SizeVect.resize( n_dims ); SizeVect.resize( n_dims );
} }
else else
CV_Error( Error::StsInternal, "Unknown dimension flag." ); CV_Error_(Error::StsInternal, ("Unknown dimension flag: %d", dims_flag));
// fill with size data // fill with size data
for ( size_t d = 0; d < SizeVect.size(); d++ ) for ( size_t d = 0; d < SizeVect.size(); d++ )
...@@ -479,7 +708,7 @@ void HDF5Impl::dscreate( const int n_dims, const int* sizes, const int type, ...@@ -479,7 +708,7 @@ void HDF5Impl::dscreate( const int n_dims, const int* sizes, const int type,
CV_Assert( compresslevel >= H5_NONE && compresslevel <= 9 ); CV_Assert( compresslevel >= H5_NONE && compresslevel <= 9 );
if ( hlexists( dslabel ) == true ) if ( hlexists( dslabel ) == true )
CV_Error( Error::StsInternal, "Requested dataset already exists." ); CV_Error_(Error::StsInternal, ("Requested dataset '%s' already exists.", dslabel.c_str()));
int channs = CV_MAT_CN( type ); int channs = CV_MAT_CN( type );
...@@ -802,7 +1031,7 @@ void HDF5Impl::dsinsert( InputArray Array, const String& dslabel, ...@@ -802,7 +1031,7 @@ void HDF5Impl::dsinsert( InputArray Array, const String& dslabel,
// check dataset exists // check dataset exists
if ( hlexists( dslabel ) == false ) if ( hlexists( dslabel ) == false )
CV_Error( Error::StsInternal, "Dataset does not exist." ); CV_Error_(Error::StsInternal, ("Dataset '%s' does not exist.", dslabel.c_str()));
Mat matrix = Array.getMat(); Mat matrix = Array.getMat();
...@@ -935,7 +1164,7 @@ void HDF5Impl::kpcreate( const int size, const String& kplabel, ...@@ -935,7 +1164,7 @@ void HDF5Impl::kpcreate( const int size, const String& kplabel,
CV_Assert( compresslevel >= H5_NONE && compresslevel <= 9 ); CV_Assert( compresslevel >= H5_NONE && compresslevel <= 9 );
if ( hlexists( kplabel ) == true ) if ( hlexists( kplabel ) == true )
CV_Error( Error::StsInternal, "Requested dataset already exists." ); CV_Error_(Error::StsInternal, ("Requested dataset '%s' already exists.", kplabel.c_str()));
hsize_t dchunk[1]; hsize_t dchunk[1];
hsize_t dsdims[1]; hsize_t dsdims[1];
...@@ -1058,7 +1287,7 @@ void HDF5Impl::kpinsert( const vector<KeyPoint> keypoints, const String& kplabel ...@@ -1058,7 +1287,7 @@ void HDF5Impl::kpinsert( const vector<KeyPoint> keypoints, const String& kplabel
// check dataset exists // check dataset exists
if ( hlexists( kplabel ) == false ) if ( hlexists( kplabel ) == false )
CV_Error( Error::StsInternal, "Dataset does not exist." ); CV_Error_(Error::StsInternal, ("Dataset '%s' does not exist.", kplabel.c_str()));
hsize_t dsddims[1]; hsize_t dsddims[1];
hsize_t doffset[1]; hsize_t doffset[1];
......
...@@ -61,6 +61,9 @@ TEST_F(HDF5_Test, create_a_single_group) ...@@ -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(group_name), true);
EXPECT_EQ(m_hdf_io->hlexists("child"), false); 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(); m_hdf_io->close();
} }
...@@ -210,3 +213,146 @@ TEST_F(HDF5_Test, write_read_dataset_2) ...@@ -210,3 +213,146 @@ TEST_F(HDF5_Test, write_read_dataset_2)
m_hdf_io->close(); 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} ...@@ -3,13 +3,13 @@ Creating Groups {#tutorial_hdf_create_groups}
Goal Goal
---- ----
This tutorial will show you: This tutorial will show you:
- How to create a HDF5 file? - How to create a HDF5 file?
- How to create a group? - How to create a group?
- How to check whether a given group exists or not? - How to check whether a given group exists or not?
- How to create a subgroup? - How to create a subgroup?
Source Code Source Code
---- ----
...@@ -35,7 +35,7 @@ Next, we create the group `Group1` ...@@ -35,7 +35,7 @@ Next, we create the group `Group1`
@snippet samples/create_groups.cpp tutorial_create_group @snippet samples/create_groups.cpp tutorial_create_group
Note that we have to check whether `/Group1` exists or not using 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. a group with an existing name. Otherwise, an error will occur.
Then, we create the subgroup named `Subgroup1`. In order to 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 ...@@ -3,12 +3,13 @@ Creating, Writing and Reading Datasets {#tutorial_hdf_create_read_write_datasets
Goal Goal
---- ----
This tutorial shows you: This tutorial shows you:
- How to create a dataset? - How to create a dataset?
- How to write a `cv::Mat` to a dataset? - How to write a `cv::Mat` to a dataset?
- How to read a `cv::Mat` from 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. in memory. Supports for other data types have not been implemented yet.
Source Code Source Code
...@@ -36,7 +37,7 @@ the dataset name is `/single`, which is inside the root group, we can use ...@@ -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 @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 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. automatically.
@warning This applies only to datasets that reside inside the root group. @warning This applies only to datasets that reside inside the root group.
...@@ -59,7 +60,7 @@ Results ...@@ -59,7 +60,7 @@ Results
---- ----
Figure 1 shows the result visualized using the tool HDFView for the file 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 of matrices for datasets that are not the direct children of the root group
are given in Figure 2 and Figure 3, respectively. 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} ...@@ -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. 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. to use this module.
- @subpage tutorial_hdf_create_groups - @subpage tutorial_hdf_create_groups
...@@ -22,3 +22,11 @@ to use this module. ...@@ -22,3 +22,11 @@ to use this module.
*Author:* Fangjun Kuang *Author:* Fangjun Kuang
You will learn how to create, read and write datasets. 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