Update image readers to style and library

Updated the image reader classes to better follow VTK-m naming
convention (i.e. use `ImageReaderPNG` instead of `PNGReader`).
The structure of the image reader class now also better matches
the interface for the VTK reader.

Also put the implementation of the image readers into the `vtkm_io`
library so that it only has to be compiled once.
This commit is contained in:
Kenneth Moreland 2020-05-28 16:49:28 -06:00
parent 0f7a509cdb
commit 1e91b32ace
12 changed files with 508 additions and 352 deletions

@ -13,7 +13,9 @@ set(headers
ErrorIO.h
DecodePNG.h
EncodePNG.h
ImageReader.h
ImageReaderBase.h
ImageReaderPNG.h
ImageReaderPNM.h
ImageWriter.h
PixelTypes.h
VTKDataSetReader.h
@ -27,7 +29,6 @@ set(headers
)
set(template_sources
ImageReader.hxx
ImageWriter.hxx
PixelTypes.hxx
)
@ -43,6 +44,9 @@ set(sources
# devices for ArrayHandle, and this requirement will go away.
set(device_sources
BOVDataSetReader.cxx
ImageReaderBase.cxx
ImageReaderPNG.cxx
ImageReaderPNM.cxx
VTKDataSetReader.cxx
VTKDataSetReaderBase.cxx
VTKDataSetWriter.cxx

@ -1,155 +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_ImageReader_h
#define vtk_m_io_ImageReader_h
#include <vtkm/Types.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/DataSet.h>
namespace vtkm
{
// Forward Declare
namespace rendering
{
class Canvas;
} // namespace rendering
namespace io
{
/// \brief Manages reading, and loading data from images
///
/// \c BaseImageReader 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 BaseImageReader can be constructed from a file, canvas, or ArrayHandle. It can
/// also be empy constructed and filled in with a dataset later.
///
/// \c BaseImageReader implements virtual methods for reading files. Ideally,
/// these methods will be overriden in various subclasses to implement specific
/// functionality for reading data to specific image file-types.
///
/// \c The templated type is used when Filling the ImageDataSet.
///
class BaseImageReader
{
public:
/// Constructs an emtpy BaseImageReader.
///
BaseImageReader() = default;
explicit BaseImageReader(const vtkm::Id& maxColorValue)
: MaxColorValue(maxColorValue)
{
}
~BaseImageReader() noexcept = default;
/// Reads image data from a file. Meant to be implemented in overriden
/// image-specific classes
///
virtual vtkm::cont::DataSet ReadFromFile(const std::string& fileName) = 0;
/// Creates an ImageDataSet from a Canvas object
///
vtkm::cont::DataSet CreateImageDataSet(const vtkm::rendering::Canvas& canvas);
/// Creates an ImageDataSet from a RGBA 32bit float color buffer
/// Assumes the color buffer is stored in row-major ordering
///
vtkm::cont::DataSet CreateImageDataSet(const vtkm::cont::ArrayHandle<vtkm::Vec4f_32>& colorBuffer,
const vtkm::Id& width,
const vtkm::Id& height);
const std::string& GetPointFieldName() const { return this->PointFieldName; }
protected:
vtkm::cont::DataSet InitializeImageDataSet(const vtkm::Id& width, const vtkm::Id& height);
std::string PointFieldName = "pixel-data";
vtkm::Id MaxColorValue{ 0 };
};
/// \brief Manages reading images using the PNG format via lodepng
///
/// \c PNGReader extends BaseImageReader and implements reading images in a valid
/// PNG format. It utilizes lodepng's decode file functions to read
/// PNG images that are automatically compressed to optimal sizes relative to
/// the actual bit complexity of the image.
///
/// \c PNGReader will automatically upsample/downsample read image data
/// to a 16 bit RGB no matter how the image is compressed. It is up to the user to
/// decide the pixel format for input PNGs
class PNGReader : public BaseImageReader
{
using Superclass = BaseImageReader;
public:
using Superclass::Superclass;
PNGReader() = default;
~PNGReader() noexcept = default;
/// Reads PNG data from the provided file and stores it
/// as a 16bit RGB value
///
vtkm::cont::DataSet ReadFromFile(const std::string& fileName) override;
/// Reads PNG data from the provided file and stores it
/// according to the method's templated PixelType
///
template <typename PixelType>
vtkm::cont::DataSet ReadFromFile(const std::string& fileName);
};
/// \brief Manages reading images using the PNM format
///
/// \c PNMImage extends BaseImage, and implements reading images from 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 read the parsed MagicNumber and MaxColorSize provided
/// are utilized to correctly parse the bits from the file
class PNMReader : public BaseImageReader
{
using Superclass = BaseImageReader;
public:
using Superclass::Superclass;
PNMReader() = default;
~PNMReader() noexcept = default;
/// Attempts to read the provided file into a DataSet object.
/// Will pull the image's MaxColorValue from the file and then Decode
/// with the appropriate RGB PixelType bit depth.
///
vtkm::cont::DataSet ReadFromFile(const std::string& fileName) override;
protected:
/// Reads image data from the provided inStream with the supplied width/height
/// Stores the data in a vector of PixelType which is converted to an DataSet
///
template <typename PixelType>
vtkm::cont::DataSet DecodeFile(std::ifstream& inStream,
const vtkm::Id& width,
const vtkm::Id& height);
// This is set to only work for P6 pnm image types for now (ie ppm)
std::string MagicNumber{ "P6" };
};
} // namespace io
} // namespace vtkm
#include <vtkm/io/ImageReader.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_ImageReader_hxx
#define vtk_m_io_ImageReader_hxx
#include <vtkm/cont/CellSetStructured.h>
#include <vtkm/cont/DataSetBuilderUniform.h>
#include <vtkm/io/ImageReader.h>
#include <vtkm/io/PixelTypes.h>
#include <vtkm/rendering/Canvas.h>
VTKM_THIRDPARTY_PRE_INCLUDE
#include <vtkm/thirdparty/lodepng/vtkmlodepng/lodepng.h>
VTKM_THIRDPARTY_POST_INCLUDE
namespace vtkm
{
namespace io
{
// Start BaseReaderImage Class Template Implementations
VTKM_CONT
vtkm::cont::DataSet BaseImageReader::CreateImageDataSet(const vtkm::rendering::Canvas& canvas)
{
return this->CreateImageDataSet(canvas.GetColorBuffer(), canvas.GetWidth(), canvas.GetHeight());
}
VTKM_CONT
vtkm::cont::DataSet BaseImageReader::CreateImageDataSet(
const vtkm::cont::ArrayHandle<vtkm::Vec4f_32>& colorBuffer,
const vtkm::Id& width,
const vtkm::Id& height)
{
vtkm::cont::ArrayHandle<vtkm::Vec4f_32>::ReadPortalType colorPortal = colorBuffer.ReadPortal();
std::vector<vtkm::Vec4f_32> fieldData;
for (vtkm::Id yIndex = 0; yIndex < height; yIndex++)
{
for (vtkm::Id xIndex = 0; xIndex < width; xIndex++)
{
vtkm::Vec4f_32 tuple = colorPortal.Get(yIndex * width + xIndex);
fieldData.push_back(tuple);
}
}
auto dataSet = this->InitializeImageDataSet(width, height);
dataSet.AddPointField(this->PointFieldName, fieldData);
return dataSet;
}
VTKM_CONT
vtkm::cont::DataSet BaseImageReader::InitializeImageDataSet(const vtkm::Id& width,
const vtkm::Id& height)
{
vtkm::cont::DataSetBuilderUniform dsb;
vtkm::Id2 dimensions(width, height);
return dsb.Create(dimensions);
}
// End BaseReaderImage Class Template Implementations
// Start PNGReader Class Template Implementations
VTKM_CONT
vtkm::cont::DataSet PNGReader::ReadFromFile(const std::string& fileName)
{
return this->ReadFromFile<io::RGBPixel_16>(fileName);
}
VTKM_CONT
template <typename PixelType>
vtkm::cont::DataSet PNGReader::ReadFromFile(const std::string& fileName)
{
unsigned char* imageData;
unsigned uwidth, uheight;
vtkm::Id width, height;
vtkm::png::lodepng_decode_file(&imageData,
&uwidth,
&uheight,
fileName.c_str(),
PixelType::PNG_COLOR_TYPE,
PixelType::BIT_DEPTH);
width = static_cast<vtkm::Id>(uwidth);
height = static_cast<vtkm::Id>(uheight);
// Fill in the data starting from the end (Images are read Top-Left to Bottom-Right,
// but are stored from Bottom-Left to Top-Right)
std::vector<vtkm::Vec4f_32> fieldData;
for (vtkm::Id yIndex = static_cast<vtkm::Id>(height - 1); yIndex >= 0; yIndex--)
{
for (vtkm::Id xIndex = 0; xIndex < static_cast<vtkm::Id>(width); xIndex++)
{
vtkm::Id index = static_cast<vtkm::Id>(yIndex * width + xIndex);
fieldData.push_back(PixelType(imageData, index).ToVec4f());
}
}
auto dataSet = this->InitializeImageDataSet(width, height);
dataSet.AddPointField(this->PointFieldName, fieldData);
free(imageData);
return dataSet;
}
// End PNGReader Class Template Implementations
// Start PNMReader Class Template Implementations
VTKM_CONT
vtkm::cont::DataSet PNMReader::ReadFromFile(const std::string& fileName)
{
std::ifstream inStream(fileName.c_str(), std::ios_base::binary | std::ios_base::in);
vtkm::Id width;
vtkm::Id height;
std::string val;
inStream >> val;
if (this->MagicNumber != val)
{
throw vtkm::cont::ErrorBadValue("MagicNumber: " + this->MagicNumber + " in file: " + fileName +
" did not match: " + val);
}
inStream >> width >> height >> this->MaxColorValue;
inStream.get();
switch (this->MaxColorValue)
{
case 255:
return this->DecodeFile<io::RGBPixel_8>(inStream, width, height);
case 65535:
return this->DecodeFile<io::RGBPixel_16>(inStream, width, height);
default:
throw vtkm::cont::ErrorBadValue("MaxColorValue: " + std::to_string(this->MaxColorValue) +
" from file: " + fileName + " was not one of: {8, 16}");
}
}
VTKM_CONT
template <typename PixelType>
vtkm::cont::DataSet PNMReader::DecodeFile(std::ifstream& inStream,
const vtkm::Id& width,
const vtkm::Id& height)
{
vtkm::UInt32 imageSize = static_cast<vtkm::UInt32>(width * height * PixelType::BYTES_PER_PIXEL);
std::vector<unsigned char> imageData(imageSize);
inStream.read((char*)imageData.data(), imageSize);
// Fill in the data starting from the end (Images are read Top-Left to Bottom-Right,
// but are stored from Bottom-Left to Top-Right)
std::vector<vtkm::Vec4f_32> fieldData;
for (vtkm::Id yIndex = height - 1; yIndex >= 0; yIndex--)
{
for (vtkm::Id xIndex = 0; xIndex < width; xIndex++)
{
vtkm::Id index = yIndex * width + xIndex;
fieldData.push_back(PixelType(imageData.data(), index).ToVec4f());
}
}
auto dataSet = this->InitializeImageDataSet(width, height);
dataSet.AddPointField(this->PointFieldName, fieldData);
return dataSet;
}
// End PNMReader Class Template Implementations
} // namespace io
} // namespace vtkm
#endif

@ -0,0 +1,51 @@
//============================================================================
// 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/ImageReaderBase.h>
#include <vtkm/cont/DataSetBuilderUniform.h>
#include <vtkm/rendering/Canvas.h>
namespace vtkm
{
namespace io
{
ImageReaderBase::ImageReaderBase(const char* filename)
: FileName(filename)
{
}
ImageReaderBase::ImageReaderBase(const std::string& filename)
: FileName(filename)
{
}
ImageReaderBase::~ImageReaderBase() noexcept
{
}
const vtkm::cont::DataSet& ImageReaderBase::ReadDataSet()
{
this->Read();
return this->DataSet;
}
void ImageReaderBase::InitializeImageDataSet(const vtkm::Id& width,
const vtkm::Id& height,
const ColorArrayType& pixels)
{
vtkm::cont::DataSetBuilderUniform dsb;
vtkm::Id2 dimensions(width, height);
this->DataSet = dsb.Create(dimensions);
this->DataSet.AddPointField(this->PointFieldName, pixels);
}
}
} // namespace vtkm::io

65
vtkm/io/ImageReaderBase.h 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.
//============================================================================
#ifndef vtk_m_io_ImageReaderBase_h
#define vtk_m_io_ImageReaderBase_h
#include <vtkm/cont/DataSet.h>
#include <vtkm/io/vtkm_io_export.h>
namespace vtkm
{
namespace io
{
/// \brief Manages reading, and loading data from images
///
/// `ImageReaderBase` 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.
///
/// `ImageReaderBase` implements virtual methods for reading files. Ideally,
/// these methods will be overriden in various subclasses to implement specific
/// functionality for reading data to specific image file-types.
///
class VTKM_IO_EXPORT ImageReaderBase
{
public:
using ColorArrayType = vtkm::cont::ArrayHandle<vtkm::Vec4f_32>;
explicit VTKM_CONT ImageReaderBase(const char* filename);
explicit VTKM_CONT ImageReaderBase(const std::string& filename);
virtual VTKM_CONT ~ImageReaderBase() noexcept;
ImageReaderBase(const ImageReaderBase&) = delete;
ImageReaderBase& operator=(const ImageReaderBase&) = delete;
VTKM_CONT const vtkm::cont::DataSet& ReadDataSet();
VTKM_CONT const vtkm::cont::DataSet& GetDataSet() const { return this->DataSet; }
VTKM_CONT const std::string& GetPointFieldName() const { return this->PointFieldName; }
VTKM_CONT void SetPointFieldName(const std::string& name) { this->PointFieldName = name; }
protected:
VTKM_CONT virtual void Read() = 0;
/// Resets the `DataSet` to hold the given pixels.
void InitializeImageDataSet(const vtkm::Id& width,
const vtkm::Id& height,
const ColorArrayType& pixels);
std::string FileName;
std::string PointFieldName = "color";
vtkm::cont::DataSet DataSet;
};
}
} // namespace vtkm::io
#endif //vtk_m_io_ImageReaderBase_h

103
vtkm/io/ImageReaderPNG.cxx Normal file

@ -0,0 +1,103 @@
//============================================================================
// 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/ImageReaderPNG.h>
#include <vtkm/io/PixelTypes.h>
namespace
{
VTKM_CONT
template <typename PixelType>
vtkm::io::ImageReaderBase::ColorArrayType ReadFromPNG(const std::string& fileName,
vtkm::Id& width,
vtkm::Id& height)
{
unsigned char* imageData;
unsigned uwidth, uheight;
vtkm::png::lodepng_decode_file(&imageData,
&uwidth,
&uheight,
fileName.c_str(),
PixelType::PNG_COLOR_TYPE,
PixelType::BIT_DEPTH);
width = static_cast<vtkm::Id>(uwidth);
height = static_cast<vtkm::Id>(uheight);
// Fill in the data starting from the end (Images are read Top-Left to Bottom-Right,
// but are stored from Bottom-Left to Top-Right)
vtkm::cont::ArrayHandle<vtkm::Vec4f_32> array;
array.Allocate(width * height);
auto portal = array.WritePortal();
vtkm::Id vtkmIndex = 0;
for (vtkm::Id yIndex = static_cast<vtkm::Id>(height - 1); yIndex >= 0; yIndex--)
{
for (vtkm::Id xIndex = 0; xIndex < static_cast<vtkm::Id>(width); xIndex++)
{
vtkm::Id pngIndex = static_cast<vtkm::Id>(yIndex * width + xIndex);
portal.Set(vtkmIndex, PixelType(imageData, pngIndex).ToVec4f());
vtkmIndex++;
}
}
free(imageData);
return array;
}
} // anonymous namespace
namespace vtkm
{
namespace io
{
ImageReaderPNG::~ImageReaderPNG() noexcept
{
}
const vtkm::cont::DataSet& ImageReaderPNG::ReadDataSet(PixelType pixelType)
{
this->Read(pixelType);
return this->GetDataSet();
}
void ImageReaderPNG::Read()
{
this->Read(PixelType::RGB_16);
}
void ImageReaderPNG::Read(PixelType pixelType)
{
vtkm::io::ImageReaderBase::ColorArrayType pixelArray;
vtkm::Id width;
vtkm::Id height;
switch (pixelType)
{
case PixelType::RGB_8:
pixelArray = ReadFromPNG<vtkm::io::RGBPixel_8>(this->FileName, width, height);
break;
case PixelType::RGB_16:
pixelArray = ReadFromPNG<vtkm::io::RGBPixel_16>(this->FileName, width, height);
break;
case PixelType::GREY_8:
pixelArray = ReadFromPNG<vtkm::io::GreyPixel_8>(this->FileName, width, height);
break;
case PixelType::GREY_16:
pixelArray = ReadFromPNG<vtkm::io::GreyPixel_16>(this->FileName, width, height);
break;
}
this->InitializeImageDataSet(width, height, pixelArray);
}
}
} // namespace vtkm::io

60
vtkm/io/ImageReaderPNG.h Normal file

@ -0,0 +1,60 @@
//============================================================================
// 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_ImageReaderPNG_h
#define vtk_m_io_ImageReaderPNG_h
#include <vtkm/io/ImageReaderBase.h>
namespace vtkm
{
namespace io
{
/// \brief Manages reading images using the PNG format via lodepng
///
/// `ImageReaderPNG` extends `ImageReaderBase` and implements reading images in a valid
/// PNG format. It utilizes lodepng's decode file functions to read
/// PNG images that are automatically compressed to optimal sizes relative to
/// the actual bit complexity of the image.
///
/// `ImageReaderPNG` will automatically upsample/downsample read image data
/// to a 16 bit RGB no matter how the image is compressed. It is up to the user to
/// decide the pixel format for input PNGs
class VTKM_IO_EXPORT ImageReaderPNG : public ImageReaderBase
{
using Superclass = ImageReaderBase;
public:
using Superclass::Superclass;
VTKM_CONT ~ImageReaderPNG() noexcept override;
ImageReaderPNG(const ImageReaderPNG&) = delete;
ImageReaderPNG& operator=(const ImageReaderPNG&) = delete;
enum class PixelType
{
RGB_8,
RGB_16,
GREY_8,
GREY_16
};
/// Reads PNG data from the provided file and stores it
/// according to the provided `PixelType`.
///
VTKM_CONT const vtkm::cont::DataSet& ReadDataSet(PixelType pixelType = PixelType::RGB_16);
protected:
VTKM_CONT void Read() override;
VTKM_CONT void Read(PixelType pixelType);
};
}
} // namespace vtkm::io
#endif //vtk_m_io_ImageReaderPNG_h

@ -0,0 +1,93 @@
//============================================================================
// 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/ImageReaderPNM.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
{
ImageReaderPNM::~ImageReaderPNM() noexcept
{
}
VTKM_CONT
void ImageReaderPNM::Read()
{
std::ifstream inStream(this->FileName.c_str(), std::ios_base::binary | std::ios_base::in);
// Currently, the only magic number supported is P6
std::string magicNum;
inStream >> magicNum;
if (magicNum != "P6")
{
throw vtkm::cont::ErrorBadValue("MagicNumber: " + magicNum + " in file: " + this->FileName +
" did not match: P6");
}
vtkm::Id width;
vtkm::Id height;
vtkm::Id maxColorValue;
inStream >> width >> height >> maxColorValue;
inStream.get();
if ((maxColorValue > 0) && (maxColorValue <= 255))
{
this->DecodeFile<vtkm::io::RGBPixel_8>(inStream, width, height);
}
else if ((maxColorValue > 255) && (maxColorValue <= 65535))
{
this->DecodeFile<vtkm::io::RGBPixel_16>(inStream, width, height);
}
else
{
throw vtkm::cont::ErrorBadValue("MaxColorValue: " + std::to_string(maxColorValue) +
" from file: " + this->FileName +
" is not in valid range of [1, 65535]");
}
}
VTKM_CONT
template <typename PixelType>
void ImageReaderPNM::DecodeFile(std::ifstream& inStream,
const vtkm::Id& width,
const vtkm::Id& height)
{
vtkm::UInt32 imageSize = static_cast<vtkm::UInt32>(width * height * PixelType::BYTES_PER_PIXEL);
std::vector<unsigned char> imageData(imageSize);
inStream.read(reinterpret_cast<char*>(imageData.data()), imageSize);
// Fill in the data starting from the end (Images are read Top-Left to Bottom-Right,
// but are stored from Bottom-Left to Top-Right)
vtkm::io::ImageReaderBase::ColorArrayType array;
array.Allocate(width * height);
auto portal = array.WritePortal();
vtkm::Id vtkmIndex = 0;
for (vtkm::Id yIndex = height - 1; yIndex >= 0; yIndex--)
{
for (vtkm::Id xIndex = 0; xIndex < width; xIndex++)
{
vtkm::Id pnmIndex = yIndex * width + xIndex;
portal.Set(vtkmIndex, PixelType(imageData.data(), pnmIndex).ToVec4f());
vtkmIndex++;
}
}
this->InitializeImageDataSet(width, height, array);
}
}
} // namespace vtkm::io

50
vtkm/io/ImageReaderPNM.h Normal file

@ -0,0 +1,50 @@
//============================================================================
// 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_ImageReaderPNM_h
#define vtk_m_io_ImageReaderPNM_h
#include <vtkm/io/ImageReaderBase.h>
namespace vtkm
{
namespace io
{
/// \brief Manages reading images using the PNM format
///
/// `ImageReaderPNM` extends `ImageReaderBase`, and implements reading images from 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 read the parsed MagicNumber and MaxColorSize provided
/// are utilized to correctly parse the bits from the file
class VTKM_IO_EXPORT ImageReaderPNM : public ImageReaderBase
{
using Superclass = ImageReaderBase;
public:
using Superclass::Superclass;
VTKM_CONT ~ImageReaderPNM() noexcept override;
ImageReaderPNM(const ImageReaderPNM&) = delete;
ImageReaderPNM& operator=(const ImageReaderPNM&) = delete;
protected:
VTKM_CONT void Read() override;
/// Reads image data from the provided inStream with the supplied width/height
/// Stores the data in a vector of PixelType which is converted to an DataSet
///
template <typename PixelType>
void DecodeFile(std::ifstream& inStream, const vtkm::Id& width, const vtkm::Id& height);
};
}
} // namespace vtkm::io
#endif //vtk_m_io_ImageReaderPNM_h

@ -9,7 +9,8 @@
//============================================================================
#include <vtkm/cont/testing/Testing.h>
#include <vtkm/io/ImageReader.h>
#include <vtkm/io/ImageReaderPNG.h>
#include <vtkm/io/ImageReaderPNM.h>
#include <vtkm/io/ImageWriter.h>
#include <vtkm/io/PixelTypes.h>
#include <vtkm/rendering/Canvas.h>
@ -58,22 +59,21 @@ void TestFilledImage(vtkm::cont::DataSet& dataSet,
}
template <typename PixelType>
void TestCreateImageDataSet(BaseImageReader& reader, const vtkm::rendering::Canvas& canvas)
void TestCreateImageDataSet(const vtkm::rendering::Canvas& canvas)
{
auto dataSet = reader.CreateImageDataSet(canvas);
TestFilledImage<PixelType>(dataSet, reader.GetPointFieldName(), canvas);
auto dataSet = canvas.GetDataSet("pixel-color");
TestFilledImage<PixelType>(dataSet, "pixel-color", canvas);
}
template <typename PixelType>
void TestReadAndWritePNG(const vtkm::rendering::Canvas& canvas, std::string image)
void TestReadAndWritePNG(const vtkm::rendering::Canvas& canvas, std::string filename)
{
auto pngReader = PNGReader();
auto pngWriter = PNGWriter();
vtkm::cont::DataSet dataSet;
bool throws = false;
try
{
pngWriter.WriteToFile(image, dataSet);
pngWriter.WriteToFile(filename, dataSet);
}
catch (const vtkm::cont::ErrorBadValue&)
{
@ -81,12 +81,21 @@ void TestReadAndWritePNG(const vtkm::rendering::Canvas& canvas, std::string imag
}
VTKM_TEST_ASSERT(throws, "Fill Image did not throw with empty data");
dataSet = pngReader.CreateImageDataSet(canvas);
pngWriter.WriteToFile(image, dataSet);
dataSet = pngReader.ReadFromFile(image);
pngWriter.WriteToFile(image, dataSet);
dataSet = pngReader.ReadFromFile(image);
TestFilledImage<PixelType>(dataSet, pngReader.GetPointFieldName(), canvas);
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());
}
pngWriter.WriteToFile(filename, dataSet);
{
vtkm::io::ImageReaderPNG reader(filename);
dataSet = reader.ReadDataSet();
TestFilledImage<PixelType>(dataSet, reader.GetPointFieldName(), canvas);
}
}
template <const vtkm::Id BitDepth>
@ -94,7 +103,6 @@ void TestReadAndWritePNM(const vtkm::rendering::Canvas& canvas)
{
using PixelType = RGBPixel<BitDepth>;
PNMWriter ppmWriter((1 << BitDepth) - 1);
PNMReader ppmReader((1 << BitDepth) - 1);
vtkm::cont::DataSet dataSet;
bool throws = false;
try
@ -107,22 +115,30 @@ void TestReadAndWritePNM(const vtkm::rendering::Canvas& canvas)
}
VTKM_TEST_ASSERT(throws, "Fill Image did not throw with empty data");
dataSet = ppmReader.CreateImageDataSet(canvas);
dataSet = canvas.GetDataSet(ppmWriter.GetPointFieldName());
ppmWriter.WriteToFile("ppmTestFile.ppm", dataSet);
dataSet = ppmReader.ReadFromFile("ppmTestFile.ppm");
{
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());
}
ppmWriter.WriteToFile("ppmTestFile2.ppm", dataSet);
dataSet = ppmReader.ReadFromFile("ppmTestFile2.ppm");
TestFilledImage<PixelType>(dataSet, ppmReader.GetPointFieldName(), canvas);
{
vtkm::io::ImageReaderPNM reader("ppmTestFile2.ppm");
dataSet = reader.ReadDataSet();
TestFilledImage<PixelType>(dataSet, reader.GetPointFieldName(), canvas);
}
}
void TestBaseImageMethods(const vtkm::rendering::Canvas& canvas)
{
auto reader = PNGReader();
TestCreateImageDataSet<RGBPixel_8>(reader, canvas);
TestCreateImageDataSet<RGBPixel_16>(reader, canvas);
TestCreateImageDataSet<GreyPixel_8>(reader, canvas);
TestCreateImageDataSet<GreyPixel_16>(reader, canvas);
TestCreateImageDataSet<RGBPixel_8>(canvas);
TestCreateImageDataSet<RGBPixel_16>(canvas);
TestCreateImageDataSet<GreyPixel_8>(canvas);
TestCreateImageDataSet<GreyPixel_16>(canvas);
}
void TestPNMImage(const vtkm::rendering::Canvas& canvas)

@ -11,6 +11,7 @@
#include <vtkm/rendering/Canvas.h>
#include <vtkm/cont/ArrayHandleCounting.h>
#include <vtkm/cont/DataSetBuilderUniform.h>
#include <vtkm/cont/TryExecute.h>
#include <vtkm/io/DecodePNG.h>
#include <vtkm/io/EncodePNG.h>
@ -272,6 +273,29 @@ Canvas::DepthBufferType& Canvas::GetDepthBuffer()
return Internals->DepthBuffer;
}
vtkm::cont::DataSet Canvas::GetDataSet(const std::string& colorFieldName,
const std::string& depthFieldName) const
{
vtkm::cont::DataSetBuilderUniform builder;
vtkm::cont::DataSet dataSet = builder.Create(vtkm::Id2(this->GetWidth(), this->GetHeight()));
if (!colorFieldName.empty())
{
dataSet.AddPointField(colorFieldName, this->GetColorBuffer());
}
if (!depthFieldName.empty())
{
dataSet.AddPointField(depthFieldName, this->GetDepthBuffer());
}
return dataSet;
}
vtkm::cont::DataSet Canvas::GetDataSet(const char* colorFieldName, const char* depthFieldName) const
{
return this->GetDataSet((colorFieldName != nullptr) ? std::string(colorFieldName) : std::string(),
(depthFieldName != nullptr) ? std::string(depthFieldName)
: std::string());
}
const vtkm::rendering::Color& Canvas::GetBackgroundColor() const
{
return Internals->BackgroundColor;

@ -16,6 +16,7 @@
#include <vtkm/Matrix.h>
#include <vtkm/Types.h>
#include <vtkm/cont/ColorTable.h>
#include <vtkm/cont/DataSet.h>
#include <vtkm/rendering/BitmapFont.h>
#include <vtkm/rendering/Camera.h>
#include <vtkm/rendering/Color.h>
@ -70,6 +71,22 @@ public:
VTKM_CONT
DepthBufferType& GetDepthBuffer();
///@{
/// \brief Gets the image in this `Canvas` as a `vtkm::cont::DataSet`.
///
/// The returned `DataSet` will be a uniform structured 2D grid. The color and depth
/// buffers will be attached as field with the given names. If the name for the color
/// or depth field is empty, then that respective field will not be added.
///
/// The arrays of the color and depth buffer are shallow copied. Thus, changes in
/// the `Canvas` may cause unexpected behavior in the `DataSet`.
///
VTKM_CONT vtkm::cont::DataSet GetDataSet(const std::string& colorFieldName = "color",
const std::string& depthFieldName = "depth") const;
VTKM_CONT vtkm::cont::DataSet GetDataSet(const char* colorFieldName,
const char* depthFieldName = "depth") const;
///@}
VTKM_CONT
void ResizeBuffers(vtkm::Id width, vtkm::Id height);