PageRenderTime 45ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/OpenEXR/exrmultipart/exrmultipart.cpp

https://github.com/meshula/openexr
C++ | 692 lines | 515 code | 91 blank | 86 comment | 79 complexity | 67eb617082e450dd6adb5898d792c268 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, BSD-3-Clause
  1. ///////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 2012, Industrial Light & Magic, a division of Lucas
  4. // Digital Ltd. LLC
  5. // Portions contributed and copyright held by others as indicated.
  6. // All rights reserved.
  7. //
  8. // Redistribution and use in source and binary forms, with or without
  9. // modification, are permitted provided that the following conditions are
  10. // met:
  11. // * Redistributions of source code must retain the above copyright
  12. // notice, this list of conditions and the following disclaimer.
  13. // * Redistributions in binary form must reproduce the above
  14. // copyright notice, this list of conditions and the following disclaimer
  15. // in the documentation and/or other materials provided with the
  16. // distribution.
  17. // * Neither the name of Industrial Light & Magic nor the names of
  18. // its contributors may be used to endorse or promote products derived
  19. // from this software without specific prior written permission.
  20. //
  21. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  24. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  25. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  26. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  27. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  28. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  29. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  30. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  31. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32. //
  33. ///////////////////////////////////////////////////////////////////////////
  34. //-----------------------------------------------------------------------------
  35. //
  36. // Utility program to combine or separate multipart image files
  37. //
  38. //-----------------------------------------------------------------------------
  39. #include <ImfMultiPartOutputFile.h>
  40. #include <ImfMultiPartInputFile.h>
  41. #include <ImfStringAttribute.h>
  42. #include <ImfChannelList.h>
  43. #include <ImfTiledInputPart.h>
  44. #include <ImfTiledOutputPart.h>
  45. #include <ImfInputPart.h>
  46. #include <ImfOutputPart.h>
  47. #include <ImfDeepScanLineInputPart.h>
  48. #include <ImfDeepScanLineOutputPart.h>
  49. #include <ImfDeepTiledInputPart.h>
  50. #include <ImfDeepTiledOutputPart.h>
  51. #include <ImfPartHelper.h>
  52. #include <ImfPartType.h>
  53. #include <OpenEXRConfig.h>
  54. #include <Iex.h>
  55. #include <iostream>
  56. #include <vector>
  57. #include <stdlib.h>
  58. #include <sstream>
  59. #include <assert.h>
  60. using std::cerr;
  61. using std::cout;
  62. using std::endl;
  63. using std::vector;
  64. using std::set;
  65. using std::ostringstream;
  66. using std::min;
  67. using std::max;
  68. using std::string;
  69. using IMATH_NAMESPACE::Box2i;
  70. using namespace OPENEXR_IMF_NAMESPACE;
  71. void
  72. copy_tile (MultiPartInputFile & input,
  73. MultiPartOutputFile & output,
  74. int inPart, int outPart)
  75. {
  76. TiledInputPart in (input, inPart);
  77. TiledOutputPart out (output, outPart);
  78. out.copyPixels (in);
  79. }
  80. void
  81. copy_tiledeep (MultiPartInputFile & input,
  82. MultiPartOutputFile & output,
  83. int inPart, int outPart)
  84. {
  85. DeepTiledInputPart in (input, inPart);
  86. DeepTiledOutputPart out (output, outPart);
  87. out.copyPixels (in);
  88. }
  89. void
  90. copy_scanline (MultiPartInputFile & input,
  91. MultiPartOutputFile & output,
  92. int inPart, int outPart)
  93. {
  94. InputPart in (input, inPart);
  95. OutputPart out (output, outPart);
  96. out.copyPixels (in);
  97. }
  98. void
  99. copy_scanlinedeep (MultiPartInputFile & input,
  100. MultiPartOutputFile & output,
  101. int inPart, int outPart)
  102. {
  103. DeepScanLineInputPart in (input, inPart);
  104. DeepScanLineOutputPart out (output, outPart);
  105. out.copyPixels (in);
  106. }
  107. void
  108. make_unique_names (vector<Header> & headers)
  109. {
  110. set<string> names;
  111. for ( size_t i = 0 ; i < headers.size() ; i++ )
  112. {
  113. std::string base_name;
  114. // if no name at all, set it to <type><partnum> (first part is part 1)
  115. if (!headers[i].hasName())
  116. {
  117. ostringstream s;
  118. s << headers[i].type() << (i + 1);
  119. base_name = s.str();
  120. headers[i].setName (base_name);
  121. }
  122. else
  123. {
  124. base_name = headers[i].name();
  125. }
  126. // check name has already been used, if so add a _<number> to it
  127. if (names.find (base_name) == names.end())
  128. {
  129. ostringstream s;
  130. size_t backup=1;
  131. do
  132. {
  133. s.clear();
  134. s << headers[i].type() << i << "_" << backup;
  135. backup++;
  136. }
  137. while (names.find(s.str()) != names.end());
  138. headers[i].setName (s.str());
  139. }
  140. }
  141. }
  142. void
  143. filename_check (vector <string> names, const char* aname)
  144. {
  145. string bname(aname);
  146. for (size_t i = 0; i < names.size(); i++)
  147. {
  148. if (bname.compare (names[i]) == 0)
  149. {
  150. cerr << "\n" << "ERROR: "
  151. "input and output file names cannot be the same." << endl;
  152. exit (1);
  153. }
  154. }
  155. }
  156. void
  157. convert(vector <const char*> in, vector<const char *> views,const char* outname, bool override)
  158. {
  159. if(in.size()!=1)
  160. {
  161. cerr <<"\n" << "ERROR: "
  162. "can only convert one file at once - use 'combine' mode for multiple files" << endl;
  163. exit(1);
  164. }
  165. try
  166. {
  167. MultiPartInputFile infile(in[0]);
  168. if(infile.parts()!=1)
  169. {
  170. cerr <<"\n" << "ERROR: "
  171. "can only convert single part EXRs to multipart EXR-2.0 files: use 'split' mode instead" << endl;
  172. exit(1);
  173. }
  174. vector<MultiViewChannelName> input_channels;
  175. string hero;
  176. if(hasMultiView(infile.header(0)))
  177. {
  178. StringVector h = multiView(infile.header(0));
  179. if(h.size()>0)
  180. {
  181. hero=h[0];
  182. }
  183. }
  184. // retrieve channel names from input file in view-friendly format
  185. GetChannelsInMultiPartFile(infile,input_channels);
  186. vector< MultiViewChannelName > output_channels = input_channels;
  187. // remap channels to multiple output parts
  188. int parts = SplitChannels(output_channels.begin(),output_channels.end(),true,hero);
  189. vector<Header> output_headers(parts);
  190. vector<FrameBuffer> output_framebuffers(parts);
  191. FrameBuffer input_framebuffer;
  192. //
  193. // make all output headers the same as the input header but
  194. // with no channels
  195. //
  196. for(int i=0;i<parts;i++)
  197. {
  198. output_headers[i]=infile.header(0);
  199. if(hasMultiView(output_headers[i]))
  200. {
  201. output_headers[i].erase("multiView");
  202. }
  203. output_headers[i].channels()=ChannelList();
  204. }
  205. make_unique_names(output_headers);
  206. const ChannelList & in_chanlist = infile.header(0).channels();
  207. int channel_count = 0;
  208. for(ChannelList::ConstIterator i=in_chanlist.begin();i!=in_chanlist.end();++i)
  209. {
  210. ++channel_count;
  211. }
  212. Box2i dataWindow = infile.header(0).dataWindow();
  213. int pixel_count = (dataWindow.size().y+1)*(dataWindow.size().x+1);
  214. int pixel_width = dataWindow.size().x+1;
  215. // offset in pixels between base of array and 0,0
  216. int pixel_base = dataWindow.min.y*pixel_width+dataWindow.min.x;
  217. vector< vector<char> > channelstore(channel_count);
  218. //
  219. // insert channels into correct header and framebuffers
  220. //
  221. for( size_t i=0 ; i<input_channels.size() ; i++ )
  222. {
  223. // read the part we should be writing channel into, insert into header
  224. int part = output_channels[i].part_number;
  225. ChannelList::ConstIterator chan = in_chanlist.find(input_channels[i].internal_name);
  226. output_headers[part].channels().insert(output_channels[i].name,chan.channel());
  227. if( output_channels[i].view!="" )
  228. {
  229. output_headers[part].setView( output_channels[i].view );
  230. }
  231. // compute size of channel
  232. size_t samplesize=sizeof(float);
  233. if(chan.channel().type==HALF)
  234. {
  235. samplesize=sizeof(half);
  236. }
  237. channelstore[i].resize(samplesize*pixel_count);
  238. output_framebuffers[part].insert(output_channels[i].name,
  239. Slice(chan.channel().type,&channelstore[i][0]-pixel_base*samplesize,
  240. samplesize,pixel_width*samplesize));
  241. input_framebuffer.insert(input_channels[i].internal_name,
  242. Slice(chan.channel().type,&channelstore[i][0]-pixel_base*samplesize,
  243. samplesize,pixel_width*samplesize));
  244. }
  245. //
  246. // create output file
  247. //
  248. MultiPartOutputFile outfile(outname,&output_headers[0],output_headers.size());
  249. InputPart inpart(infile,0);
  250. //
  251. // read file
  252. //
  253. inpart.setFrameBuffer(input_framebuffer);
  254. inpart.readPixels(dataWindow.min.y,dataWindow.max.y);
  255. //
  256. // write each part
  257. //
  258. for(size_t i=0;i<output_framebuffers.size();i++)
  259. {
  260. OutputPart outpart(outfile,i);
  261. outpart.setFrameBuffer(output_framebuffers[i]);
  262. outpart.writePixels(dataWindow.max.y+1-dataWindow.min.y);
  263. }
  264. }
  265. catch (IEX_NAMESPACE::BaseExc &e)
  266. {
  267. cerr << "\n" << "ERROR:" << endl;
  268. cerr << e.what() << endl;
  269. exit (1);
  270. }
  271. }
  272. void
  273. combine (vector <const char*> in, vector<const char *> views,const char* outname, bool override)
  274. {
  275. size_t numInputs = in.size();
  276. int numparts;
  277. vector<int> partnums;
  278. vector<MultiPartInputFile *> inputs;
  279. vector<MultiPartInputFile *> fordelete;
  280. MultiPartInputFile *infile;
  281. vector<Header> headers;
  282. vector<string> fornamecheck;
  283. //
  284. // parse all inputs
  285. //
  286. for (size_t i = 0 ; i < numInputs; i++)
  287. {
  288. // if input is <file>:<partnum> then extract part number,
  289. // else get all parts
  290. string filename (in[i]);
  291. size_t colon = filename.rfind (':');
  292. if (colon == string::npos)
  293. {
  294. fornamecheck.push_back (filename);
  295. try
  296. {
  297. infile = new MultiPartInputFile (filename.c_str());
  298. fordelete.push_back (infile);
  299. numparts = infile->parts();
  300. //copy header from all parts of input to our header array
  301. for (int j = 0; j < numparts; j++)
  302. {
  303. inputs.push_back (infile);
  304. headers.push_back (infile->header(j));
  305. if( views[i] != NULL )
  306. {
  307. headers[headers.size()-1].setView( views[i] );
  308. }
  309. partnums.push_back (j);
  310. }
  311. }
  312. catch (IEX_NAMESPACE::BaseExc &e)
  313. {
  314. cerr << "\n" << "ERROR:" << endl;
  315. cerr << e.what() << endl;
  316. exit (1);
  317. }
  318. }
  319. else
  320. {
  321. string num = filename.substr (colon + 1);
  322. numparts = atoi (num.c_str());
  323. filename = filename.substr (0, colon);
  324. fornamecheck.push_back (filename);
  325. try
  326. {
  327. infile = new MultiPartInputFile (filename.c_str());
  328. fordelete.push_back (infile);
  329. if (numparts >= infile->parts())
  330. {
  331. cerr << "ERROR: you asked for part " << numparts << " in " << in[i];
  332. cerr << ", which only has " << infile->parts() << " parts\n";
  333. exit (1);
  334. }
  335. //copy header from required part of input to our header array
  336. inputs.push_back (infile);
  337. headers.push_back (infile->header(numparts));
  338. if( views[i] != NULL )
  339. {
  340. headers[headers.size()-1].setView( views[i] );
  341. }
  342. partnums.push_back (numparts);
  343. }
  344. catch (IEX_NAMESPACE::BaseExc &e)
  345. {
  346. cerr << "\n" << "ERROR:" << endl;
  347. cerr << e.what()<< endl;
  348. exit (1);
  349. }
  350. }
  351. }
  352. filename_check (fornamecheck, outname);
  353. //
  354. // sort out names - make unique
  355. //
  356. if (numInputs>1)
  357. {
  358. make_unique_names (headers);
  359. }
  360. //
  361. // do combine
  362. //
  363. try
  364. {
  365. MultiPartOutputFile temp (outname, &headers[0],
  366. headers.size(), override);
  367. }
  368. catch (IEX_NAMESPACE::BaseExc &e)
  369. {
  370. cerr << "\n" << "ERROR: " << e.what() << endl;
  371. exit (1);
  372. }
  373. MultiPartOutputFile out (outname, &headers[0], headers.size(), override);
  374. for (size_t p = 0 ; p < partnums.size();p++)
  375. {
  376. Header header = headers[p];
  377. std::string type = header.type();
  378. if (type == SCANLINEIMAGE)
  379. {
  380. cout << "part " << p << ": "<< "scanlineimage" << endl;
  381. copy_scanline (*inputs[p], out, partnums[p], p);
  382. }
  383. else if (type == TILEDIMAGE)
  384. {
  385. cout << "part " << p << ": "<< "tiledimage" << endl;
  386. copy_tile (*inputs[p], out, partnums[p], p);
  387. }
  388. else if (type == DEEPSCANLINE)
  389. {
  390. cout << "part " << p << ": "<< "deepscanlineimage" << endl;
  391. copy_scanlinedeep (*inputs[p], out, partnums[p], p);
  392. }
  393. else if (type == DEEPTILE)
  394. {
  395. cout << "part " << p << ": "<< "deeptile" << endl;
  396. copy_tiledeep (*inputs[p], out, partnums[p], p);
  397. }
  398. }
  399. for (size_t k = 0; k < fordelete.size(); k++) {
  400. delete fordelete[k];
  401. }
  402. inputs.clear();
  403. fordelete.size();
  404. cout << "\n" << "Combine Success" << endl;
  405. }
  406. void
  407. separate (vector <const char*> in, const char* out, bool override)
  408. {
  409. if (in.size() > 1)
  410. {
  411. cerr << "ERROR: -separate only take one input file\n"
  412. "syntax: exrmultipart -separate -i infile.exr -o outfileBaseName\n";
  413. exit (1);
  414. }
  415. //
  416. // parse the multipart input
  417. //
  418. string filename (in[0]);
  419. MultiPartInputFile *inputimage;
  420. int numOutputs;
  421. vector<string> fornamecheck;
  422. // add check for existance of the file
  423. try
  424. {
  425. MultiPartInputFile temp (filename.c_str());
  426. }
  427. catch (IEX_NAMESPACE::BaseExc &e)
  428. {
  429. cerr << "\n" << "ERROR: " << e.what() << endl;
  430. exit (1);
  431. }
  432. inputimage = new MultiPartInputFile (filename.c_str());
  433. numOutputs = inputimage->parts();
  434. cout << "numOutputs: " << numOutputs << endl;
  435. //
  436. // set outputs names
  437. //
  438. for (int p = 0 ; p <numOutputs;p++)
  439. {
  440. string outfilename (out);
  441. //add number to outfilename
  442. std::ostringstream oss;
  443. oss << '.' << p + 1;
  444. outfilename += oss.str();
  445. outfilename += ".exr";
  446. cout << "outputfilename: " << outfilename << endl;
  447. fornamecheck.push_back (outfilename);
  448. }
  449. filename_check (fornamecheck, in[0]);
  450. //
  451. // separate outputs
  452. //
  453. for (int p = 0 ; p < numOutputs; p++)
  454. {
  455. Header header = inputimage->header (p);
  456. MultiPartOutputFile out (fornamecheck[p].c_str(), &header, 1, override);
  457. std::string type = header.type();
  458. if (type == "scanlineimage")
  459. {
  460. cout << "scanlineimage" << endl;
  461. copy_scanline (*inputimage, out, p, 0);
  462. }
  463. else if (type == "tiledimage")
  464. {
  465. cout << "tiledimage" << endl;
  466. copy_tile (*inputimage, out, p, 0);
  467. }
  468. else if (type == "deepscanline")
  469. {
  470. cout << "deepscanline" << endl;
  471. copy_scanlinedeep (*inputimage, out, p, 0);
  472. }
  473. else if (type == "deeptile")
  474. {
  475. cout << "deeptile" << endl;
  476. copy_tiledeep (*inputimage, out, p, 0);
  477. }
  478. }
  479. delete inputimage;
  480. cout << "\n" << "Seperate Success" << endl;
  481. }
  482. void
  483. usageMessage (const char argv[])
  484. {
  485. cerr << argv << " handles the combining and splitting of multipart data\n";
  486. cerr << "\n" << "Usage: "
  487. "exrmultipart -combine -i input.exr[:partnum] "
  488. "[input2.exr[:partnum]] [...] -o outfile.exr [options]\n";
  489. cerr << " or: exrmultipart -separate -i infile.exr -o outfileBaseName "
  490. "[options]\n";
  491. cerr << " or: exrmultipart -convert -i infile.exr -o outfile.exr "
  492. "[options]\n";
  493. cerr << "\n" << "Options:\n";
  494. cerr << "-override [0/1] 0-do not override conflicting shared "
  495. "attributes [default]\n"
  496. " 1-override conflicting shared attributes\n";
  497. cerr << "-view name (after specifying -i) "
  498. "assign following inputs to view 'name'\n";
  499. exit (1);
  500. }
  501. int
  502. main (int argc, char * argv[])
  503. {
  504. if (argc < 6)
  505. {
  506. usageMessage (argv[0]);
  507. }
  508. vector <const char*> inFiles;
  509. vector <const char*> views;
  510. const char* view = 0;
  511. const char *outFile = 0;
  512. bool override = false;
  513. int i = 1;
  514. int mode = 0; // 0-do not read input, 1-infiles, 2-outfile, 3-override, 4-view
  515. while (i < argc)
  516. {
  517. if (!strcmp (argv[i], "-h"))
  518. {
  519. usageMessage (argv[0]);
  520. }
  521. if (!strcmp (argv[i], "-i"))
  522. {
  523. mode = 1;
  524. }
  525. else if (!strcmp (argv[i], "-o"))
  526. {
  527. mode = 2;
  528. }
  529. else if (!strcmp (argv[i], "-override"))
  530. {
  531. mode = 3;
  532. }
  533. else if (!strcmp (argv[i], "-view"))
  534. {
  535. if(mode !=1 )
  536. {
  537. usageMessage (argv[0]);
  538. return 1;
  539. }
  540. mode = 4;
  541. }
  542. else
  543. {
  544. switch (mode)
  545. {
  546. case 1:
  547. inFiles.push_back (argv[i]);
  548. views.push_back (view);
  549. break;
  550. case 2: outFile = argv[i];
  551. break;
  552. case 3: override = atoi (argv[i]);
  553. break;
  554. case 4: view = argv[i];
  555. mode=1;
  556. break;
  557. }
  558. }
  559. i++;
  560. }
  561. // check input and output files found or not
  562. if (inFiles.size() == 0)
  563. {
  564. cerr << "\n" << "ERROR: found no input files" << endl;
  565. exit (1);
  566. }
  567. cout << "input:" << endl;
  568. for (size_t i = 0; i < inFiles.size(); i++)
  569. {
  570. cout << " " << inFiles[i];
  571. if(views[i]) cout << " in view " << views[i];
  572. cout << endl;
  573. }
  574. if (!outFile)
  575. {
  576. cerr << "\n"<<"ERROR: found no output file" << endl;
  577. exit (1);
  578. }
  579. cout << "output:\n " << outFile << endl;
  580. cout << "override:" << override << "\n" << endl;
  581. if (!strcmp (argv[1], "-combine"))
  582. {
  583. cout << "-combine multipart input " << endl;
  584. combine (inFiles, views, outFile, override);
  585. }
  586. else if (!strcmp(argv[1], "-separate"))
  587. {
  588. cout << "-separate multipart input " << endl;
  589. separate (inFiles, outFile, override);
  590. }
  591. else if(!strcmp(argv[1],"-convert"))
  592. {
  593. cout << "-convert input to EXR2 multipart" << endl;
  594. convert (inFiles, views, outFile, override);
  595. }
  596. else
  597. {
  598. usageMessage (argv[0]);
  599. }
  600. return 0;
  601. }