🛠️🐜 Antkeeper superbuild with dependencies included https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

199 lines
5.2 KiB

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <vector>
  4. #include "tinyexr.h"
  5. #define TINY_DNG_WRITER_IMPLEMENTATION
  6. #include "tiny_dng_writer.h"
  7. static bool Create32bitFpTiff(
  8. const float *data, // [width x height x in_channels]
  9. const size_t width,
  10. const size_t height,
  11. const size_t in_channels,
  12. const size_t channels,
  13. tinydngwriter::DNGImage *dng_image) {
  14. if (in_channels < 1) return false;
  15. unsigned int image_width = uint32_t(width);
  16. unsigned int image_height = uint32_t(height);
  17. //dng_image->SetSubfileType(false, false, false);
  18. dng_image->SetImageWidth(image_width);
  19. dng_image->SetImageLength(image_height);
  20. dng_image->SetRowsPerStrip(image_height);
  21. dng_image->SetSamplesPerPixel(uint16_t(channels));
  22. std::vector<uint16_t> bps(channels);
  23. for (size_t i = 0; i < bps.size(); i++) {
  24. bps[i] = 32;
  25. }
  26. dng_image->SetBitsPerSample(static_cast<unsigned int>(channels), bps.data());
  27. dng_image->SetPlanarConfig(tinydngwriter::PLANARCONFIG_CONTIG);
  28. dng_image->SetCompression(tinydngwriter::COMPRESSION_NONE);
  29. if (channels == 1) {
  30. dng_image->SetPhotometric(
  31. tinydngwriter::PHOTOMETRIC_BLACK_IS_ZERO); // grayscale
  32. } else {
  33. dng_image->SetPhotometric(
  34. tinydngwriter::PHOTOMETRIC_RGB);
  35. }
  36. dng_image->SetXResolution(1.0);
  37. dng_image->SetYResolution(1.0);
  38. dng_image->SetResolutionUnit(tinydngwriter::RESUNIT_NONE);
  39. std::vector<uint16_t> formats(channels);
  40. for (size_t i = 0; i < formats.size(); i++) {
  41. formats[i] = tinydngwriter::SAMPLEFORMAT_IEEEFP;
  42. }
  43. dng_image->SetSampleFormat(static_cast<unsigned int>(channels), formats.data());
  44. std::vector<float> buf;
  45. buf.resize(size_t(channels) * image_width * image_height);
  46. for (size_t i = 0; i < image_width * image_height; i++) {
  47. size_t in_c = 0;
  48. for (size_t c = 0; c < channels; c++) {
  49. buf[channels * i + c] = data[in_channels * i + in_c];
  50. in_c++;
  51. in_c = std::min(in_c, in_channels - 1);
  52. }
  53. }
  54. //size_t max_dump_pixels = 4096;
  55. //for (size_t i = 0; i < std::min(max_dump_pixels, buf.size()); i++) {
  56. // std::cout << "val[" << i << "] = " << buf[i] << "\n";
  57. //}
  58. //std::cout << "last = " << buf.at(image_width * image_height * channels - 1) << "\n";
  59. // We must retain pointer address of `buf` until calling DNGWriter::WriteToFile
  60. dng_image->SetImageData(reinterpret_cast<unsigned char *>(buf.data()),
  61. buf.size() * sizeof(float));
  62. if (!dng_image->Error().empty()) {
  63. std::cout << "Err: " << dng_image->Error() << "\n";
  64. return false;
  65. }
  66. return true;
  67. }
  68. int main(int argc, char** argv)
  69. {
  70. if (argc < 3) {
  71. printf("Usage: exr2fptiff input.exr output.tiff\n");
  72. exit(-1);
  73. }
  74. std::string input_filename = argv[1];
  75. std::string output_filename = argv[2];
  76. // Get # of layers
  77. size_t num_layers{0};
  78. {
  79. EXRVersion exr_version;
  80. {
  81. int ret = ParseEXRVersionFromFile(&exr_version, input_filename.c_str());
  82. if (ret != 0) {
  83. std::cerr << "Invalid EXR file: " << input_filename << "\n";
  84. return EXIT_FAILURE;
  85. }
  86. if (exr_version.multipart) {
  87. std::cerr << "Multipart EXR file is not supported in this example.\n";
  88. return EXIT_FAILURE;
  89. }
  90. }
  91. EXRHeader exr_header;
  92. InitEXRHeader(&exr_header);
  93. const char* err = nullptr;
  94. int ret = ParseEXRHeaderFromFile(&exr_header, &exr_version, argv[1], &err);
  95. if (ret != TINYEXR_SUCCESS) {
  96. if (err) {
  97. std::cerr << "Parse EXR error: " << err << "\n";
  98. FreeEXRErrorMessage(err); // free's buffer for an error message
  99. } else {
  100. std::cerr << "Parse EXR error.\n";
  101. }
  102. return EXIT_FAILURE;
  103. }
  104. num_layers = size_t(exr_header.num_channels);
  105. if (num_layers == 0) {
  106. std::cerr << "no layers found\n";
  107. return EXIT_FAILURE;
  108. }
  109. if (num_layers > 4) {
  110. std::cerr << "This program supports up to 4(e.g. RGBA) layers.\n";
  111. return EXIT_FAILURE;
  112. }
  113. FreeEXRHeader(&exr_header);
  114. }
  115. std::cout << "# of channels = " << num_layers << "\n";
  116. // Use legacy but easy-to-use API to read image.
  117. float *rgba{nullptr};
  118. int width;
  119. int height;
  120. {
  121. const char *err;
  122. int ret = LoadEXR(&rgba, &width, &height, input_filename.c_str(), &err);
  123. if (ret != TINYEXR_SUCCESS) {
  124. if (err) {
  125. std::cerr << "Load EXR error: " << err << "\n";
  126. FreeEXRErrorMessage(err); // free's buffer for an error message
  127. } else {
  128. std::cerr << "Load EXR error.\n";
  129. }
  130. return EXIT_FAILURE;
  131. }
  132. }
  133. bool big_endian = false;
  134. tinydngwriter::DNGImage tiff;
  135. tiff.SetBigEndian(big_endian);
  136. bool ret = Create32bitFpTiff(rgba, size_t(width), size_t(height), /* in_channels */4, size_t(num_layers), &tiff);
  137. if (!ret) {
  138. std::cerr << "Failed to create floating point tiff data\n";
  139. return EXIT_FAILURE;
  140. }
  141. // 4. Free image data
  142. free(rgba);
  143. tinydngwriter::DNGWriter dng_writer(big_endian);
  144. ret = dng_writer.AddImage(&tiff);
  145. if (!ret) {
  146. std::cerr << "Failed to add TIFF image to TIFF writer.\n";
  147. return EXIT_FAILURE;
  148. }
  149. // 5. write tiff
  150. std::string err;
  151. ret = dng_writer.WriteToFile(output_filename.c_str(), &err);
  152. if (!err.empty()) {
  153. std::cerr << err;
  154. }
  155. if (!ret) {
  156. return EXIT_FAILURE;
  157. }
  158. return EXIT_SUCCESS;
  159. }