From fac489644f29c5a37e098993a95976efb59e55ba Mon Sep 17 00:00:00 2001 From: Li-Ta Lo Date: Mon, 4 Jan 2021 10:01:49 -0700 Subject: [PATCH] HDF5 unit tests Move HDF5ImageWriter/Reader to its own unit test. Use user supplied field name as HDF5 dataset name. --- vtkm/io/ImageReaderHDF5.cxx | 17 +++- vtkm/io/ImageWriterBase.cxx | 5 -- vtkm/io/ImageWriterBase.h | 4 +- vtkm/io/ImageWriterHDF5.cxx | 9 +- vtkm/io/ImageWriterHDF5.h | 5 ++ vtkm/io/testing/CMakeLists.txt | 5 ++ vtkm/io/testing/UnitTestHDF5Image.cxx | 109 ++++++++++++++++++++++++ vtkm/io/testing/UnitTestImageWriter.cxx | 48 ----------- 8 files changed, 142 insertions(+), 60 deletions(-) create mode 100644 vtkm/io/testing/UnitTestHDF5Image.cxx diff --git a/vtkm/io/ImageReaderHDF5.cxx b/vtkm/io/ImageReaderHDF5.cxx index 2f49daf2d..46a33f0b3 100644 --- a/vtkm/io/ImageReaderHDF5.cxx +++ b/vtkm/io/ImageReaderHDF5.cxx @@ -23,22 +23,31 @@ void ImageReaderHDF5::Read() // need to find width, height and pixel type. auto fileid = H5Fopen(this->FileName.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT); - if (!H5IMis_image(fileid, "image")) + const auto fieldName = this->PointFieldName.c_str(); + if (!H5IMis_image(fileid, fieldName)) throw vtkm::io::ErrorIO{ "Not an HDF5 image file" }; hsize_t width, height, nplanes; hssize_t npals; char interlace[16]; - if (H5IMget_image_info(fileid, "image", &width, &height, &nplanes, interlace, &npals) < 0) + if (H5IMget_image_info(fileid, fieldName, &width, &height, &nplanes, interlace, &npals) < 0) throw vtkm::io ::ErrorIO{ "Can not get image info" }; // We don't use the H5IMread_image() since it only supports 8 bit pixel. hid_t did; - if ((did = H5Dopen2(fileid, "image", H5P_DEFAULT)) < 0) + if ((did = H5Dopen2(fileid, fieldName, H5P_DEFAULT)) < 0) throw vtkm::io::ErrorIO{ "Can not open image dataset" }; if (strncmp(interlace, "INTERLACE_PIXEL", 15) != 0) - throw vtkm::io::ErrorIO{ "Unsupported interlace mode" }; + { + std::string message = "Unsupported interlace mode: "; + message += interlace; + message += + ". Currently, only the INTERLACE_PIXEL mode is supported. See " + "https://portal.hdfgroup.org/display/HDF5/HDF5+Image+and+Palette+Specification%2C+Version+1.2" + " for more details on the HDF5 image convention."; + throw vtkm::io::ErrorIO{ message }; + } std::vector buffer; auto type_size = H5LDget_dset_type_size(did, nullptr); diff --git a/vtkm/io/ImageWriterBase.cxx b/vtkm/io/ImageWriterBase.cxx index cfb5e540c..0466c0e12 100644 --- a/vtkm/io/ImageWriterBase.cxx +++ b/vtkm/io/ImageWriterBase.cxx @@ -29,11 +29,6 @@ ImageWriterBase::ImageWriterBase(const std::string& filename) ImageWriterBase::~ImageWriterBase() noexcept {} -void ImageWriterBase::WriteDataSet(const vtkm::cont::DataSet& dataSet) -{ - this->WriteDataSet(dataSet, std::string{}); -} - void ImageWriterBase::WriteDataSet(const vtkm::cont::DataSet& dataSet, const std::string& colorFieldName) { diff --git a/vtkm/io/ImageWriterBase.h b/vtkm/io/ImageWriterBase.h index e6cce5969..e10624ca3 100644 --- a/vtkm/io/ImageWriterBase.h +++ b/vtkm/io/ImageWriterBase.h @@ -52,8 +52,8 @@ public: /// `ArrayHandle` of `vtkm::Vec4f_32`). If no color field name is given, /// the first point field that matches this criteria is written. /// - VTKM_CONT void WriteDataSet(const vtkm::cont::DataSet& dataSet); - VTKM_CONT void WriteDataSet(const vtkm::cont::DataSet& dataSet, const std::string& colorField); + VTKM_CONT void WriteDataSet(const vtkm::cont::DataSet& dataSet, + const std::string& colorField = {}); ///@} enum class PixelDepth diff --git a/vtkm/io/ImageWriterHDF5.cxx b/vtkm/io/ImageWriterHDF5.cxx index 0115aed9e..e0b817b97 100644 --- a/vtkm/io/ImageWriterHDF5.cxx +++ b/vtkm/io/ImageWriterHDF5.cxx @@ -35,6 +35,13 @@ namespace io { ImageWriterHDF5::~ImageWriterHDF5() noexcept = default; +void ImageWriterHDF5::WriteDataSet(const vtkm::cont::DataSet& dataSet, + const std::string& colorField) +{ + this->fieldName = colorField; + Superclass::WriteDataSet(dataSet, colorField); +} + template herr_t ImageWriterHDF5::WriteToFile(vtkm::Id width, vtkm::Id height, const ColorArrayType& pixels) { @@ -56,7 +63,7 @@ herr_t ImageWriterHDF5::WriteToFile(vtkm::Id width, vtkm::Id height, const Color } // Shamelessly copied from H5IMmake_image_24bit() implementation. - auto dset_name = "image"; + auto dset_name = this->fieldName.c_str(); // The image is stored as height*width*3 array of UCHAR/UINT16, i.e. INTERLACE_PIXEL hsize_t dims[] = { hsize_t(height), hsize_t(width), 3 }; diff --git a/vtkm/io/ImageWriterHDF5.h b/vtkm/io/ImageWriterHDF5.h index c85dbc007..180f13a86 100644 --- a/vtkm/io/ImageWriterHDF5.h +++ b/vtkm/io/ImageWriterHDF5.h @@ -35,6 +35,9 @@ public: ImageWriterHDF5(const ImageWriterHDF5&) = delete; ImageWriterHDF5& operator=(const ImageWriterHDF5&) = delete; + VTKM_CONT void WriteDataSet(const vtkm::cont::DataSet& dataSet, + const std::string& colorField = {}); + protected: VTKM_CONT void Write(vtkm::Id width, vtkm::Id height, const ColorArrayType& pixels) override; @@ -43,6 +46,8 @@ private: VTKM_CONT herr_t WriteToFile(vtkm::Id width, vtkm::Id height, const ColorArrayType& pixels); hid_t fileid = 0; + // FIXME: a hack for the moment, design a better API. + std::string fieldName; static constexpr auto IMAGE_CLASS = "IMAGE"; static constexpr auto IMAGE_VERSION = "1.2"; diff --git a/vtkm/io/testing/CMakeLists.txt b/vtkm/io/testing/CMakeLists.txt index 1fe0ade5d..b1126067d 100644 --- a/vtkm/io/testing/CMakeLists.txt +++ b/vtkm/io/testing/CMakeLists.txt @@ -23,6 +23,11 @@ if(VTKm_ENABLE_RENDERING) UnitTestImageWriter.cxx ) + if (VTKm_ENABLE_HDF5_IO) + set(unit_tests ${unit_tests} + UnitTestHDF5Image.cxx) + endif() + set(unit_test_libraries ${unit_test_libraries} vtkm_rendering) endif() diff --git a/vtkm/io/testing/UnitTestHDF5Image.cxx b/vtkm/io/testing/UnitTestHDF5Image.cxx new file mode 100644 index 000000000..45aa449bd --- /dev/null +++ b/vtkm/io/testing/UnitTestHDF5Image.cxx @@ -0,0 +1,109 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +//============================================================================ + +#include +#include +#include +#include +#include + +#include +namespace +{ + +using namespace vtkm::io; +using namespace vtkm::rendering; + +void TestFilledImage(vtkm::cont::DataSet& dataSet, + const std::string& fieldName, + const vtkm::rendering::Canvas& canvas) +{ + VTKM_TEST_ASSERT(dataSet.HasPointField(fieldName), "Point Field Not Found: " + fieldName); + + auto pointField = dataSet.GetPointField(fieldName); + VTKM_TEST_ASSERT(pointField.GetNumberOfValues() == canvas.GetWidth() * canvas.GetHeight(), + "wrong image dimensions"); + VTKM_TEST_ASSERT(pointField.GetData().template IsType>(), + "wrong ArrayHandle type"); + auto pixelPortal = + pointField.GetData().template Cast>().ReadPortal(); + + auto colorPortal = canvas.GetColorBuffer().ReadPortal(); + + VTKM_TEST_ASSERT(test_equal_portals(pixelPortal, colorPortal)); +} + +void TestCreateImageDataSet(const vtkm::rendering::Canvas& canvas) +{ + std::cout << "TestCreateImageDataSet" << std::endl; + auto dataSet = canvas.GetDataSet("pixel-color"); + TestFilledImage(dataSet, "pixel-color", canvas); +} + +void TestReadAndWriteHDF5(const vtkm::rendering::Canvas& canvas, + std::string filename, + vtkm::io::ImageWriterBase::PixelDepth pixelDepth) +{ + std::cout << "TestReadAndWriteHDF5 - " << filename << std::endl; + bool throws = false; + try + { + vtkm::io::ImageWriterHDF5 writer(filename); + vtkm::cont::DataSet dataSet; + writer.WriteDataSet(dataSet, "color"); + } + catch (const vtkm::cont::Error&) + { + throws = true; + } + VTKM_TEST_ASSERT(throws, "Fill Image did not throw with empty data"); + + { + vtkm::io::ImageWriterHDF5 writer(filename); + writer.SetPixelDepth(pixelDepth); + writer.WriteDataSet(canvas.GetDataSet(), "color"); + } + { + vtkm::io::ImageReaderHDF5 reader(filename); + vtkm::cont::DataSet dataSet = reader.ReadDataSet(); + } + { + vtkm::io::ImageWriterHDF5 writer(filename); + writer.SetPixelDepth(pixelDepth); + writer.WriteDataSet(canvas.GetDataSet(), "color"); + } + { + vtkm::io::ImageReaderHDF5 reader(filename); + vtkm::cont::DataSet dataSet = reader.ReadDataSet(); + TestFilledImage(dataSet, reader.GetPointFieldName(), canvas); + } +} + +void TestHDF5Image() +{ + vtkm::rendering::Canvas canvas(16, 16); + canvas.SetBackgroundColor(vtkm::rendering::Color::red); + canvas.Clear(); + // Line from top left to bottom right, ensures correct transposedness + canvas.AddLine(-0.9, 0.9, 0.9, -0.9, 2.0f, vtkm::rendering::Color::black); + vtkm::Bounds colorBarBounds(-0.8, -0.6, -0.8, 0.8, 0, 0); + canvas.AddColorBar(colorBarBounds, vtkm::cont::ColorTable("inferno"), false); + canvas.BlendBackground(); + + TestReadAndWriteHDF5(canvas, "hdf5RGB8Test.h5", vtkm::io::ImageWriterBase::PixelDepth::PIXEL_8); + TestReadAndWriteHDF5(canvas, "hdf5RGB16Test.h5", vtkm::io::ImageWriterBase::PixelDepth::PIXEL_16); +} + +} // namespace + +int UnitTestHDF5Image(int argc, char* argv[]) +{ + return vtkm::cont::testing::Testing::Run(TestHDF5Image, argc, argv); +} diff --git a/vtkm/io/testing/UnitTestImageWriter.cxx b/vtkm/io/testing/UnitTestImageWriter.cxx index 0aee032f4..41ccbca08 100644 --- a/vtkm/io/testing/UnitTestImageWriter.cxx +++ b/vtkm/io/testing/UnitTestImageWriter.cxx @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -19,7 +18,6 @@ #include #include -#include namespace { @@ -131,45 +129,6 @@ void TestReadAndWritePNM(const vtkm::rendering::Canvas& canvas, } } -void TestReadAndWriteHDF5(const vtkm::rendering::Canvas& canvas, - std::string filename, - vtkm::io::ImageWriterBase::PixelDepth pixelDepth) -{ - std::cout << "TestReadAndWriteHDF5 - " << filename << std::endl; - bool throws = false; - try - { - vtkm::io::ImageWriterHDF5 writer(filename); - vtkm::cont::DataSet dataSet; - writer.WriteDataSet(dataSet); - } - catch (const vtkm::cont::Error&) - { - throws = true; - } - VTKM_TEST_ASSERT(throws, "Fill Image did not throw with empty data"); - - { - vtkm::io::ImageWriterHDF5 writer(filename); - writer.SetPixelDepth(pixelDepth); - writer.WriteDataSet(canvas.GetDataSet()); - } - { - vtkm::io::ImageReaderHDF5 reader(filename); - vtkm::cont::DataSet dataSet = reader.ReadDataSet(); - } - { - vtkm::io::ImageWriterHDF5 writer(filename); - writer.SetPixelDepth(pixelDepth); - writer.WriteDataSet(canvas.GetDataSet()); - } - { - vtkm::io::ImageReaderHDF5 reader(filename); - vtkm::cont::DataSet dataSet = reader.ReadDataSet(); - TestFilledImage(dataSet, reader.GetPointFieldName(), canvas); - } -} - void TestBaseImageMethods(const vtkm::rendering::Canvas& canvas) { TestCreateImageDataSet(canvas); @@ -187,12 +146,6 @@ void TestPNGImage(const vtkm::rendering::Canvas& canvas) TestReadAndWritePNG(canvas, "pngRGB16Test.png", vtkm::io::ImageWriterBase::PixelDepth::PIXEL_16); } -void TestHDF5Image(const vtkm::rendering::Canvas& canvas) -{ - TestReadAndWriteHDF5(canvas, "hdf5RGB8Test.h5", vtkm::io::ImageWriterBase::PixelDepth::PIXEL_8); - TestReadAndWriteHDF5(canvas, "hdf5RGB16Test.h5", vtkm::io::ImageWriterBase::PixelDepth::PIXEL_16); -} - void TestImage() { vtkm::rendering::Canvas canvas(16, 16); @@ -208,7 +161,6 @@ void TestImage() TestBaseImageMethods(canvas); TestPNMImage(canvas); TestPNGImage(canvas); - TestHDF5Image(canvas); } }