io/image.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // SPDX-License-Identifier: BSD-3-Clause | ||
| 2 | #pragma once | ||
| 3 | |||
| 4 | #include <array> | ||
| 5 | #include <cstddef> | ||
| 6 | #include <cstdint> | ||
| 7 | #include <filesystem> | ||
| 8 | #include <optional> | ||
| 9 | #include <stdexcept> | ||
| 10 | #include <string_view> | ||
| 11 | #include <tuple> | ||
| 12 | #include <vector> | ||
| 13 | |||
| 14 | #include "chrono_batch_render/device/tensor.h" | ||
| 15 | #include "chrono_batch_render/export.h" | ||
| 16 | #include "chrono_batch_render/util/span.h" | ||
| 17 | |||
| 18 | namespace chrono::batch_render { | ||
| 19 | |||
| 20 | enum class ImageEncoding : uint8_t { Srgb, Linear }; | ||
| 21 | |||
| 22 | struct Rgba { | ||
| 23 | float r = 0; | ||
| 24 | float g = 0; | ||
| 25 | float b = 0; | ||
| 26 | float a = 0; | ||
| 27 | |||
| 28 | 24 | bool operator==(const Rgba& other) const { | |
| 29 | 24 | return r == other.r && g == other.g && b == other.b && a == other.a; | |
| 30 | } | ||
| 31 | |||
| 32 | bool operator!=(const Rgba& other) const { | ||
| 33 | return !(*this == other); | ||
| 34 | } | ||
| 35 | }; | ||
| 36 | |||
| 37 | 73 | struct CHRONO_BATCH_RENDER_API Image { | |
| 38 | uint32_t width = 0; | ||
| 39 | uint32_t height = 0; | ||
| 40 | std::vector<Rgba> pixels; | ||
| 41 | |||
| 42 | Image() = default; | ||
| 43 | |||
| 44 | 58 | Image(uint32_t w, uint32_t h, Rgba fill = {}) | |
| 45 | 58 | : width(w), height(h), pixels(size_t(w) * h, fill) {} | |
| 46 | |||
| 47 | 1835078 | Rgba& Pixel(uint32_t x, uint32_t y) { | |
| 48 | 1835078 | return pixels[size_t(y) * width + x]; | |
| 49 | } | ||
| 50 | |||
| 51 | 15 | const Rgba& Pixel(uint32_t x, uint32_t y) const { | |
| 52 | 15 | return pixels[size_t(y) * width + x]; | |
| 53 | } | ||
| 54 | |||
| 55 | Rgba Mean() const; | ||
| 56 | }; | ||
| 57 | |||
| 58 | 22 | struct Rgba8Image { | |
| 59 | uint32_t width = 0; | ||
| 60 | uint32_t height = 0; | ||
| 61 | std::vector<uint32_t> texels; | ||
| 62 | }; | ||
| 63 | |||
| 64 | CHRONO_BATCH_RENDER_API Image LoadImageFile(const std::filesystem::path& path, | ||
| 65 | ImageEncoding encoding); | ||
| 66 | CHRONO_BATCH_RENDER_API Image LoadImageBytes(Span<const std::byte> bytes, std::string_view name, | ||
| 67 | ImageEncoding encoding); | ||
| 68 | CHRONO_BATCH_RENDER_API bool IsPngOrJpeg(const std::filesystem::path& path); | ||
| 69 | |||
| 70 | CHRONO_BATCH_RENDER_API Rgba8Image LoadRgba8File(const std::filesystem::path& path); | ||
| 71 | CHRONO_BATCH_RENDER_API Rgba8Image LoadRgba8Bytes(Span<const std::byte> bytes, | ||
| 72 | std::string_view name); | ||
| 73 | |||
| 74 | 68 | struct CHRONO_BATCH_RENDER_API Pixels { | |
| 75 | int64_t rows = 0; | ||
| 76 | int64_t cols = 0; | ||
| 77 | int64_t channels = 1; | ||
| 78 | device::DType type = device::DType::UInt8; | ||
| 79 | std::vector<std::byte> bytes; | ||
| 80 | |||
| 81 | Pixels() = default; | ||
| 82 | Pixels(const device::Tensor& tensor); | ||
| 83 | Pixels(Span<const std::byte> data, device::DType elementType, int64_t rowCount, | ||
| 84 | int64_t colCount, int64_t channelCount = 1); | ||
| 85 | |||
| 86 | template <class T> | ||
| 87 | 26 | Pixels(Span<const T> values, int64_t rowCount, int64_t colCount, int64_t channelCount = 1) | |
| 88 | 26 | : Pixels(Span<const std::byte>(reinterpret_cast<const std::byte*>(values.data()), | |
| 89 | values.size_bytes()), | ||
| 90 | 26 | device::ElementType<T>(), rowCount, colCount, channelCount) {} | |
| 91 | |||
| 92 | template <class T> | ||
| 93 | 26 | Pixels(const std::vector<T>& values, int64_t rowCount, int64_t colCount, | |
| 94 | int64_t channelCount = 1) | ||
| 95 | 26 | : Pixels(Span<const T>(values), rowCount, colCount, channelCount) {} | |
| 96 | |||
| 97 | 88 | template <class T> Span<const T> Values() const { | |
| 98 | 88 | if (device::ElementType<T>() != type) { | |
| 99 | 1 | throw std::invalid_argument("Values of a type unlike the pixels' elements"); | |
| 100 | } | ||
| 101 | 87 | return {reinterpret_cast<const T*>(bytes.data()), bytes.size() / sizeof(T)}; | |
| 102 | } | ||
| 103 | }; | ||
| 104 | |||
| 105 | CHRONO_BATCH_RENDER_API Image ImageFromTensor(const device::Tensor& tensor, ImageEncoding encoding); | ||
| 106 | CHRONO_BATCH_RENDER_API std::vector<uint8_t> ToRgba8(const Image& image, ImageEncoding encoding); | ||
| 107 | |||
| 108 | CHRONO_BATCH_RENDER_API void WritePng(const std::filesystem::path& path, const Image& image); | ||
| 109 | CHRONO_BATCH_RENDER_API void WritePng(const std::filesystem::path& path, const Pixels& image); | ||
| 110 | CHRONO_BATCH_RENDER_API void WriteJpeg(const std::filesystem::path& path, const Image& image, | ||
| 111 | int quality = 92); | ||
| 112 | CHRONO_BATCH_RENDER_API void WriteJpeg(const std::filesystem::path& path, const Pixels& image, | ||
| 113 | int quality = 92); | ||
| 114 | CHRONO_BATCH_RENDER_API void WriteExr(const std::filesystem::path& path, const Image& image); | ||
| 115 | CHRONO_BATCH_RENDER_API void WriteExr(const std::filesystem::path& path, const Pixels& image); | ||
| 116 | CHRONO_BATCH_RENDER_API std::filesystem::path WriteImage(std::filesystem::path path, | ||
| 117 | const Pixels& image); | ||
| 118 | |||
| 119 | enum class Colormap : uint8_t { Grayscale, Turbo, Viridis }; | ||
| 120 | |||
| 121 | struct ColorizeOptions { | ||
| 122 | Colormap colormap = Colormap::Grayscale; | ||
| 123 | std::optional<float> minimum; | ||
| 124 | std::optional<float> maximum; | ||
| 125 | bool logarithmic = false; | ||
| 126 | bool reversed = false; | ||
| 127 | bool centered = false; | ||
| 128 | uint32_t channel = 0; | ||
| 129 | |||
| 130 | bool operator==(const ColorizeOptions& other) const { | ||
| 131 | return std::tie(colormap, minimum, maximum, logarithmic, reversed, centered, channel) == | ||
| 132 | std::tie(other.colormap, other.minimum, other.maximum, other.logarithmic, | ||
| 133 | other.reversed, other.centered, other.channel); | ||
| 134 | } | ||
| 135 | }; | ||
| 136 | |||
| 137 | inline constexpr ColorizeOptions kDepthColors{Colormap::Grayscale, std::nullopt, std::nullopt, true, | ||
| 138 | true}; | ||
| 139 | |||
| 140 | CHRONO_BATCH_RENDER_API std::array<uint8_t, 4> ColormapColor(Colormap colormap, float position); | ||
| 141 | |||
| 142 | CHRONO_BATCH_RENDER_API Pixels ColorizeScalars(const Pixels& values, | ||
| 143 | const ColorizeOptions& options = {}); | ||
| 144 | CHRONO_BATCH_RENDER_API Pixels ColorizeHdr(const Pixels& color, float exposure = 0); | ||
| 145 | CHRONO_BATCH_RENDER_API Pixels ColorizeNormals(const Pixels& normals); | ||
| 146 | CHRONO_BATCH_RENDER_API Pixels ColorizeIds(const Pixels& ids); | ||
| 147 | CHRONO_BATCH_RENDER_API std::array<uint8_t, 4> IdColor(Span<const uint32_t> words); | ||
| 148 | CHRONO_BATCH_RENDER_API device::Tensor ColorizeScalars(const device::Tensor& values, | ||
| 149 | const ColorizeOptions& options = {}); | ||
| 150 | CHRONO_BATCH_RENDER_API device::Tensor ColorizeScalars(const device::Tensor& values, | ||
| 151 | const ColorizeOptions& options, | ||
| 152 | device::Tensor& bounds); | ||
| 153 | CHRONO_BATCH_RENDER_API device::Tensor ColorizeHdr(const device::Tensor& color, float exposure = 0); | ||
| 154 | CHRONO_BATCH_RENDER_API device::Tensor ColorizeNormals(const device::Tensor& normals); | ||
| 155 | CHRONO_BATCH_RENDER_API device::Tensor ColorizeIds(const device::Tensor& ids); | ||
| 156 | |||
| 157 | } | ||
| 158 |