UFO 1.0.0
An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown
Loading...
Searching...
No Matches
qtp.hpp
1
42#ifndef UFO_IO_QTP_HPP
43#define UFO_IO_QTP_HPP
44
45// UFO
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>
51
52// TODO: Remove
53#include <ufo/io/jpeg.hpp>
54#include <ufo/io/png.hpp>
55
56// STL
57#include <algorithm>
58#include <bit>
59#include <cstdio>
60#include <filesystem>
61#include <print>
62
63namespace ufo
64{
65namespace detail
66{
67template <ColorType CT, bool Alpha>
68struct QTPNode {
69 using value_type = ColorFine<CT, Alpha, false>;
70
71 std::array<std::unique_ptr<QTPNode>, 4> children{};
72 value_type data{};
73
74 [[nodiscard]] static std::uint32_t code(std::uint32_t x, std::uint32_t y)
75 {
76 return Morton<2>::encode32(x, y);
77 }
78
79 [[nodiscard]] static std::uint32_t numLevelsRequired(std::uint32_t x, std::uint32_t y)
80 {
81 // TODO: Correct?
82 return (std::bit_width(code(x, y)) / 2) + 1;
83 }
84
85 void add(std::uint32_t code, value_type const& color, std::uint32_t level)
86 {
87 // data += color;
88
89 if (0 == level) {
90 data = color;
91 return;
92 }
93
94 std::uint32_t node_index = (code >> ((level - 1) * 2)) & 0b11;
95
96 if (!children[node_index]) {
97 children[node_index] = std::make_unique<QTPNode>();
98 }
99
100 children[node_index]->add(code, color, level - 1);
101 }
102
103 [[nodiscard]] value_type operator()(std::uint32_t code, std::uint32_t level) const
104 {
105 if (0 == level) {
106 return data;
107 }
108
109 std::uint32_t node_index = (code >> ((level - 1) * 2)) & 0b11;
110
111 if (!children[node_index]) {
112 return data;
113 } else {
114 return (*children[node_index])(code, level - 1);
115 }
116 }
117
118 [[nodiscard]] bool isLeaf() const
119 {
120 return std::all_of(children.begin(), children.end(),
121 [](auto const& child) { return nullptr == child; });
122 }
123
124 [[nodiscard]] bool isParent() const { return !isLeaf(); }
125
126 [[nodiscard]] bool hasAllChildren() const
127 {
128 return std::all_of(children.begin(), children.end(),
129 [](auto const& child) { return nullptr != child; });
130 }
131
132 void addChildData(std::vector<value_type>& data) const
133 {
134 if (isLeaf()) {
135 data.push_back(this->data);
136 return;
137 }
138
139 if (10000 < data.size()) {
140 return;
141 }
142
143 for (auto const& child : children) {
144 if (child) {
145 child->addChildData(data);
146 }
147 }
148 }
149
150 [[nodiscard]] bool compressible(float threshold_sq)
151 {
152 // std::vector<value_type> child_colors;
153 // addChildData(child_colors);
154
155 // for (std::size_t i{}; child_colors.size() > i; ++i) {
156 // for (std::size_t j{i + 1}; child_colors.size() > j; ++j) {
157 // float de = deltaEOkSquared(child_colors[i], child_colors[j]);
158 // // std::println("Delta E squared: {}", de);
159 // if (threshold_sq < de) {
160 // return false;
161 // }
162 // }
163 // }
164
165 // return true;
166
167 if (!hasAllChildren()) {
168 return false;
169 }
170
171 std::array<FineLab, 4> colors{};
172 for (std::size_t i{}; 4 > i; ++i) {
173 colors[i] = children[i]->data;
174 }
175
176 FineLab mean = average(colors);
177
178 for (std::size_t i{}; colors.size() > i; ++i) {
179 if (threshold_sq < deltaEEuclideanSquared(mean, colors[i])) {
180 return false;
181 }
182 }
183
184 data = mean;
185
186 // for (std::size_t i{}; children.size() > i; ++i) {
187 // for (std::size_t j{i + 1}; children.size() > j; ++j) {
188 // if (threshold_sq < deltaEEuclideanSquared(children[i]->data, children[j]->data))
189 // { return false;
190 // }
191 // }
192 // }
193
194 return true;
195 }
196
197 bool compress(float threshold_sq)
198 {
199 // if (isLeaf()) {
200 // return;
201 // }
202
203 // // static std::size_t node_count = 0;
204 // // ++node_count;
205 // // std::println("Visiting node count: {}", node_count);
206
207 // if (compressible(threshold_sq)) {
208 // static std::size_t count = 0;
209 // ++count;
210 // std::println("Compressing node count: {}", count);
211 // for (auto& child : children) {
212 // child.reset();
213 // }
214 // } else {
215 // for (auto& child : children) {
216 // if (child) {
217 // child->compress(threshold_sq);
218 // }
219 // }
220 // }
221
222 if (isLeaf()) {
223 return true;
224 }
225
226 // static std::size_t node_count = 0;
227 // ++node_count;
228 // std::println("Visiting node count: {}", node_count);
229
230 bool all_children_compressed = true;
231 for (auto& child : children) {
232 if (child) {
233 bool comp = child->compress(threshold_sq);
234 all_children_compressed = all_children_compressed && comp;
235 }
236 }
237
238 if (all_children_compressed && compressible(threshold_sq)) {
239 static std::size_t count = 0;
240 ++count;
241 std::println("Compressing node count: {}", count);
242 for (auto& child : children) {
243 child.reset();
244 }
245 return true;
246 }
247
248 return false;
249 }
250
251 [[nodiscard]] std::uint32_t numLeaves() const
252 {
253 // if (isLeaf()) {
254 // return 1;
255 // }
256
257 std::uint32_t count = 0;
258 for (auto const& child : children) {
259 if (child) {
260 count += child->numLeaves();
261 }
262 }
263 return 0 == count ? 1 : count;
264 }
265};
266} // namespace detail
267
268[[nodiscard]] ImageProperties imagePropertiesQTP(std::filesystem::path const& file);
269
270template <ColorType CT, class T, bool Alpha, bool Weight>
271bool readQTP(std::filesystem::path const& file, Image<Color<CT, T, Alpha, Weight>>& image)
272{
273 FileHandler fp(file.c_str(), "rb");
274
275 if (!fp) {
276 std::println(stderr, "[UFO | Read QTP] Failed to open file: {}", file.string());
277 return false;
278 }
279
280 // TODO: Implement
281
282 return false;
283}
284
285// Compression quality (0..100; 5-95 is most useful range,\n"
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)
289{
290 FileHandler fp(file.c_str(), "wb");
291
292 if (!fp) {
293 std::println(stderr, "[UFO | Write QTP] Failed to create file: {}", file.string());
294 return false;
295 }
296
297 std::uint32_t width = image.cols();
298 std::uint32_t height = image.rows();
299 int num_channels = ColorType::GRAY == CT ? 1 : 3;
300
301 constexpr ColorType const CT2 =
302 ColorType::GRAY == CT ? ColorType::GRAY : ColorType::LAB;
303
304 detail::QTPNode<CT2, Alpha> root;
305
306 std::uint32_t num_levels = root.numLevelsRequired(height - 1, width - 1);
307
308 if constexpr (ColorType::GRAY == CT) {
309 // TODO: Implement
310 } else {
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);
316 }
317 }
318
319 float threshold = 0.0025;
320
321 root.compress(threshold * threshold);
322
323 std::println("Number of leaves: {}\n", root.numLeaves());
324 std::println("Original number of pixels: {}\n", width * height);
325
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));
330 }
331 }
332
333 writePNG(file.string() + ".png", new_image);
334 writeJPEG(file.string() + ".jpg", new_image);
335
336 // TODO: Implement
337 }
338
339 return true;
340}
341} // namespace ufo
342
343#endif // UFO_IO_QTP_HPP
RAII wrapper around a C std::FILE* handle.
Image class for storing and manipulating 2D pixel data.
Definition image.hpp:78
Identifies any UFO color instantiation (Gray, Lab, Lch, Lrgb, Rgb).
Definition concepts.hpp:73
constexpr C average(C a, C const &b)
Computes the average of two colors.
Definition average.hpp:77
constexpr float deltaEEuclideanSquared(C const &color, C const &sample)
Computes the squared Euclidean distance between two colors.
Definition delta_e.hpp:86
All vision-related classes and functions.
Definition cloud.hpp:49
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
Metadata describing the pixel layout of an image file.
Oklab perceptual color.
Definition lab.hpp:77