dynamic_image.rst 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. Dynamic images and image views
  2. ==============================
  3. The GIL extension called ``dynamic_image`` allows for images, image views
  4. or any GIL constructs to have their parameters defined at run time.
  5. The color space, channel depth, channel ordering, and interleaved/planar
  6. structure of an image are defined by the type of its template argument, which
  7. makes them compile-time bound. Often some of these parameters are available
  8. only at run time. Consider, for example, writing a module that opens the image
  9. at a given file path, rotates it and saves it back in its original color space
  10. and channel depth. How can we possibly write this using our generic image?
  11. What type is the image loading code supposed to return?
  12. Here is an example:
  13. .. code-block:: cpp
  14. #include <boost/gil/extension/dynamic_image/dynamic_image_all.hpp>
  15. using namespace boost;
  16. #define ASSERT_SAME(A,B) static_assert(is_same< A,B >::value, "")
  17. // Define the set of allowed images
  18. typedef mpl::vector<rgb8_image_t, cmyk16_planar_image_t> my_images_t;
  19. // Create any_image class (or any_image_view) class
  20. typedef any_image<my_images_t> my_any_image_t;
  21. // Associated view types are available (equivalent to the ones in image_t)
  22. typedef any_image_view<mpl::vector2<rgb8_view_t, cmyk16_planar_view_t > > AV;
  23. ASSERT_SAME(my_any_image_t::view_t, AV);
  24. typedef any_image_view<mpl::vector2<rgb8c_view_t, cmyk16c_planar_view_t> > CAV;
  25. ASSERT_SAME(my_any_image_t::const_view_t, CAV);
  26. ASSERT_SAME(my_any_image_t::const_view_t, my_any_image_t::view_t::const_t);
  27. typedef any_image_view<mpl::vector2<rgb8_step_view_t, cmyk16_planar_step_view_t> > SAV;
  28. ASSERT_SAME(typename dynamic_x_step_type<my_any_image_t::view_t>::type, SAV);
  29. // Assign it a concrete image at run time:
  30. my_any_image_t myImg = my_any_image_t(rgb8_image_t(100,100));
  31. // Change it to another at run time. The previous image gets destroyed
  32. myImg = cmyk16_planar_image_t(200,100);
  33. // Assigning to an image not in the allowed set throws an exception
  34. myImg = gray8_image_t(); // will throw std::bad_cast
  35. The ``any_image`` and ``any_image_view`` subclass from GIL ``variant`` class,
  36. which breaks down the instantiated type into a non-templated underlying base
  37. type and a unique instantiation type identifier. The underlying base instance
  38. is represented as a block of bytes.
  39. The block is large enough to hold the largest of the specified types.
  40. GIL variant is similar to ``boost::variant`` in spirit (hence we borrow the
  41. name from there) but it differs in several ways from the current boost
  42. implementation. Perhaps the biggest difference is that GIL variant always
  43. takes a single argument, which is a model of MPL Random Access Sequence
  44. enumerating the allowed types. Having a single interface allows GIL variant
  45. to be used easier in generic code. Synopsis:
  46. .. code-block:: cpp
  47. template <typename Types> // models MPL Random Access Container
  48. class variant
  49. {
  50. ... _bits;
  51. std::size_t _index;
  52. public:
  53. typedef Types types_t;
  54. variant();
  55. variant(const variant& v);
  56. virtual ~variant();
  57. variant& operator=(const variant& v);
  58. template <typename TS> friend bool operator==(const variant<TS>& x, const variant<TS>& y);
  59. template <typename TS> friend bool operator!=(const variant<TS>& x, const variant<TS>& y);
  60. // Construct/assign to type T. Throws std::bad_cast if T is not in Types
  61. template <typename T> explicit variant(const T& obj);
  62. template <typename T> variant& operator=(const T& obj);
  63. // Construct/assign by swapping T with its current instance. Only possible if they are swappable
  64. template <typename T> explicit variant(T& obj, bool do_swap);
  65. template <typename T> void move_in(T& obj);
  66. template <typename T> static bool has_type();
  67. template <typename T> const T& _dynamic_cast() const;
  68. template <typename T> T& _dynamic_cast();
  69. template <typename T> bool current_type_is() const;
  70. };
  71. template <typename UOP, typename Types>
  72. UOP::result_type apply_operation(variant<Types>& v, UOP op);
  73. template <typename UOP, typename Types>
  74. UOP::result_type apply_operation(const variant<Types>& v, UOP op);
  75. template <typename BOP, typename Types1, typename Types2>
  76. BOP::result_type apply_operation( variant<Types1>& v1, variant<Types2>& v2, UOP op);
  77. template <typename BOP, typename Types1, typename Types2>
  78. BOP::result_type apply_operation(const variant<Types1>& v1, variant<Types2>& v2, UOP op);
  79. template <typename BOP, typename Types1, typename Types2>
  80. BOP::result_type apply_operation(const variant<Types1>& v1, const variant<Types2>& v2, UOP op);
  81. GIL ``any_image_view`` and ``any_image`` are subclasses of ``variant``:
  82. .. code-block:: cpp
  83. template <typename ImageViewTypes>
  84. class any_image_view : public variant<ImageViewTypes>
  85. {
  86. public:
  87. typedef ... const_t; // immutable equivalent of this
  88. typedef std::ptrdiff_t x_coord_t;
  89. typedef std::ptrdiff_t y_coord_t;
  90. typedef point<std::ptrdiff_t> point_t;
  91. any_image_view();
  92. template <typename T> explicit any_image_view(const T& obj);
  93. any_image_view(const any_image_view& v);
  94. template <typename T> any_image_view& operator=(const T& obj);
  95. any_image_view& operator=(const any_image_view& v);
  96. // parameters of the currently instantiated view
  97. std::size_t num_channels() const;
  98. point_t dimensions() const;
  99. x_coord_t width() const;
  100. y_coord_t height() const;
  101. };
  102. template <typename ImageTypes>
  103. class any_image : public variant<ImageTypes>
  104. {
  105. typedef variant<ImageTypes> parent_t;
  106. public:
  107. typedef ... const_view_t;
  108. typedef ... view_t;
  109. typedef std::ptrdiff_t x_coord_t;
  110. typedef std::ptrdiff_t y_coord_t;
  111. typedef point<std::ptrdiff_t> point_t;
  112. any_image();
  113. template <typename T> explicit any_image(const T& obj);
  114. template <typename T> explicit any_image(T& obj, bool do_swap);
  115. any_image(const any_image& v);
  116. template <typename T> any_image& operator=(const T& obj);
  117. any_image& operator=(const any_image& v);
  118. void recreate(const point_t& dims, unsigned alignment=1);
  119. void recreate(x_coord_t width, y_coord_t height, unsigned alignment=1);
  120. std::size_t num_channels() const;
  121. point_t dimensions() const;
  122. x_coord_t width() const;
  123. y_coord_t height() const;
  124. };
  125. Operations are invoked on variants via ``apply_operation`` passing a
  126. function object to perform the operation. The code for every allowed
  127. type in the variant is instantiated and the appropriate instantiation
  128. is selected via a switch statement. Since image view algorithms
  129. typically have time complexity at least linear on the number of
  130. pixels, the single switch statement of image view variant adds
  131. practically no measurable performance overhead compared to templated
  132. image views.
  133. Variants behave like the underlying type. Their copy constructor will
  134. invoke the copy constructor of the underlying instance. Equality
  135. operator will check if the two instances are of the same type and then
  136. invoke their ``operator==``, etc. The default constructor of a variant
  137. will default-construct the first type. That means that
  138. ``any_image_view`` has shallow default-constructor, copy-constructor,
  139. assignment and equality comparison, whereas ``any_image`` has deep
  140. ones.
  141. It is important to note that even though ``any_image_view`` and
  142. ``any_image`` resemble the static ``image_view`` and ``image``, they
  143. do not model the full requirements of ``ImageViewConcept`` and
  144. ``ImageConcept``. In particular they don't provide access to the
  145. pixels. There is no "any_pixel" or "any_pixel_iterator" in GIL. Such
  146. constructs could be provided via the ``variant`` mechanism, but doing
  147. so would result in inefficient algorithms, since the type resolution
  148. would have to be performed per pixel. Image-level algorithms should be
  149. implemented via ``apply_operation``. That said, many common operations
  150. are shared between the static and dynamic types. In addition, all of
  151. the image view transformations and many STL-like image view algorithms
  152. have overloads operating on ``any_image_view``, as illustrated with
  153. ``copy_pixels``:
  154. .. code-block:: cpp
  155. rgb8_view_t v1(...); // concrete image view
  156. bgr8_view_t v2(...); // concrete image view compatible with v1 and of the same size
  157. any_image_view<Types> av(...); // run-time specified image view
  158. // Copies the pixels from v1 into v2.
  159. // If the pixels are incompatible triggers compile error
  160. copy_pixels(v1,v2);
  161. // The source or destination (or both) may be run-time instantiated.
  162. // If they happen to be incompatible, throws std::bad_cast
  163. copy_pixels(v1, av);
  164. copy_pixels(av, v2);
  165. copy_pixels(av, av);
  166. By having algorithm overloads supporting dynamic constructs, we create
  167. a base upon which it is possible to write algorithms that can work
  168. with either compile-time or runtime images or views. The following
  169. code, for example, uses the GIL I/O extension to turn an image on disk
  170. upside down:
  171. .. code-block:: cpp
  172. #include <boost\gil\extension\io\jpeg_dynamic_io.hpp>
  173. template <typename Image> // Could be rgb8_image_t or any_image<...>
  174. void save_180rot(const std::string& file_name)
  175. {
  176. Image img;
  177. jpeg_read_image(file_name, img);
  178. jpeg_write_view(file_name, rotated180_view(view(img)));
  179. }
  180. It can be instantiated with either a compile-time or a runtime image
  181. because all functions it uses have overloads taking runtime
  182. constructs. For example, here is how ``rotated180_view`` is
  183. implemented:
  184. .. code-block:: cpp
  185. // implementation using templated view
  186. template <typename View>
  187. typename dynamic_xy_step_type<View>::type rotated180_view(const View& src) { ... }
  188. namespace detail
  189. {
  190. // the function, wrapped inside a function object
  191. template <typename Result> struct rotated180_view_fn
  192. {
  193. typedef Result result_type;
  194. template <typename View> result_type operator()(const View& src) const
  195. {
  196. return result_type(rotated180_view(src));
  197. }
  198. };
  199. }
  200. // overloading of the function using variant. Takes and returns run-time bound view.
  201. // The returned view has a dynamic step
  202. template <typename ViewTypes> inline // Models MPL Random Access Container of models of ImageViewConcept
  203. typename dynamic_xy_step_type<any_image_view<ViewTypes> >::type rotated180_view(const any_image_view<ViewTypes>& src)
  204. {
  205. return apply_operation(src,detail::rotated180_view_fn<typename dynamic_xy_step_type<any_image_view<ViewTypes> >::type>());
  206. }
  207. Variants should be used with caution (especially algorithms that take
  208. more than one variant) because they instantiate the algorithm for
  209. every possible model that the variant can take. This can take a toll
  210. on compile time and executable size. Despite these limitations,
  211. ``variant`` is a powerful technique that allows us to combine the
  212. speed of compile-time resolution with the flexibility of run-time
  213. resolution. It allows us to treat images of different parameters
  214. uniformly as a collection and store them in the same container.