farm-ng-core
image_view.h
Go to the documentation of this file.
1 
2 // Copyright (c) 2011, Hauke Strasdat
3 // Copyright (c) 2012, Steven Lovegrove
4 // Copyright (c) 2021, farm-ng, inc.
5 //
6 // Use of this source code is governed by an MIT-style
7 // license that can be found in the LICENSE file or at
8 // https://opensource.org/licenses/MIT.
9 
10 /// ImageView and MutImageView, non owning images types.
11 ///
12 /// See image.h for Image and MutImage, owning images types.
13 ///
14 /// Note that it is a conscious API decision to follow "shallow-compare" type
15 /// semantic for ImageView, MutImageView, Image and MutImage. Similar
16 /// "shallow-compare" types are: std::span (shallow-compare reference type), and
17 /// std::unique_ptr (shallow compare unique ownership type).
18 /// This is in contrast to regular types such as std::vector, std::string and
19 /// reference types which mimic regular type semantic such as std::string_view.
20 /// Also see https://abseil.io/blog/20180531-regular-types.
21 
22 #pragma once
23 
24 #include "sophus/calculus/region.h"
25 #include "sophus/image/layout.h"
26 
27 namespace sophus {
28 
29 // Types are largely inspired / derived from Pangolin.
30 
31 template <class TPixel, class TAllocator>
32 class MutImage;
33 
34 template <class TPixel, class TAllocator>
35 class Image;
36 
37 /// A view of an (immutable) image, which does not own the data.
38 ///
39 /// The API of ImageView allows for read-only access. There is an escape hook
40 /// for write access, see MutImageView::unsafeConstCast below.
41 ///
42 /// ImageViews are nullable. In that case `this->isEmpty()` is true.
43 ///
44 ///
45 /// Details on equality comparison, the state of the object, and
46 /// const-correctness.
47 ///
48 /// ImageView is a "shallow-compare type" similar to std::span<Pixel const> and
49 /// std::unique_ptr<Pixel const>. In particular, we define that the state of an
50 /// ImageView instance consists of the layout of the image ``layout_`` (see
51 /// ImageLayout) and the pointer address to the first pixel ``ptr_``. No
52 /// public member method can change the pointer nor the layout, hence they are
53 /// all marked const.
54 template <class TPixel>
55 struct ImageView {
56  using Pixel = TPixel;
57 
58  /// Default constructor creates an empty image.
59  ImageView() = default;
60 
61  /// Creates view from layout and pointer to first pixel.
62  ImageView(ImageLayout layout, TPixel const* ptr) noexcept
63  : layout_(layout), ptr_(ptr) {}
64 
65  /// Creates view from image size and pointer to first pixel. The image is
66  /// assumed to be contiguous and the pitch is set accordingly.
67  explicit ImageView(sophus::ImageSize image_size, TPixel const* ptr) noexcept
68  : ImageView(ImageLayout::makeFromSize<TPixel>(image_size), ptr) {}
69 
70  /// Returns true if view is empty.
71  [[nodiscard]] auto isEmpty() const -> bool { return this->ptr_ == nullptr; }
72 
73  /// Returns true if view is contiguous.
74  [[nodiscard]] auto isContiguous() const -> bool {
75  return imageSize().width * sizeof(TPixel) == layout().pitchBytes();
76  }
77 
78  /// Returns ImageSize.
79  /// It is {0,0} if view is empty.
80  [[nodiscard]] auto imageSize() const -> sophus::ImageSize const& {
81  return layout_.imageSize();
82  }
83 
84  /// Returns ImageLayout.
85  /// It is {{0,0}, 0} is view is empty.
86  [[nodiscard]] auto layout() const -> ImageLayout const& { return layout_; }
87 
88  [[nodiscard]] auto area() const -> size_t { return layout().area(); }
89  [[nodiscard]] auto width() const -> int { return layout().width(); }
90  [[nodiscard]] auto height() const -> int { return layout().height(); }
91  [[nodiscard]] auto pitchBytes() const -> size_t {
92  return layout().pitchBytes();
93  }
94  [[nodiscard]] auto sizeBytes() const -> size_t {
95  return layout().sizeBytes();
96  }
97 
98  /// Returns true if u is in [0, width).
99  [[nodiscard]] auto colInBounds(int u) const -> bool {
100  return u >= 0 && u < layout_.width();
101  }
102 
103  /// Returns true if v is in [0, height).
104  [[nodiscard]] auto rowInBounds(int v) const -> bool {
105  return v >= 0 && v < layout_.height();
106  }
107 
108  /// Returns v-th row pointer.
109  ///
110  /// Precondition: v must be in [0, height).
111  [[nodiscard]] auto rowPtr(int v) const -> TPixel const* {
112  return (TPixel*)((uint8_t*)(ptr_) + v * layout_.pitchBytes());
113  }
114 
115  /// Returns pixel u, v.
116  ///
117  /// Precondition: u must be in [0, width) and v must be in [0, height).
118  ///
119  /// Note:
120  /// * No panic if u.v is invalid,
121  ///
122  /// This is not the most necessarily the efficient function to call - e.g.
123  /// when iterating over the whole image. Use the following instead:
124  ///
125  /// for (int v=0; v<view.layout().height(); ++v) {
126  /// TPixel const* row = img.rowPtr(v);
127  /// for (int u=0; u<view.layout().width(); ++u) {
128  /// PixetT p = row[u];
129  /// }
130  /// }
131  [[nodiscard]] auto operator()(int u, int v) const -> TPixel const& {
132  return rowPtr(v)[u];
133  }
134 
135  [[nodiscard]] auto operator()(Eigen::Vector2i uv) const -> TPixel const& {
136  return this->operator()(uv[0], uv[1]);
137  }
138 
139  /// Returns pointer to first pixel.
140  [[nodiscard]] auto ptr() const -> TPixel const* { return ptr_; }
141 
142  /// Returns subview.
143  [[nodiscard]] auto subview(Eigen::Vector2i uv, sophus::ImageSize size) const
144  -> ImageView {
145  SOPHUS_ASSERT(colInBounds(uv[0]));
146  SOPHUS_ASSERT(rowInBounds(uv[1]));
147  SOPHUS_ASSERT_LE(uv.x() + size.width, layout_.width());
148  SOPHUS_ASSERT_LE(uv.y() + size.height, layout_.height());
149  return ImageView(
150  ImageLayout::makeFromSizeAndPitch<TPixel>(size, layout_.pitchBytes()),
151  rowPtr(uv.y()) + uv.x());
152  }
153 
154  /// Performs reduction / fold on image view.
155  template <class TFunc>
156  void visit(TFunc const& user_function) const {
157  SOPHUS_ASSERT(!this->isEmpty());
158 
159  for (int v = 0; v < this->layout_.height(); ++v) {
160  TPixel const* p = this->rowPtr(v);
161  TPixel const* end_of_row = p + this->layout_.width();
162  for (; p != end_of_row; ++p) {
163  user_function(*p);
164  }
165  }
166  }
167 
168  /// Performs reduction / fold on image view.
169  template <class TReduceOp, class TVal>
170  [[nodiscard]] auto reduce(TReduceOp const& reduce_op, TVal val = TVal{}) const
171  -> TVal {
172  SOPHUS_ASSERT(!this->isEmpty()); // NOLINT
173 
174  for (int v = 0; v < this->layout_.height(); ++v) {
175  TPixel const* p = this->rowPtr(v);
176  TPixel const* end_of_row = p + this->layout_.width();
177  for (; p != end_of_row; ++p) {
178  reduce_op(*p, val);
179  }
180  }
181  return val;
182  }
183 
184  /// Performs reduction / fold on image view with short circuit condition.
185  template <class TShortCircuitReduceOp, class TVal>
186  [[nodiscard]] auto shortCircuitReduce(
187  TShortCircuitReduceOp const& short_circuit_reduce_op,
188  TVal val = TVal{}) const -> TVal {
189  SOPHUS_ASSERT(!this->isEmpty());
190 
191  for (int v = 0; v < this->layout_.height(); ++v) {
192  TPixel const* p = this->rowPtr(v);
193  TPixel const* end_of_row = p + this->layout_.width();
194  for (; p != end_of_row; ++p) {
195  if (short_circuit_reduce_op(*p, val)) {
196  return val;
197  }
198  }
199  }
200  return val;
201  }
202 
203  /// The equality operator is deleted to avoid confusion. Since ImageView is a
204  /// "shallow-copy" type, a consistently defined equality would check for
205  /// equality of its (shallow) state:
206  ///
207  /// ```this->layout_ == rhs.layout() && this->ptr_ == rhs.ptr_````
208  ///
209  /// However, some users might expect that equality would check for pixel
210  /// values equality and return true for identical copies of data blocks.
211  ///
212  /// Here we follow std::span which also does not offer equality comparisons.
213  auto operator==(ImageView const& rhs) const -> bool = delete;
214 
215  /// The in-equality operator is deleted to avoid confusion.
216  auto operator!=(ImageView const& rhs) const -> bool = delete;
217 
218  /// Returns true both views have the same size and contain the same data.
219  [[nodiscard]] auto hasSameData(ImageView const& rhs) const -> bool {
220  if (!(this->imageSize() == rhs.imageSize())) {
221  return false;
222  }
223  for (int v = 0; v < this->layout_.height(); ++v) {
224  TPixel const* p = this->rowPtr(v);
225  TPixel const* rhs_p = rhs.rowPtr(v);
226 
227  TPixel const* end_of_row = p + this->layout_.width();
228  for (; p != end_of_row; ++p, ++rhs_p) {
229  if (*p != *rhs_p) {
230  return false;
231  }
232  }
233  }
234  return true;
235  }
236 
237  protected:
238  /// Resets view such that it is empty.
239  void setViewToEmpty() { *this = {}; }
240 
241  ImageLayout layout_ = {}; // NOLINT
242  TPixel const* ptr_ = nullptr; // NOLINT
243 
244  private:
245  template <class TT, class TAllocator>
246  friend class MutImage;
247 
248  template <class TT, class TAllocator>
249  friend class Image;
250 };
251 
252 namespace details {
253 
254 template <class TPixel>
255 auto finiteInterval(sophus::ImageView<TPixel> const& image) -> Region<TPixel> {
256  return image.reduce(
257  [](TPixel v, auto& min_max) {
258  if (isFinite(v)) {
259  min_max.extend(v);
260  }
261  },
263 }
264 
265 // TODO: make member function?
266 template <class TPixel>
267 inline auto imageCoordsInterval(
268  sophus::ImageView<TPixel> const& image, int border = 0) -> Region2I {
269  return imageCoordsInterval(image.imageSize(), border);
270 }
271 
272 template <class TPixel>
273 [[nodiscard]] auto checkedPixelAccess(
274  ImageView<TPixel> const& view,
275  int u,
276  int v,
277  std::string const& file,
278  int line,
279  std::string const& str) -> TPixel const& {
280  if (!view.colInBounds(u) || !view.rowInBounds(v)) {
281  FARM_IMPL_LOG_PRINTLN("[SOPHUS_PIXEL in {}:{}]", file, line);
283  "pixel `{},{}` not in image with size {} x {}",
284  u,
285  v,
286  view.imageSize().width,
287  view.imageSize().height);
288  if (!str.empty()) {
289  ::fmt::print(stderr, "{}", str);
290  }
291  FARM_IMPL_ABORT();
292  }
293  return view(u, v);
294 }
295 } // namespace details
296 
297 } // namespace sophus
298 
299 #define SOPHUS_PIXEL_MUT(img, u, v, ...) \
300  ::sophus::details::checkedPixelAccessMut( \
301  img, u, v, __FILE__, __LINE__, SOPHUS_FORMAT(__VA_ARGS__))
sophus::ImageLayout::height
auto height() const -> int
Definition: layout.h:55
SOPHUS_ASSERT
#define SOPHUS_ASSERT(...)
Definition: common.h:40
FARM_IMPL_LOG_PRINTLN
#define FARM_IMPL_LOG_PRINTLN(...)
Definition: format.h:56
sophus::ImageView::width
auto width() const -> int
Definition: image_view.h:89
sophus::ImageView::isEmpty
auto isEmpty() const -> bool
Returns true if view is empty.
Definition: image_view.h:71
sophus::ImageView::Pixel
TPixel Pixel
Definition: image_view.h:56
sophus::ImageView::visit
void visit(TFunc const &user_function) const
Performs reduction / fold on image view.
Definition: image_view.h:156
sophus::ImageLayout::pitchBytes
auto pitchBytes() const -> size_t
Definition: layout.h:57
sophus::ImageView::setViewToEmpty
void setViewToEmpty()
Resets view such that it is empty.
Definition: image_view.h:239
core.event_service.str
str
Definition: event_service.py:547
sophus::Image
Image read-only access to pixels and shared ownership, hence cheap to copy. Type is nullable.
Definition: image.h:31
sophus::ImageLayout
Layout of the image: width, height and pitch in bytes.
Definition: layout.h:23
sophus
Image MutImage, owning images types.
Definition: num_diff.h:20
sophus::ImageView::area
auto area() const -> size_t
Definition: image_view.h:88
sophus::ImageView::ptr
auto ptr() const -> TPixel const *
Returns pointer to first pixel.
Definition: image_view.h:140
sophus::ImageView
A view of an (immutable) image, which does not own the data.
Definition: image_view.h:55
sophus::ImageView::rowInBounds
auto rowInBounds(int v) const -> bool
Returns true if v is in [0, height).
Definition: image_view.h:104
sophus::ImageView::hasSameData
auto hasSameData(ImageView const &rhs) const -> bool
Returns true both views have the same size and contain the same data.
Definition: image_view.h:219
sophus::ImageLayout::width
auto width() const -> int
Definition: layout.h:54
sophus::ImageView::layout
auto layout() const -> ImageLayout const &
Returns ImageLayout. It is {{0,0}, 0} is view is empty.
Definition: image_view.h:86
region.h
sophus::ImageView::imageSize
auto imageSize() const -> sophus::ImageSize const &
Returns ImageSize. It is {0,0} if view is empty.
Definition: image_view.h:80
sophus::Region::empty
static auto empty() noexcept -> Region< TPoint >
Creates and empty region.
Definition: region.h:72
sophus::ImageView::ImageView
ImageView()=default
Default constructor creates an empty image.
sophus::ImageView::operator!=
auto operator!=(ImageView const &rhs) const -> bool=delete
The in-equality operator is deleted to avoid confusion.
sophus::MutImage
A image with write access to pixels and exclusive ownership. There is no copy constr / copy assignmen...
Definition: image_view.h:32
sophus::ImageView::colInBounds
auto colInBounds(int u) const -> bool
Returns true if u is in [0, width).
Definition: image_view.h:99
sophus::ImageView::operator()
auto operator()(Eigen::Vector2i uv) const -> TPixel const &
Definition: image_view.h:135
sophus::ImageView::ImageView
ImageView(sophus::ImageSize image_size, TPixel const *ptr) noexcept
Creates view from image size and pointer to first pixel. The image is assumed to be contiguous and th...
Definition: image_view.h:67
sophus::imageCoordsInterval
auto imageCoordsInterval(ImageSize image_size, int border=0) -> Region2I
Definition: image_size.h:61
SOPHUS_ASSERT_LE
#define SOPHUS_ASSERT_LE(...)
Definition: common.h:44
sophus::ImageView::pitchBytes
auto pitchBytes() const -> size_t
Definition: image_view.h:91
FARM_IMPL_ABORT
#define FARM_IMPL_ABORT()
Definition: format.h:62
sophus::ImageView::isContiguous
auto isContiguous() const -> bool
Returns true if view is contiguous.
Definition: image_view.h:74
sophus::isFinite
auto isFinite(TPoint const &p) -> bool
Definition: vector_space.h:74
sophus::ImageView::operator==
auto operator==(ImageView const &rhs) const -> bool=delete
The equality operator is deleted to avoid confusion. Since ImageView is a "shallow-copy" type,...
sophus::ImageView::reduce
auto reduce(TReduceOp const &reduce_op, TVal val=TVal{}) const -> TVal
Performs reduction / fold on image view.
Definition: image_view.h:170
layout.h
sophus::ImageView::layout_
ImageLayout layout_
Definition: image_view.h:241
sophus::ImageView::sizeBytes
auto sizeBytes() const -> size_t
Definition: image_view.h:94
sophus::ImageView::ImageView
ImageView(ImageLayout layout, TPixel const *ptr) noexcept
Creates view from layout and pointer to first pixel.
Definition: image_view.h:62
sophus::ImageView::rowPtr
auto rowPtr(int v) const -> TPixel const *
Returns v-th row pointer.
Definition: image_view.h:111
sophus::ImageView::height
auto height() const -> int
Definition: image_view.h:90
sophus::Region2I
Region2< int > Region2I
Definition: region.h:27
sophus::ImageView::operator()
auto operator()(int u, int v) const -> TPixel const &
Returns pixel u, v.
Definition: image_view.h:131
sophus::ImageLayout::imageSize
auto imageSize() const -> sophus::ImageSize const &
Definition: layout.h:51
sophus::ImageView::ptr_
TPixel const * ptr_
Definition: image_view.h:242
sophus::ImageSize
Image size, hence its width and height.
Definition: image_size.h:21
sophus::Region
A region is a closed interval [a, b] with a being the lower bound (=min) and b being the upper bound ...
Definition: region.h:19
sophus::ImageView::shortCircuitReduce
auto shortCircuitReduce(TShortCircuitReduceOp const &short_circuit_reduce_op, TVal val=TVal{}) const -> TVal
Performs reduction / fold on image view with short circuit condition.
Definition: image_view.h:186
sophus::ImageView::subview
auto subview(Eigen::Vector2i uv, sophus::ImageSize size) const -> ImageView
Returns subview.
Definition: image_view.h:143