From 653d1ab92059908ba7a57fcb0a5c2709b7747130 Mon Sep 17 00:00:00 2001 From: Matthew Hancher Date: Mon, 25 Sep 2006 19:58:40 +0000 Subject: [PATCH] Added support for TIFF, OpenEXR, and PDS image types, and some simple file reading tests. Also added in the image statistics functions. --- configure.ac | 6 + src/vw/Core/Exception.h | 3 + src/vw/FileIO.h | 15 + src/vw/FileIO/DiskImageResource.cc | 17 ++ src/vw/FileIO/DiskImageResourceOpenEXR.cc | 293 +++++++++++++++++++ src/vw/FileIO/DiskImageResourceOpenEXR.h | 82 ++++++ src/vw/FileIO/DiskImageResourcePDS.cc | 258 +++++++++++++++++ src/vw/FileIO/DiskImageResourcePDS.h | 107 +++++++ src/vw/FileIO/DiskImageResourceTIFF.cc | 300 ++++++++++++++++++++ src/vw/FileIO/DiskImageResourceTIFF.h | 78 +++++ src/vw/FileIO/Makefile.am | 24 +- src/vw/FileIO/tests/Makefile.am | 2 +- src/vw/FileIO/tests/TestDiskImageResource.h | 93 +++++- src/vw/FileIO/tests/rgb2x2.jpg | Bin 0 -> 15649 bytes src/vw/FileIO/tests/rgb2x2.png | Bin 0 -> 165 bytes src/vw/FileIO/tests/rgb2x2.tif | Bin 0 -> 14760 bytes src/vw/Image/Makefile.am | 3 +- src/vw/Image/Statistics.h | 165 +++++++++++ 18 files changed, 1438 insertions(+), 8 deletions(-) create mode 100644 src/vw/FileIO/DiskImageResourceOpenEXR.cc create mode 100644 src/vw/FileIO/DiskImageResourceOpenEXR.h create mode 100644 src/vw/FileIO/DiskImageResourcePDS.cc create mode 100644 src/vw/FileIO/DiskImageResourcePDS.h create mode 100644 src/vw/FileIO/DiskImageResourceTIFF.cc create mode 100644 src/vw/FileIO/DiskImageResourceTIFF.h create mode 100644 src/vw/FileIO/tests/rgb2x2.jpg create mode 100644 src/vw/FileIO/tests/rgb2x2.png create mode 100644 src/vw/FileIO/tests/rgb2x2.tif create mode 100644 src/vw/Image/Statistics.h diff --git a/configure.ac b/configure.ac index fe130c4e5..ffb0124c1 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/src/vw/Core/Exception.h b/src/vw/Core/Exception.h index 3b32cfd1e..626111f29 100644 --- a/src/vw/Core/Exception.h +++ b/src/vw/Core/Exception.h @@ -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); diff --git a/src/vw/FileIO.h b/src/vw/FileIO.h index 30382aac3..95bcfb679 100644 --- a/src/vw/FileIO.h +++ b/src/vw/FileIO.h @@ -26,4 +26,19 @@ #ifndef __VW_FILEIO_H__ #define __VW_FILEIO_H__ +#include +#include + +#if defined(HAVE_PKG_PNG) && HAVE_PKG_PNG==1 +#include +#endif + +#if defined(HAVE_PKG_TIFF) && HAVE_PKG_TIFF==1 +#include +#endif + +#if defined(HAVE_PKG_OPENEXR) && HAVE_PKG_OPENEXR==1 +#include +#endif + #endif // __VW_FILEIO_H__ diff --git a/src/vw/FileIO/DiskImageResource.cc b/src/vw/FileIO/DiskImageResource.cc index c77445072..4082bac05 100644 --- a/src/vw/FileIO/DiskImageResource.cc +++ b/src/vw/FileIO/DiskImageResource.cc @@ -35,11 +35,20 @@ #include #include +#include #if defined(VW_HAVE_PKG_PNG) && VW_HAVE_PKG_PNG==1 #include #endif +#if defined(VW_HAVE_PKG_TIFF) && VW_HAVE_PKG_TIFF==1 +#include +#endif + +#if defined(VW_HAVE_PKG_OPENEXR) && VW_HAVE_PKG_OPENEXR==1 +#include +#endif + namespace { typedef std::map OpenMapType; typedef std::map CreateMapType; @@ -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 ) { diff --git a/src/vw/FileIO/DiskImageResourceOpenEXR.cc b/src/vw/FileIO/DiskImageResourceOpenEXR.cc new file mode 100644 index 000000000..f83d922d4 --- /dev/null +++ b/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 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +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 *inputArrays[m_format.planes]; + Imf::ChannelList::ConstIterator channel = m_file_ptr->header().channels().begin(); + std::vector 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(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 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 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 *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(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 ); +} diff --git a/src/vw/FileIO/DiskImageResourceOpenEXR.h b/src/vw/FileIO/DiskImageResourceOpenEXR.h new file mode 100644 index 000000000..ab0279940 --- /dev/null +++ b/src/vw/FileIO/DiskImageResourceOpenEXR.h @@ -0,0 +1,82 @@ +// __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.h +/// +/// Provides support for the OpenEXR file format. +/// +#ifndef __VW_FILEIO_DISK_IMAGE_RESOUCE_OPEN_EXR_H__ +#define __VW_FILEIO_DISK_IMAGE_RESOUCE_OPEN_EXR_H__ + +#include + +#include +#include + +#include +#include +#include + +namespace vw { + + class DiskImageResourceOpenEXR : public DiskImageResource { + public: + + DiskImageResourceOpenEXR( std::string const& filename ) + { + m_file_ptr = NULL; + open( filename ); + } + + DiskImageResourceOpenEXR( std::string const& filename, + GenericImageFormat const& format ) + { + m_file_ptr = NULL; + create( filename, format ); + } + + virtual ~DiskImageResourceOpenEXR() { + if (m_file_ptr) + delete m_file_ptr; + } + + virtual void read( GenericImageBuffer const& dest ) const; + virtual void write( GenericImageBuffer const& dest ); + virtual void flush() {} + + void open( std::string const& filename ); + + void create( std::string const& filename, + GenericImageFormat const& format ); + + static DiskImageResource* construct_open( std::string const& filename ); + + static DiskImageResource* construct_create( std::string const& filename, + GenericImageFormat const& format ); + + private: + std::string m_filename; + Imf::InputFile* m_file_ptr; + }; + +} // namespace vw + +#endif // __VW_FILEIO_DISK_IMAGE_RESOUCE_OPEN_EXR_H__ diff --git a/src/vw/FileIO/DiskImageResourcePDS.cc b/src/vw/FileIO/DiskImageResourcePDS.cc new file mode 100644 index 000000000..149983b2e --- /dev/null +++ b/src/vw/FileIO/DiskImageResourcePDS.cc @@ -0,0 +1,258 @@ +// __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 DiskImageResourcePDS.cc +/// +#ifdef _MSC_VER +#pragma warning(disable:4244) +#pragma warning(disable:4267) +#pragma warning(disable:4996) +#endif + +#include +#include +#include +#include +using namespace std; + +#include +using namespace boost; + +#include +#include + + +// static String_value_mapping Compression_type_mapping[] = { +// {"HUFFMAN_FIRST_DIFFERENCE", COMPRESSION_TYPE_HUFFMAN}, +// {"CLEM-JPEG", COMPRESSION_TYPE_JPEG}, +// {"MOC-PRED-X-5", COMPRESSION_TYPE_MOC}, +// {"MOC-DCT-2", COMPRESSION_TYPE_MOC}, +// PPVL_STRING_VALUE_MAPPING_END +// }; + + +/// Close the file when the object is destroyed +vw::DiskImageResourcePDS::~DiskImageResourcePDS() { + this->flush(); +} + +/// Flush the buffered data to disk +void vw::DiskImageResourcePDS::flush() { +} + +void vw::DiskImageResourcePDS::parse_pds_header(std::vector const& header) { + + for (int i = 0; i < header.size(); i++) { + string line = header[i]; + + // Locate lines that have a key/value pair (with key = value syntax) + vector split_vector; + split( split_vector, line, is_any_of("=") ); + if (split_vector.size() == 2) { + trim_left(split_vector[0]); + trim_right(split_vector[0]); + trim_left(split_vector[1]); + trim_right(split_vector[1]); + m_header_entries[split_vector[0]] = split_vector[1]; + } + } +} + +/// Bind the resource to a file for reading. Confirm that we can open +/// the file and that it has a sane pixel format. +void vw::DiskImageResourcePDS::open( std::string const& filename ) { + + FILE* input_file = fopen(filename.c_str(), "r"); + if( ! input_file ) throw vw::IOErr() << "Failed to open \"" << filename << "\"."; + m_filename = filename; + + char c_line[2048]; + int i = 0; + + std::vector header; + + // Read the entire header section and place it into a vector of + // strings where each string is one line of the file. + const static int MAX_PDS_HEADER_SIZE = 1000; + while ( fgets(c_line, 2048, input_file) ) { + i++; + if ((c_line[0] == 'E' && c_line[1] == 'N' && c_line[2] == 'D' && c_line[3] != '_') || + (i > MAX_PDS_HEADER_SIZE)) + break; + header.push_back(std::string(c_line)); + } + fclose(input_file); + + // The the data into an associative contain (std::map). Key/value + // pairs are located by searching for strings seperated by the + // equals sign "=". + parse_pds_header(header); + + try { + vector keys; + + // Query for the number of columns + keys.push_back("LINE_SAMPLES"); + keys.push_back("NS"); + keys.push_back("/IMAGE/LINE_SAMPLES"); + m_format.cols = atol(query(keys).c_str()); + + // Query for the number of rows + keys.clear(); + keys.push_back("NL"); + keys.push_back("IMAGE_LINES"); + keys.push_back("LINES"); + keys.push_back("/IMAGE/LINES"); + m_format.rows = atol(query(keys).c_str()); + + // Image planes (if there is no tag in the PDS for bands, we + // assume one image plane) + try { + keys.clear(); + keys.push_back("BANDS"); + keys.push_back("/IMAGE/BANDS"); + m_format.planes = atol(query(keys).c_str()); + } catch (NotFoundErr &e) { + m_format.planes = 1; + } + + // pixel type + keys.clear(); + keys.push_back("SAMPLE_TYPE"); + string format_str = query(keys); + keys.clear(); + keys.push_back("SAMPLE_BITS"); + string sample_bits_str = query(keys); + + if (format_str == "UNSIGNED_INTEGER") { + + if (sample_bits_str == "8") + m_format.channel_type = VW_CHANNEL_UINT8; + else if (sample_bits_str == "16") + m_format.channel_type = VW_CHANNEL_UINT16; + + } else if (format_str == "MSB_INTEGER") { + + if (sample_bits_str == "8") + m_format.channel_type = VW_CHANNEL_INT8; + else if (sample_bits_str == "16") + m_format.channel_type = VW_CHANNEL_INT16; + + } else { + throw IOErr() << "DiskImageResourcePDS: Unsupported pixel type in \"" << filename << "\"."; + } + + // Number of bytes in the PDS header (essentially the offset + // before the image data begins. + keys.clear(); + keys.push_back("RECORD_BYTES"); + keys.push_back("/RECSIZE"); + keys.push_back("HEADER_RECORD_BYTES"); + m_image_data_offset = atol(query(keys).c_str()); + + } catch (vw::NotFoundErr &e) { + throw IOErr() << "DiskImageResourcePDS: could not find critical information in the image header.\n"; + } + + switch( m_format.planes ) { + case 1: m_format.pixel_format = VW_PIXEL_GRAY; break; + case 2: m_format.pixel_format = VW_PIXEL_GRAYA; m_format.planes=1; break; + case 3: m_format.pixel_format = VW_PIXEL_RGB; m_format.planes=1; break; + case 4: m_format.pixel_format = VW_PIXEL_RGBA; m_format.planes=1; break; + default: m_format.pixel_format = VW_PIXEL_SCALAR; break; + } + + // For debugging: + // std::cout << "\tImage Dimensions: " << m_format.cols << "x" << m_format.rows << "x" << m_format.planes << "\n"; + // std::cout << "\tImage Format: " << m_format.channel_type << " " << m_format.pixel_format << "\n"; +} + +/// Bind the resource to a file for writing. +void vw::DiskImageResourcePDS::create( std::string const& filename, + GenericImageFormat const& format ) +{ + throw NoImplErr() << "The PDS driver does not yet support creation of PDS files"; +} + +/// Read the disk image into the given buffer. +void vw::DiskImageResourcePDS::read( GenericImageBuffer const& dest ) const +{ + VW_ASSERT( dest.format.cols==cols() && dest.format.rows==rows(), + IOErr() << "Buffer has wrong dimensions in PDS read." ); + + // Re-open the file, and shift the file offset to the position of + // the first image byte (as indicated by the PDS header) + FILE* input_file = fopen(m_filename.c_str(), "r"); + if( ! input_file ) throw vw::IOErr() << "Failed to open \"" << m_filename << "\"."; + fseek(input_file, m_image_data_offset, 0); + + // Grab the pixel data from the file. + unsigned int total_pixels = m_format.cols * m_format.rows * m_format.planes; + unsigned int bytes_per_pixel; + if (m_format.channel_type == VW_CHANNEL_UINT8 || + m_format.channel_type == VW_CHANNEL_INT8) { + bytes_per_pixel = 1; + } else if (m_format.channel_type == VW_CHANNEL_UINT16 || + m_format.channel_type == VW_CHANNEL_INT16) { + bytes_per_pixel = 2; + } + uint8* image_data = new uint8[total_pixels * bytes_per_pixel]; + unsigned bytes_read = fread(image_data, bytes_per_pixel, total_pixels, input_file); + if (bytes_read != total_pixels) + throw IOErr() << "DiskImageResourcePDS: An error occured while reading the image data."; + + // Convert the endian-ness of the data + for (int i = 0; i < total_pixels * bytes_per_pixel; i+=2) { + uint8 temp = image_data[i+1]; + image_data[i+1] = image_data[i]; + image_data[i] = temp; + } + + // set up a generic image buffer around the PDS data. + GenericImageBuffer src; + src.data = image_data; + src.format = m_format; + src.cstride = bytes_per_pixel; + src.rstride = bytes_per_pixel * m_format.cols; + src.pstride = bytes_per_pixel * m_format.cols * m_format.rows; + + convert( dest, src ); + + delete[] image_data; + fclose(input_file); +} + +// Write the given buffer into the disk image. +void vw::DiskImageResourcePDS::write( GenericImageBuffer const& src ) +{ + throw NoImplErr() << "The PDS driver does not yet support creation of PDS files"; +} + +// A FileIO hook to open a file for reading +vw::DiskImageResource* vw::DiskImageResourcePDS::construct_open( std::string const& filename ) { + return new DiskImageResourcePDS( filename ); +} + +// A FileIO hook to open a file for writing +vw::DiskImageResource* vw::DiskImageResourcePDS::construct_create( std::string const& filename, + GenericImageFormat const& format ) { + return new DiskImageResourcePDS( filename, format ); +} diff --git a/src/vw/FileIO/DiskImageResourcePDS.h b/src/vw/FileIO/DiskImageResourcePDS.h new file mode 100644 index 000000000..69a8b03ee --- /dev/null +++ b/src/vw/FileIO/DiskImageResourcePDS.h @@ -0,0 +1,107 @@ +// __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 DiskImageResourcePDS.h +/// +/// Provides support for some NASA mission data from the Planetary +/// Data System (PDS). +/// +#ifndef __VW_FILEIO_DISK_IMAGE_RESOUCE_PDS_H__ +#define __VW_FILEIO_DISK_IMAGE_RESOUCE_PDS_H__ + +#include +#include +#include + +#include +#include +#include +#include + +namespace vw { + + class DiskImageResourcePDS : public DiskImageResource { + public: + + DiskImageResourcePDS( std::string const& filename ) + { + open( filename ); + } + + DiskImageResourcePDS( std::string const& filename, + GenericImageFormat const& format ) + { + create( filename, format ); + } + + virtual ~DiskImageResourcePDS(); + + virtual void read( GenericImageBuffer const& dest ) const; + virtual void write( GenericImageBuffer const& dest ); + virtual void flush(); + + /// Query for a value in the PDS header. The returned value will + /// be a string regardless of whether or not the value is a + /// numerical type (i.e. it's up to you to convert to numerical + /// types from a string where appropriate). If no value is found + /// for a matching key, a vw::NotFoundErr will be thrown. + std::string query(std::string const& key) { + std::map::iterator query_object = m_header_entries.find(key); + if (query_object != m_header_entries.end()) + return (*query_object).second; + else + throw NotFoundErr() << "DiskImageResourcePDS: no matching value found for \"" << key << "\""; + } + + /// Search for a value that might match several keys. The first + /// key with a value in the table will be used to extract that + /// value, so order does matter. If no value is found for a + /// matching key, a vw::NotFoundErr will be thrown. + std::string query(std::vector const& keys) { + for (int i = 0; i < keys.size(); i++) { + std::map::iterator query_object = m_header_entries.find(keys[i]); + if (query_object != m_header_entries.end()) + return (*query_object).second; + } + throw NotFoundErr() << "DiskImageResourcePDS: no matching value found for the keys provided."; + } + + + void open( std::string const& filename ); + + void create( std::string const& filename, + GenericImageFormat const& format ); + + static DiskImageResource* construct_open( std::string const& filename ); + + static DiskImageResource* construct_create( std::string const& filename, + GenericImageFormat const& format ); + + private: + void parse_pds_header(std::vector const& header); + std::string m_filename; + std::map m_header_entries; + int m_image_data_offset; + }; + +} // namespace vw + +#endif // __VW_FILEIO_DISK_IMAGE_RESOUCE_PDS_H__ diff --git a/src/vw/FileIO/DiskImageResourceTIFF.cc b/src/vw/FileIO/DiskImageResourceTIFF.cc new file mode 100644 index 000000000..ae42a3b9c --- /dev/null +++ b/src/vw/FileIO/DiskImageResourceTIFF.cc @@ -0,0 +1,300 @@ +// __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/DiskImageResourceTIFF.cc +/// +/// Provides support for TIFF image files. +/// + +#ifdef _MSC_VER +#pragma warning(disable:4244) +#pragma warning(disable:4267) +#pragma warning(disable:4996) +#endif + +#include + +#include + +#include +#include + +// Define the following macro if you want libtiff to show warning messages +// #define VW_TIFF_SHOW_WARNINGS + +void tiff_warning_handler(const char* module, const char* frmt, va_list ap) { +#ifdef VW_TIFF_SHOW_WARNINGS + std::cout << "Warning "; + if (module) { printf(frmt, module); } + else { printf(frmt, ""); } + printf("\n"); +#endif +} + +void tiff_error_handler(const char* module, const char* frmt, va_list ap) { + char error_msg[2048]; + if (module) { snprintf(error_msg, 2048, frmt, module); } + else { snprintf(error_msg, 2048, frmt, ""); } + throw vw::IOErr() << "DiskImageResourceTIFF: A libtiff error occured. " << std::string(error_msg); +} + + +/// Close the TIFF file when the object is destroyed +vw::DiskImageResourceTIFF::~DiskImageResourceTIFF() { + this->flush(); +} + +/// Flush the buffered data to disk +void vw::DiskImageResourceTIFF::flush() { + if (m_tif_ptr) { + TIFFFlush((TIFF*) m_tif_ptr); + m_tif_ptr = NULL; + } +} + +/// Bind the resource to a file for reading. Confirm that we can open +/// the file and that it has a sane pixel format. +void vw::DiskImageResourceTIFF::open( std::string const& filename ) +{ + TIFFSetWarningHandler(&tiff_warning_handler); + TIFFSetErrorHandler(&tiff_error_handler); + + TIFF* tif = TIFFOpen(filename.c_str(), "r"); + if( !tif ) throw vw::IOErr() << "Failed to open \"" << filename << "\" using libTIFF."; + m_tif_ptr = (void*) tif; + m_filename = filename; + + TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &(m_format.cols)); + TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &(m_format.rows)); + TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &(m_format.planes)); + + uint32 sample_format = 0, bits_per_sample = 0; + TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bits_per_sample); + TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &sample_format); + // Some TIFF files don't actually specify the sample format, but in + // this case the format is almost always UINT, so we force this + // assumption here. + if (sample_format == 0) { + sample_format = SAMPLEFORMAT_UINT; + } + + switch (sample_format) { + case SAMPLEFORMAT_UINT: + if (bits_per_sample == 8) + m_format.channel_type = VW_CHANNEL_UINT8; + else if (bits_per_sample == 16) + m_format.channel_type = VW_CHANNEL_UINT16; + else if (bits_per_sample == 32) + m_format.channel_type = VW_CHANNEL_UINT32; + else if (bits_per_sample == 64) + m_format.channel_type = VW_CHANNEL_UINT64; + else + throw IOErr() << "DiskImageResourceTIFF: Unsupported pixel format."; + break; + case SAMPLEFORMAT_INT: + if (bits_per_sample == 8) + m_format.channel_type = VW_CHANNEL_INT8; + else if (bits_per_sample == 16) + m_format.channel_type = VW_CHANNEL_INT16; + else if (bits_per_sample == 32) + m_format.channel_type = VW_CHANNEL_INT32; + else if (bits_per_sample == 64) + m_format.channel_type = VW_CHANNEL_INT64; + else + throw IOErr() << "DiskImageResourceTIFF: Unsupported pixel format."; + break; + case SAMPLEFORMAT_IEEEFP: + if (bits_per_sample == 16) + m_format.channel_type = VW_CHANNEL_FLOAT16; + else if (bits_per_sample == 32) + m_format.channel_type = VW_CHANNEL_FLOAT32; + else if (bits_per_sample == 64) + m_format.channel_type = VW_CHANNEL_FLOAT64; + else + throw IOErr() << "DiskImageResourceTIFF: Unsupported pixel format."; + break; + default: + throw IOErr() << "DiskImageResourceTIFF: Unsupported pixel format."; + } + + uint32 plane_configuration = 0; + TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &plane_configuration); + + // XXX TODO: Tiff might actually provide us with some info on + // colorimetric interpretation of the channels, so maybe we should + // try to use that here as well? + if (plane_configuration == PLANARCONFIG_CONTIG) { + switch( m_format.planes ) { + case 1: m_format.pixel_format = VW_PIXEL_GRAY; break; + case 2: m_format.pixel_format = VW_PIXEL_GRAYA; m_format.planes=1; break; + case 3: m_format.pixel_format = VW_PIXEL_RGB; m_format.planes=1; break; + case 4: m_format.pixel_format = VW_PIXEL_RGBA; m_format.planes=1; break; + default: m_format.pixel_format = VW_PIXEL_SCALAR; break; + } + } else { + m_format.pixel_format = VW_PIXEL_SCALAR; + } +} + +/// Bind the resource to a file for writing. +void vw::DiskImageResourceTIFF::create( std::string const& filename, + GenericImageFormat const& format ) +{ + if( format.planes!=1 && format.pixel_format!=VW_PIXEL_SCALAR ) + throw NoImplErr() << "TIFF doesn't support multi-plane images with compound pixel types."; + + // Set the TIFF warning and error handlers to Vision Workbench + // functions, so that we can handle them ourselves. + TIFFSetWarningHandler(&tiff_warning_handler); + TIFFSetErrorHandler(&tiff_error_handler); + + TIFF* tif = TIFFOpen(filename.c_str(), "w"); + if( !tif ) throw vw::IOErr() << "Failed to create \"" << filename << "\" using libTIFF."; + m_tif_ptr = (void*) tif; + + m_filename = filename; + m_format = format; +} + +/// Read the disk image into the given buffer. +void vw::DiskImageResourceTIFF::read( GenericImageBuffer const& dest ) const +{ + VW_ASSERT( dest.format.cols==cols() && dest.format.rows==rows(), + IOErr() << "Buffer has wrong dimensions in TIFF read." ); + + TIFF* tif = (TIFF*) m_tif_ptr; + uint32 image_length = 0, image_width = 0, config = 0; + + TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &image_length); + TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &image_width); + TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &config); + uint32 scanline_size = TIFFScanlineSize(tif); + + // Allocate a buffer for reading in the data and read it, scanline + // by scanline from disk. + tdata_t buf = _TIFFmalloc(scanline_size * image_length * m_format.planes); + uint16 s, nsamples; + + for (uint32 p = 0; p < m_format.planes; p++) { + for (uint32 row = 0; row < image_length; row++) { + TIFFReadScanline(tif, (uint8*)buf + row*scanline_size+p*scanline_size*image_length, row, p); + } + } + + // Set up a generic image buffer around the tdata_t buf object that + // tiff used to copy it's data. + GenericImageBuffer src; + src.data = (uint8*)buf; + src.format = m_format; + src.cstride = scanline_size / image_width; + src.rstride = scanline_size; + src.pstride = scanline_size * image_length; + + convert( dest, src ); + _TIFFfree(buf); +} + +// Write the given buffer into the disk image. +void vw::DiskImageResourceTIFF::write( GenericImageBuffer const& src ) +{ + VW_ASSERT( src.format.cols==cols() && src.format.rows==rows(), + IOErr() << "Buffer has wrong dimensions in TIFF write." ); + + TIFF* tif = (TIFF*) m_tif_ptr; + + TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, m_format.cols); + TIFFSetField(tif, TIFFTAG_IMAGELENGTH, m_format.rows); + TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8*channel_size(m_format.channel_type)); + + if (m_format.pixel_format == VW_PIXEL_RGB || + m_format.pixel_format == VW_PIXEL_RGBA) { + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + } else { + TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); + } + + switch (m_format.channel_type) { + case VW_CHANNEL_INT8: + case VW_CHANNEL_INT16: + case VW_CHANNEL_INT32: + case VW_CHANNEL_INT64: + TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_INT); + break; + case VW_CHANNEL_UINT8: + case VW_CHANNEL_UINT16: + case VW_CHANNEL_UINT32: + case VW_CHANNEL_UINT64: + TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT); + break; + case VW_CHANNEL_FLOAT16: + case VW_CHANNEL_FLOAT32: + case VW_CHANNEL_FLOAT64: + TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP); + break; + default: + throw IOErr() << "DiskImageResourceTIFF: Unsupported VW channel type."; + } + + // Allocate some buffer memory for the output data + uint32 scanline_size = num_channels(m_format.pixel_format) * channel_size(m_format.channel_type) * m_format.cols; + tdata_t buf = _TIFFmalloc(scanline_size * m_format.rows * m_format.planes); + + // Set up the generic image buffer and convert the data into this buffer. + GenericImageBuffer dst; + dst.data = (uint8*)buf; + dst.format = m_format; + dst.cstride = num_channels(m_format.pixel_format) * channel_size(m_format.channel_type); + dst.rstride = dst.cstride * m_format.cols; + dst.pstride = dst.rstride * m_format.rows; + convert( dst, src ); + + if (m_format.pixel_format == VW_PIXEL_SCALAR) { + // Multi-plane images with simple pixel types are stored in seperate + // planes in the TIFF image. + TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_SEPARATE); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, m_format.planes); + } else { + // Compound pixel types are stored contiguously in TIFF files + TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, num_channels(m_format.pixel_format)); + } + + // Write the image data to disk. + for (uint32 p = 0; p < m_format.planes; p++) { + for (uint32 row = 0; row < m_format.rows; row++) { + TIFFWriteScanline(tif, (uint8*)buf + row*scanline_size + p * scanline_size * m_format.rows, row); + } + } + + // Clean up + _TIFFfree(buf); +} + +// A FileIO hook to open a file for reading +vw::DiskImageResource* vw::DiskImageResourceTIFF::construct_open( std::string const& filename ) { + return new DiskImageResourceTIFF( filename ); +} + +// A FileIO hook to open a file for writing +vw::DiskImageResource* vw::DiskImageResourceTIFF::construct_create( std::string const& filename, + GenericImageFormat const& format ) { + return new DiskImageResourceTIFF( filename, format ); +} diff --git a/src/vw/FileIO/DiskImageResourceTIFF.h b/src/vw/FileIO/DiskImageResourceTIFF.h new file mode 100644 index 000000000..132703fa5 --- /dev/null +++ b/src/vw/FileIO/DiskImageResourceTIFF.h @@ -0,0 +1,78 @@ +// __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/DiskImageResourceTIFF.h +/// +/// Provides support for TIFF image files. +/// +#ifndef __VW_FILEIO_DISK_IMAGE_RESOUCE_TIFF_H__ +#define __VW_FILEIO_DISK_IMAGE_RESOUCE_TIFF_H__ + +#include + +#include +#include +#include + +namespace vw { + + class DiskImageResourceTIFF : public DiskImageResource { + public: + + DiskImageResourceTIFF( std::string const& filename ) + { + m_tif_ptr = NULL; + open( filename ); + } + + DiskImageResourceTIFF( std::string const& filename, + GenericImageFormat const& format ) + { + m_tif_ptr = NULL; + create( filename, format ); + } + + virtual ~DiskImageResourceTIFF(); + + virtual void read( GenericImageBuffer const& dest ) const; + virtual void write( GenericImageBuffer const& dest ); + virtual void flush(); + + void open( std::string const& filename ); + + void create( std::string const& filename, + GenericImageFormat const& format ); + + static DiskImageResource* construct_open( std::string const& filename ); + + static DiskImageResource* construct_create( std::string const& filename, + GenericImageFormat const& format ); + + private: + + std::string m_filename; + void* m_tif_ptr; + + }; + +} // namespace vw + +#endif // __VW_FILEIO_DISK_IMAGE_RESOUCE_TIFF_H__ diff --git a/src/vw/FileIO/Makefile.am b/src/vw/FileIO/Makefile.am index e3fb74941..e75e728d6 100644 --- a/src/vw/FileIO/Makefile.am +++ b/src/vw/FileIO/Makefile.am @@ -25,9 +25,26 @@ if MAKE_MODULE_FILEIO -include_HEADERS = DiskImageResource.h DiskImageResourcePNG.h +if HAVE_PKG_PNG +png_headers = DiskImageResourcePNG.h +png_sources = DiskImageResourcePNG.cc +endif + +if HAVE_PKG_TIFF +tiff_headers = DiskImageResourceTIFF.h +tiff_sources = DiskImageResourceTIFF.cc +endif -libvwFileIO_la_SOURCES = DiskImageResource.cc DiskImageResourcePNG.cc +if HAVE_PKG_OPENEXR +openexr_headers = DiskImageResourceOpenEXR.h +openexr_sources = DiskImageResourceOpenEXR.cc +endif + +include_HEADERS = DiskImageResource.h DiskImageResourcePDS.h \ + $(png_headers) $(tiff_headers) $(openexr_headers) + +libvwFileIO_la_SOURCES = DiskImageResource.cc DiskImageResourcePDS.cc \ + $(png_sources) $(tiff_sources) $(openexr_sources) libvwFileIO_la_CPPFLAGS = @MODULE_FILEIO_CPPFLAGS@ libvwFileIO_la_LIBADD = @MODULE_FILEIO_LDFLAGS@ @@ -44,7 +61,4 @@ SUBDIRS = . tests includedir = $(prefix)/include/vw/FileIO -AM_CPPFLAGS = -AM_LDFLAGS = - include $(top_srcdir)/config/rules.mak diff --git a/src/vw/FileIO/tests/Makefile.am b/src/vw/FileIO/tests/Makefile.am index b732810e9..2fe18855b 100644 --- a/src/vw/FileIO/tests/Makefile.am +++ b/src/vw/FileIO/tests/Makefile.am @@ -39,7 +39,7 @@ AM_CPPFLAGS = @MODULE_FILEIO_CPPFLAGS@ -I$(top_srcdir)/thirdparty/cxxtest AM_LDFLAGS = @PKG_FILEIO_LDFLAGS@ check_PROGRAMS = $(TESTS) -EXTRA_DIST = $(patsubst %,%.h,$(TESTS)) +EXTRA_DIST = $(patsubst %,%.h,$(TESTS)) rgb2x2.png rgb2x2.tif rgb2x2.jpg CLEANFILES = $(patsubst %,%.cxx,$(TESTS)) include $(top_srcdir)/config/rules.mak diff --git a/src/vw/FileIO/tests/TestDiskImageResource.h b/src/vw/FileIO/tests/TestDiskImageResource.h index 68ea8f3a8..4755f2928 100644 --- a/src/vw/FileIO/tests/TestDiskImageResource.h +++ b/src/vw/FileIO/tests/TestDiskImageResource.h @@ -20,15 +20,106 @@ // __END_LICENSE__ // TestDiskImageResource.h +#define CXXTEST_ABORT_TEST_ON_FAIL #include #include +#include +#include +#include + using namespace vw; class TestDiskImageResource : public CxxTest::TestSuite { public: - void test_dummy() {} + void test_read_image_rgb_png_uint8() { +#if defined(VW_HAVE_PKG_PNG) && VW_HAVE_PKG_PNG==1 + ImageView > image; + TS_ASSERT_THROWS_NOTHING( read_image( image, "rgb2x2.png" ) ); + TS_ASSERT_EQUALS( image.cols(), 2 ); + TS_ASSERT_EQUALS( image.rows(), 2 ); + TS_ASSERT_EQUALS( image.planes(), 1 ); + TS_ASSERT_EQUALS( image(0,0).r(), 128 ); + TS_ASSERT_EQUALS( image(0,0).g(), 128 ); + TS_ASSERT_EQUALS( image(0,0).b(), 128 ); + TS_ASSERT_EQUALS( image(1,0).r(), 85 ); + TS_ASSERT_EQUALS( image(1,0).g(), 0 ); + TS_ASSERT_EQUALS( image(1,0).b(), 0 ); + TS_ASSERT_EQUALS( image(0,1).r(), 0 ); + TS_ASSERT_EQUALS( image(0,1).g(), 170 ); + TS_ASSERT_EQUALS( image(0,1).b(), 0 ); + TS_ASSERT_EQUALS( image(1,1).r(), 0 ); + TS_ASSERT_EQUALS( image(1,1).g(), 0 ); + TS_ASSERT_EQUALS( image(1,1).b(), 255 ); +#endif + } + + void test_read_image_rgb_png_float() { +#if defined(VW_HAVE_PKG_PNG) && VW_HAVE_PKG_PNG==1 + ImageView > image; + TS_ASSERT_THROWS_NOTHING( read_image( image, "rgb2x2.png" ) ); + TS_ASSERT_EQUALS( image.cols(), 2 ); + TS_ASSERT_EQUALS( image.rows(), 2 ); + TS_ASSERT_EQUALS( image.planes(), 1 ); + TS_ASSERT_DELTA( image(0,0).r(), 0.50196, 1e-5 ); + TS_ASSERT_DELTA( image(0,0).g(), 0.50196, 1e-5 ); + TS_ASSERT_DELTA( image(0,0).b(), 0.50196, 1e-5 ); + TS_ASSERT_DELTA( image(1,0).r(), 0.33333, 1e-5 ); + TS_ASSERT_EQUALS( image(1,0).g(), 0 ); + TS_ASSERT_EQUALS( image(1,0).b(), 0 ); + TS_ASSERT_EQUALS( image(0,1).r(), 0 ); + TS_ASSERT_DELTA( image(0,1).g(), 0.66667, 1e-5 ); + TS_ASSERT_EQUALS( image(0,1).b(), 0 ); + TS_ASSERT_EQUALS( image(1,1).r(), 0 ); + TS_ASSERT_EQUALS( image(1,1).g(), 0 ); + TS_ASSERT_DELTA( image(1,1).b(), 1.0, 1e-5 ); +#endif + } + + void test_read_image_rgb_tif_uint8() { +#if defined(VW_HAVE_PKG_TIFF) && VW_HAVE_PKG_TIFF==1 + ImageView > image; + TS_ASSERT_THROWS_NOTHING( read_image( image, "rgb2x2.tif" ) ); + TS_ASSERT_EQUALS( image.cols(), 2 ); + TS_ASSERT_EQUALS( image.rows(), 2 ); + TS_ASSERT_EQUALS( image.planes(), 1 ); + TS_ASSERT_EQUALS( image(0,0).r(), 128 ); + TS_ASSERT_EQUALS( image(0,0).g(), 128 ); + TS_ASSERT_EQUALS( image(0,0).b(), 128 ); + TS_ASSERT_EQUALS( image(1,0).r(), 85 ); + TS_ASSERT_EQUALS( image(1,0).g(), 0 ); + TS_ASSERT_EQUALS( image(1,0).b(), 0 ); + TS_ASSERT_EQUALS( image(0,1).r(), 0 ); + TS_ASSERT_EQUALS( image(0,1).g(), 170 ); + TS_ASSERT_EQUALS( image(0,1).b(), 0 ); + TS_ASSERT_EQUALS( image(1,1).r(), 0 ); + TS_ASSERT_EQUALS( image(1,1).g(), 0 ); + TS_ASSERT_EQUALS( image(1,1).b(), 255 ); +#endif + } + + void test_read_image_rgb_tif_float() { +#if defined(VW_HAVE_PKG_TIFF) && VW_HAVE_PKG_TIFF==1 + ImageView > image; + TS_ASSERT_THROWS_NOTHING( read_image( image, "rgb2x2.tif" ) ); + TS_ASSERT_EQUALS( image.cols(), 2 ); + TS_ASSERT_EQUALS( image.rows(), 2 ); + TS_ASSERT_EQUALS( image.planes(), 1 ); + TS_ASSERT_DELTA( image(0,0).r(), 0.50196, 1e-5 ); + TS_ASSERT_DELTA( image(0,0).g(), 0.50196, 1e-5 ); + TS_ASSERT_DELTA( image(0,0).b(), 0.50196, 1e-5 ); + TS_ASSERT_DELTA( image(1,0).r(), 0.33333, 1e-5 ); + TS_ASSERT_EQUALS( image(1,0).g(), 0 ); + TS_ASSERT_EQUALS( image(1,0).b(), 0 ); + TS_ASSERT_EQUALS( image(0,1).r(), 0 ); + TS_ASSERT_DELTA( image(0,1).g(), 0.66667, 1e-5 ); + TS_ASSERT_EQUALS( image(0,1).b(), 0 ); + TS_ASSERT_EQUALS( image(1,1).r(), 0 ); + TS_ASSERT_EQUALS( image(1,1).g(), 0 ); + TS_ASSERT_DELTA( image(1,1).b(), 1.0, 1e-5 ); +#endif + } }; // class TestDiskImageResource diff --git a/src/vw/FileIO/tests/rgb2x2.jpg b/src/vw/FileIO/tests/rgb2x2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a5c3a2ad0baeacef86b9aeb7a3c11b3164ca34ac GIT binary patch literal 15649 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 Nz-<@owKnwO`XAC25itM& literal 0 HcmV?d00001 diff --git a/src/vw/Image/Makefile.am b/src/vw/Image/Makefile.am index a4d876245..e9e419fdd 100644 --- a/src/vw/Image/Makefile.am +++ b/src/vw/Image/Makefile.am @@ -29,7 +29,8 @@ include_HEADERS = PixelIterator.h ImageViewBase.h PixelAccessors.h \ ImageView.h ImageViewRef.h PixelTypeInfo.h PixelMath.h \ PixelTypes.h GenericImageBuffer.h Manipulation.h \ PerPixelViews.h ImageMath.h EdgeExtend.h Interpolation.h \ - Convolution.h Filter.h Filter.h Filter.tcc Transform.h + Convolution.h Filter.h Filter.h Filter.tcc Transform.h \ + Statistics.h libvwImage_la_SOURCES = GenericImageBuffer.cc Filter.cc libvwImage_la_LIBADD = @MODULE_IMAGE_LDFLAGS@ diff --git a/src/vw/Image/Statistics.h b/src/vw/Image/Statistics.h new file mode 100644 index 000000000..343cb73ce --- /dev/null +++ b/src/vw/Image/Statistics.h @@ -0,0 +1,165 @@ +// __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 ImageStatistics.h +/// +/// These functions compute the min, max, minmax, and +/// mean value of an image on a per-channel basis. +/// + +// TODO: How do we deal with alpha channels here? +#ifndef __VW_IMAGE_STATISTICS_H__ +#define __VW_IMAGE_STATISTICS_H__ + +#include + +#include +#include + +namespace vw { + + /// Compute the minimum value stored in all of the channels of all of the planes of the images. + template + typename CompoundChannelType::type + min_channel_value( const ImageViewBase& view_ ) { + const ViewT& view = view_.impl(); + + typedef typename ViewT::pixel_accessor pixel_accessor; + typedef typename ViewT::pixel_type pixel_type; + typedef typename CompoundChannelType::type channel_type; + + pixel_type pix = view(0,0); + channel_type min = compound_select_channel(pix,0); + + pixel_accessor plane_acc = view.origin(); + for (unsigned p = 0; p < view.planes(); p++, plane_acc.next_plane()) { + pixel_accessor col_acc = plane_acc; + for (unsigned i = 0; i < view.cols(); i++, col_acc.next_col()) { + pixel_accessor row_acc = col_acc; + for (unsigned j = 0; j < view.rows(); j++, row_acc.next_row()) { + typename ViewT::result_type pix = *row_acc; + for (unsigned channel = 0; channel < view.channels(); channel++) { + channel_type channel_value = compound_select_channel(pix,channel); + if( channel_value < min ) min = channel_value; + } + } + } + } + return min; + } + + + /// Compute the maximum value stored in all of the channels of all of the planes of the images. + template + typename CompoundChannelType::type + max_channel_value( const ImageViewBase& view_ ) { + const ViewT& view = view_.impl(); + + typedef typename ViewT::pixel_accessor pixel_accessor; + typedef typename ViewT::pixel_type pixel_type; + typedef typename CompoundChannelType::type channel_type; + + pixel_type pix = view(0,0); + channel_type max = compound_select_channel(pix,0); + + pixel_accessor plane_acc = view.origin(); + for (unsigned p = 0; p < view.planes(); p++, plane_acc.next_plane()) { + pixel_accessor col_acc = plane_acc; + for (unsigned i = 0; i < view.cols(); i++, col_acc.next_col()) { + pixel_accessor row_acc = col_acc; + for (unsigned j = 0; j < view.rows(); j++, row_acc.next_row()) { + typename ViewT::result_type pix = *row_acc; + for (unsigned channel = 0; channel < view.channels(); channel++) { + channel_type channel_value = compound_select_channel(pix,channel); + if( channel_value > max ) max = channel_value; + } + } + } + } + return max; + } + + + /// Simultaneously compute the min and max value in all of the + /// channels of all of the planes of the image. + template + void min_max_channel_values( const ImageViewBase &view_, + typename CompoundChannelType::type &min, + typename CompoundChannelType::type &max ) { + const ViewT& view = view_.impl(); + + typedef typename ViewT::pixel_accessor pixel_accessor; + typedef typename ViewT::pixel_type pixel_type; + typedef typename CompoundChannelType::type channel_type; + + pixel_type pix = view(0,0); + min = compound_select_channel(pix,0); + max = compound_select_channel(pix,0); + + pixel_accessor plane_acc = view.origin(); + for (unsigned p = 0; p < view.planes(); p++, plane_acc.next_plane()) { + pixel_accessor col_acc = plane_acc; + for (unsigned i = 0; i < view.cols(); i++, col_acc.next_col()) { + pixel_accessor row_acc = col_acc; + for (unsigned j = 0; j < view.rows(); j++, row_acc.next_row()) { + typename ViewT::result_type pix = *row_acc; + for (unsigned channel = 0; channel < view.channels(); channel++) { + channel_type channel_value = compound_select_channel(pix,channel); + if( channel_value < min ) min = channel_value; + if( channel_value > max ) max = channel_value; + } + } + } + } + } + + /// Compute the mean value stored in all of the channels of all of the planes of the image. + template + typename CompoundChannelType::type + mean_channel_value( const ImageViewBase &view_ ) { + const ViewT& view = view_.impl(); + + typedef typename ViewT::pixel_accessor pixel_accessor; + typedef typename CompoundChannelType::type channel_type; + + double accum = 0; + + pixel_accessor plane_acc = view.origin(); + for (unsigned p = 0; p < view.planes(); p++, plane_acc.next_plane()) { + pixel_accessor col_acc = plane_acc; + for (unsigned i = 0; i < view.cols(); i++, col_acc.next_col()) { + pixel_accessor row_acc = col_acc; + for (unsigned j = 0; j < view.rows(); j++, row_acc.next_row()) { + typename ViewT::result_type pix = *row_acc; + for (unsigned channel = 0; channel < view.channels(); channel++) { + channel_type channel_value = compound_select_channel(pix,channel); + accum += channel_value; + } + } + } + } + return channel_type(accum / (view.planes() * view.rows() * view.cols() * view.channels())); + } + +} // namespace vw + + +#endif // __VW_IMAGE_STATISTICS_H__