static int exr_is_multilayer(InputFile *file) { const StringAttribute *comments = file->header().findTypedAttribute<StringAttribute>("BlenderMultiChannel"); const ChannelList &channels = file->header().channels(); std::set <std::string> layerNames; /* will not include empty layer names */ channels.layers(layerNames); if (comments || layerNames.size() > 1) return 1; if (layerNames.size()) { /* if layerNames is not empty, it means at least one layer is non-empty, * but it also could be layers without names in the file and such case * shall be considered a multilayer exr * * that's what we do here: test whether there're empty layer names together * with non-empty ones in the file */ for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); i++) { std::string layerName = i.name(); size_t pos = layerName.rfind ('.'); if (pos == std::string::npos) return 1; } } return 0; }
void ChannelListAttribute::writeValueTo (OStream &os, int version) const { for (ChannelList::ConstIterator i = _value.begin(); i != _value.end(); ++i) { // // Write name // Xdr::write <StreamIO> (os, i.name()); // // Write Channel struct // Xdr::write <StreamIO> (os, int (i.channel().type)); Xdr::pad <StreamIO> (os, 4); Xdr::write <StreamIO> (os, i.channel().xSampling); Xdr::write <StreamIO> (os, i.channel().ySampling); } // // Write end of list marker // Xdr::write <StreamIO> (os, ""); }
ChannelList channelsInView (const string & viewName, const ChannelList & channelList, const StringVector & multiView) { // // Return a list of all channels belonging to view viewName. // ChannelList q; for (ChannelList::ConstIterator i = channelList.begin(); i != channelList.end(); ++i) { // // Get view name for this channel // string view = viewFromChannelName (i.name(), multiView); // // Insert channel into q if it's a member of view viewName // if (view == viewName) q.insert (i.name(), i.channel()); } return q; }
/* read from file */ int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *height) { ExrHandle *data = (ExrHandle *)handle; if (BLI_exists(filename) && BLI_file_size(filename) > 32) { /* 32 is arbitrary, but zero length files crashes exr */ /* avoid crash/abort when we don't have permission to write here */ try { data->ifile_stream = new IFileStream(filename); data->ifile = new InputFile(*(data->ifile_stream)); } catch (const std::exception &) { delete data->ifile; delete data->ifile_stream; data->ifile = NULL; data->ifile_stream = NULL; } if (data->ifile) { Box2i dw = data->ifile->header().dataWindow(); data->width = *width = dw.max.x - dw.min.x + 1; data->height = *height = dw.max.y - dw.min.y + 1; const ChannelList &channels = data->ifile->header().channels(); for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) IMB_exr_add_channel(data, NULL, i.name(), 0, 0, NULL); return 1; } } return 0; }
/* debug only */ static void exr_print_filecontents(InputFile *file) { const ChannelList &channels = file->header().channels(); for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) { const Channel &channel = i.channel(); printf("OpenEXR-load: Found channel %s of type %d\n", i.name(), channel.type); } }
// Read the header of an EXR file. // (ref) http://www.openexr.com/ReadingAndWritingImageFiles.pdf int vtkOpenEXR::exrInfo(const char *fileName, std::vector<std::string> &channelNames) { // parse filename QFileInfo fi(fileName); QString extension = fi.suffix().toLower(); if ( extension.compare("exr") != 0 ) { // qDebug() << "the exr file was not found"; return false; } if (!fi.exists()) { // qDebug() << "the exr file was not found"; return false; } int numChannels = 0; try { RgbaInputFile file(fileName); const ChannelList &ch = file.header().channels(); int ic = 0; for (ChannelList::ConstIterator i = ch.begin(); i != ch.end(); ++i) { const Channel &channel = i.channel(); const char* channelName = i.name(); // by Min PixelType type = channel.type; const char* t = (type == UINT) ? "uint" : ((type == HALF) ? "half" : "float"); channelNames.push_back(channelName); ++ic; // qDebug() << "channel " << ic << ": " << t << ": " << channelName ; } numChannels = ic; const StringAttribute *comments = file.header().findTypedAttribute <StringAttribute> ("comments"); const M44fAttribute *cameraTransform = file.header().findTypedAttribute <M44fAttribute> ("cameraTransform"); // if (comments) // qDebug() << "comments\n " << comments->value() ; // if (cameraTransform) // qDebug() << "cameraTransform\n" << cameraTransform->value() << flush; } catch (const std::exception &exc) { // qDebug() << exc.what(); return false; } return numChannels; // number of channels }
void EXRImageReader::channelNames( vector<string> &names ) { open( true ); const ChannelList &channels = m_inputFile->header().channels(); names.clear(); for( ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i ) { names.push_back( i.name() ); } }
void EXRReaderPlugin::updateCombos() { const std::string filename( getAbsoluteFirstFilename() ); //TUTTLE_COUT("update Combo"); if( bfs::exists( filename ) ) { // read dims InputFile in( filename.c_str() ); const Header& h = in.header(); const ChannelList& cl = h.channels(); // Hide output channel selection till we don't select a channel. for( std::size_t i = 0; i < _vChannelChoice.size(); ++i ) { //_vChannelChoice[i]->setIsSecret( true ); _vChannelChoice[i]->resetOptions(); } _vChannelNames.clear(); for( ChannelList::ConstIterator it = cl.begin(); it != cl.end(); ++it ) { _vChannelNames.push_back( it.name() ); //TUTTLE_COUT_VAR( it.name() ); for( std::size_t j = 0; j < _vChannelChoice.size(); ++j ) { _vChannelChoice[j]->appendOption( it.name() ); } ++_channels; switch( _channels ) { case 1: { for( std::size_t j = 0; j < _vChannelChoice.size(); j++ ) _vChannelChoice.at(j)->setValue( 0 ); break; } case 3: { for( std::size_t j = 0; j < _vChannelChoice.size() - 1; j++ ) _vChannelChoice.at(j)->setValue( 2 - j ); _vChannelChoice.at(3)->setValue( 0 ); break; } case 4: { for( std::size_t j = 0; j < _vChannelChoice.size(); j++ ) _vChannelChoice.at(j)->setValue( 3 - j ); break; } } } } }
/* for non-multilayer, map R G B A channel names to something that's in this file */ static const char *exr_rgba_channelname(InputFile *file, const char *chan) { const ChannelList &channels = file->header().channels(); for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) { /* const Channel &channel = i.channel(); */ /* Not used yet */ const char *str = i.name(); int len = strlen(str); if (len) { if (BLI_strcasecmp(chan, str + len - 1) == 0) { return str; } } } return chan; }
size_t calculateBytesPerPixel (const Header &header) { const ChannelList &channels = header.channels(); size_t bytesPerPixel = 0; for (ChannelList::ConstIterator c = channels.begin(); c != channels.end(); ++c) { bytesPerPixel += pixelTypeSize (c.channel().type); } return bytesPerPixel; }
void printChannelList (const ChannelList &cl) { for (ChannelList::ConstIterator i = cl.begin(); i != cl.end(); ++i) { cout << "\n " << i.name() << ", "; printPixelType (i.channel().type); cout << ", sampling " << i.channel().xSampling << " " << i.channel().ySampling; if (i.channel().pLinear) cout << ", plinear"; } }
void EXRReader::open() { // open file and read dimensions m_data.reset( new EXRReaderData(filename().c_str()) ); int width = m_data->dtw_.max.x - m_data->dtw_.min.x + 1; int height = m_data->dtw_.max.y - m_data->dtw_.min.y + 1; assert(width > 0); assert(height > 0); // check that file contains RGB data bool red = false; bool green = false; bool blue = false; const ChannelList &channels = m_data->file_.header().channels(); for ( ChannelList::ConstIterator i = channels.begin(), iEnd = channels.end(); i != iEnd; ++i ) { if ( !strcmp(i.name(), "R") ) red = true; else if ( !strcmp(i.name(), "G") ) green = true; else if ( !strcmp(i.name(), "B") ) blue = true; } if ( !(red && green && blue) ) { throw pfs::io::InvalidHeader("OpenEXR file " + filename() + " does " \ " not contain RGB data"); } // check boundaries /* if ( (m_data->dtw_.min.x < m_data->dw_.min.x && m_data->dtw_.max.x > m_data->dw_.max.x) || (m_data->dtw_.min.y < m_data->dw_.min.y && m_data->dtw_.max.y > m_data->dw_.max.y) ) { throw pfs::io::InvalidHeader("No support for OpenEXR files DataWindow" \ " greater than DisplayWindow" ); } */ setWidth(width); setHeight(height); }
// Prepares a framebuffer for the requested channels, allocating also the // appropriate Matlab memory void vtkOpenEXR::prepareFrameBuffer(FrameBuffer & fb, const Box2i & dataWindow, const ChannelList & channels, const std::vector<std::string> & requestedChannels, std::vector< Array2D<float> *> & outData) // std::vector<mxArray *> & outMatlabData { assert(!requestedChannels.empty()); const Box2i & dw = dataWindow; const int width = dw.max.x - dw.min.x + 1; const int height = dw.max.y - dw.min.y + 1; // The "weird" strides are because Matlab uses column-major order (check this out later) // const int xStride = height; // for matlab // const int yStride = 1; const int xStride = 1; const int yStride = width; // for c++ // Offset for all the slices const off_t offset = - (dw.min.x * xStride + dw.min.y * yStride); // why negative? for (size_t i = 0; i != requestedChannels.size(); ++i) { // Allocate the memory //mxArray * data = mxCreateNumericMatrix(height, width, mxSINGLE_CLASS, mxREAL); Array2D<float> * data = new Array2D<float>; //Array2D<float> * data(height, width); data->resizeErase(height, width); outData[i] = data; float * ptr = static_cast<float*>(data[0][0]); // check this out // Get the appropriate sampling factors int xSampling = 1, ySampling = 1; ChannelList::ConstIterator cIt = channels.find(requestedChannels[i].c_str()); if (cIt != channels.end()) { xSampling = cIt.channel().xSampling; ySampling = cIt.channel().ySampling; } // Insert the slice in the framebuffer fb.insert(requestedChannels[i].c_str(), Slice(FLOAT, (char*)(ptr + offset), sizeof(float) * xStride, sizeof(float) * yStride, xSampling, ySampling)); } }
/* read from file */ int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *height) { ExrHandle *data= (ExrHandle *)handle; if(BLI_exists(filename) && BLI_filepathsize(filename)>32) { /* 32 is arbitrary, but zero length files crashes exr */ data->ifile = new InputFile(filename); if(data->ifile) { Box2i dw = data->ifile->header().dataWindow(); data->width= *width = dw.max.x - dw.min.x + 1; data->height= *height = dw.max.y - dw.min.y + 1; const ChannelList &channels = data->ifile->header().channels(); for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) IMB_exr_add_channel(data, NULL, i.name(), 0, 0, NULL); return 1; } } return 0; }
void EXRReaderPlugin::updateCombos() { const std::string filepath( getAbsoluteFirstFilename() ); if( ! bfs::exists( filepath ) ) return; // read dims InputFile in( filepath.c_str() ); const Header& h = in.header(); const ChannelList& cl = h.channels(); _par = h.pixelAspectRatio(); Imf::Compression compression = h.compression(); switch( compression ) { case RLE_COMPRESSION: _paramFileCompression->setValue( eParamCompression_RLE ); break; case ZIPS_COMPRESSION: _paramFileCompression->setValue( eParamCompression_ZIPS ); break; case ZIP_COMPRESSION: _paramFileCompression->setValue( eParamCompression_ZIP ); break; case PIZ_COMPRESSION: _paramFileCompression->setValue( eParamCompression_PIZ ); break; case PXR24_COMPRESSION: _paramFileCompression->setValue( eParamCompression_PXR24 ); break; case B44_COMPRESSION: _paramFileCompression->setValue( eParamCompression_B44 ); break; case B44A_COMPRESSION: _paramFileCompression->setValue( eParamCompression_B44A ); break; case NO_COMPRESSION: _paramFileCompression->setValue( eParamCompression_None ); break; default: _paramFileCompression->setValue( eParamCompression_None ); TUTTLE_LOG_WARNING( "EXRReader: Unknown compression " << compression ); break; } // Hide output channel selection till we don't select a channel. for( std::size_t i = 0; i < _paramsChannelChoice.size(); ++i ) { //_vChannelChoice[i]->setIsSecret( true ); _paramsChannelChoice[i]->resetOptions(); } _channelNames.clear(); for( ChannelList::ConstIterator it = cl.begin(); it != cl.end(); ++it ) { _channelNames.push_back( it.name() ); //TUTTLE_LOG_VAR( it.name() ); for( std::size_t j = 0; j < _paramsChannelChoice.size(); ++j ) { _paramsChannelChoice[j]->appendOption( it.name() ); } } switch( _channelNames.size() ) { case 1: { for( std::size_t j = 0; j < _paramsChannelChoice.size(); j++ ) _paramsChannelChoice.at(j)->setValue( 0 ); break; } case 3: { for( std::size_t j = 0; j < _paramsChannelChoice.size() - 1; j++ ) _paramsChannelChoice.at(j)->setValue( 2 - j ); _paramsChannelChoice.at(3)->setValue( 0 ); break; } case 4: { for( std::size_t j = 0; j < _paramsChannelChoice.size(); j++ ) _paramsChannelChoice.at(j)->setValue( 3 - j ); break; } } const std::string& firstChannelName = _channelNames[ _paramsChannelChoice[0]->getValue() ]; Imf::PixelType firstChannelType = cl.find(firstChannelName.c_str()).channel().type; switch( firstChannelType ) { case Imf::UINT: _paramFileBitDepth->setValue( eTuttlePluginFileBitDepth32 ); break; case Imf::HALF: _paramFileBitDepth->setValue( eTuttlePluginFileBitDepth16f ); break; case Imf::FLOAT: _paramFileBitDepth->setValue( eTuttlePluginFileBitDepth32f ); break; default: _paramFileBitDepth->setValue( eTuttlePluginFileBitDepthNone ); TUTTLE_LOG_WARNING( "EXRReader: Unknown bit depth " << firstChannelType ); break; } }
void ScanLineInputFile::setFrameBuffer (const FrameBuffer &frameBuffer) { Lock lock (*_streamData); const ChannelList &channels = _data->header.channels(); for (FrameBuffer::ConstIterator j = frameBuffer.begin(); j != frameBuffer.end(); ++j) { ChannelList::ConstIterator i = channels.find (j.name()); if (i == channels.end()) continue; if (i.channel().xSampling != j.slice().xSampling || i.channel().ySampling != j.slice().ySampling) THROW (IEX_NAMESPACE::ArgExc, "X and/or y subsampling factors " "of \"" << i.name() << "\" channel " "of input file \"" << fileName() << "\" are " "not compatible with the frame buffer's " "subsampling factors."); } // // Check if the new frame buffer descriptor is // compatible with the image file header. // if (!GLOBAL_SYSTEM_LITTLE_ENDIAN) { _data->optimizationMode._destination._format = OptimizationMode::PIXELFORMAT_OTHER; _data->optimizationMode._source._format = OptimizationMode::PIXELFORMAT_OTHER; } else { StringVector * v=NULL; if(hasMultiView(_data->header)) { v = &multiView(_data->header); } _data->optimizationMode = detectOptimizationMode(frameBuffer, channels,v); } // TODO-pk this disables optimization // _data->optimizationMode._destination._format = Imf::OptimizationMode::PIXELFORMAT_OTHER; // // Initialize the slice table for readPixels(). // vector<InSliceInfo> slices; ChannelList::ConstIterator i = channels.begin(); for (FrameBuffer::ConstIterator j = frameBuffer.begin(); j != frameBuffer.end(); ++j) { while (i != channels.end() && strcmp (i.name(), j.name()) < 0) { // // Channel i is present in the file but not // in the frame buffer; data for channel i // will be skipped during readPixels(). // slices.push_back (InSliceInfo (i.channel().type, i.channel().type, 0, // base 0, // xStride 0, // yStride i.channel().xSampling, i.channel().ySampling, false, // fill true, // skip 0.0)); // fillValue ++i; } bool fill = false; if (i == channels.end() || strcmp (i.name(), j.name()) > 0) { // // Channel i is present in the frame buffer, but not in the file. // In the frame buffer, slice j will be filled with a default value. // fill = true; } slices.push_back (InSliceInfo (j.slice().type, fill? j.slice().type: i.channel().type, j.slice().base, j.slice().xStride, j.slice().yStride, j.slice().xSampling, j.slice().ySampling, fill, false, // skip j.slice().fillValue)); if (i != channels.end() && !fill) ++i; } // // Store the new frame buffer. // _data->frameBuffer = frameBuffer; _data->slices = slices; }
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { char *exrFileName; double *mxData; int width = 0; int height = 0; int nSlices = 0; if (nrhs != 1 || !mxIsChar(prhs[0])) { mexErrMsgTxt("Usage: ReadMultichannelEXR('exrFile')\n"); } // read the exr file with OpenEXR general interface exrFileName = mxArrayToString(prhs[0]); InputFile file (exrFileName); // query general file properties Box2i dw = file.header().dataWindow(); width = dw.max.x - dw.min.x + 1; height = dw.max.y - dw.min.y + 1; const ChannelList &channels = file.header().channels(); for (ChannelList::ConstIterator iter = channels.begin(); iter != channels.end(); ++iter) { nSlices++; } mexPrintf("Read \"%s\": width=%d height=%d nSlices=%d\n", exrFileName, width, height, nSlices); mxFree(exrFileName); // return a struct with info about image slices mwSize nDims = 2; mwSize infoDims[] = {1, 0}; infoDims[1] = nSlices; const char* fieldNames[] = {"name", "pixelType", "xSampling", "ySampling", "isLinear"}; int nFields = sizeof(fieldNames)/sizeof(fieldNames[0]); plhs[0] = mxCreateStructArray(nDims, infoDims, nFields, fieldNames); // return a double array with size [height width nSlices] nDims = 3; mwSize dataDims[] = {0, 0, 0}; dataDims[0] = height; dataDims[1] = width; dataDims[2] = nSlices; plhs[1] = mxCreateNumericArray(nDims, dataDims, mxDOUBLE_CLASS, mxREAL); if (NULL == plhs[1]) { mexPrintf("Could not allocate image array of size [%d %d %d]\n", dataDims[0], dataDims[1], dataDims[2]); return; } double* data = mxGetPr(plhs[1]); // fill in info struct and data array int channelIndex = 0; for (ChannelList::ConstIterator iter = channels.begin(); iter != channels.end(); ++iter) { const Channel &channel = iter.channel(); // fill in info struct for this channel mxSetField(plhs[0], channelIndex, "name", mxCreateString(iter.name())); mxSetField(plhs[0], channelIndex, "xSampling", mxCreateDoubleScalar((double)channel.xSampling)); mxSetField(plhs[0], channelIndex, "ySampling", mxCreateDoubleScalar((double)channel.ySampling)); mxSetField(plhs[0], channelIndex, "isLinear", mxCreateLogicalScalar((mxLogical)channel.pLinear)); // fill in a slice of the data matrix for this channel // memory allocation depends on slice pixel type mxArray* typeName = NULL; FrameBuffer frameBuffer; switch (channel.type) { case UINT:{ typeName = mxCreateString("UINT"); // copy slice data from file to a frame buffer Array2D<MATLAB_UINT32> slicePixels; slicePixels.resizeErase(height, width); frameBuffer.insert(iter.name(), Slice(HALF, (char *)(&slicePixels[0][0] - dw.min.x - dw.min.y * width), sizeof(slicePixels[0][0]) * 1, sizeof(slicePixels[0][0]) * width, channel.xSampling, channel.ySampling, 0.0)); file.setFrameBuffer (frameBuffer); file.readPixels (dw.min.y, dw.max.y); // copy slice data from frame buffer to data array for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { data[M3I(height, width, i, j, channelIndex)] = slicePixels[i][j]; } } break; } case HALF:{ typeName = mxCreateString("HALF"); // copy slice data from file to a frame buffer Array2D<half> slicePixels; slicePixels.resizeErase(height, width); frameBuffer.insert(iter.name(), Slice(HALF, (char *)(&slicePixels[0][0] - dw.min.x - dw.min.y * width), sizeof(slicePixels[0][0]) * 1, sizeof(slicePixels[0][0]) * width, channel.xSampling, channel.ySampling, 0.0)); file.setFrameBuffer (frameBuffer); file.readPixels (dw.min.y, dw.max.y); // copy slice data from frame buffer to data array for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { data[M3I(height, width, i, j, channelIndex)] = slicePixels[i][j]; } } break; } case FLOAT:{ typeName = mxCreateString("FLOAT"); // copy slice data from file to a frame buffer Array2D<half> slicePixels; slicePixels.resizeErase(height, width); frameBuffer.insert(iter.name(), Slice(HALF, (char *)(&slicePixels[0][0] - dw.min.x - dw.min.y * width), sizeof(slicePixels[0][0]) * 1, sizeof(slicePixels[0][0]) * width, channel.xSampling, channel.ySampling, 0.0)); file.setFrameBuffer (frameBuffer); file.readPixels (dw.min.y, dw.max.y); // copy slice data from frame buffer to data array for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { data[M3I(height, width, i, j, channelIndex)] = slicePixels[i][j]; } } break; } default:{ typeName = mxCreateString("UNKNOWN"); break; } } mxSetField(plhs[0], channelIndex, "pixelType", typeName); channelIndex++; //mexPrintf(" channel \"%s\": type=%d xSampling=%d ySampling=%d, isLinear=%d\n", // iter.name(), channel.type, // channel.xSampling, channel.ySampling, channel.pLinear); } }
void OutputFile::setFrameBuffer (const FrameBuffer &frameBuffer) { Lock lock (*_data); // // Check if the new frame buffer descriptor // is compatible with the image file header. // const ChannelList &channels = _data->header.channels(); for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) { FrameBuffer::ConstIterator j = frameBuffer.find (i.name()); if (j == frameBuffer.end()) continue; if (i.channel().type != j.slice().type) { THROW (Iex::ArgExc, "Pixel type of \"" << i.name() << "\" channel " "of output file \"" << fileName() << "\" is " "not compatible with the frame buffer's " "pixel type."); } if (i.channel().xSampling != j.slice().xSampling || i.channel().ySampling != j.slice().ySampling) { THROW (Iex::ArgExc, "X and/or y subsampling factors " "of \"" << i.name() << "\" channel " "of output file \"" << fileName() << "\" are " "not compatible with the frame buffer's " "subsampling factors."); } } // // Initialize slice table for writePixels(). // vector<OutSliceInfo> slices; for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) { FrameBuffer::ConstIterator j = frameBuffer.find (i.name()); if (j == frameBuffer.end()) { // // Channel i is not present in the frame buffer. // In the file, channel i will contain only zeroes. // slices.push_back (OutSliceInfo (i.channel().type, 0, // base 0, // xStride, 0, // yStride, i.channel().xSampling, i.channel().ySampling, true)); // zero } else { // // Channel i is present in the frame buffer. // slices.push_back (OutSliceInfo (j.slice().type, j.slice().base, j.slice().xStride, j.slice().yStride, j.slice().xSampling, j.slice().ySampling, false)); // zero } } // // Store the new frame buffer. // _data->frameBuffer = frameBuffer; _data->slices = slices; }
void Header::sanityCheck (bool isTiled, bool isMultipartFile) const { // // The display window and the data window must each // contain at least one pixel. In addition, the // coordinates of the window corners must be small // enough to keep expressions like max-min+1 or // max+min from overflowing. // const Box2i &displayWindow = this->displayWindow(); if (displayWindow.min.x > displayWindow.max.x || displayWindow.min.y > displayWindow.max.y || displayWindow.min.x <= -(INT_MAX / 2) || displayWindow.min.y <= -(INT_MAX / 2) || displayWindow.max.x >= (INT_MAX / 2) || displayWindow.max.y >= (INT_MAX / 2)) { throw IEX_NAMESPACE::ArgExc ("Invalid display window in image header."); } const Box2i &dataWindow = this->dataWindow(); if (dataWindow.min.x > dataWindow.max.x || dataWindow.min.y > dataWindow.max.y || dataWindow.min.x <= -(INT_MAX / 2) || dataWindow.min.y <= -(INT_MAX / 2) || dataWindow.max.x >= (INT_MAX / 2) || dataWindow.max.y >= (INT_MAX / 2)) { throw IEX_NAMESPACE::ArgExc ("Invalid data window in image header."); } if (maxImageWidth > 0 && maxImageWidth < (dataWindow.max.x - dataWindow.min.x + 1)) { THROW (IEX_NAMESPACE::ArgExc, "The width of the data window exceeds the " "maximum width of " << maxImageWidth << "pixels."); } if (maxImageHeight > 0 && maxImageHeight < dataWindow.max.y - dataWindow.min.y + 1) { THROW (IEX_NAMESPACE::ArgExc, "The width of the data window exceeds the " "maximum width of " << maxImageHeight << "pixels."); } // chunk table must be smaller than the maximum image area // (only reachable for unknown types or damaged files: will have thrown earlier // for regular image types) if( maxImageHeight>0 && maxImageWidth>0 && hasChunkCount() && chunkCount()>Int64(maxImageWidth)*Int64(maxImageHeight)) { THROW (IEX_NAMESPACE::ArgExc, "chunkCount exceeds maximum area of " << Int64(maxImageWidth)*Int64(maxImageHeight) << " pixels." ); } // // The pixel aspect ratio must be greater than 0. // In applications, numbers like the the display or // data window dimensions are likely to be multiplied // or divided by the pixel aspect ratio; to avoid // arithmetic exceptions, we limit the pixel aspect // ratio to a range that is smaller than theoretically // possible (real aspect ratios are likely to be close // to 1.0 anyway). // float pixelAspectRatio = this->pixelAspectRatio(); const float MIN_PIXEL_ASPECT_RATIO = 1e-6f; const float MAX_PIXEL_ASPECT_RATIO = 1e+6f; if (pixelAspectRatio < MIN_PIXEL_ASPECT_RATIO || pixelAspectRatio > MAX_PIXEL_ASPECT_RATIO) { throw IEX_NAMESPACE::ArgExc ("Invalid pixel aspect ratio in image header."); } // // The screen window width must not be less than 0. // The size of the screen window can vary over a wide // range (fish-eye lens to astronomical telescope), // so we can't limit the screen window width to a // small range. // float screenWindowWidth = this->screenWindowWidth(); if (screenWindowWidth < 0) throw IEX_NAMESPACE::ArgExc ("Invalid screen window width in image header."); // // If the file has multiple parts, verify that each header has attribute // name and type. // (TODO) We may want to check more stuff here. // if (isMultipartFile) { if (!hasName()) { throw IEX_NAMESPACE::ArgExc ("Headers in a multipart file should" " have name attribute."); } if (!hasType()) { throw IEX_NAMESPACE::ArgExc ("Headers in a multipart file should" " have type attribute."); } } const std::string & part_type=hasType() ? type() : ""; if(part_type!="" && !isSupportedType(part_type)) { // // skip remaining sanity checks with unsupported types - they may not hold // return; } // // If the file is tiled, verify that the tile description has reasonable // values and check to see if the lineOrder is one of the predefined 3. // If the file is not tiled, then the lineOrder can only be INCREASING_Y // or DECREASING_Y. // LineOrder lineOrder = this->lineOrder(); if (isTiled) { if (!hasTileDescription()) { throw IEX_NAMESPACE::ArgExc ("Tiled image has no tile " "description attribute."); } const TileDescription &tileDesc = tileDescription(); if (tileDesc.xSize <= 0 || tileDesc.ySize <= 0) throw IEX_NAMESPACE::ArgExc ("Invalid tile size in image header."); if (maxTileWidth > 0 && maxTileWidth < int(tileDesc.xSize)) { THROW (IEX_NAMESPACE::ArgExc, "The width of the tiles exceeds the maximum " "width of " << maxTileWidth << "pixels."); } if (maxTileHeight > 0 && maxTileHeight < int(tileDesc.ySize)) { THROW (IEX_NAMESPACE::ArgExc, "The width of the tiles exceeds the maximum " "width of " << maxTileHeight << "pixels."); } if (tileDesc.mode != ONE_LEVEL && tileDesc.mode != MIPMAP_LEVELS && tileDesc.mode != RIPMAP_LEVELS) throw IEX_NAMESPACE::ArgExc ("Invalid level mode in image header."); if (tileDesc.roundingMode != ROUND_UP && tileDesc.roundingMode != ROUND_DOWN) throw IEX_NAMESPACE::ArgExc ("Invalid level rounding mode in image header."); if (lineOrder != INCREASING_Y && lineOrder != DECREASING_Y && lineOrder != RANDOM_Y) throw IEX_NAMESPACE::ArgExc ("Invalid line order in image header."); } else { if (lineOrder != INCREASING_Y && lineOrder != DECREASING_Y) throw IEX_NAMESPACE::ArgExc ("Invalid line order in image header."); } // // The compression method must be one of the predefined values. // if (!isValidCompression (this->compression())) throw IEX_NAMESPACE::ArgExc ("Unknown compression type in image header."); if(isDeepData(part_type)) { if (!isValidDeepCompression (this->compression())) throw IEX_NAMESPACE::ArgExc ("Compression type in header not valid for deep data"); } // // Check the channel list: // // If the file is tiled then for each channel, the type must be one of the // predefined values, and the x and y sampling must both be 1. // // If the file is not tiled then for each channel, the type must be one // of the predefined values, the x and y coordinates of the data window's // upper left corner must be divisible by the x and y subsampling factors, // and the width and height of the data window must be divisible by the // x and y subsampling factors. // const ChannelList &channels = this->channels(); if (isTiled) { for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) { if (i.channel().type != UINT && i.channel().type != HALF && i.channel().type != FLOAT) { THROW (IEX_NAMESPACE::ArgExc, "Pixel type of \"" << i.name() << "\" " "image channel is invalid."); } if (i.channel().xSampling != 1) { THROW (IEX_NAMESPACE::ArgExc, "The x subsampling factor for the " "\"" << i.name() << "\" channel " "is not 1."); } if (i.channel().ySampling != 1) { THROW (IEX_NAMESPACE::ArgExc, "The y subsampling factor for the " "\"" << i.name() << "\" channel " "is not 1."); } } } else { for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) { if (i.channel().type != UINT && i.channel().type != HALF && i.channel().type != FLOAT) { THROW (IEX_NAMESPACE::ArgExc, "Pixel type of \"" << i.name() << "\" " "image channel is invalid."); } if (i.channel().xSampling < 1) { THROW (IEX_NAMESPACE::ArgExc, "The x subsampling factor for the " "\"" << i.name() << "\" channel " "is invalid."); } if (i.channel().ySampling < 1) { THROW (IEX_NAMESPACE::ArgExc, "The y subsampling factor for the " "\"" << i.name() << "\" channel " "is invalid."); } if (dataWindow.min.x % i.channel().xSampling) { THROW (IEX_NAMESPACE::ArgExc, "The minimum x coordinate of the " "image's data window is not a multiple " "of the x subsampling factor of " "the \"" << i.name() << "\" channel."); } if (dataWindow.min.y % i.channel().ySampling) { THROW (IEX_NAMESPACE::ArgExc, "The minimum y coordinate of the " "image's data window is not a multiple " "of the y subsampling factor of " "the \"" << i.name() << "\" channel."); } if ((dataWindow.max.x - dataWindow.min.x + 1) % i.channel().xSampling) { THROW (IEX_NAMESPACE::ArgExc, "Number of pixels per row in the " "image's data window is not a multiple " "of the x subsampling factor of " "the \"" << i.name() << "\" channel."); } if ((dataWindow.max.y - dataWindow.min.y + 1) % i.channel().ySampling) { THROW (IEX_NAMESPACE::ArgExc, "Number of pixels per column in the " "image's data window is not a multiple " "of the y subsampling factor of " "the \"" << i.name() << "\" channel."); } } } }
void ScanLineInputFile::setFrameBuffer (const FrameBuffer &frameBuffer) { Lock lock (*_streamData); const ChannelList &channels = _data->header.channels(); for (FrameBuffer::ConstIterator j = frameBuffer.begin(); j != frameBuffer.end(); ++j) { ChannelList::ConstIterator i = channels.find (j.name()); if (i == channels.end()) continue; if (i.channel().xSampling != j.slice().xSampling || i.channel().ySampling != j.slice().ySampling) THROW (IEX_NAMESPACE::ArgExc, "X and/or y subsampling factors " "of \"" << i.name() << "\" channel " "of input file \"" << fileName() << "\" are " "not compatible with the frame buffer's " "subsampling factors."); } // optimization is possible if this is a little endian system // and both inputs and outputs are half floats // bool optimizationPossible = true; if (!GLOBAL_SYSTEM_LITTLE_ENDIAN) { optimizationPossible =false; } vector<sliceOptimizationData> optData; // // Initialize the slice table for readPixels(). // vector<InSliceInfo> slices; ChannelList::ConstIterator i = channels.begin(); // current offset of channel: pixel data starts at offset*width into the // decompressed scanline buffer size_t offset = 0; for (FrameBuffer::ConstIterator j = frameBuffer.begin(); j != frameBuffer.end(); ++j) { while (i != channels.end() && strcmp (i.name(), j.name()) < 0) { // // Channel i is present in the file but not // in the frame buffer; data for channel i // will be skipped during readPixels(). // slices.push_back (InSliceInfo (i.channel().type, i.channel().type, 0, // base 0, // xStride 0, // yStride i.channel().xSampling, i.channel().ySampling, false, // fill true, // skip 0.0)); // fillValue switch(i.channel().type) { case OPENEXR_IMF_INTERNAL_NAMESPACE::HALF : offset++; break; case OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT : offset+=2; break; case OPENEXR_IMF_INTERNAL_NAMESPACE::UINT : offset+=2; break; } ++i; } bool fill = false; if (i == channels.end() || strcmp (i.name(), j.name()) > 0) { // // Channel i is present in the frame buffer, but not in the file. // In the frame buffer, slice j will be filled with a default value. // fill = true; } slices.push_back (InSliceInfo (j.slice().type, fill? j.slice().type: i.channel().type, j.slice().base, j.slice().xStride, j.slice().yStride, j.slice().xSampling, j.slice().ySampling, fill, false, // skip j.slice().fillValue)); if(!fill && i.channel().type!=OPENEXR_IMF_INTERNAL_NAMESPACE::HALF) { optimizationPossible = false; } if(j.slice().type != OPENEXR_IMF_INTERNAL_NAMESPACE::HALF) { optimizationPossible = false; } if(j.slice().xSampling!=1 || j.slice().ySampling!=1) { optimizationPossible = false; } if(optimizationPossible) { sliceOptimizationData dat; dat.base = j.slice().base; dat.fill = fill; dat.fillValue = j.slice().fillValue; dat.offset = offset; dat.xStride = j.slice().xStride; dat.yStride = j.slice().yStride; dat.xSampling = j.slice().xSampling; dat.ySampling = j.slice().ySampling; optData.push_back(dat); } if(!fill) { switch(i.channel().type) { case OPENEXR_IMF_INTERNAL_NAMESPACE::HALF : offset++; break; case OPENEXR_IMF_INTERNAL_NAMESPACE::FLOAT : offset+=2; break; case OPENEXR_IMF_INTERNAL_NAMESPACE::UINT : offset+=2; break; } } if (i != channels.end() && !fill) ++i; } if(optimizationPossible) { // // check optimisibility // based on channel ordering and fill channel positions // sort(optData.begin(),optData.end()); _data->optimizationMode = detectOptimizationMode(optData); } if(!optimizationPossible || _data->optimizationMode._optimizable==false) { optData = vector<sliceOptimizationData>(); _data->optimizationMode._optimizable=false; } // // Store the new frame buffer. // _data->frameBuffer = frameBuffer; _data->slices = slices; _data->optimizationData = optData; }
void DeepScanLineOutputFile::setFrameBuffer (const DeepFrameBuffer &frameBuffer) { Lock lock (*_data->_streamData); // // Check if the new frame buffer descriptor // is compatible with the image file header. // const ChannelList &channels = _data->header.channels(); for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) { DeepFrameBuffer::ConstIterator j = frameBuffer.find (i.name()); if (j == frameBuffer.end()) continue; if (i.channel().type != j.slice().type) { THROW (IEX_NAMESPACE::ArgExc, "Pixel type of \"" << i.name() << "\" channel " "of output file \"" << fileName() << "\" is " "not compatible with the frame buffer's " "pixel type."); } if (i.channel().xSampling != j.slice().xSampling || i.channel().ySampling != j.slice().ySampling) { THROW (IEX_NAMESPACE::ArgExc, "X and/or y subsampling factors " "of \"" << i.name() << "\" channel " "of output file \"" << fileName() << "\" are " "not compatible with the frame buffer's " "subsampling factors."); } } // // Store the pixel sample count table. // (TODO) Support for different sampling rates? // const Slice& sampleCountSlice = frameBuffer.getSampleCountSlice(); if (sampleCountSlice.base == 0) { throw IEX_NAMESPACE::ArgExc ("Invalid base pointer, please set a proper sample count slice."); } else { _data->sampleCountSliceBase = sampleCountSlice.base; _data->sampleCountXStride = sampleCountSlice.xStride; _data->sampleCountYStride = sampleCountSlice.yStride; } // // Initialize slice table for writePixels(). // Pixel sample count slice is not presented in the header, // so it wouldn't be added here. // Store the pixel base pointer table. // (TODO) Support for different sampling rates? // vector<OutSliceInfo*> slices; for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) { DeepFrameBuffer::ConstIterator j = frameBuffer.find (i.name()); if (j == frameBuffer.end()) { // // Channel i is not present in the frame buffer. // In the file, channel i will contain only zeroes. // slices.push_back (new OutSliceInfo (i.channel().type, NULL,// base 0,// sampleStride, 0,// xStride 0,// yStride i.channel().xSampling, i.channel().ySampling, true)); // zero } else { // // Channel i is present in the frame buffer. // slices.push_back (new OutSliceInfo (j.slice().type, j.slice().base, j.slice().sampleStride, j.slice().xStride, j.slice().yStride, j.slice().xSampling, j.slice().ySampling, false)); // zero } } // // Store the new frame buffer. // _data->frameBuffer = frameBuffer; for (size_t i = 0; i < _data->slices.size(); i++) delete _data->slices[i]; _data->slices = slices; }
void TiledOutputFile::setFrameBuffer (const FrameBuffer &frameBuffer) { Lock lock (*_streamData); // // Check if the new frame buffer descriptor // is compatible with the image file header. // const ChannelList &channels = _data->header.channels(); for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) { FrameBuffer::ConstIterator j = frameBuffer.find (i.name()); if (j == frameBuffer.end()) continue; if (i.channel().type != j.slice().type) THROW (IEX_NAMESPACE::ArgExc, "Pixel type of \"" << i.name() << "\" channel " "of output file \"" << fileName() << "\" is " "not compatible with the frame buffer's " "pixel type."); if (j.slice().xSampling != 1 || j.slice().ySampling != 1) THROW (IEX_NAMESPACE::ArgExc, "All channels in a tiled file must have" "sampling (1,1)."); } // // Initialize slice table for writePixels(). // vector<TOutSliceInfo> slices; for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) { FrameBuffer::ConstIterator j = frameBuffer.find (i.name()); if (j == frameBuffer.end()) { // // Channel i is not present in the frame buffer. // In the file, channel i will contain only zeroes. // slices.push_back (TOutSliceInfo (i.channel().type, 0, // base 0, // xStride, 0, // yStride, true)); // zero } else { // // Channel i is present in the frame buffer. // slices.push_back (TOutSliceInfo (j.slice().type, j.slice().base, j.slice().xStride, j.slice().yStride, false, // zero (j.slice().xTileCoords)? 1: 0, (j.slice().yTileCoords)? 1: 0)); } } // // Store the new frame buffer. // _data->frameBuffer = frameBuffer; _data->slices = slices; }
void makeMultiView (const vector <string> &viewNames, const vector <const char *> &inFileNames, const char *outFileName, Compression compression, bool verbose) { Header header; Image image; FrameBuffer outFb; // // Find the size of the dataWindow, check files // Box2i d; for (int i = 0; i < viewNames.size(); ++i) { InputFile in (inFileNames[i]); if (verbose) { cout << "reading file " << inFileNames[i] << " " "for " << viewNames[i] << " view" << endl; } if (hasMultiView (in.header())) { THROW (IEX_NAMESPACE::NoImplExc, "The image in file " << inFileNames[i] << " is already a " "multi-view image. Cannot combine multiple multi-view " "images."); } header = in.header(); if (i == 0) { d=header.dataWindow(); }else{ d.extendBy(header.dataWindow()); } } image.resize (d); header.dataWindow()=d; // blow away channels; we'll rebuild them header.channels()=ChannelList(); // // Read the input image files // for (int i = 0; i < viewNames.size(); ++i) { InputFile in (inFileNames[i]); if (verbose) { cout << "reading file " << inFileNames[i] << " " "for " << viewNames[i] << " view" << endl; } FrameBuffer inFb; for (ChannelList::ConstIterator j = in.header().channels().begin(); j != in.header().channels().end(); ++j) { const Channel &inChannel = j.channel(); string inChanName = j.name(); string outChanName = insertViewName (inChanName, viewNames, i); image.addChannel (outChanName, inChannel); image.channel(outChanName).black(); header.channels().insert (outChanName, inChannel); inFb.insert (inChanName, image.channel(outChanName).slice()); outFb.insert (outChanName, image.channel(outChanName).slice()); } in.setFrameBuffer (inFb); in.readPixels (in.header().dataWindow().min.y, in.header().dataWindow().max.y); } // // Write the output image file // { header.compression() = compression; addMultiView (header, viewNames); OutputFile out (outFileName, header); if (verbose) cout << "writing file " << outFileName << endl; out.setFrameBuffer (outFb); out.writePixels (header.dataWindow().max.y - header.dataWindow().min.y + 1); } }
void ScanLineInputFile::setFrameBuffer (const FrameBuffer &frameBuffer) { Lock lock (*_data); // // Check if the new frame buffer descriptor is // compatible with the image file header. // const ChannelList &channels = _data->header.channels(); for (FrameBuffer::ConstIterator j = frameBuffer.begin(); j != frameBuffer.end(); ++j) { ChannelList::ConstIterator i = channels.find (j.name()); if (i == channels.end()) continue; if (i.channel().xSampling != j.slice().xSampling || i.channel().ySampling != j.slice().ySampling) THROW (Iex::ArgExc, "X and/or y subsampling factors " "of \"" << i.name() << "\" channel " "of input file \"" << fileName() << "\" are " "not compatible with the frame buffer's " "subsampling factors."); } // // Initialize the slice table for readPixels(). // vector<InSliceInfo> slices; ChannelList::ConstIterator i = channels.begin(); for (FrameBuffer::ConstIterator j = frameBuffer.begin(); j != frameBuffer.end(); ++j) { while (i != channels.end() && strcmp (i.name(), j.name()) < 0) { // // Channel i is present in the file but not // in the frame buffer; data for channel i // will be skipped during readPixels(). // slices.push_back (InSliceInfo (i.channel().type, i.channel().type, 0, // base 0, // xStride 0, // yStride i.channel().xSampling, i.channel().ySampling, false, // fill true, // skip 0.0)); // fillValue ++i; } bool fill = false; if (i == channels.end() || strcmp (i.name(), j.name()) > 0) { // // Channel i is present in the frame buffer, but not in the file. // In the frame buffer, slice j will be filled with a default value. // fill = true; } slices.push_back (InSliceInfo (j.slice().type, fill? j.slice().type: i.channel().type, j.slice().base, j.slice().xStride, j.slice().yStride, j.slice().xSampling, j.slice().ySampling, fill, false, // skip j.slice().fillValue)); if (i != channels.end() && !fill) ++i; } // // Store the new frame buffer. // _data->frameBuffer = frameBuffer; _data->slices = slices; }
/* creates channels, makes a hierarchy and assigns memory to channels */ static ExrHandle *imb_exr_begin_read_mem(InputFile *file, int width, int height) { ExrLayer *lay; ExrPass *pass; ExrChannel *echan; ExrHandle *data= (ExrHandle *)IMB_exr_get_handle(); int a; char layname[EXR_TOT_MAXNAME], passname[EXR_TOT_MAXNAME]; data->ifile= file; data->width= width; data->height= height; const ChannelList &channels = data->ifile->header().channels(); for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) IMB_exr_add_channel(data, NULL, i.name(), 0, 0, NULL); /* now try to sort out how to assign memory to the channels */ /* first build hierarchical layer list */ for(echan= (ExrChannel *)data->channels.first; echan; echan= echan->next) { if( imb_exr_split_channel_name(echan, layname, passname) ) { ExrLayer *lay= imb_exr_get_layer(&data->layers, layname); ExrPass *pass= imb_exr_get_pass(&lay->passes, passname); pass->chan[pass->totchan]= echan; pass->totchan++; if(pass->totchan>=EXR_PASS_MAXCHAN) break; } } if(echan) { printf("error, too many channels in one pass: %s\n", echan->name); IMB_exr_close(data); return NULL; } /* with some heuristics, try to merge the channels in buffers */ for(lay= (ExrLayer *)data->layers.first; lay; lay= lay->next) { for(pass= (ExrPass *)lay->passes.first; pass; pass= pass->next) { if(pass->totchan) { pass->rect= (float *)MEM_mapallocN(width*height*pass->totchan*sizeof(float), "pass rect"); if(pass->totchan==1) { echan= pass->chan[0]; echan->rect= pass->rect; echan->xstride= 1; echan->ystride= width; pass->chan_id[0]= echan->chan_id; } else { char lookup[256]; memset(lookup, 0, sizeof(lookup)); /* we can have RGB(A), XYZ(W), UVA */ if(pass->totchan==3 || pass->totchan==4) { if(pass->chan[0]->chan_id=='B' || pass->chan[1]->chan_id=='B' || pass->chan[2]->chan_id=='B') { lookup[(unsigned int)'R']= 0; lookup[(unsigned int)'G']= 1; lookup[(unsigned int)'B']= 2; lookup[(unsigned int)'A']= 3; } else if(pass->chan[0]->chan_id=='Y' || pass->chan[1]->chan_id=='Y' || pass->chan[2]->chan_id=='Y') { lookup[(unsigned int)'X']= 0; lookup[(unsigned int)'Y']= 1; lookup[(unsigned int)'Z']= 2; lookup[(unsigned int)'W']= 3; } else { lookup[(unsigned int)'U']= 0; lookup[(unsigned int)'V']= 1; lookup[(unsigned int)'A']= 2; } for(a=0; a<pass->totchan; a++) { echan= pass->chan[a]; echan->rect= pass->rect + lookup[(unsigned int)echan->chan_id]; echan->xstride= pass->totchan; echan->ystride= width*pass->totchan; pass->chan_id[ (unsigned int)lookup[(unsigned int)echan->chan_id] ]= echan->chan_id; } } else { /* unknown */ for(a=0; a<pass->totchan; a++) { echan= pass->chan[a]; echan->rect= pass->rect + a; echan->xstride= pass->totchan; echan->ystride= width*pass->totchan; pass->chan_id[a]= echan->chan_id; } } } } } } return data; }
void readFrames( int argc, char* argv[] ) { pfs::DOMIO pfsio; bool verbose = false; bool keepRGB = false; // Parse command line parameters static struct option cmdLineOptions[] = { { "help", no_argument, NULL, 'h' }, { "verbose", no_argument, NULL, 'v' }, { "keep-rgb", no_argument, NULL, 'k' }, { "linear", no_argument, NULL, 'l' }, { NULL, 0, NULL, 0 } }; static const char optstring[] = "lkhv"; pfs::FrameFileIterator it( argc, argv, "rb", NULL, NULL, optstring, cmdLineOptions ); int optionIndex = 0; while( 1 ) { int c = getopt_long (argc, argv, optstring, cmdLineOptions, &optionIndex); if( c == -1 ) break; switch( c ) { case 'h': printHelp(); throw QuietException(); case 'v': verbose = true; break; case 'k': keepRGB = true; break; case 'l': std::cerr << PROG_NAME << " warning: linearize option ignored for an HDR input!" << std::endl; break; case '?': throw QuietException(); case ':': throw QuietException(); } } VERBOSE_STR << "keep RGB channels untouch: " << (keepRGB ? "yes" : "no") << std::endl; while( true ) { pfs::FrameFile ff = it.getNextFrameFile(); if( ff.fh == NULL ) break; // No more frames it.closeFrameFile( ff ); InputFile file( ff.fileName ); FrameBuffer frameBuffer; Box2i dw = file.header().displayWindow(); Box2i dtw = file.header().dataWindow(); int width = dw.max.x - dw.min.x + 1; int height = dw.max.y - dw.min.y + 1; if( dtw.min.x < dw.min.x && dtw.max.x > dw.max.x || dtw.min.y < dw.min.y && dtw.max.y > dw.max.y ) throw pfs::Exception( "No support for OpenEXR files DataWidnow greater than DisplayWindow" ); pfs::Frame *frame = pfsio.createFrame( width, height ); const ChannelList &channels = file.header().channels(); bool processColorChannels = false; pfs::Channel *X, *Y, *Z; if( !keepRGB ) { // Keep RGB channels as they are const Channel *rChannel = channels.findChannel( "R" ); const Channel *gChannel = channels.findChannel( "G" ); const Channel *bChannel = channels.findChannel( "B" ); if( rChannel!=NULL && gChannel!=NULL && bChannel!=NULL ) { frame->createXYZChannels( X, Y, Z ); frameBuffer.insert( "R", // name Slice( FLOAT, // type // (char*)(X->getRawData()), (char*)(X->getRawData()), sizeof(float), // xStride sizeof(float) * width,// yStride 1, 1, // x/y sampling 0.0)); // fillValue frameBuffer.insert( "G", // name Slice( FLOAT, // type (char*)(Y->getRawData()), sizeof(float), // xStride sizeof(float) * width,// yStride 1, 1, // x/y sampling 0.0)); // fillValue frameBuffer.insert( "B", // name Slice( FLOAT, // type (char*)(Z->getRawData()), sizeof(float), // xStride sizeof(float) * width,// yStride 1, 1, // x/y sampling 0.0)); // fillValue processColorChannels = true; } } for( ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i ) { const Channel &channel = i.channel(); if( processColorChannels ) { // Skip color channels if( !strcmp( i.name(), "R" ) || !strcmp( i.name(), "G" ) || !strcmp( i.name(), "B" ) ) continue; } const char *channelName = i.name(); if( !strcmp( channelName, "Z" ) ) { channelName = "DEPTH"; } pfs::Channel *pfsCh = frame->createChannel( channelName ); frameBuffer.insert( i.name(), // name Slice( FLOAT, // type (char *)pfsCh->getRawData(), sizeof(float), // xStride sizeof(float) * width,// yStride 1, 1, // x/y sampling 0.0)); // fillValue } // Copy attributes to tags { for( Header::ConstIterator it = file.header().begin(); it != file.header().end(); it++ ) { const char *attribName = it.name(); const char *colon = strstr( attribName, ":" ); const StringAttribute *attrib = file.header().findTypedAttribute<StringAttribute>(attribName); if( attrib == NULL ) continue; // Skip if type is not String // fprintf( stderr, "Tag: %s = %s\n", attribName, attrib->value().c_str() ); if( colon == NULL ) { // frame tag frame->getTags()->setString( attribName, escapeString(attrib->value()).c_str() ); } else { // channel tag string channelName = string( attribName, colon-attribName ); pfs::Channel *ch = frame->getChannel( channelName.c_str() ); if( ch == NULL ) { fprintf( stderr, PROG_NAME ": Warning! Can not set tag for '%s' channel because it does not exist\n", channelName.c_str() ); continue; } ch->getTags()->setString( colon+1, escapeString( attrib->value() ).c_str() ); } } } file.setFrameBuffer( frameBuffer ); file.readPixels( dw.min.y, dw.max.y ); VERBOSE_STR << "reading file (linear) '" << ff.fileName << "'" << std::endl; if( processColorChannels ) { // Rescale values if WhiteLuminance is present if( hasWhiteLuminance( file.header() ) ) { float scaleFactor = whiteLuminance( file.header() ); int pixelCount = frame->getHeight()*frame->getWidth(); for( int i = 0; i < pixelCount; i++ ) { (*X)(i) *= scaleFactor; (*Y)(i) *= scaleFactor; (*Z)(i) *= scaleFactor; } const StringAttribute *relativeLum = file.header().findTypedAttribute<StringAttribute>("RELATIVE_LUMINANCE"); const char *luminanceTag = frame->getTags()->getString( "LUMINANCE" ); if( luminanceTag == NULL ) frame->getTags()->setString( "LUMINANCE", "ABSOLUTE" ); } pfs::transformColorSpace( pfs::CS_RGB, X, Y, Z, pfs::CS_XYZ, X, Y, Z ); } frame->getTags()->setString( "FILE_NAME", ff.fileName ); pfsio.writeFrame( frame, stdout ); pfsio.freeFrame( frame ); } }