/
DiskImageResourceOpenEXR.cc
293 lines (251 loc) · 11.6 KB
/
DiskImageResourceOpenEXR.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
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 );
}