46#include <ufo/io/file_handler.hpp>
47#include <ufo/io/image_properties.hpp>
48#include <ufo/morton/morton.hpp>
49#include <ufo/vision/color.hpp>
50#include <ufo/vision/image.hpp>
53#include <ufo/io/jpeg.hpp>
54#include <ufo/io/png.hpp>
67template <ColorType CT,
bool Alpha>
69 using value_type = ColorFine<CT, Alpha, false>;
71 std::array<std::unique_ptr<QTPNode>, 4> children{};
74 [[nodiscard]]
static std::uint32_t code(std::uint32_t x, std::uint32_t y)
79 [[nodiscard]]
static std::uint32_t numLevelsRequired(std::uint32_t x, std::uint32_t y)
82 return (std::bit_width(code(x, y)) / 2) + 1;
85 void add(std::uint32_t code, value_type
const& color, std::uint32_t level)
94 std::uint32_t node_index = (code >> ((level - 1) * 2)) & 0b11;
96 if (!children[node_index]) {
97 children[node_index] = std::make_unique<QTPNode>();
100 children[node_index]->add(code, color, level - 1);
103 [[nodiscard]] value_type operator()(std::uint32_t code, std::uint32_t level)
const
109 std::uint32_t node_index = (code >> ((level - 1) * 2)) & 0b11;
111 if (!children[node_index]) {
114 return (*children[node_index])(code, level - 1);
118 [[nodiscard]]
bool isLeaf()
const
120 return std::all_of(children.begin(), children.end(),
121 [](
auto const& child) { return nullptr == child; });
124 [[nodiscard]]
bool isParent()
const {
return !isLeaf(); }
126 [[nodiscard]]
bool hasAllChildren()
const
128 return std::all_of(children.begin(), children.end(),
129 [](
auto const& child) { return nullptr != child; });
132 void addChildData(std::vector<value_type>& data)
const
135 data.push_back(this->data);
139 if (10000 < data.size()) {
143 for (
auto const& child : children) {
145 child->addChildData(data);
150 [[nodiscard]]
bool compressible(
float threshold_sq)
167 if (!hasAllChildren()) {
171 std::array<FineLab, 4> colors{};
172 for (std::size_t i{}; 4 > i; ++i) {
173 colors[i] = children[i]->data;
178 for (std::size_t i{}; colors.size() > i; ++i) {
197 bool compress(
float threshold_sq)
230 bool all_children_compressed =
true;
231 for (
auto& child : children) {
233 bool comp = child->compress(threshold_sq);
234 all_children_compressed = all_children_compressed && comp;
238 if (all_children_compressed && compressible(threshold_sq)) {
239 static std::size_t count = 0;
241 std::println(
"Compressing node count: {}", count);
242 for (
auto& child : children) {
251 [[nodiscard]] std::uint32_t numLeaves()
const
257 std::uint32_t count = 0;
258 for (
auto const& child : children) {
260 count += child->numLeaves();
263 return 0 == count ? 1 : count;
268[[nodiscard]]
ImageProperties imagePropertiesQTP(std::filesystem::path
const& file);
270template <ColorType CT,
class T,
bool Alpha,
bool Weight>
276 std::println(stderr,
"[UFO | Read QTP] Failed to open file: {}", file.string());
286template <ColorType CT,
class T,
bool Alpha,
bool Weight>
287bool writeQTP(std::filesystem::path
const& file,
288 Image<Color<CT, T, Alpha, Weight>>
const& image,
int quality = 90)
290 FileHandler fp(file.c_str(),
"wb");
293 std::println(stderr,
"[UFO | Write QTP] Failed to create file: {}", file.string());
297 std::uint32_t width = image.cols();
298 std::uint32_t height = image.rows();
299 int num_channels = ColorType::GRAY == CT ? 1 : 3;
301 constexpr ColorType
const CT2 =
302 ColorType::GRAY == CT ? ColorType::GRAY : ColorType::LAB;
304 detail::QTPNode<CT2, Alpha> root;
306 std::uint32_t num_levels = root.numLevelsRequired(height - 1, width - 1);
308 if constexpr (ColorType::GRAY == CT) {
311 for (std::size_t i{}; height > i; ++i) {
312 for (std::size_t j{}; width > j; ++j) {
313 auto const& color = image.at(i, j);
314 auto lab_color = convert<FineLab>(color);
315 root.add(root.code(i, j), lab_color, num_levels);
319 float threshold = 0.0025;
321 root.compress(threshold * threshold);
323 std::println(
"Number of leaves: {}\n", root.numLeaves());
324 std::println(
"Original number of pixels: {}\n", width * height);
326 Image<SmallRGB> new_image(height, width);
327 for (std::size_t i{}; height > i; ++i) {
328 for (std::size_t j{}; width > j; ++j) {
329 new_image.at(i, j) = convert<SmallRGB>(root(root.code(i, j), num_levels));
333 writePNG(file.string() +
".png", new_image);
334 writeJPEG(file.string() +
".jpg", new_image);
RAII wrapper around a C std::FILE* handle.
Image class for storing and manipulating 2D pixel data.
Identifies any UFO color instantiation (Gray, Lab, Lch, Lrgb, Rgb).
constexpr C average(C a, C const &b)
Computes the average of two colors.
constexpr float deltaEEuclideanSquared(C const &color, C const &sample)
Computes the squared Euclidean distance between two colors.
All vision-related classes and functions.
bool writePNG(std::filesystem::path const &file, Image< Color< CT, T, Alpha, Weight > > const &image)
Writes an Image to a PNG file.
Metadata describing the pixel layout of an image file.