Banuba SDK
transformation.hpp
1 #pragma once
2 
3 #include <bnb/types/base_types.hpp>
4 #include <bnb/utils/event.hpp>
5 #include <bnb/utils/defs.hpp>
6 #include <bnb/types/pixel_rect.hpp>
9 
10 
11 #include <memory>
12 #include <array>
13 #include <optional>
14 
15 namespace bnb
16 {
17  /**
18  * @addtogroup Types
19  * @{
20  */
21 
22  enum class rect_fit_mode : uint8_t
23  {
24  fit_width, //!< Always fit to width, rect_scale = w_t / w_f
25  fit_height, //!< Always fit to height, rect_scale = h_t / h_f
26  fit_inside, //!< Fit all source inside target = fit to min rect_scale of width and height modes
27  fit_outside //!< Fit to fill all target = fit to max rect_scale of width and height modes
28  };
29 
30  //! Adjust rects to have the same aspect ratio as if fitting source_rect into target_rect according to mode,
31  //! return source_rect, target_rect have the same (+-rounding) aspect ratio,
32  //! and are always not exceeding corresponding input rect, preserving original centers.
33  //! May do some per-axis scale adjustments within small margin for fast, integral and/or pixel-perfect scaling between pixel rects
34  void fit_rects_aspect_ratio(pixel_rect& source_rect, pixel_rect& target_rect, rect_fit_mode mode = rect_fit_mode::fit_inside);
35 
36  void fill_exterior(uint8_t* pixels, uint32_t width, uint32_t height, uint32_t channels, pixel_rect rect, uint8_t color, bool edge_repeat = false);
37 
38 
39  //! This class implements affine or perspective 2d transformations
40  //! Standard usage implies transformation from common basis into other(common, image or standard) basis
41  //! (e.g. written as (common -> image) in documentation)
42  //!
43  //!
44  //! Frequently used basises are:
45  //! <f ^f >f vf: direction of face-up vector left, up, right, down
46  //!
47  //! standard basis:
48  //! ^ y
49  //! | ^f
50  //! .--> x
51  //!
52  //! common basis =
53  //! image(buffer) basis:
54  //! .--> x
55  //! | ^f
56  //! v y
57  //!
58  class BNB_EXPORT transformation : public interfaces::transformation
59  {
60  public:
61  static const int mat_s = 3;
62 
63  /// 3x3 row-maj transform matrix
64  using mat_t = std::array<float, mat_s * mat_s>;
65 
66  /// Rotation is counter clockwise(only in standart basis):
67  /// - Rotating vector(1,0) (= x basis vector) by 90deg
68  /// results in vector(0,1) (= y basis vector)
69  /// - In image buffer basis(y points down) the rotation would go clockwise
70  enum class rotate_t : uint32_t
71  {
72  deg_0 = 0,
73  deg_90 = 90,
74  deg_180 = 180,
75  deg_270 = 270
76  };
77 
79  {
80  float scale_x = 1.0f, scale_y = 1.0f;
81  float trans_x = 0.0f, trans_y = 0.0f;
82  rotate_t rotate = rotate_t::deg_0;
83  bool flip_x = false, flip_y = false;
84 
85  // approximate comparison for float components
86  bool operator==(const affine_coeffs_t& t) const noexcept;
87  };
88 
89  /// Constructs identity transform
91 
92  /// Constructs from mat_t
93  explicit transformation(const mat_t& mat);
94  explicit transformation(mat_t::const_pointer mat);
95 
96  /// Constructs rotate transformation
97  explicit transformation(rotate_t rotate);
98 
99  /// Constructs affine transformation
100  explicit transformation(float scale_x, float scale_y = 1.f, float t_x = 0, float t_y = 0, rotate_t rotate = rotate_t::deg_0, bool flip_x = false, bool flip_y = false);
101 
102  /// Constructs affine transformation
103  explicit transformation(affine_coeffs_t coeffs);
104 
105  /// Constructs transformation from source to target rectangle
106  /// Rotation and flips are around rectangles' center
107  explicit transformation(pixel_rect source_rect, pixel_rect target_rect, rotate_t rotate = rotate_t::deg_0, bool flip_x = false, bool flip_y = false);
108 
109  ~transformation();
110 
111  transformation(transformation&& t) noexcept;
112  transformation& operator=(transformation&& t) noexcept;
113 
114  transformation(const transformation& t);
115  transformation& operator=(const transformation& t);
116 
117  /// Applies transform t after this
118  /// e.g. {rotate >> translate;} rotates first: (initial -> rotated) >> (rotated -> translated) = (initial -> translated)
119  transformation operator>>(const transformation& t) const noexcept;
120 
121  /// Apply transform to point
122  point2d operator*(const point2d& point) const noexcept;
123 
124  bool operator==(const transformation& t) const noexcept;
125 
126  /**
127  * Get the inverse of the transformation
128  * @throw std::logic_error when matrix is singular
129  */
130  transformation inverse() const;
131 
132  /// Clone the transformation
133  transformation clone() const noexcept;
134 
135  /// Pointer to matrix data in memory (row major)
136  mat_t::const_pointer data() const noexcept;
137 
138  /// Returns 3x3 row-maj transform matrix
139  mat_t get_mat() const noexcept;
140 
141  /// Returns transposed matrix data (column major, opengl)
142  mat_t transposed_data() const noexcept;
143 
144  /// Cast to string for debug purposes
145  std::string to_string() const noexcept;
146 
147  /// Checks if last row is [0, 0, 1] up to float precision
148  bool is_affine() const noexcept;
149 
150  /// Normalizes transform to be exactly affine.
151  /// Results are invalid in case last row is not [~0, ~0, C]
152  void normalize_affine() noexcept;
153 
154  /// Tries to extract affine coefficients from transformation
155  std::optional<transformation::affine_coeffs_t> extract_affine_coeffs() const;
156 
157  /// Get reference to static flip-only transform
158  static const transformation& get_flip_instance(bool flip_x, bool flip_y);
159 
160  // iface
161  std::shared_ptr<interfaces::transformation> chain_right(const std::shared_ptr<interfaces::transformation>& t) const override
162  {
163  return std::make_shared<transformation>(*this >> static_cast<transformation&>(*t));
164  }
165  interfaces::point2d transform_point(const interfaces::point2d& p) const override;
166  interfaces::pixel_rect transform_rect(const interfaces::pixel_rect& rect) const override;
167  bool equals(const std::shared_ptr<interfaces::transformation>& t) const override
168  {
169  return *this == static_cast<transformation&>(*t);
170  }
171  std::shared_ptr<interfaces::transformation> inverse_j() const override
172  {
173  return std::make_shared<transformation>(inverse());
174  }
175  std::shared_ptr<interfaces::transformation> clone_j() const override
176  {
177  return std::make_shared<transformation>(clone());
178  }
179  std::vector<float> get_mat_j() const override
180  {
181  auto m = get_mat();
182  return {m.begin(), m.end()};
183  }
184 
185  private:
186  mat_t m_mat;
187  };
188 
189  template<typename T>
190  struct transformable_event : public base_event<T>
191  {
192  /// (common -> some event data basis) transformation
194  /// rectangle area in common basis that encloses all valid & usable data
196  };
197 
198 
199  /**
200  * Transform src image to dst
201  * @param src, src_w, src_h source uint8_t image buffer, width and height
202  * @param dst, dst_w, dst_h destination uint8_t image buffer, width and height
203  * @param channels number of channels per pixel
204  * @param t transformation (src -> dst) to apply
205  */
206  void transform(const uint8_t* src, uint32_t src_w, uint32_t src_h, uint8_t* dst, uint32_t dst_w, uint32_t dst_h, uint32_t channels, const transformation& t);
207 
208 
209  /**
210  * Transform src image to dst
211  * @param src, src_w, src_h source 32 bit float image buffer, width and height
212  * @param dst, dst_w, dst_h destination 32 bit float image buffer, width and height
213  * @param channels number of channels per pixel
214  * @param t transformation (src -> dst) to apply
215  */
216  void transform(const float* src, uint32_t src_w, uint32_t src_h, float* dst, uint32_t dst_w, uint32_t dst_h, uint32_t channels, const transformation& t);
217 
218 
219  /**
220  * Transform src image to dst through common basis
221  * NOTE: both transformations should be (common -> image)
222  * @param src, src_w, src_h source image buffer, width and height
223  * @param dst, dst_w, dst_h destination image buffer, width and height
224  * @param channels number of channels per pixel
225  * @param from,to transformations (common -> image) to apply by formula (from -> common -> to)
226  */
227  inline void transform(const uint8_t* src, uint32_t src_w, uint32_t src_h, uint8_t* dst, uint32_t dst_w, uint32_t dst_h, uint32_t channels, const transformation& from, const transformation& to)
228  {
229  transform(src, src_w, src_h, dst, dst_w, dst_h, channels, from.inverse() >> to);
230  }
231 
232  /**
233  * Transform src image to dst
234  * param src, dst source, destination image buffers
235  * param w, h source, destination width and height
236  * param channels number of channels per pixel
237  * param t transformation (src -> dst) to apply
238  */
239  inline void transform1x1(const uint8_t* src, uint8_t* dst, uint32_t w, uint32_t h, uint32_t channels, const transformation& t)
240  {
241  return transform(src, w, h, dst, w, h, channels, t);
242  }
243 
244 
245  /**
246  * Apply transformation to point
247  * @param point 2D point to transform
248  * @param t transformation to apply
249  * @return result 2D point
250  */
251  point2d transform(const point2d& point, const transformation& t);
252 
253  /**
254  * Transform point through common basis
255  * Apply transformation to point from some basis to another one through common basis
256  * NOTE: both transformations should be (common -> image)
257  * param point 2D point to transform
258  * param from,to transformations (common -> image) to apply by formula (from -> common -> to)
259  * return result 2D point
260  */
261  inline point2d transform(const point2d& point, const transformation& from, const transformation& to)
262  {
263  return transform(point, from.inverse() >> to);
264  }
265 
266  /**
267  * Apply transformation to rect
268  * Result is normalized wrt. flips/rotates so that w > 0 && h > 0
269  * @param rect pixel_rect to transform
270  * @param t transformation to apply
271  * @return result pixel_rect
272  */
273  pixel_rect transform(const pixel_rect& rect, const transformation& t);
274 
275 
277  {
278  auto pt = bnb::transform(point2d{{p.x, p.y}}, *this);
279  return {pt.x, pt.y};
280  }
281  inline interfaces::pixel_rect transformation::transform_rect(const interfaces::pixel_rect& rect) const
282  {
283  return bnb::transform({rect}, *this).get_iface();
284  }
285 
286  /** @} */ // endgroup types
287 
288 } // namespace bnb
bnb::pixel_rect
Definition: pixel_rect.hpp:14
bnb::interfaces::transformation
Definition: transformation.hpp:20
point2d.hpp
bnb::transformation::rotate_t
rotate_t
Rotation is counter clockwise(only in standart basis):
Definition: transformation.hpp:70
bnb::transformation::transform_point
interfaces::point2d transform_point(const interfaces::point2d &p) const override
Apply transform to point.
Definition: transformation.hpp:276
bnb::transform1x1
void transform1x1(const uint8_t *src, uint8_t *dst, uint32_t w, uint32_t h, uint32_t channels, const transformation &t)
Transform src image to dst param src, dst source, destination image buffers param w,...
Definition: transformation.hpp:239
bnb::transformable_event
Definition: transformation.hpp:190
bnb::rect_fit_mode::fit_inside
@ fit_inside
Fit all source inside target = fit to min rect_scale of width and height modes.
bnb::transformation::affine_coeffs_t
Definition: transformation.hpp:78
bnb::rect_fit_mode::fit_height
@ fit_height
Always fit to height, rect_scale = h_t / h_f.
bnb::fit_rects_aspect_ratio
void fit_rects_aspect_ratio(pixel_rect &source_rect, pixel_rect &target_rect, rect_fit_mode mode=rect_fit_mode::fit_inside)
Adjust rects to have the same aspect ratio as if fitting source_rect into target_rect according to mo...
bnb::transformation
This class implements affine or perspective 2d transformations Standard usage implies transformation ...
Definition: transformation.hpp:58
bnb::interfaces::pixel_rect
Definition: pixel_rect.hpp:15
bnb::point2d
Definition: base_types.hpp:42
bnb::rect_fit_mode::fit_width
@ fit_width
Always fit to width, rect_scale = w_t / w_f.
bnb::transformation::mat_t
std::array< float, mat_s *mat_s > mat_t
3x3 row-maj transform matrix
Definition: transformation.hpp:64
bnb::transformable_event::basis_transform
transformation basis_transform
(common -> some event data basis) transformation
Definition: transformation.hpp:193
bnb::interfaces::point2d
Definition: point2d.hpp:14
bnb::transformation::inverse_j
std::shared_ptr< interfaces::transformation > inverse_j() const override
Get the inverse of the transformation.
Definition: transformation.hpp:171
bnb::transformation::get_mat_j
std::vector< float > get_mat_j() const override
Returns 3x3 row-maj transform matrix.
Definition: transformation.hpp:179
bnb::rect_fit_mode
rect_fit_mode
Definition: transformation.hpp:22
bnb::transformation::inverse
transformation inverse() const
Get the inverse of the transformation.
transformation.hpp
bnb::transformable_event::full_roi
pixel_rect full_roi
rectangle area in common basis that encloses all valid & usable data
Definition: transformation.hpp:195
bnb::rect_fit_mode::fit_outside
@ fit_outside
Fit to fill all target = fit to max rect_scale of width and height modes.
bnb::base_event
Definition: event.hpp:51
bnb::transform
pixel_rect transform(const pixel_rect &rect, const transformation &t)
Apply transformation to rect Result is normalized wrt.
bnb::transformation::clone_j
std::shared_ptr< interfaces::transformation > clone_j() const override
Clone the transformation.
Definition: transformation.hpp:175