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