UFO 1.0.0
An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown
Loading...
Searching...
No Matches
image.hpp
1
41#ifndef UFO_VISION_IMAGE_HPP
42#define UFO_VISION_IMAGE_HPP
43
44// UFO
45#include <ufo/vision/color.hpp>
46
47// STL
48#include <algorithm>
49#include <cstddef>
50#include <format>
51#include <iterator>
52#include <memory>
53#include <ranges>
54#include <span>
55#include <stdexcept>
56#include <version>
57
58namespace ufo
59{
76template <class T>
77class Image
78{
79 public:
83 using value_type = T;
87 using size_type = std::size_t;
91 using difference_type = std::ptrdiff_t;
95 using reference = T&;
99 using const_reference = T const&;
103 using pointer = T*;
107 using const_pointer = T const*;
111 using iterator = T*;
115 using const_iterator = T const*;
119 using reverse_iterator = std::reverse_iterator<iterator>;
123 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
124
128 Image() = default;
129
134 Image(Image const& other)
135 : data_(std::make_unique_for_overwrite<T[]>(other.size()))
136 , rows_(other.rows_)
137 , cols_(other.cols_)
138 {
139 std::ranges::copy(other, begin());
140 }
141
147 Image(Image&&) = default;
148
156 Image(size_type rows, size_type cols, T const& value = T{})
157 : data_(std::make_unique_for_overwrite<T[]>(rows * cols)), rows_(rows), cols_(cols)
158 {
159 std::ranges::fill(*this, value);
160 }
161
169 Image& operator=(Image const& rhs)
170 {
171 if (this != &rhs) {
172 resize(rhs.rows_, rhs.cols_);
173 std::ranges::copy(rhs, begin());
174 }
175 return *this;
176 }
177
184 Image& operator=(Image&&) = default;
185
196 [[nodiscard]] auto& operator[](this auto& self, size_type index)
197 {
198 return self.data_[index];
199 }
200
210 [[nodiscard]] auto& operator[](this auto& self, size_type row, size_type col)
211 {
212 return self.data_[self.index(row, col)];
213 }
214
224 [[nodiscard]] auto& at(this auto& self, size_type row, size_type col)
225 {
226 if (self.rows() <= row || self.cols() <= col) {
227 throw std::out_of_range(
228 std::format("Image::at({}, {}): index out of range [0, {}) x [0, {})", row, col,
229 self.rows(), self.cols()));
230 }
231 return self.data_[self.index(row, col)];
232 }
233
242 [[nodiscard]] std::span<T> row(size_type r) noexcept
243 {
244 return {data_.get() + r * cols_, cols_};
245 }
246
252 [[nodiscard]] std::span<T const> row(size_type r) const noexcept
253 {
254 return {data_.get() + r * cols_, cols_};
255 }
256
269 [[nodiscard]] auto column(this auto& self, size_type c) noexcept
270 {
271 return std::ranges::iota_view(size_type{}, self.rows_) |
272 std::views::transform(
273 [&self, c](size_type r) -> decltype(auto) { return self[r, c]; });
274 }
275
282 [[nodiscard]] auto begin(this auto& self) noexcept { return self.data_.get(); }
283
288 [[nodiscard]] const_iterator cbegin() const noexcept { return begin(); }
289
296 [[nodiscard]] auto rbegin(this auto& self) noexcept
297 {
298 return std::make_reverse_iterator(self.end());
299 }
300
305 [[nodiscard]] const_reverse_iterator crbegin() const noexcept { return rbegin(); }
306
313 [[nodiscard]] auto end(this auto& self) noexcept
314 {
315 return self.data_.get() + self.size();
316 }
317
322 [[nodiscard]] const_iterator cend() const noexcept { return end(); }
323
330 [[nodiscard]] auto rend(this auto& self) noexcept
331 {
332 return std::make_reverse_iterator(self.begin());
333 }
334
339 [[nodiscard]] const_reverse_iterator crend() const noexcept { return rend(); }
340
349 [[nodiscard]] auto data(this auto& self) noexcept { return self.data_.get(); }
350
356 [[nodiscard]] bool empty() const noexcept { return 0 == size(); }
357
362 [[nodiscard]] constexpr size_type rows() const noexcept { return rows_; }
363
368 [[nodiscard]] constexpr size_type cols() const noexcept { return cols_; }
369
374 [[nodiscard]] constexpr size_type size() const noexcept { return rows_ * cols_; }
375
384 [[nodiscard]] constexpr size_type index(size_type row, size_type col) const noexcept
385 {
386 return row * cols_ + col;
387 }
388
395 void swap(Image& other) noexcept
396 {
397 using std::swap;
398 swap(data_, other.data_);
399 swap(rows_, other.rows_);
400 swap(cols_, other.cols_);
401 }
402
412 {
413 if (rows * cols != size()) {
414 data_ = std::make_unique_for_overwrite<T[]>(rows * cols);
415 }
416 rows_ = rows;
417 cols_ = cols;
418 }
419
424 void fill(T const& value) { std::ranges::fill(*this, value); }
425
429 void clear() noexcept
430 {
431 data_.reset();
432 rows_ = 0;
433 cols_ = 0;
434 }
435
440 [[nodiscard]] Image transposed() const
441 {
442 Image ret(cols_, rows_);
443 for (size_type r = 0; r < rows(); ++r) {
444 for (size_type c = 0; c < cols(); ++c) {
445 ret[c, r] = (*this)[r, c];
446 }
447 }
448 return ret;
449 }
450
454 void flipHorizontal() noexcept
455 {
456 for (size_type r = 0; r < rows(); ++r) {
457 auto r_span = row(r);
458 std::ranges::reverse(r_span);
459 }
460 }
461
465 void flipVertical() noexcept
466 {
467 for (size_type r = 0; r < rows() / 2; ++r) {
468 auto r1 = row(r);
469 auto r2 = row(rows() - 1 - r);
470 std::ranges::swap_ranges(r1, r2);
471 }
472 }
473
480 [[nodiscard]] Image rotate90(bool clockwise = true) const
481 {
482 Image ret(cols_, rows_);
483 if (clockwise) {
484 for (size_type r = 0; r < rows(); ++r) {
485 for (size_type c = 0; c < cols(); ++c) {
486 ret[c, rows() - 1 - r] = (*this)[r, c];
487 }
488 }
489 } else {
490 for (size_type r = 0; r < rows(); ++r) {
491 for (size_type c = 0; c < cols(); ++c) {
492 ret[cols() - 1 - c, r] = (*this)[r, c];
493 }
494 }
495 }
496 return ret;
497 }
498
508 [[nodiscard]] T sample(float r, float c) const
509 {
510 if (empty()) {
511 return T{};
512 }
513
514 r = std::clamp(r, 0.0f, 1.0f) * static_cast<float>(rows() - 1);
515 c = std::clamp(c, 0.0f, 1.0f) * static_cast<float>(cols() - 1);
516
517 size_type r0 = static_cast<size_type>(std::floor(r));
518 size_type c0 = static_cast<size_type>(std::floor(c));
519 size_type r1 = std::min(r0 + 1, rows() - 1);
520 size_type c1 = std::min(c0 + 1, cols() - 1);
521
522 float dr = r - static_cast<float>(r0);
523 float dc = c - static_cast<float>(c0);
524
525 auto s00 = (*this)[r0, c0];
526 auto s01 = (*this)[r0, c1];
527 auto s10 = (*this)[r1, c0];
528 auto s11 = (*this)[r1, c1];
529
530 if constexpr (requires { s00 * 1.0f; }) {
531 return static_cast<T>(s00 * ((1.0f - dr) * (1.0f - dc)) + s01 * ((1.0f - dr) * dc) +
532 s10 * (dr * (1.0f - dc)) + s11 * (dr * dc));
533 } else {
534 return (dr < 0.5f) ? ((dc < 0.5f) ? s00 : s01) : ((dc < 0.5f) ? s10 : s11);
535 }
536 }
537
546 [[nodiscard]] T samplePixel(float r, float c) const
547 {
548 if (empty()) {
549 return T{};
550 }
551 float r_norm = (rows() > 1) ? r / static_cast<float>(rows() - 1) : 0.0f;
552 float c_norm = (cols() > 1) ? c / static_cast<float>(cols() - 1) : 0.0f;
553 return sample(r_norm, c_norm);
554 }
555
562 {
563 if (rows == rows_ && cols == cols_) {
564 return;
565 }
566 *this = rescaled(rows, cols);
567 }
568
576 {
577 Image ret(rows, cols);
578 for (size_type r = 0; r < rows; ++r) {
579 float r_norm = (rows > 1) ? static_cast<float>(r) / (rows - 1) : 0.0f;
580 for (size_type c = 0; c < cols; ++c) {
581 float c_norm = (cols > 1) ? static_cast<float>(c) / (cols - 1) : 0.0f;
582 ret[r, c] = sample(r_norm, c_norm);
583 }
584 }
585 return ret;
586 }
587
592 void upscale(float factor)
593 {
594 rescale(static_cast<size_type>(rows() * factor),
595 static_cast<size_type>(cols() * factor));
596 }
597
602 void downscale(float factor)
603 {
604 rescale(static_cast<size_type>(rows() / factor),
605 static_cast<size_type>(cols() / factor));
606 }
607
608 private:
609 std::unique_ptr<T[]> data_;
610 size_type rows_{};
611 size_type cols_{};
612};
613
619template <class T>
620void swap(Image<T>& lhs, Image<T>& rhs) noexcept
621{
622 lhs.swap(rhs);
623}
624
649
650//
651// Convert
652//
653
664template <class From, class To>
665void convert(Image<From> const& src, Image<To>& dest)
666{
667 if constexpr (std::is_same_v<To, From>) {
668 dest = src;
669 } else {
670 dest.resize(src.rows(), src.cols());
671 std::ranges::transform(src, dest.begin(),
672 [](auto const& x) { return convert<To>(x); });
673 }
674}
675
686template <class To, class From>
687[[nodiscard]] Image<To> convert(Image<From> const& image)
688{
689 if constexpr (std::is_same_v<To, From>) {
690 return image;
691 } else {
692 Image<To> ret(image.rows(), image.cols());
693 std::ranges::transform(image, ret.begin(),
694 [](auto const& x) { return convert<To>(x); });
695 return ret;
696 }
697}
698
702} // namespace ufo
703
704#endif // UFO_VISION_IMAGE_HPP
Image class for storing and manipulating 2D pixel data.
Definition image.hpp:78
Image transposed() const
Returns a transposed copy of the image.
Definition image.hpp:440
void swap(Image &other) noexcept
Swaps the contents of this image with other.
Definition image.hpp:395
std::span< T const > row(size_type r) const noexcept
Returns a const span over all pixels in the given row.
Definition image.hpp:252
void resize(size_type rows, size_type cols)
Resizes the image to the given dimensions.
Definition image.hpp:411
Image(Image &&)=default
Move-constructs an image, transferring ownership of pixel data.
const_reverse_iterator crbegin() const noexcept
Returns a const reverse iterator to the last pixel.
Definition image.hpp:305
Image & operator=(Image &&)=default
Move-assigns from another image, transferring ownership of pixel data.
Image()=default
Constructs an empty image with no pixels (rows = cols = 0).
void downscale(float factor)
Downscales the image by the given factor.
Definition image.hpp:602
Image & operator=(Image const &rhs)
Copy-assigns from another image, performing a deep copy of all pixel data.
Definition image.hpp:169
const_iterator cend() const noexcept
Returns a const iterator one past the last pixel.
Definition image.hpp:322
void clear() noexcept
Clears the image, releasing all memory.
Definition image.hpp:429
Image rescaled(size_type rows, size_type cols) const
Returns a rescaled copy of the image using bilinear interpolation.
Definition image.hpp:575
void upscale(float factor)
Upscales the image by the given factor.
Definition image.hpp:592
T * pointer
The type of a pointer to an element.
Definition image.hpp:103
std::span< T > row(size_type r) noexcept
Returns a span over all pixels in the given row.
Definition image.hpp:242
auto & at(this auto &self, size_type row, size_type col)
Returns a (const) reference to the pixel at (row, col) with bounds checking.
Definition image.hpp:224
auto data(this auto &self) noexcept
Returns a pointer to the underlying contiguous pixel array.
Definition image.hpp:349
T sample(float r, float c) const
Samples the image at (row, col) using bilinear interpolation.
Definition image.hpp:508
const_iterator cbegin() const noexcept
Returns a const iterator to the first pixel.
Definition image.hpp:288
auto rend(this auto &self) noexcept
Returns a reverse iterator to one before the first pixel.
Definition image.hpp:330
Image rotate90(bool clockwise=true) const
Returns a rotated copy of the image.
Definition image.hpp:480
T * iterator
The type of an iterator to an element.
Definition image.hpp:111
std::size_t size_type
The type of the size of the image.
Definition image.hpp:87
constexpr size_type index(size_type row, size_type col) const noexcept
Computes the flat row-major index for position (row, col).
Definition image.hpp:384
std::reverse_iterator< iterator > reverse_iterator
The type of a reverse iterator to an element.
Definition image.hpp:119
constexpr size_type size() const noexcept
Returns the total number of pixels (rows() * cols()).
Definition image.hpp:374
const_reverse_iterator crend() const noexcept
Returns a const reverse iterator to one before the first pixel.
Definition image.hpp:339
T const * const_pointer
The type of a const pointer to an element.
Definition image.hpp:107
Image(Image const &other)
Copy-constructs an image, performing a deep copy of all pixel data.
Definition image.hpp:134
auto & operator[](this auto &self, size_type index)
Returns a (const) reference to the pixel at the given flat index.
Definition image.hpp:196
void flipHorizontal() noexcept
Flips the image horizontally in-place.
Definition image.hpp:454
T & reference
The type of a reference to an element.
Definition image.hpp:95
T value_type
The type of the elements stored in the image.
Definition image.hpp:83
Image(size_type rows, size_type cols, T const &value=T{})
Constructs an image with the given dimensions, filling every pixel with value.
Definition image.hpp:156
std::ptrdiff_t difference_type
The type of the difference between two sizes.
Definition image.hpp:91
bool empty() const noexcept
Returns true if the image has no pixels (i.e., size() == 0).
Definition image.hpp:356
constexpr size_type rows() const noexcept
Returns the number of rows (height) in the image.
Definition image.hpp:362
T const & const_reference
The type of a const reference to an element.
Definition image.hpp:99
auto end(this auto &self) noexcept
Returns an iterator one past the last pixel.
Definition image.hpp:313
void flipVertical() noexcept
Flips the image vertically in-place.
Definition image.hpp:465
T const * const_iterator
The type of a const iterator to an element.
Definition image.hpp:115
auto begin(this auto &self) noexcept
Returns an iterator to the first pixel.
Definition image.hpp:282
T samplePixel(float r, float c) const
Samples the image at pixel (row, col) using bilinear interpolation.
Definition image.hpp:546
void rescale(size_type rows, size_type cols)
Rescales the image to the given dimensions using bilinear interpolation.
Definition image.hpp:561
auto column(this auto &self, size_type c) noexcept
Returns a lazy view over all pixels in the given column.
Definition image.hpp:269
std::reverse_iterator< const_iterator > const_reverse_iterator
The type of a const reverse iterator to an element.
Definition image.hpp:123
auto & operator[](this auto &self, size_type row, size_type col)
Returns a (const) reference to the pixel at (row, col).
Definition image.hpp:210
void fill(T const &value)
Fills the image with the given value.
Definition image.hpp:424
constexpr size_type cols() const noexcept
Returns the number of columns (width) in the image.
Definition image.hpp:368
auto rbegin(this auto &self) noexcept
Returns a reverse iterator to the last pixel.
Definition image.hpp:296
STL namespace.
All vision-related classes and functions.
Definition cloud.hpp:49
constexpr To convert(Vec< Dim, U > const &v) noexcept
Converts a vector to a different Vec type, truncating or zero-padding dimensions.
Definition vec.hpp:1059