mirror of
https://gitlab.kitware.com/vtk/vtk-m
synced 2024-09-08 13:23:51 +00:00
Update image writes to style and library
Updated the image writer classes to better follow VTK-m naming convention (i.e. use `ImageWriterPNG` instead of `PNGWriter`). The structure of the image writer class now also better matches the interface for the VTK writer. Also put the implementation of the image writers into the `vtkm_io` library so that it only has to be compiled once.
This commit is contained in:
parent
1e91b32ace
commit
6c97054b7f
@ -16,7 +16,9 @@ set(headers
|
||||
ImageReaderBase.h
|
||||
ImageReaderPNG.h
|
||||
ImageReaderPNM.h
|
||||
ImageWriter.h
|
||||
ImageWriterBase.h
|
||||
ImageWriterPNG.h
|
||||
ImageWriterPNM.h
|
||||
PixelTypes.h
|
||||
VTKDataSetReader.h
|
||||
VTKDataSetReaderBase.h
|
||||
@ -29,7 +31,6 @@ set(headers
|
||||
)
|
||||
|
||||
set(template_sources
|
||||
ImageWriter.hxx
|
||||
PixelTypes.hxx
|
||||
)
|
||||
|
||||
@ -47,6 +48,9 @@ set(device_sources
|
||||
ImageReaderBase.cxx
|
||||
ImageReaderPNG.cxx
|
||||
ImageReaderPNM.cxx
|
||||
ImageWriterBase.cxx
|
||||
ImageWriterPNG.cxx
|
||||
ImageWriterPNM.cxx
|
||||
VTKDataSetReader.cxx
|
||||
VTKDataSetReaderBase.cxx
|
||||
VTKDataSetWriter.cxx
|
||||
|
@ -1,140 +0,0 @@
|
||||
//============================================================================
|
||||
// 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.
|
||||
//============================================================================
|
||||
#ifndef vtk_m_io_ImageWriter_h
|
||||
#define vtk_m_io_ImageWriter_h
|
||||
|
||||
#include <vtkm/Types.h>
|
||||
#include <vtkm/cont/ArrayHandle.h>
|
||||
#include <vtkm/cont/DataSet.h>
|
||||
|
||||
namespace vtkm
|
||||
{
|
||||
namespace io
|
||||
{
|
||||
|
||||
/// \brief Manages writing, and loading data from images
|
||||
///
|
||||
/// \c BaseImageWriter implements methods for loading imaging data from a canvas or
|
||||
/// ArrayHandle and storing that data in a vtkm::cont::DataSet. Image rgb values
|
||||
/// are represented as a point field in a 2D uniform dataset.
|
||||
///
|
||||
/// \c BaseImageWriter can be constructed from a file, canvas, or ArrayHandle. It can
|
||||
/// also be empy constructed and filled in with a dataset later.
|
||||
///
|
||||
/// \c BaseImageWriter implements virtual methods for writing files. Ideally,
|
||||
/// these methods will be overriden in various subclasses to implement specific
|
||||
/// functionality for writing data to specific image file-types.
|
||||
///
|
||||
class BaseImageWriter
|
||||
{
|
||||
public:
|
||||
/// Constructs an emtpy BaseImageWriter.
|
||||
///
|
||||
BaseImageWriter() = default;
|
||||
explicit BaseImageWriter(const vtkm::Id& maxColorValue)
|
||||
: MaxColorValue(maxColorValue)
|
||||
{
|
||||
}
|
||||
~BaseImageWriter() noexcept = default;
|
||||
|
||||
/// Write and store ImageDataSet to a file. Meant to be implemented in
|
||||
/// overriden image-specific classes
|
||||
///
|
||||
virtual void WriteToFile(const std::string& fileName,
|
||||
const vtkm::cont::DataSet& dataSet) const = 0;
|
||||
|
||||
vtkm::Id GetImageWidth(vtkm::cont::DataSet dataSet) const;
|
||||
vtkm::Id GetImageHeight(vtkm::cont::DataSet dataSet) const;
|
||||
|
||||
const std::string& GetPointFieldName() const { return this->PointFieldName; }
|
||||
void SetMaxColorValue(const vtkm::Id& maxColorValue) { this->MaxColorValue = maxColorValue; }
|
||||
|
||||
protected:
|
||||
std::string PointFieldName = "pixel-data";
|
||||
vtkm::Id MaxColorValue{ 0 };
|
||||
};
|
||||
|
||||
/// \brief Manages writing images using the PNM format
|
||||
///
|
||||
/// \c PNMWriter extends BaseImageWriter, and implements writing images in a
|
||||
/// valid PNM format (for magic number P6). More details on the PNM
|
||||
/// format can be found here: http://netpbm.sourceforge.net/doc/ppm.html
|
||||
///
|
||||
/// When a file is writen the MaxColorValue found in the file is used to
|
||||
/// determine the PixelType required to stored PixelType is instead dependent
|
||||
/// upon the read MaxColorValue obtained from the file
|
||||
class PNMWriter : public BaseImageWriter
|
||||
{
|
||||
using Superclass = BaseImageWriter;
|
||||
|
||||
public:
|
||||
using Superclass::Superclass;
|
||||
PNMWriter() = default;
|
||||
~PNMWriter() noexcept = default;
|
||||
|
||||
/// Attempts to write the ImageDataSet to a PNM file. The MaxColorValue
|
||||
/// set in the file with either be selected from the stored MaxColorValue
|
||||
/// member variable, or from the templated type if MaxColorValue hasn't been
|
||||
/// set from a read file.
|
||||
///
|
||||
void WriteToFile(const std::string& fileName, const vtkm::cont::DataSet& dataSet) const override;
|
||||
|
||||
protected:
|
||||
/// Writes image data stored in ImageDataSet to the provided outStream
|
||||
/// Casts the data to the provided PixelType
|
||||
///
|
||||
template <typename PixelType>
|
||||
void EncodeFile(std::ofstream& outStream, const vtkm::cont::DataSet& dataSet) const;
|
||||
|
||||
// Currently only works with P6 PNM files (PPM)
|
||||
std::string MagicNumber{ "P6" };
|
||||
};
|
||||
|
||||
/// \brief Manages writing images using the PNG format via lodepng
|
||||
///
|
||||
/// \c PNGWriter extends BaseImageWriter and implements writing images in a valid
|
||||
/// PNG format. It utilizes lodepng's encode file functions to write
|
||||
/// PNG images that are automatically compressed to optimal sizes relative to
|
||||
/// the actual bit complexity of the image.
|
||||
///
|
||||
/// \c PNGImage will automatically upsample/downsample written image data
|
||||
/// to the supplied templated PixelType. For example, it is possible to write
|
||||
/// a 1-bit greyscale image into a 16bit RGB PNG object. It is up to the user to
|
||||
/// decide the pixel format for output PNGs
|
||||
class PNGWriter : public BaseImageWriter
|
||||
{
|
||||
using Superclass = BaseImageWriter;
|
||||
|
||||
public:
|
||||
using Superclass::Superclass;
|
||||
PNGWriter() = default;
|
||||
~PNGWriter() noexcept = default;
|
||||
|
||||
/// Writes stored data matched to the class's templated type
|
||||
/// to a file in PNG format. Relies upon the lodepng encoding
|
||||
/// method to optimize compression and choose the best storage format.
|
||||
///
|
||||
void WriteToFile(const std::string& fileName, const vtkm::cont::DataSet& dataSet) const override;
|
||||
|
||||
/// Writes stored data matched to the method's templated type
|
||||
/// to a file in PNG format. Relies upon the lodepng encoding
|
||||
/// method to optimize compression and choose the best storage format.
|
||||
///
|
||||
template <typename PixelType>
|
||||
void WriteToFile(const std::string& fileName, const vtkm::cont::DataSet& dataSet) const;
|
||||
};
|
||||
|
||||
|
||||
} // namespace io
|
||||
} // namespace vtkm
|
||||
|
||||
#include <vtkm/io/ImageWriter.hxx>
|
||||
|
||||
#endif
|
@ -1,172 +0,0 @@
|
||||
//============================================================================
|
||||
// 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.
|
||||
//============================================================================
|
||||
#ifndef vtk_m_io_ImageWriter_hxx
|
||||
#define vtk_m_io_ImageWriter_hxx
|
||||
|
||||
#include <vtkm/cont/CellSetStructured.h>
|
||||
#include <vtkm/cont/DataSetBuilderUniform.h>
|
||||
#include <vtkm/io/ImageWriter.h>
|
||||
#include <vtkm/io/PixelTypes.h>
|
||||
|
||||
VTKM_THIRDPARTY_PRE_INCLUDE
|
||||
#include <vtkm/thirdparty/lodepng/vtkmlodepng/lodepng.h>
|
||||
VTKM_THIRDPARTY_POST_INCLUDE
|
||||
|
||||
namespace vtkm
|
||||
{
|
||||
namespace io
|
||||
{
|
||||
|
||||
VTKM_CONT
|
||||
vtkm::Id BaseImageWriter::GetImageWidth(vtkm::cont::DataSet dataSet) const
|
||||
{
|
||||
if (dataSet.GetNumberOfCoordinateSystems() > 0)
|
||||
{
|
||||
// Add 1 since the Bounds are 0 indexed
|
||||
return static_cast<vtkm::Id>(dataSet.GetCoordinateSystem().GetBounds().X.Max) + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
VTKM_CONT
|
||||
vtkm::Id BaseImageWriter::GetImageHeight(vtkm::cont::DataSet dataSet) const
|
||||
{
|
||||
if (dataSet.GetNumberOfCoordinateSystems() > 0)
|
||||
{
|
||||
// Add 1 since the Bounds are 0 indexed
|
||||
return static_cast<vtkm::Id>(dataSet.GetCoordinateSystem().GetBounds().Y.Max) + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
VTKM_CONT
|
||||
void PNMWriter::WriteToFile(const std::string& fileName, const vtkm::cont::DataSet& dataSet) const
|
||||
{
|
||||
if (!dataSet.HasField(this->PointFieldName))
|
||||
{
|
||||
throw vtkm::cont::ErrorBadValue(
|
||||
"No pixel data found in DataSet, cannot write without image data!");
|
||||
}
|
||||
|
||||
std::ofstream outStream(fileName.c_str(), std::ios_base::binary | std::ios_base::out);
|
||||
outStream << this->MagicNumber << std::endl
|
||||
<< this->GetImageWidth(dataSet) << " " << this->GetImageHeight(dataSet) << std::endl;
|
||||
|
||||
switch (this->MaxColorValue)
|
||||
{
|
||||
case 0:
|
||||
this->EncodeFile<RGBPixel_8>(outStream, dataSet);
|
||||
break;
|
||||
case 255:
|
||||
this->EncodeFile<RGBPixel_8>(outStream, dataSet);
|
||||
break;
|
||||
case 65535:
|
||||
this->EncodeFile<RGBPixel_16>(outStream, dataSet);
|
||||
break;
|
||||
default:
|
||||
throw vtkm::cont::ErrorBadValue("MaxColorValue: " + std::to_string(this->MaxColorValue) +
|
||||
" was not one of: {255, 65535}");
|
||||
}
|
||||
}
|
||||
|
||||
VTKM_CONT
|
||||
template <typename PixelType>
|
||||
void PNMWriter::EncodeFile(std::ofstream& outStream, const vtkm::cont::DataSet& dataSet) const
|
||||
{
|
||||
outStream << PixelType::MAX_COLOR_VALUE << std::endl;
|
||||
auto pixelField = dataSet.GetPointField(this->PointFieldName)
|
||||
.GetData()
|
||||
.template Cast<vtkm::cont::ArrayHandle<vtkm::Vec4f_32>>();
|
||||
auto pixelPortal = pixelField.ReadPortal();
|
||||
|
||||
vtkm::UInt32 imageSize =
|
||||
static_cast<vtkm::UInt32>(pixelField.GetNumberOfValues() * PixelType::BYTES_PER_PIXEL);
|
||||
std::vector<unsigned char> imageData(imageSize);
|
||||
|
||||
// Write out the data starting from the end (Images are stored Bottom-Left to Top-Right,
|
||||
// but are viewed from Top-Left to Bottom-Right)
|
||||
vtkm::Id imageIndex = 0;
|
||||
vtkm::Id imageHeight = this->GetImageHeight(dataSet);
|
||||
vtkm::Id imageWidth = this->GetImageWidth(dataSet);
|
||||
for (vtkm::Id yIndex = imageHeight - 1; yIndex >= 0; yIndex--)
|
||||
{
|
||||
for (vtkm::Id xIndex = 0; xIndex < imageWidth; xIndex++, imageIndex++)
|
||||
{
|
||||
vtkm::Id index = yIndex * imageWidth + xIndex;
|
||||
PixelType(pixelPortal.Get(index)).FillImageAtIndexWithPixel(imageData.data(), imageIndex);
|
||||
}
|
||||
}
|
||||
outStream.write((char*)imageData.data(), imageSize);
|
||||
outStream.close();
|
||||
}
|
||||
|
||||
VTKM_CONT
|
||||
void PNGWriter::WriteToFile(const std::string& fileName, const vtkm::cont::DataSet& dataSet) const
|
||||
{
|
||||
switch (this->MaxColorValue)
|
||||
{
|
||||
case 0:
|
||||
WriteToFile<RGBPixel_8>(fileName, dataSet);
|
||||
break;
|
||||
case 255:
|
||||
WriteToFile<RGBPixel_8>(fileName, dataSet);
|
||||
break;
|
||||
case 65535:
|
||||
WriteToFile<RGBPixel_16>(fileName, dataSet);
|
||||
break;
|
||||
default:
|
||||
throw vtkm::cont::ErrorBadValue("MaxColorValue: " + std::to_string(this->MaxColorValue) +
|
||||
" was not one of: {255, 65535}");
|
||||
}
|
||||
}
|
||||
|
||||
VTKM_CONT
|
||||
template <typename PixelType>
|
||||
void PNGWriter::WriteToFile(const std::string& fileName, const vtkm::cont::DataSet& dataSet) const
|
||||
{
|
||||
if (!dataSet.HasField(this->PointFieldName))
|
||||
{
|
||||
throw vtkm::cont::ErrorBadValue(
|
||||
"No pixel data found in DataSet, cannot write without image data!");
|
||||
}
|
||||
|
||||
auto pixelField = dataSet.GetPointField(this->PointFieldName)
|
||||
.GetData()
|
||||
.template Cast<vtkm::cont::ArrayHandle<vtkm::Vec4f_32>>();
|
||||
auto pixelPortal = pixelField.ReadPortal();
|
||||
std::vector<unsigned char> imageData(static_cast<typename std::vector<unsigned char>::size_type>(
|
||||
pixelField.GetNumberOfValues() * PixelType::BYTES_PER_PIXEL));
|
||||
|
||||
// Write out the data starting from the end (Images are stored Bottom-Left to Top-Right,
|
||||
// but are viewed from Top-Left to Bottom-Right)
|
||||
vtkm::Id imageIndex = 0;
|
||||
vtkm::Id imageHeight = this->GetImageHeight(dataSet);
|
||||
vtkm::Id imageWidth = this->GetImageWidth(dataSet);
|
||||
for (vtkm::Id yIndex = imageHeight - 1; yIndex >= 0; yIndex--)
|
||||
{
|
||||
for (vtkm::Id xIndex = 0; xIndex < imageWidth; xIndex++, imageIndex++)
|
||||
{
|
||||
vtkm::Id index = yIndex * imageWidth + xIndex;
|
||||
PixelType(pixelPortal.Get(index)).FillImageAtIndexWithPixel(imageData.data(), imageIndex);
|
||||
}
|
||||
}
|
||||
|
||||
vtkm::png::lodepng_encode_file(fileName.c_str(),
|
||||
imageData.data(),
|
||||
static_cast<unsigned>(imageWidth),
|
||||
static_cast<unsigned>(imageHeight),
|
||||
PixelType::PNG_COLOR_TYPE,
|
||||
PixelType::BIT_DEPTH);
|
||||
}
|
||||
|
||||
} // namespace io
|
||||
} // namespace vtkm
|
||||
|
||||
#endif
|
88
vtkm/io/ImageWriterBase.cxx
Normal file
88
vtkm/io/ImageWriterBase.cxx
Normal file
@ -0,0 +1,88 @@
|
||||
//============================================================================
|
||||
// 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 <vtkm/io/ImageWriterBase.h>
|
||||
|
||||
#include <vtkm/cont/Logging.h>
|
||||
|
||||
namespace vtkm
|
||||
{
|
||||
namespace io
|
||||
{
|
||||
|
||||
ImageWriterBase::ImageWriterBase(const char* filename)
|
||||
: FileName(filename)
|
||||
{
|
||||
}
|
||||
|
||||
ImageWriterBase::ImageWriterBase(const std::string& filename)
|
||||
: FileName(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)
|
||||
{
|
||||
using CellSetType = vtkm::cont::CellSetStructured<2>;
|
||||
if (!dataSet.GetCellSet().IsType<CellSetType>())
|
||||
{
|
||||
throw vtkm::cont::ErrorBadType(
|
||||
"Image writers can only write data sets with 2D structured data.");
|
||||
}
|
||||
CellSetType cellSet = dataSet.GetCellSet().Cast<CellSetType>();
|
||||
vtkm::Id2 cellDimensions = cellSet.GetCellDimensions();
|
||||
// Number of points is one more in each dimension than number of cells
|
||||
vtkm::Id width = cellDimensions[0] + 1;
|
||||
vtkm::Id height = cellDimensions[1] + 1;
|
||||
|
||||
vtkm::cont::Field colorField;
|
||||
if (!colorFieldName.empty())
|
||||
{
|
||||
if (!dataSet.HasPointField(colorFieldName))
|
||||
{
|
||||
throw vtkm::cont::ErrorBadValue("Data set does not have requested field " + colorFieldName);
|
||||
}
|
||||
colorField = dataSet.GetPointField(colorFieldName);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Find a field of the correct type.
|
||||
vtkm::Id numFields = dataSet.GetNumberOfFields();
|
||||
bool foundField = false;
|
||||
for (vtkm::Id fieldId = 0; fieldId < numFields; ++fieldId)
|
||||
{
|
||||
colorField = dataSet.GetField(fieldId);
|
||||
if ((colorField.GetAssociation() == vtkm::cont::Field::Association::POINTS) &&
|
||||
(colorField.GetData().IsType<ColorArrayType>()))
|
||||
{
|
||||
foundField = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundField)
|
||||
{
|
||||
throw vtkm::cont::ErrorBadValue(
|
||||
"Data set does not have any fields that look like color data.");
|
||||
}
|
||||
}
|
||||
|
||||
this->Write(width, height, colorField.GetData().Cast<ColorArrayType>());
|
||||
}
|
||||
}
|
||||
} // namespace vtkm::io
|
81
vtkm/io/ImageWriterBase.h
Normal file
81
vtkm/io/ImageWriterBase.h
Normal file
@ -0,0 +1,81 @@
|
||||
//============================================================================
|
||||
// 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.
|
||||
//============================================================================
|
||||
#ifndef vtk_m_io_ImageWriterBase_h
|
||||
#define vtk_m_io_ImageWriterBase_h
|
||||
|
||||
#include <vtkm/cont/DataSet.h>
|
||||
|
||||
#include <vtkm/io/vtkm_io_export.h>
|
||||
|
||||
namespace vtkm
|
||||
{
|
||||
namespace io
|
||||
{
|
||||
|
||||
/// \brief Manages writing, and loading data from images
|
||||
///
|
||||
/// `ImageWriterBase` implements methods for loading imaging data from a canvas or
|
||||
/// ArrayHandle and storing that data in a vtkm::cont::DataSet. Image rgb values
|
||||
/// are represented as a point field in a 2D uniform dataset.
|
||||
///
|
||||
/// `ImageWriterBase` can be constructed from a file, canvas, or ArrayHandle. It can
|
||||
/// also be empy constructed and filled in with a dataset later.
|
||||
///
|
||||
/// `ImageWriterBase` implements virtual methods for writing files. Ideally,
|
||||
/// these methods will be overriden in various subclasses to implement specific
|
||||
/// functionality for writing data to specific image file-types.
|
||||
///
|
||||
class VTKM_IO_EXPORT ImageWriterBase
|
||||
{
|
||||
public:
|
||||
using ColorArrayType = vtkm::cont::ArrayHandle<vtkm::Vec4f_32>;
|
||||
|
||||
VTKM_CONT ImageWriterBase(const char* filename);
|
||||
VTKM_CONT ImageWriterBase(const std::string& filename);
|
||||
VTKM_CONT virtual ~ImageWriterBase() noexcept;
|
||||
ImageWriterBase(const ImageWriterBase&) = delete;
|
||||
ImageWriterBase& operator=(const ImageWriterBase&) = delete;
|
||||
|
||||
///@{
|
||||
/// \brief Write the color field of a data set to an image file.
|
||||
///
|
||||
/// The `DataSet` must have a 2D structured cell set.
|
||||
///
|
||||
/// The specified color field must be of type `ColorArrayType` (a basic
|
||||
/// `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);
|
||||
///@}
|
||||
|
||||
enum class PixelDepth
|
||||
{
|
||||
PIXEL_8,
|
||||
PIXEL_16
|
||||
};
|
||||
|
||||
///@{
|
||||
/// You can specify the number of bits used by each color channel with the `PixelDepth`.
|
||||
///
|
||||
VTKM_CONT PixelDepth GetPixelDepth() const { return this->Depth; }
|
||||
VTKM_CONT void SetPixelDepth(PixelDepth depth) { this->Depth = depth; }
|
||||
///@}
|
||||
|
||||
protected:
|
||||
std::string FileName;
|
||||
PixelDepth Depth = PixelDepth::PIXEL_8;
|
||||
|
||||
VTKM_CONT virtual void Write(vtkm::Id width, vtkm::Id height, const ColorArrayType& pixels) = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif //vtk_m_io_ImageWriterBase_h
|
69
vtkm/io/ImageWriterPNG.cxx
Normal file
69
vtkm/io/ImageWriterPNG.cxx
Normal file
@ -0,0 +1,69 @@
|
||||
//============================================================================
|
||||
// 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 <vtkm/io/ImageWriterPNG.h>
|
||||
|
||||
#include <vtkm/io/PixelTypes.h>
|
||||
|
||||
VTKM_THIRDPARTY_PRE_INCLUDE
|
||||
#include <vtkm/thirdparty/lodepng/vtkmlodepng/lodepng.h>
|
||||
VTKM_THIRDPARTY_POST_INCLUDE
|
||||
|
||||
namespace vtkm
|
||||
{
|
||||
namespace io
|
||||
{
|
||||
|
||||
ImageWriterPNG::~ImageWriterPNG() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
void ImageWriterPNG::Write(vtkm::Id width, vtkm::Id height, const ColorArrayType& pixels)
|
||||
{
|
||||
switch (this->Depth)
|
||||
{
|
||||
case PixelDepth::PIXEL_8:
|
||||
this->WriteToFile<vtkm::io::RGBPixel_8>(width, height, pixels);
|
||||
break;
|
||||
case PixelDepth::PIXEL_16:
|
||||
WriteToFile<vtkm::io::RGBPixel_16>(width, height, pixels);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename PixelType>
|
||||
void ImageWriterPNG::WriteToFile(vtkm::Id width, vtkm::Id height, const ColorArrayType& pixels)
|
||||
{
|
||||
auto pixelPortal = pixels.ReadPortal();
|
||||
std::vector<unsigned char> imageData(static_cast<typename std::vector<unsigned char>::size_type>(
|
||||
pixels.GetNumberOfValues() * PixelType::BYTES_PER_PIXEL));
|
||||
|
||||
// Write out the data starting from the end (Images are stored Bottom-Left to Top-Right,
|
||||
// but are viewed from Top-Left to Bottom-Right)
|
||||
vtkm::Id pngIndex = 0;
|
||||
for (vtkm::Id yIndex = height - 1; yIndex >= 0; yIndex--)
|
||||
{
|
||||
for (vtkm::Id xIndex = 0; xIndex < width; xIndex++)
|
||||
{
|
||||
vtkm::Id vtkmIndex = yIndex * width + xIndex;
|
||||
PixelType(pixelPortal.Get(vtkmIndex)).FillImageAtIndexWithPixel(imageData.data(), pngIndex);
|
||||
pngIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
vtkm::png::lodepng_encode_file(this->FileName.c_str(),
|
||||
imageData.data(),
|
||||
static_cast<unsigned>(width),
|
||||
static_cast<unsigned>(height),
|
||||
PixelType::PNG_COLOR_TYPE,
|
||||
PixelType::BIT_DEPTH);
|
||||
}
|
||||
}
|
||||
} // namespace vtkm::io
|
46
vtkm/io/ImageWriterPNG.h
Normal file
46
vtkm/io/ImageWriterPNG.h
Normal file
@ -0,0 +1,46 @@
|
||||
//============================================================================
|
||||
// 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.
|
||||
//============================================================================
|
||||
#ifndef vtk_m_io_ImageWriterPNG_h
|
||||
#define vtk_m_io_ImageWriterPNG_h
|
||||
|
||||
#include <vtkm/io/ImageWriterBase.h>
|
||||
|
||||
namespace vtkm
|
||||
{
|
||||
namespace io
|
||||
{
|
||||
|
||||
/// \brief Manages writing images using the PNG format via lodepng
|
||||
///
|
||||
/// \c ImageWriterPNG extends vtkm::io::ImageWriterBase and implements writing images in a valid
|
||||
/// PNG format. It utilizes lodepng's encode file functions to write
|
||||
/// PNG images that are automatically compressed to optimal sizes relative to
|
||||
/// the actual bit complexity of the image.
|
||||
///
|
||||
class VTKM_IO_EXPORT ImageWriterPNG : public vtkm::io::ImageWriterBase
|
||||
{
|
||||
using Superclass = vtkm::io::ImageWriterBase;
|
||||
|
||||
public:
|
||||
using Superclass::Superclass;
|
||||
VTKM_CONT ~ImageWriterPNG() noexcept override;
|
||||
ImageWriterPNG(const ImageWriterPNG&) = delete;
|
||||
ImageWriterPNG& operator=(const ImageWriterPNG&) = delete;
|
||||
|
||||
protected:
|
||||
VTKM_CONT void Write(vtkm::Id width, vtkm::Id height, const ColorArrayType& pixels) override;
|
||||
|
||||
template <typename PixelType>
|
||||
VTKM_CONT void WriteToFile(vtkm::Id width, vtkm::Id height, const ColorArrayType& pixels);
|
||||
};
|
||||
}
|
||||
} // namespace vtkm::io
|
||||
|
||||
#endif //vtk_m_io_ImageWriterPNG_h
|
65
vtkm/io/ImageWriterPNM.cxx
Normal file
65
vtkm/io/ImageWriterPNM.cxx
Normal file
@ -0,0 +1,65 @@
|
||||
//============================================================================
|
||||
// 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 <vtkm/io/ImageWriterPNM.h>
|
||||
|
||||
#include <vtkm/io/PixelTypes.h>
|
||||
|
||||
namespace vtkm
|
||||
{
|
||||
namespace io
|
||||
{
|
||||
|
||||
ImageWriterPNM::~ImageWriterPNM() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
void ImageWriterPNM::Write(vtkm::Id width, vtkm::Id height, const ColorArrayType& pixels)
|
||||
{
|
||||
switch (this->Depth)
|
||||
{
|
||||
case PixelDepth::PIXEL_8:
|
||||
this->WriteToFile<vtkm::io::RGBPixel_8>(width, height, pixels);
|
||||
break;
|
||||
case PixelDepth::PIXEL_16:
|
||||
WriteToFile<vtkm::io::RGBPixel_16>(width, height, pixels);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename PixelType>
|
||||
void ImageWriterPNM::WriteToFile(vtkm::Id width, vtkm::Id height, const ColorArrayType& pixels)
|
||||
{
|
||||
std::ofstream outStream(this->FileName.c_str(), std::ios_base::binary | std::ios_base::out);
|
||||
outStream << "P6\n" << width << " " << height << "\n";
|
||||
|
||||
outStream << PixelType::MAX_COLOR_VALUE << "\n";
|
||||
auto pixelPortal = pixels.ReadPortal();
|
||||
|
||||
vtkm::UInt32 imageSize =
|
||||
static_cast<vtkm::UInt32>(pixels.GetNumberOfValues() * PixelType::BYTES_PER_PIXEL);
|
||||
std::vector<unsigned char> imageData(imageSize);
|
||||
|
||||
// Write out the data starting from the end (Images are stored Bottom-Left to Top-Right,
|
||||
// but are viewed from Top-Left to Bottom-Right)
|
||||
vtkm::Id pnmIndex = 0;
|
||||
for (vtkm::Id yIndex = height - 1; yIndex >= 0; yIndex--)
|
||||
{
|
||||
for (vtkm::Id xIndex = 0; xIndex < width; xIndex++, pnmIndex++)
|
||||
{
|
||||
vtkm::Id vtkmIndex = yIndex * width + xIndex;
|
||||
PixelType(pixelPortal.Get(vtkmIndex)).FillImageAtIndexWithPixel(imageData.data(), pnmIndex);
|
||||
}
|
||||
}
|
||||
outStream.write((char*)imageData.data(), imageSize);
|
||||
outStream.close();
|
||||
}
|
||||
}
|
||||
} // namespace vtkm::io
|
53
vtkm/io/ImageWriterPNM.h
Normal file
53
vtkm/io/ImageWriterPNM.h
Normal file
@ -0,0 +1,53 @@
|
||||
//============================================================================
|
||||
// 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.
|
||||
//============================================================================
|
||||
#ifndef vtk_m_io_ImageWriterPNM_h
|
||||
#define vtk_m_io_ImageWriterPNM_h
|
||||
|
||||
#include <vtkm/io/ImageWriterBase.h>
|
||||
|
||||
namespace vtkm
|
||||
{
|
||||
namespace io
|
||||
{
|
||||
|
||||
/// \brief Manages writing images using the PNM format
|
||||
///
|
||||
/// `ImageWriterPNM` extends `ImageWriterBase`, and implements writing images in a
|
||||
/// valid PNM format (for magic number P6). More details on the PNM
|
||||
/// format can be found here: http://netpbm.sourceforge.net/doc/ppm.html
|
||||
///
|
||||
/// When a file is writen the MaxColorValue found in the file is used to
|
||||
/// determine the PixelType required to stored PixelType is instead dependent
|
||||
/// upon the read MaxColorValue obtained from the file
|
||||
class VTKM_IO_EXPORT ImageWriterPNM : public vtkm::io::ImageWriterBase
|
||||
{
|
||||
using Superclass = vtkm::io::ImageWriterBase;
|
||||
|
||||
public:
|
||||
using Superclass::Superclass;
|
||||
VTKM_CONT ~ImageWriterPNM() noexcept override;
|
||||
ImageWriterPNM(const ImageWriterPNM&) = delete;
|
||||
ImageWriterPNM& operator=(const ImageWriterPNM&) = delete;
|
||||
|
||||
/// Attempts to write the ImageDataSet to a PNM file. The MaxColorValue
|
||||
/// set in the file with either be selected from the stored MaxColorValue
|
||||
/// member variable, or from the templated type if MaxColorValue hasn't been
|
||||
/// set from a read file.
|
||||
///
|
||||
VTKM_CONT void Write(vtkm::Id width, vtkm::Id height, const ColorArrayType& pixels) override;
|
||||
|
||||
protected:
|
||||
template <typename PixelType>
|
||||
VTKM_CONT void WriteToFile(vtkm::Id width, vtkm::Id height, const ColorArrayType& pixels);
|
||||
};
|
||||
}
|
||||
} // namespace vtkm::io
|
||||
|
||||
#endif //vtk_m_io_ImageWriterPNM_h
|
@ -11,7 +11,8 @@
|
||||
#include <vtkm/cont/testing/Testing.h>
|
||||
#include <vtkm/io/ImageReaderPNG.h>
|
||||
#include <vtkm/io/ImageReaderPNM.h>
|
||||
#include <vtkm/io/ImageWriter.h>
|
||||
#include <vtkm/io/ImageWriterPNG.h>
|
||||
#include <vtkm/io/ImageWriterPNM.h>
|
||||
#include <vtkm/io/PixelTypes.h>
|
||||
#include <vtkm/rendering/Canvas.h>
|
||||
#include <vtkm/rendering/Color.h>
|
||||
@ -24,7 +25,6 @@ namespace
|
||||
using namespace vtkm::io;
|
||||
using namespace vtkm::rendering;
|
||||
|
||||
template <typename PixelType>
|
||||
void TestFilledImage(vtkm::cont::DataSet& dataSet,
|
||||
const std::string& fieldName,
|
||||
const vtkm::rendering::Canvas& canvas)
|
||||
@ -40,119 +40,111 @@ void TestFilledImage(vtkm::cont::DataSet& dataSet,
|
||||
pointField.GetData().template Cast<vtkm::cont::ArrayHandle<vtkm::Vec4f_32>>().ReadPortal();
|
||||
|
||||
auto colorPortal = canvas.GetColorBuffer().ReadPortal();
|
||||
for (vtkm::Id y = 0; y < canvas.GetHeight(); y++)
|
||||
{
|
||||
std::ostringstream row;
|
||||
row << "[";
|
||||
for (vtkm::Id x = 0; x < canvas.GetWidth(); x++)
|
||||
{
|
||||
auto tuple = colorPortal.Get(y * canvas.GetWidth() + x);
|
||||
auto pixelVec = PixelType(pixelPortal.Get(y * canvas.GetWidth() + x));
|
||||
std::ostringstream data;
|
||||
data << pixelVec << ":" << PixelType(tuple) << std::endl;
|
||||
VTKM_TEST_ASSERT(pixelVec == PixelType(tuple),
|
||||
"Stored pixel did not match canvas value" + data.str());
|
||||
row << pixelVec << ",";
|
||||
}
|
||||
row << "]";
|
||||
}
|
||||
|
||||
VTKM_TEST_ASSERT(test_equal_portals(pixelPortal, colorPortal));
|
||||
}
|
||||
|
||||
template <typename PixelType>
|
||||
void TestCreateImageDataSet(const vtkm::rendering::Canvas& canvas)
|
||||
{
|
||||
std::cout << "TestCreateImageDataSet" << std::endl;
|
||||
auto dataSet = canvas.GetDataSet("pixel-color");
|
||||
TestFilledImage<PixelType>(dataSet, "pixel-color", canvas);
|
||||
TestFilledImage(dataSet, "pixel-color", canvas);
|
||||
}
|
||||
|
||||
template <typename PixelType>
|
||||
void TestReadAndWritePNG(const vtkm::rendering::Canvas& canvas, std::string filename)
|
||||
void TestReadAndWritePNG(const vtkm::rendering::Canvas& canvas,
|
||||
std::string filename,
|
||||
vtkm::io::ImageWriterBase::PixelDepth pixelDepth)
|
||||
{
|
||||
auto pngWriter = PNGWriter();
|
||||
vtkm::cont::DataSet dataSet;
|
||||
std::cout << "TestReadAndWritePNG - " << filename << std::endl;
|
||||
bool throws = false;
|
||||
try
|
||||
{
|
||||
pngWriter.WriteToFile(filename, dataSet);
|
||||
vtkm::io::ImageWriterPNG writer(filename);
|
||||
vtkm::cont::DataSet dataSet;
|
||||
writer.WriteDataSet(dataSet);
|
||||
}
|
||||
catch (const vtkm::cont::ErrorBadValue&)
|
||||
catch (const vtkm::cont::Error&)
|
||||
{
|
||||
throws = true;
|
||||
}
|
||||
VTKM_TEST_ASSERT(throws, "Fill Image did not throw with empty data");
|
||||
|
||||
dataSet = canvas.GetDataSet(pngWriter.GetPointFieldName());
|
||||
pngWriter.WriteToFile(filename, dataSet);
|
||||
{
|
||||
vtkm::io::ImageReaderPNG reader(filename);
|
||||
dataSet = reader.ReadDataSet();
|
||||
// TODO: Fix this
|
||||
vtkm::cont::Field field = dataSet.GetField(reader.GetPointFieldName());
|
||||
dataSet.AddPointField(pngWriter.GetPointFieldName(), field.GetData());
|
||||
vtkm::io::ImageWriterPNG writer(filename);
|
||||
writer.SetPixelDepth(pixelDepth);
|
||||
writer.WriteDataSet(canvas.GetDataSet());
|
||||
}
|
||||
pngWriter.WriteToFile(filename, dataSet);
|
||||
{
|
||||
vtkm::io::ImageReaderPNG reader(filename);
|
||||
dataSet = reader.ReadDataSet();
|
||||
TestFilledImage<PixelType>(dataSet, reader.GetPointFieldName(), canvas);
|
||||
vtkm::cont::DataSet dataSet = reader.ReadDataSet();
|
||||
}
|
||||
{
|
||||
vtkm::io::ImageWriterPNG writer(filename);
|
||||
writer.SetPixelDepth(pixelDepth);
|
||||
writer.WriteDataSet(canvas.GetDataSet());
|
||||
}
|
||||
{
|
||||
vtkm::io::ImageReaderPNG reader(filename);
|
||||
vtkm::cont::DataSet dataSet = reader.ReadDataSet();
|
||||
TestFilledImage(dataSet, reader.GetPointFieldName(), canvas);
|
||||
}
|
||||
}
|
||||
|
||||
template <const vtkm::Id BitDepth>
|
||||
void TestReadAndWritePNM(const vtkm::rendering::Canvas& canvas)
|
||||
void TestReadAndWritePNM(const vtkm::rendering::Canvas& canvas,
|
||||
std::string filename,
|
||||
vtkm::io::ImageWriterBase::PixelDepth pixelDepth)
|
||||
{
|
||||
using PixelType = RGBPixel<BitDepth>;
|
||||
PNMWriter ppmWriter((1 << BitDepth) - 1);
|
||||
vtkm::cont::DataSet dataSet;
|
||||
std::cout << "TestReadAndWritePNM - " << filename << std::endl;
|
||||
bool throws = false;
|
||||
try
|
||||
{
|
||||
ppmWriter.WriteToFile("ppmTestFile" + std::to_string(BitDepth) + "bit.ppm", dataSet);
|
||||
vtkm::io::ImageWriterPNM writer(filename);
|
||||
vtkm::cont::DataSet dataSet;
|
||||
writer.WriteDataSet(dataSet);
|
||||
}
|
||||
catch (const vtkm::cont::ErrorBadValue&)
|
||||
catch (const vtkm::cont::Error&)
|
||||
{
|
||||
throws = true;
|
||||
}
|
||||
VTKM_TEST_ASSERT(throws, "Fill Image did not throw with empty data");
|
||||
|
||||
dataSet = canvas.GetDataSet(ppmWriter.GetPointFieldName());
|
||||
ppmWriter.WriteToFile("ppmTestFile.ppm", dataSet);
|
||||
{
|
||||
vtkm::io::ImageReaderPNM reader("ppmTestFile.ppm");
|
||||
dataSet = reader.ReadDataSet();
|
||||
// TODO: Fix this
|
||||
vtkm::cont::Field field = dataSet.GetField(reader.GetPointFieldName());
|
||||
dataSet.AddPointField(ppmWriter.GetPointFieldName(), field.GetData());
|
||||
vtkm::io::ImageWriterPNM writer(filename);
|
||||
writer.SetPixelDepth(pixelDepth);
|
||||
writer.WriteDataSet(canvas.GetDataSet());
|
||||
}
|
||||
ppmWriter.WriteToFile("ppmTestFile2.ppm", dataSet);
|
||||
{
|
||||
vtkm::io::ImageReaderPNM reader("ppmTestFile2.ppm");
|
||||
dataSet = reader.ReadDataSet();
|
||||
TestFilledImage<PixelType>(dataSet, reader.GetPointFieldName(), canvas);
|
||||
vtkm::io::ImageReaderPNM reader(filename);
|
||||
vtkm::cont::DataSet dataSet = reader.ReadDataSet();
|
||||
}
|
||||
{
|
||||
vtkm::io::ImageWriterPNM writer(filename);
|
||||
writer.SetPixelDepth(pixelDepth);
|
||||
writer.WriteDataSet(canvas.GetDataSet());
|
||||
}
|
||||
{
|
||||
vtkm::io::ImageReaderPNM reader(filename);
|
||||
vtkm::cont::DataSet dataSet = reader.ReadDataSet();
|
||||
TestFilledImage(dataSet, reader.GetPointFieldName(), canvas);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TestBaseImageMethods(const vtkm::rendering::Canvas& canvas)
|
||||
{
|
||||
TestCreateImageDataSet<RGBPixel_8>(canvas);
|
||||
TestCreateImageDataSet<RGBPixel_16>(canvas);
|
||||
TestCreateImageDataSet<GreyPixel_8>(canvas);
|
||||
TestCreateImageDataSet<GreyPixel_16>(canvas);
|
||||
TestCreateImageDataSet(canvas);
|
||||
}
|
||||
|
||||
void TestPNMImage(const vtkm::rendering::Canvas& canvas)
|
||||
{
|
||||
TestReadAndWritePNM<8>(canvas);
|
||||
TestReadAndWritePNM<16>(canvas);
|
||||
TestReadAndWritePNM(canvas, "pnmRGB8Test.png", vtkm::io::ImageWriterBase::PixelDepth::PIXEL_8);
|
||||
TestReadAndWritePNM(canvas, "pnmRGB16Test.png", vtkm::io::ImageWriterBase::PixelDepth::PIXEL_16);
|
||||
}
|
||||
|
||||
void TestPNGImage(const vtkm::rendering::Canvas& canvas)
|
||||
{
|
||||
TestReadAndWritePNG<RGBPixel_8>(canvas, "pngRGB8Test.png");
|
||||
TestReadAndWritePNG<RGBPixel_16>(canvas, "pngRGB16Test.png");
|
||||
TestReadAndWritePNG<GreyPixel_8>(canvas, "pngGrey8Test.png");
|
||||
TestReadAndWritePNG<GreyPixel_16>(canvas, "pngGrey16Test.png");
|
||||
TestReadAndWritePNG(canvas, "pngRGB8Test.png", vtkm::io::ImageWriterBase::PixelDepth::PIXEL_8);
|
||||
TestReadAndWritePNG(canvas, "pngRGB16Test.png", vtkm::io::ImageWriterBase::PixelDepth::PIXEL_16);
|
||||
}
|
||||
|
||||
void TestImage()
|
||||
|
Loading…
Reference in New Issue
Block a user