/////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2012, Industrial Light & Magic, a division of Lucas // Digital Ltd. LLC // Portions contributed and copyright held by others as indicated. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Industrial Light & Magic nor the names of // its contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // /////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- // // Utility program to combine or separate multipart image files // //----------------------------------------------------------------------------- #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // pair #include #include #include #include #include using std::cerr; using std::cout; using std::endl; using std::vector; using std::pair; using std::set; using std::ostringstream; using std::min; using std::max; using std::string; using std::make_pair; using IMATH_NAMESPACE::Box2i; using namespace OPENEXR_IMF_NAMESPACE; #if defined(ANDROID) || defined(__ANDROID_API__) #define IMF_PATH_SEPARATOR "/" #elif defined(_WIN32) || defined(_WIN64) || defined(__MWERKS__) || defined(PLATFORM_WINDOWS) #define IMF_PATH_SEPARATOR "\\" #else #define IMF_PATH_SEPARATOR "/" #endif void copy_tile (MultiPartInputFile & input, MultiPartOutputFile & output, int inPart, int outPart) { TiledInputPart in (input, inPart); TiledOutputPart out (output, outPart); out.copyPixels (in); } void copy_tiledeep (MultiPartInputFile & input, MultiPartOutputFile & output, int inPart, int outPart) { DeepTiledInputPart in (input, inPart); DeepTiledOutputPart out (output, outPart); out.copyPixels (in); } void copy_scanline (MultiPartInputFile & input, MultiPartOutputFile & output, int inPart, int outPart) { InputPart in (input, inPart); OutputPart out (output, outPart); out.copyPixels (in); } void copy_scanlinedeep (MultiPartInputFile & input, MultiPartOutputFile & output, int inPart, int outPart) { DeepScanLineInputPart in (input, inPart); DeepScanLineOutputPart out (output, outPart); out.copyPixels (in); } bool is_number(const std::string& s) { std::string::const_iterator it = s.begin(); while (it != s.end() && std::isdigit(*it)) ++it; return !s.empty() && it == s.end(); } void parse_partname (string & part_name) { // strip off a path delimitation size_t posSlash = part_name.rfind(IMF_PATH_SEPARATOR); if (posSlash != string::npos) part_name = part_name.substr(posSlash+1); // strip the exr ext size_t pos = part_name.rfind(".exr"); if (pos != string::npos) { part_name = part_name.substr(0, pos); // strip off the frame number size_t pos2 = part_name.rfind("."); if (pos2 != string::npos) { string frame = part_name.substr(pos2+1, pos); if (is_number(frame)) part_name = part_name.substr(0, pos2); } } } /// /// If input is <...>::: then extract part number, /// Else, use all parts /// void parse_filename (string & file_name, string & part_name, bool & force_part_name, int & part_num) { force_part_name = false; part_name = file_name; // default is the file_name size_t doublecolon = file_name.rfind ("::"); if (doublecolon != string::npos) { part_name = file_name.substr (doublecolon+2); file_name = file_name.substr (0, doublecolon); force_part_name = true; } else { parse_partname(part_name); } size_t colon = file_name.rfind (':'); if (colon == string::npos) { part_num = -1; // use all parts } else { string num = file_name.substr (colon + 1); if (is_number(num)) { part_num = atoi (num.c_str()); file_name = file_name.substr (0, colon); } else { cerr <<"\n" << "ERROR: part number must be a number" << endl; exit(1); } } } void make_unique_names (vector
& headers) { set names; for ( size_t i = 0 ; i < headers.size() ; i++ ) { Header & h = headers[i]; std::string base_name; // if no name at all, set it to (first part is part 1) if (!h.hasName()) { // We should not be here, we have populated all headers with names. cerr << "\n" << "Software Error: header does not have a valid name" << ":" << __LINE__ << endl; exit (1); } else { base_name = h.name(); } // check name has already been used, if so add a _ to it if (names.find (base_name) != names.end()) { ostringstream s; size_t backup=1; do { s.clear(); s << h.name() << "_" << i << "_" << backup; backup++; } while (names.find(s.str()) != names.end()); h.setName (s.str()); } names.insert (h.name()); } } void filename_check (vector names, const char* aname) { string bname(aname); for (size_t i = 0; i < names.size(); i++) { if (bname.compare (names[i]) == 0) { cerr << "\n" << "ERROR: " "input and output file names cannot be the same." << endl; exit (1); } } } void convert(vector in, vector views, const char* outname, bool override) { if(in.size()!=1) { cerr <<"\n" << "ERROR: " "can only convert one file at once - use 'combine' mode for multiple files" << endl; exit(1); } try { MultiPartInputFile infile(in[0]); if(infile.parts() != 1) { cerr <<"\n" << "ERROR: " "can only convert single part EXRs to multipart EXR-2.0 files: use 'split' mode instead" << endl; exit(1); } vector input_channels; string hero; if(hasMultiView(infile.header(0))) { StringVector h = multiView (infile.header(0)); if(h.size()>0) { hero=h[0]; } } // retrieve channel names from input file in view-friendly format GetChannelsInMultiPartFile(infile,input_channels); vector< MultiViewChannelName > output_channels = input_channels; // remap channels to multiple output parts int parts = SplitChannels(output_channels.begin(),output_channels.end(),true,hero); vector
output_headers(parts); vector output_framebuffers(parts); FrameBuffer input_framebuffer; // // make all output headers the same as the input header but // with no channels // for(int i=0;i > channelstore(channel_count); // // insert channels into correct header and framebuffers // for( size_t i=0 ; i in, vector views, const char* outname, bool override) { size_t numInputs = in.size(); int numparts; vector partnums; vector inputs; vector fordelete; MultiPartInputFile *infile; vector
headers; vector fornamecheck; // // parse all inputs // // Input format : // We support the following syntax for each input // [:][::] for (size_t i = 0 ; i < numInputs; i++) { string filename (in[i]); string partname; bool forcepartname; int partnum; parse_filename (filename, partname, forcepartname, partnum); if (partnum == -1) { fornamecheck.push_back (filename); try { infile = new MultiPartInputFile (filename.c_str()); fordelete.push_back (infile); numparts = infile->parts(); //copy header from all parts of input to our header array for (int j = 0; j < numparts; j++) { inputs.push_back (infile); Header h = infile->header(j); if (!h.hasName() || forcepartname) h.setName (partname); headers.push_back (h); if( views[i] != NULL ) headers[headers.size()-1].setView( views[i] ); partnums.push_back (j); } } catch (IEX_NAMESPACE::BaseExc &e) { cerr << "\n" << "ERROR:" << endl; cerr << e.what() << endl; exit (1); } } // no user parts specified else { fornamecheck.push_back (filename); try { infile = new MultiPartInputFile (filename.c_str()); fordelete.push_back (infile); if (partnum >= infile->parts()) { cerr << "ERROR: you asked for part " << partnum << " in " << in[i]; cerr << ", which only has " << infile->parts() << " parts\n"; exit (1); } //copy header from required part of input to our header array inputs.push_back (infile); Header h = infile->header(partnum); if (!h.hasName() || forcepartname) h.setName (partname); headers.push_back (h); if( views[i] != NULL ) headers[headers.size()-1].setView( views[i] ); partnums.push_back (partnum); } catch (IEX_NAMESPACE::BaseExc &e) { cerr << "\n" << "ERROR:" << endl; cerr << e.what()<< endl; exit (1); } } // user parts specified } filename_check (fornamecheck, outname); // // sort out names - make unique // if (numInputs>1) { make_unique_names (headers); } // // do combine // // early bail if need be try { MultiPartOutputFile temp (outname, &headers[0], headers.size(), override); } catch (IEX_NAMESPACE::BaseExc &e) { cerr << "\n" << "ERROR: " << e.what() << endl; exit (1); } MultiPartOutputFile out (outname, &headers[0], headers.size(), override); for (size_t p = 0 ; p < partnums.size();p++) { std::string type = headers[p].type(); if (type == SCANLINEIMAGE) { cout << "part " << p << ": "<< "scanlineimage" << endl; copy_scanline (*inputs[p], out, partnums[p], p); } else if (type == TILEDIMAGE) { cout << "part " << p << ": "<< "tiledimage" << endl; copy_tile (*inputs[p], out, partnums[p], p); } else if (type == DEEPSCANLINE) { cout << "part " << p << ": "<< "deepscanlineimage" << endl; copy_scanlinedeep (*inputs[p], out, partnums[p], p); } else if (type == DEEPTILE) { cout << "part " << p << ": "<< "deeptile" << endl; copy_tiledeep (*inputs[p], out, partnums[p], p); } } for (size_t k = 0; k < fordelete.size(); k++) { delete fordelete[k]; } inputs.clear(); fordelete.size(); cout << "\n" << "Combine Success" << endl; } void separate (vector in, const char* out, bool override) { if (in.size() > 1) { cerr << "ERROR: -separate only take one input file\n" "syntax: exrmultipart -separate -i infile.exr -o outfileBaseName\n"; exit (1); } // // parse the multipart input // string filename (in[0]); MultiPartInputFile *inputimage; int numOutputs; vector fornamecheck; // add check for existance of the file try { MultiPartInputFile temp (filename.c_str()); } catch (IEX_NAMESPACE::BaseExc &e) { cerr << "\n" << "ERROR: " << e.what() << endl; exit (1); } inputimage = new MultiPartInputFile (filename.c_str()); numOutputs = inputimage->parts(); cout << "numOutputs: " << numOutputs << endl; // // set outputs names // for (int p = 0 ; p header (p); MultiPartOutputFile out (fornamecheck[p].c_str(), &header, 1, override); std::string type = header.type(); if (type == "scanlineimage") { cout << "scanlineimage" << endl; copy_scanline (*inputimage, out, p, 0); } else if (type == "tiledimage") { cout << "tiledimage" << endl; copy_tile (*inputimage, out, p, 0); } else if (type == "deepscanline") { cout << "deepscanline" << endl; copy_scanlinedeep (*inputimage, out, p, 0); } else if (type == "deeptile") { cout << "deeptile" << endl; copy_tiledeep (*inputimage, out, p, 0); } } delete inputimage; cout << "\n" << "Seperate Success" << endl; } void usageMessage (const char argv[]) { cout << argv << " handles the combining and splitting of multipart data\n"; cout << "\n" << "Usage: " "exrmultipart -combine -i input.exr[:partnum][::partname] " "[input2.exr[:partnum]][::partname] [...] -o outfile.exr [options]\n"; cout << " or: exrmultipart -separate -i infile.exr -o outfileBaseName " "[options]\n"; cout << " or: exrmultipart -convert -i infile.exr -o outfile.exr " "[options]\n"; cout << "\n" << "Options:\n"; cout << "-override [0/1] 0-do not override conflicting shared " "attributes [default]\n" " 1-override conflicting shared attributes\n"; cout << "-view name (after specifying -i) " "assign following inputs to view 'name'\n"; exit (1); } int main (int argc, char * argv[]) { if (argc < 6) { usageMessage (argv[0]); } vector inFiles; vector views; const char* view = 0; const char *outFile = 0; bool override = false; int i = 1; int mode = 0; // 0-do not read input, 1-infiles, 2-outfile, 3-override, 4-view while (i < argc) { if (!strcmp (argv[i], "-h")) { usageMessage (argv[0]); } if (!strcmp (argv[i], "-i")) { mode = 1; } else if (!strcmp (argv[i], "-o")) { mode = 2; } else if (!strcmp (argv[i], "-override")) { mode = 3; } else if (!strcmp (argv[i], "-view")) { if(mode !=1 ) { usageMessage (argv[0]); return 1; } mode = 4; } else { switch (mode) { case 1: inFiles.push_back (argv[i]); views.push_back (view); break; case 2: outFile = argv[i]; break; case 3: override = atoi (argv[i]); break; case 4: view = argv[i]; mode=1; break; } } i++; } // check input and output files found or not if (inFiles.size() == 0) { cerr << "\n" << "ERROR: found no input files" << endl; exit (1); } cout << "input:" << endl; for (size_t i = 0; i < inFiles.size(); i++) { cout << " " << inFiles[i]; if(views[i]) cout << " in view " << views[i]; cout << endl; } if (!outFile) { cerr << "\n"<<"ERROR: found no output file" << endl; exit (1); } cout << "output:\n " << outFile << endl; cout << "override:" << override << "\n" << endl; if (!strcmp (argv[1], "-combine")) { cout << "-combine multipart input " << endl; combine (inFiles, views, outFile, override); } else if (!strcmp(argv[1], "-separate")) { cout << "-separate multipart input " << endl; separate (inFiles, outFile, override); } else if(!strcmp(argv[1],"-convert")) { cout << "-convert input to EXR2 multipart" << endl; convert (inFiles, views, outFile, override); } else { usageMessage (argv[0]); } return 0; }