UFO 1.0.0
An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown
Loading...
Searching...
No Matches
png.hpp
1
42#ifndef UFO_IO_PNG_HPP
43#define UFO_IO_PNG_HPP
44
45// UFO
46#include <ufo/io/file_handler.hpp>
47#include <ufo/io/image_properties.hpp>
48#include <ufo/vision/color.hpp>
49#include <ufo/vision/image.hpp>
50
51// STL
52#include <algorithm>
53#include <cstdio>
54#include <filesystem>
55#include <print>
56#include <type_traits>
57
58namespace ufo
59{
60namespace detail
61{
77bool readPNG(std::FILE* fp, void* image, std::size_t length, int fmt);
78
97bool writePNG(std::FILE* fp, void const* image, std::size_t length, std::uint32_t width,
98 std::uint32_t height, int color_type, int bit_depth);
99} // namespace detail
100
114[[nodiscard]] ImageProperties imagePropertiesPNG(std::filesystem::path const& file);
115
137template <ColorType CT, class T, bool Alpha, bool Weight>
138bool readPNG(std::filesystem::path const& file, Image<Color<CT, T, Alpha, Weight>>& image)
139{
140 FileHandler fp(file, "rb");
141
142 if (!fp) {
143 std::println(stderr, "[UFO | Read PNG] Failed to open file: {}", file.string());
144 return false;
145 }
146
147 auto fun = [&image](std::FILE* fp, auto& tmp, int fmt) {
148 using C = typename std::remove_cvref_t<decltype(tmp)>::value_type;
149 if (detail::readPNG(fp, tmp.data(), sizeof(C) * tmp.size(), fmt)) {
150 if constexpr (has_alpha_v<C>) {
151 for (auto& p : tmp) {
152 if (0 == alpha(p)) {
153 if constexpr (ColorType::GRAY == color_type_v<C>) {
154 p.gray = 0;
155 } else {
156 p.red = 0;
157 p.green = 0;
158 p.blue = 0;
159 }
160 }
161 }
162 }
163 image = convert<Color<CT, T, Alpha, Weight>>(tmp);
164 return true;
165 }
166 return false;
167 };
168
169 auto const prop = imagePropertiesPNG(file);
170 if (prop.alpha && prop.grayscale && 8 >= prop.bit_depth) {
171 int fmt = 16; // SPNG_FMT_GA8
173 return fun(fp.get(), tmp, fmt);
174 } else if (prop.grayscale && 16 == prop.bit_depth) {
175 int fmt = 32; // SPNG_FMT_GA16
177 prop.width);
178 return fun(fp.get(), tmp, fmt);
179 } else if (prop.alpha && !prop.grayscale && 8 == prop.bit_depth) {
180 int fmt = 1; // SPNG_FMT_RGBA8
181 Image<Color<ColorType::RGB, std::uint8_t, true, false>> tmp(prop.height, prop.width);
182 return fun(fp.get(), tmp, fmt);
183 } else if (!prop.grayscale && 16 == prop.bit_depth) {
184 int fmt = 2; // SPNG_FMT_RGBA16
186 return fun(fp.get(), tmp, fmt);
187 } else if (!prop.alpha && prop.grayscale && 8 >= prop.bit_depth) {
188 int fmt = 64; // SPNG_FMT_G8
190 prop.width);
191 return fun(fp.get(), tmp, fmt);
192 } else if (!prop.alpha && !prop.grayscale && 8 == prop.bit_depth) {
193 int fmt = 4; // SPNG_FMT_RGB8
195 return fun(fp.get(), tmp, fmt);
196 } else {
197 std::println(stderr, "[UFO | Read PNG] Unknown PNG type");
198 return false;
199 }
200}
201
220template <ColorType CT, class T, bool Alpha, bool Weight>
221bool writePNG(std::filesystem::path const& file,
223{
224 FileHandler fp(file, "wb");
225
226 if (!fp) {
227 std::println(stderr, "[UFO | Write PNG] Failed to create file: {}", file.string());
228 return false;
229 }
230
231 std::uint32_t const width = image.cols();
232 std::uint32_t const height = image.rows();
233 int const bit_depth = sizeof(std::uint8_t) < sizeof(T) ? 16 : 8;
234
235 // SPNG colour type and channel count derived entirely from template parameters.
236 constexpr int color_type = (ColorType::GRAY == CT) ? (Alpha ? 4 : 0) // GA or G
237 : (Alpha ? 6 : 2); // RGBA or RGB
238 constexpr int channels = (ColorType::GRAY == CT) ? (Alpha ? 2 : 1) : (Alpha ? 4 : 3);
239
240 std::size_t const length =
241 static_cast<std::size_t>(width) * height * (bit_depth / 8) * channels;
242
243 // Canonical output types: strip Weight, normalise scalar width.
244 constexpr ColorType CT2 = (ColorType::GRAY == CT) ? ColorType::GRAY : ColorType::RGB;
245 using T2 =
246 std::conditional_t<sizeof(std::uint8_t) < sizeof(T), std::uint16_t, std::uint8_t>;
247
248 if constexpr (CT == CT2 && std::is_same_v<T, T2> && !Weight) {
249 return detail::writePNG(fp.get(), image.data(), length, width, height, color_type,
250 bit_depth);
251 } else {
252 Image<Color<CT2, T2, Alpha, false>> data(image.rows(), image.cols());
253 std::ranges::transform(image, data.begin(), [](auto x) {
254 return convert<Color<CT2, T2, Alpha, false>>(x);
255 });
256 return detail::writePNG(fp.get(), data.data(), length, width, height, color_type,
257 bit_depth);
258 }
259}
260} // namespace ufo
261
262#endif // UFO_IO_PNG_HPP
RAII wrapper around a C std::FILE* handle.
std::FILE * get() const noexcept
Returns the underlying FILE*.
Image class for storing and manipulating 2D pixel data.
Definition image.hpp:78
auto data(this auto &self) noexcept
Returns a pointer to the underlying contiguous pixel array.
Definition image.hpp:349
auto begin(this auto &self) noexcept
Returns an iterator to the first pixel.
Definition image.hpp:282
Identifies any UFO color instantiation (Gray, Lab, Lch, Lrgb, Rgb).
Definition concepts.hpp:73
@ Alpha
Include an alpha channel.
All vision-related classes and functions.
Definition cloud.hpp:49
bool readPNG(std::filesystem::path const &file, Image< Color< CT, T, Alpha, Weight > > &image)
Reads a PNG file into an Image of the specified colour type.
Definition png.hpp:138
constexpr T length(Vec< Dim, T > const &v) noexcept
Computes the Euclidean length (magnitude) of a vector.
Definition vec.hpp:1101
bool writePNG(std::filesystem::path const &file, Image< Color< CT, T, Alpha, Weight > > const &image)
Writes an Image to a PNG file.
Definition png.hpp:221
ImageProperties imagePropertiesPNG(std::filesystem::path const &file)
Reads the metadata of a PNG file without decoding pixel data.
Definition png.cpp:147
constexpr alpha_type_t< C > alpha(C color) noexcept
Returns the un-weighted alpha of color.
Definition alpha.hpp:105
Represents a generic scalar weight as a single float value.
Definition weight.hpp:67