Skip to content

Commit

Permalink
Added support for TIFF, OpenEXR, and PDS image types, and
Browse files Browse the repository at this point in the history
some simple file reading tests.  Also added in the image 
statistics functions.
  • Loading branch information
Matthew Hancher committed Sep 25, 2006
1 parent eff0531 commit 653d1ab
Show file tree
Hide file tree
Showing 18 changed files with 1,438 additions and 8 deletions.
6 changes: 6 additions & 0 deletions configure.ac
Expand Up @@ -216,6 +216,12 @@ AX_PKG(VCL, [], [-lvcl], [vcl_algorithm.h], [vcl])
AX_PKG(VXL, [VCL], [-lvgl_algo -lvil_algo -lvnl_algo -lvul -lvgl -lvnl -lvil -lvcl -lnetlib],
[vil/vil_image_view.h], [core])

# These are here (instead of inside the PKG macro where they belong)
# for backwards compatability with older versions of automake.
AM_CONDITIONAL(HAVE_PKG_PNG, [test "$HAVE_PKG_PNG" = "yes"])
AM_CONDITIONAL(HAVE_PKG_TIFF, [test "$HAVE_PKG_TIFF" = "yes"])
AM_CONDITIONAL(HAVE_PKG_JPEG, [test "$HAVE_PKG_JPEG" = "yes"])
AM_CONDITIONAL(HAVE_PKG_OPENEXR, [test "$HAVE_PKG_OPENEXR" = "yes"])

##################################################
# module definitions
Expand Down
3 changes: 3 additions & 0 deletions src/vw/Core/Exception.h
Expand Up @@ -181,6 +181,9 @@ namespace vw {
/// Invalid type exception
VW_DEFINE_EXCEPTION(TypeErr, Exception);

/// Not found exception
VW_DEFINE_EXCEPTION(NotFoundErr, Exception);

/// Unimplemented functionality exception
VW_DEFINE_EXCEPTION(NoImplErr, Exception);

Expand Down
15 changes: 15 additions & 0 deletions src/vw/FileIO.h
Expand Up @@ -26,4 +26,19 @@
#ifndef __VW_FILEIO_H__
#define __VW_FILEIO_H__

#include <vw/FileIO/DiskImageResource.h>
#include <vw/FileIO/DiskImageResourcePDS.h>

#if defined(HAVE_PKG_PNG) && HAVE_PKG_PNG==1
#include <vw/FileIO/DiskImageResourcePNG.h>
#endif

#if defined(HAVE_PKG_TIFF) && HAVE_PKG_TIFF==1
#include <vw/FileIO/DiskImageResourceTIFF.h>
#endif

#if defined(HAVE_PKG_OPENEXR) && HAVE_PKG_OPENEXR==1
#include <vw/FileIO/DiskImageResourceOpenEXR.h>
#endif

#endif // __VW_FILEIO_H__
17 changes: 17 additions & 0 deletions src/vw/FileIO/DiskImageResource.cc
Expand Up @@ -35,11 +35,20 @@
#include <boost/algorithm/string.hpp>

#include <vw/FileIO/DiskImageResource.h>
#include <vw/FileIO/DiskImageResourcePDS.h>

#if defined(VW_HAVE_PKG_PNG) && VW_HAVE_PKG_PNG==1
#include <vw/FileIO/DiskImageResourcePNG.h>
#endif

#if defined(VW_HAVE_PKG_TIFF) && VW_HAVE_PKG_TIFF==1
#include <vw/FileIO/DiskImageResourceTIFF.h>
#endif

#if defined(VW_HAVE_PKG_OPENEXR) && VW_HAVE_PKG_OPENEXR==1
#include <vw/FileIO/DiskImageResourceOpenEXR.h>
#endif

namespace {
typedef std::map<std::string,vw::DiskImageResource::construct_open_func> OpenMapType;
typedef std::map<std::string,vw::DiskImageResource::construct_create_func> CreateMapType;
Expand Down Expand Up @@ -72,9 +81,17 @@ static void register_default_file_types() {
static bool already = false;
if( already ) return;
already = true;
vw::DiskImageResource::register_file_type( ".img", &vw::DiskImageResourcePDS::construct_open, &vw::DiskImageResourcePDS::construct_create );
#if defined(VW_HAVE_PKG_PNG) && VW_HAVE_PKG_PNG==1
vw::DiskImageResource::register_file_type( ".png", &vw::DiskImageResourcePNG::construct_open, &vw::DiskImageResourcePNG::construct_create );
#endif
#if defined(VW_HAVE_PKG_TIFF) && VW_HAVE_PKG_TIFF==1
vw::DiskImageResource::register_file_type( ".tif", &vw::DiskImageResourceTIFF::construct_open, &vw::DiskImageResourceTIFF::construct_create );
vw::DiskImageResource::register_file_type( ".tif", &vw::DiskImageResourceTIFF::construct_open, &vw::DiskImageResourceTIFF::construct_create );
#endif
#if defined(VW_HAVE_PKG_OPENEXR) && VW_HAVE_PKG_OPENEXR==1
vw::DiskImageResource::register_file_type( ".exr", &vw::DiskImageResourceOpenEXR::construct_open, &vw::DiskImageResourceOpenEXR::construct_create );
#endif
}

vw::DiskImageResource* vw::DiskImageResource::open( std::string const& filename ) {
Expand Down
293 changes: 293 additions & 0 deletions src/vw/FileIO/DiskImageResourceOpenEXR.cc
@@ -0,0 +1,293 @@
// __BEGIN_LICENSE__
//
// Copyright (C) 2006 United States Government as represented by the
// Administrator of the National Aeronautics and Space Administration
// (NASA). All Rights Reserved.
//
// This software is distributed under the NASA Open Source Agreement
// (NOSA), version 1.3. The NOSA has been approved by the Open Source
// Initiative. See the file COPYING at the top of the distribution
// directory tree for the complete NOSA document.
//
// THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY
// KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT
// LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO
// SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR
// A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT
// DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE.
//
// __END_LICENSE__

/// \file FileIO/DiskImageResourceOpenEXR.cc
///
/// Provides support for the OpenEXR file format.
///

#ifdef _MSC_VER
#pragma warning(disable:4244)
#pragma warning(disable:4267)
#pragma warning(disable:4996)
#endif

#include <vector>

#include <ImfInputFile.h>
#include <ImfOutputFile.h>
#include <ImfChannelList.h>
#include <ImfStringAttribute.h>
#include <ImfMatrixAttribute.h>
#include <ImfArray.h>
#include <ImfChannelList.h>

#include <vw/Core/Exception.h>
#include <vw/Image/PixelTypes.h>
#include <vw/Image/Statistics.h>
#include <vw/FileIO/DiskImageResourceOpenEXR.h>

namespace {

// This little type computation routine helps us to determine what
// to label the channels in the OpenEXR file given the pixel type of
// the source image.
static std::string openexr_channel_string_of_pixel_type(const int pixel_format,
const int channel) {
if (pixel_format == vw::VW_PIXEL_RGB) {
switch (channel) {
case 0 : return "R"; break;
case 1 : return "G"; break;
case 2 : return "B"; break;
default: throw vw::ArgumentErr() << "ChannelStringOfPixelType: Invalid Channel Number";
}

} else if (pixel_format == vw::VW_PIXEL_RGBA) {
switch (channel) {
case 0 : return "R"; break;
case 1 : return "G"; break;
case 2 : return "B"; break;
case 3 : return "A"; break;
default: throw vw::ArgumentErr() << "ChannelStringOfPixelType: Invalid Channel Number";
}

} else {
std::ostringstream m_stream;
m_stream << "Channel" << channel;
return m_stream.str();
}
}

}

// Bind the resource to a file for reading. Confirm that we can open
// the file and that it has a sane pixel format. In general VIL does
// not give us any useful information about the pixel format, so we
// make some guesses here based on the channel count.
void vw::DiskImageResourceOpenEXR::open( std::string const& filename )
{
try {
// Open Image file and read the header
m_filename = filename;

// Check to make sure that the file_ptr is not already in use.
if (m_file_ptr)
throw IOErr() << "Disk image resources do not yet support reuse.";

m_file_ptr = new Imf::InputFile(filename.c_str());
Imf::FrameBuffer frameBuffer;

// Find the width and height of the image
Imath::Box2i dw = m_file_ptr->header().dataWindow();
m_format.cols = int(dw.max.x - dw.min.x + 1);
m_format.rows = int(dw.max.y - dw.min.y + 1);

// Determine the number of image channels
Imf::ChannelList::ConstIterator iter = m_file_ptr->header().channels().begin();
int num_channels = 0;
while( iter++ != m_file_ptr->header().channels().end() )
num_channels++;
m_format.planes = num_channels;

// For now, we only support reading in multi-plane, single channel
// images.
m_format.pixel_format = VW_PIXEL_SCALAR;

} catch (Iex::BaseExc e) {
throw vw::IOErr() << "DiskImageResourceOpenEXR: could not open " << filename << ":\n\t" << e.what();
}
}

// Bind the resource to a file for writing.
void vw::DiskImageResourceOpenEXR::create( std::string const& filename,
GenericImageFormat const& format )
{
VW_ASSERT(format.planes == 1 || format.pixel_format==VW_PIXEL_SCALAR,
NoImplErr() << "DiskImageResourceOpenEXR: Cannot create " << filename << "\n\t"
<< "The image cannot have both multiple channels and multiple planes.\n");

m_filename = filename;
m_format = format;
m_format.channel_type = VW_CHANNEL_FLOAT32;
m_format.planes = std::max( format.planes, num_channels( format.pixel_format ) );
}

// Read the disk image into the given buffer.
void vw::DiskImageResourceOpenEXR::read( GenericImageBuffer const& dest ) const
{
VW_ASSERT( dest.format.cols==cols() && dest.format.rows==rows(),
IOErr() << "Buffer has wrong dimensions in OpenEXR read." );

if (!m_file_ptr)
throw LogicErr() << "DiskImageResourceOpenEXR: Could not read file. No file has been opened.";

try {
// Find the width and height of the image
Imath::Box2i dw = m_file_ptr->header().dataWindow();
int height = m_format.rows;
int width = m_format.cols;

// Set up the OpenEXR data structures necessary to read all of
// the channels out of the file and execute the call to readPixels().
Imf::Array2D<float> *inputArrays[m_format.planes];
Imf::ChannelList::ConstIterator channel = m_file_ptr->header().channels().begin();
std::vector<std::string> channel_names(m_format.planes);
for (int i=0; channel != m_file_ptr->header().channels().end(); ++channel, ++i) {
channel_names[i] = channel.name();
}

// OpenEXR seems to order channels in the file alphabetically
// (dumb!), rather than in the order in which they were saved.
// This means that we need to reorder the channel names when
// they are labelled as RGB or RGBA. For other channel naming
// schemes, we just go with alphabetical, since that's all we've
// got.
if ( m_format.planes == 3 ) {
if (find(channel_names.begin(), channel_names.end(), "R") != channel_names.end() &&
find(channel_names.begin(), channel_names.end(), "G") != channel_names.end() &&
find(channel_names.begin(), channel_names.end(), "B") != channel_names.end()) {
channel_names[0] = "R";
channel_names[1] = "G";
channel_names[2] = "B";
}
} else if ( m_format.planes == 4 ) {
if (find(channel_names.begin(), channel_names.end(), "R") != channel_names.end() &&
find(channel_names.begin(), channel_names.end(), "G") != channel_names.end() &&
find(channel_names.begin(), channel_names.end(), "B") != channel_names.end() &&
find(channel_names.begin(), channel_names.end(), "A") != channel_names.end()) {
channel_names[0] = "R";
channel_names[1] = "G";
channel_names[2] = "B";
channel_names[2] = "A";
}
}

Imf::FrameBuffer frameBuffer;
for (int nn = 0; nn < m_format.planes; nn++) {
inputArrays[nn] = new Imf::Array2D<float>(height,width);
// std::cout << "Reading channel " << channel_names[nn] << "\n";
frameBuffer.insert (channel_names[nn].c_str(), Imf::Slice (Imf::FLOAT, (char *) (&(*inputArrays[nn])[0][0]),
sizeof ((*inputArrays[nn])[0][0]) * 1,
sizeof ((*inputArrays[nn])[0][0]) * width, 1, 1, 0.0));
}
m_file_ptr->setFrameBuffer (frameBuffer);
m_file_ptr->readPixels (dw.min.y, dw.max.y);

// Copy the pixels over into a ImageView object.
//
// Recast to the templatized pixel type in the process.
ImageView<float> src_image(m_format.cols, m_format.rows, m_format.planes);
for (int nn = 0; nn < m_format.planes; nn++) {
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
src_image(i,j,nn) = (*inputArrays[nn])[j][i];
}
}
}
GenericImageBuffer src(src_image);
convert( dest, src );

// Print out the image size and number of channels
std::cout << "OpenEXR file " << m_filename
<< "\t" << m_format.planes << " x " << width << " x " << height << "\n";

// Clean up
for (int nn = 0; nn < m_format.planes; nn++) {
delete inputArrays[nn];
}

} catch (Iex::BaseExc e) {
throw vw::IOErr() << "Failed to open " << m_filename << " using the OpenEXR image reader.\n\t" << e.what();
}
}

// Write the given buffer into the disk image.
void vw::DiskImageResourceOpenEXR::write( GenericImageBuffer const& src )
{
VW_ASSERT( src.format.cols==cols() && src.format.rows==rows(),
IOErr() << "Buffer has wrong dimensions in OpenEXR write." );

// This is pretty simple since we always write 8-bit integer files.
// Note that we handle multi-channel images with interleaved planes.
// We've already ensured that either planes==1 or channels==1.
ImageView<float> openexr_image( m_format.cols, m_format.rows, m_format.planes );
GenericImageBuffer dst(openexr_image);
convert( dst, src );

float* pixels[dst.format.planes];
Imf::Array2D<float> *floatArrays[dst.format.planes];
std::string labels[dst.format.planes];

try {
// Create the file header with the appropriate number of
// channels. Label the channels in order starting with "Channel 0".
Imf::Header header (dst.format.cols,dst.format.rows);
for (int nn = 0; nn < dst.format.planes; nn++) {
labels[nn] = openexr_channel_string_of_pixel_type(m_format.pixel_format, nn);
// std::cout << "Writing channel " << nn << ": " << labels[nn] << "\n";
header.channels().insert (labels[nn].c_str(), Imf::Channel (Imf::FLOAT));
floatArrays[nn] = new Imf::Array2D<float>(dst.format.rows,dst.format.cols);
}

// Open the file handle and create an empty framebuffer object.
Imf::OutputFile file (m_filename.c_str(), header);
Imf::FrameBuffer frameBuffer;

// Copy the actual data into temporary memory, which will
// ultimately be written to the file.
for (unsigned int nn = 0; nn < dst.format.planes; nn++)
for (unsigned int i = 0; i < dst.format.cols; i++)
for (unsigned int j = 0; j < dst.format.rows; j++)
(*floatArrays[nn])[j][i] = openexr_image(i,j,nn);

// Build the framebuffer out of the various image channels
for (unsigned int nn = 0; nn < dst.format.planes; nn++) {
pixels[nn] = &((*floatArrays[nn])[0][0]);
frameBuffer.insert (labels[nn].c_str(),
Imf::Slice (Imf::FLOAT, (char *) pixels[nn],
sizeof (*(pixels[nn])) * 1,
sizeof (*(pixels[nn])) * dst.format.cols));
}

// Write the data to disk
file.setFrameBuffer (frameBuffer);
file.writePixels (dst.format.rows);

// Clean up
for (unsigned int nn = 0; nn < dst.format.planes; nn++)
delete floatArrays[nn];

} catch (Iex::BaseExc e) {
throw vw::IOErr() << "DiskImageResourceOpenEXR: Failed to write " << m_filename << ".\n\t" << e.what();
}

}

// A FileIO hook to open a file for reading
vw::DiskImageResource* vw::DiskImageResourceOpenEXR::construct_open( std::string const& filename ) {
return new DiskImageResourceOpenEXR( filename );
}

// A FileIO hook to open a file for writing
vw::DiskImageResource* vw::DiskImageResourceOpenEXR::construct_create( std::string const& filename,
GenericImageFormat const& format ) {
return new DiskImageResourceOpenEXR( filename, format );
}

0 comments on commit 653d1ab

Please sign in to comment.