Image.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. // The MIT License(MIT)
  2. //
  3. // Copyright(c) 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy of
  6. // this software and associated documentation files(the "Software"), to deal in
  7. // the Software without restriction, including without limitation the rights to
  8. // use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies of
  9. // the Software, and to permit persons to whom the Software is furnished to do so,
  10. // subject to the following conditions :
  11. //
  12. // The above copyright notice and this permission notice shall be included in all
  13. // copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  17. // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR
  18. // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  19. // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  20. // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  21. #include "Image.h"
  22. #define TINYEXR_IMPLEMENTATION
  23. #include <tinyexr.h>
  24. #define STB_IMAGE_IMPLEMENTATION
  25. #include <stb_image.h>
  26. #define STB_IMAGE_WRITE_IMPLEMENTATION
  27. #include <stb_image_write.h>
  28. #include <unordered_map>
  29. #include "Utilities.h"
  30. namespace img
  31. {
  32. using fp16_t = uint16_t;
  33. inline fp16_t floatToHalf(float v)
  34. {
  35. tinyexr::FP32 fp32;
  36. fp32.f = v;
  37. return tinyexr::float_to_half_full(fp32).u;
  38. }
  39. inline float halfToFloat(fp16_t v)
  40. {
  41. tinyexr::FP16 fp16;
  42. fp16.u = v;
  43. tinyexr::FP32 fp32 = tinyexr::half_to_float(fp16);
  44. return fp32.f;
  45. }
  46. template<typename T, typename K>
  47. inline K convertTo(T v) { return K(v); }
  48. template<>
  49. inline float convertTo(uint8_t v) { return v / 255.f; }
  50. template<>
  51. inline fp16_t convertTo(uint8_t v) { return floatToHalf(v / 255.f); }
  52. template<>
  53. inline uint8_t convertTo(float v) { return uint8_t(v * 255.f); }
  54. template<>
  55. inline fp16_t convertTo(float v) { return floatToHalf(v); }
  56. template<>
  57. inline uint8_t convertTo(fp16_t v) { return uint8_t(halfToFloat(v) * 255.f); }
  58. template<>
  59. inline float convertTo(fp16_t v) { return halfToFloat(v); }
  60. template<typename T>
  61. inline T alphaMax() { return 255; }
  62. template<>
  63. inline float alphaMax() { return 1.f; }
  64. template<>
  65. inline fp16_t alphaMax() { return floatToHalf(1.f); }
  66. template<typename T, typename K>
  67. inline void convertToFmt(uint8_t* input, uint8_t* output, uint32_t width, uint32_t height, uint32_t inputChannels, uint32_t inputRowPitch, uint32_t outputChannels, uint32_t outputRowPitch)
  68. {
  69. constexpr size_t BOut = sizeof(K); // bytes per pixel
  70. constexpr size_t BIn = sizeof(T);
  71. for (size_t y = 0; y < height; ++y)
  72. {
  73. for (size_t x = 0; x < width; ++x)
  74. {
  75. size_t output_offset = y * outputRowPitch + x * outputChannels * BOut;
  76. size_t input_offset = y * inputRowPitch + x * inputChannels * BIn;
  77. ((K*)&output[output_offset])[0] = convertTo<T, K>(((T*)&input[input_offset])[0]);
  78. ((K*)&output[output_offset])[1] = convertTo<T, K>(((T*)&input[input_offset])[1]);
  79. ((K*)&output[output_offset])[2] = convertTo<T, K>(((T*)&input[input_offset])[2]);
  80. if (outputChannels > 3)
  81. ((K*)&output[output_offset])[3] = inputChannels > 3 ? convertTo<T, K>(((T*)&input[input_offset])[3]) : alphaMax<K>();
  82. }
  83. }
  84. }
  85. template<typename T, typename K>
  86. inline void convertToFmtPlanesABGR(uint8_t* input, uint8_t* output, uint32_t width, uint32_t height, uint32_t inputChannels, uint32_t inputRowPitch, uint32_t outputChannels, uint32_t outputRowPitch)
  87. {
  88. constexpr size_t BOut = sizeof(K); // bytes per pixel
  89. constexpr size_t BIn = sizeof(T);
  90. size_t planeSize = size_t(outputRowPitch) * height;
  91. for (size_t y = 0; y < height; ++y)
  92. {
  93. for (size_t x = 0; x < width; ++x)
  94. {
  95. size_t input_offset = y * inputRowPitch + x * inputChannels * BIn;
  96. size_t output_offset0 = (y * outputRowPitch) + x * BOut;
  97. size_t output_offset1 = output_offset0 + planeSize;
  98. size_t output_offset2 = output_offset1 + planeSize;
  99. if (outputChannels == 3)
  100. {
  101. *((K*)&output[output_offset0]) = convertTo<T, K>(((T*)&input[input_offset])[2]);
  102. *((K*)&output[output_offset1]) = convertTo<T, K>(((T*)&input[input_offset])[1]);
  103. *((K*)&output[output_offset2]) = convertTo<T, K>(((T*)&input[input_offset])[0]);
  104. }
  105. else
  106. {
  107. size_t output_offset3 = output_offset2 + planeSize;
  108. *((K*)&output[output_offset0]) = inputChannels > 3 ? convertTo<T, K>(((T*)&input[input_offset])[3]) : alphaMax<K>();
  109. *((K*)&output[output_offset1]) = convertTo<T, K>(((T*)&input[input_offset])[2]);
  110. *((K*)&output[output_offset2]) = convertTo<T, K>(((T*)&input[input_offset])[1]);
  111. *((K*)&output[output_offset3]) = convertTo<T, K>(((T*)&input[input_offset])[0]);
  112. }
  113. }
  114. }
  115. }
  116. uint32_t bytesPerPixel(Fmt fmt)
  117. {
  118. static std::unordered_map<Fmt, uint32_t> Bpp{ {Fmt::R8G8B8A8, 4}, {Fmt::R32G32B32A32, 16}, {Fmt::R16G16B16A16, 8} };
  119. return Bpp[fmt];
  120. }
  121. void load(const std::string& fileName, std::vector<uint8_t>& data, uint32_t& width, uint32_t& height, uint32_t& outRowPitch, Fmt outFormat, uint32_t outRowPitchAlignment)
  122. {
  123. std::string extension = std::filesystem::path(fileName).extension().string();
  124. for (auto& e : extension) e = std::tolower(e);
  125. if (extension == ".exr")
  126. {
  127. loadEXR(fileName, data, width, height, outRowPitch, outFormat, outRowPitchAlignment);
  128. }
  129. else if (extension == ".png")
  130. {
  131. loadPNG(fileName, data, width, height, outRowPitch, outFormat, outRowPitchAlignment);
  132. }
  133. }
  134. void loadPNG(const std::string& fileName, std::vector<uint8_t>& data, uint32_t& width, uint32_t& height, uint32_t& outRowPitch, Fmt outFormat, uint32_t outRowPitchAlignment)
  135. {
  136. uint32_t infileChannels;
  137. uint8_t* image = stbi_load(fileName.c_str(), (int*)&width, (int*)&height, (int*)&infileChannels, STBI_rgb_alpha);
  138. if (image == nullptr)
  139. throw std::runtime_error("Failed to load PNG Image : " + fileName);
  140. uint32_t inChannels = 4; // stb_image has already converted data to RGBA
  141. uint32_t inputRowPitch = width * inChannels;
  142. uint32_t outChannels = 4; // Hardcoded all output formats have 4 channel
  143. outRowPitch = Align(width * bytesPerPixel(outFormat), outRowPitchAlignment);
  144. uint32_t imageSize = outRowPitch * height;
  145. data.resize(imageSize);
  146. switch (outFormat)
  147. {
  148. case Fmt::R8G8B8A8:
  149. convertToFmt<uint8_t, uint8_t>(image, data.data(), width, height, inChannels, inputRowPitch, outChannels, outRowPitch);
  150. break;
  151. case Fmt::R32G32B32A32:
  152. convertToFmt<uint8_t, float>(image, data.data(), width, height, inChannels, inputRowPitch, outChannels, outRowPitch);
  153. break;
  154. case Fmt::R16G16B16A16:
  155. convertToFmt<uint8_t, fp16_t>(image, data.data(), width, height, inChannels, inputRowPitch, outChannels, outRowPitch);
  156. break;
  157. }
  158. stbi_image_free(image);
  159. }
  160. void loadEXR(const std::string& fileName, std::vector<uint8_t>& data, uint32_t& width, uint32_t& height, uint32_t& outRowPitch, Fmt outFormat, uint32_t outRowPitchAlignment)
  161. {
  162. uint32_t inChannels = 4; // fixed to only 4 channel EXR files
  163. float* image;
  164. const char* err = nullptr;
  165. int ret = LoadEXR(&image, (int*)&width, (int*)&height, fileName.c_str(), &err);
  166. uint32_t inputRowPitch = width * inChannels * sizeof(float);
  167. if (ret != TINYEXR_SUCCESS) {
  168. std::string serr = err;
  169. FreeEXRErrorMessage(err);
  170. throw std::runtime_error("Failed to load EXR Image : " + fileName + " Error: " + serr);
  171. }
  172. uint32_t outChannels = 4; // Hardcoded all output formats have 4 channel
  173. outRowPitch = Align(width * bytesPerPixel(outFormat), outRowPitchAlignment);
  174. uint32_t imageSize = outRowPitch * height;
  175. data.resize(imageSize);
  176. switch (outFormat)
  177. {
  178. case Fmt::R8G8B8A8:
  179. convertToFmt<float, uint8_t>((uint8_t*)image, data.data(), width, height, inChannels, inputRowPitch, outChannels, outRowPitch);
  180. break;
  181. case Fmt::R32G32B32A32:
  182. convertToFmt<float, float>((uint8_t*)image, data.data(), width, height, inChannels, inputRowPitch, outChannels, outRowPitch);
  183. break;
  184. case Fmt::R16G16B16A16:
  185. convertToFmt<float, fp16_t>((uint8_t*)image, data.data(), width, height, inChannels, inputRowPitch, outChannels, outRowPitch);
  186. break;
  187. }
  188. free(image);
  189. }
  190. void save(const std::string& fileName, uint8_t* data, uint32_t width, uint32_t height, uint32_t channels, uint32_t rowPitch, Fmt format)
  191. {
  192. std::string extension = std::filesystem::path(fileName).extension().string();
  193. for (auto& e : extension) e = std::tolower(e);
  194. if (extension == ".exr")
  195. {
  196. saveEXR(fileName, data, width, height, channels, rowPitch, format);
  197. }
  198. else if (extension == ".png")
  199. {
  200. savePNG(fileName, data, width, height, channels, rowPitch, format);
  201. }
  202. }
  203. void savePNG(const std::string& fileName, uint8_t* data, uint32_t width, uint32_t height, uint32_t channels, uint32_t rowPitch, Fmt format)
  204. {
  205. constexpr uint32_t outputChannels = 4;
  206. std::vector<uint8_t> image(size_t(width) * height * outputChannels);
  207. uint32_t outputRowPitch = width * outputChannels * sizeof(uint8_t);
  208. switch (format)
  209. {
  210. case Fmt::R8G8B8A8:
  211. convertToFmt<uint8_t, uint8_t>(data, image.data(), width, height, channels, rowPitch, outputChannels, outputRowPitch);
  212. break;
  213. case Fmt::R32G32B32A32:
  214. convertToFmt<float, uint8_t>(data, image.data(), width, height, channels, rowPitch, outputChannels, outputRowPitch);
  215. break;
  216. case Fmt::R16G16B16A16:
  217. convertToFmt<fp16_t, uint8_t>(data, image.data(), width, height, channels, rowPitch, outputChannels, outputRowPitch);
  218. break;
  219. }
  220. stbi_write_png(fileName.c_str(), width, height, outputChannels, image.data(), outputRowPitch);
  221. }
  222. void saveEXR(const std::string& fileName, uint8_t* data, uint32_t width, uint32_t height, uint32_t channels, uint32_t rowPitch, Fmt format)
  223. {
  224. EXRHeader header;
  225. InitEXRHeader(&header);
  226. EXRImage image;
  227. InitEXRImage(&image);
  228. constexpr uint32_t outputChannels = 4;
  229. image.num_channels = outputChannels;
  230. uint32_t plane_size = width * height;
  231. uint32_t outputRowPitch = width * sizeof(float);
  232. std::vector<float> images(size_t(outputChannels) * plane_size);
  233. switch (format)
  234. {
  235. case Fmt::R8G8B8A8:
  236. convertToFmtPlanesABGR<uint8_t, float>(data, (uint8_t*)images.data(), width, height, channels, rowPitch, outputChannels, outputRowPitch);
  237. break;
  238. case Fmt::R32G32B32A32:
  239. convertToFmtPlanesABGR<float, float>(data, (uint8_t*)images.data(), width, height, channels, rowPitch, outputChannels, outputRowPitch);
  240. break;
  241. case Fmt::R16G16B16A16:
  242. convertToFmtPlanesABGR<fp16_t, float>(data, (uint8_t*)images.data(), width, height, channels, rowPitch, outputChannels, outputRowPitch);
  243. break;
  244. }
  245. float* image_ptr[outputChannels];
  246. for (size_t i = 0; i < outputChannels; ++i)
  247. image_ptr[i] = &images[i * plane_size];
  248. image.images = (unsigned char**)image_ptr;
  249. image.width = width;
  250. image.height = height;
  251. header.num_channels = outputChannels;
  252. header.channels = (EXRChannelInfo*)malloc(sizeof(EXRChannelInfo) * header.num_channels);
  253. // Must be (A)BGR order, since most of EXR viewers expect this channel order.
  254. header.channels[0].name[0] = 'A'; header.channels[0].name[1] = '\0';
  255. header.channels[1].name[0] = 'B'; header.channels[1].name[1] = '\0';
  256. header.channels[2].name[0] = 'G'; header.channels[2].name[1] = '\0';
  257. header.channels[3].name[0] = 'R'; header.channels[3].name[1] = '\0';
  258. header.pixel_types = (int*)malloc(sizeof(int) * header.num_channels);
  259. header.requested_pixel_types = (int*)malloc(sizeof(int) * header.num_channels);
  260. for (int i = 0; i < header.num_channels; i++) {
  261. header.pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT;
  262. header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT;
  263. }
  264. const char* err = nullptr;
  265. int ret = SaveEXRImageToFile(&image, &header, fileName.c_str(), &err);
  266. if (ret != TINYEXR_SUCCESS) {
  267. std::string serr = err;
  268. FreeEXRErrorMessage(err);
  269. throw std::runtime_error("Failed to save EXR Image : " + fileName + " Error: " + serr);
  270. }
  271. free(header.channels);
  272. free(header.pixel_types);
  273. free(header.requested_pixel_types);
  274. }
  275. }