1Image View
2==========
3
4.. contents::
5   :local:
6   :depth: 2
7
8Overview
9--------
10
11An image view is a generalization of STL range concept to multiple dimensions.
12Similar to ranges (and iterators), image views are shallow, don't own the
13underlying data and don't propagate their constness over the data.
14For example, a constant image view cannot be resized, but may allow modifying
15the pixels. For pixel-immutable operations, use constant-value image view
16(also called non-mutable image view). Most general N-dimensional views satisfy
17the following concept:
18
19.. code-block:: cpp
20
21  concept RandomAccessNDImageViewConcept<Regular View>
22  {
23    typename value_type;      // for pixel-based views, the pixel type
24    typename reference;       // result of dereferencing
25    typename difference_type; // result of operator-(iterator,iterator) (1-dimensional!)
26    typename const_t;  where RandomAccessNDImageViewConcept<View>; // same as View, but over immutable values
27    typename point_t;  where PointNDConcept<point_t>; // N-dimensional point
28    typename locator;  where RandomAccessNDLocatorConcept<locator>; // N-dimensional locator.
29    typename iterator; where RandomAccessTraversalConcept<iterator>; // 1-dimensional iterator over all values
30    typename reverse_iterator; where RandomAccessTraversalConcept<reverse_iterator>;
31    typename size_type;       // the return value of size()
32
33    // Equivalent to RandomAccessNDLocatorConcept::axis
34    template <size_t D> struct axis {
35        typename coord_t = point_t::axis<D>::coord_t;
36        typename iterator; where RandomAccessTraversalConcept<iterator>;   // iterator along D-th axis.
37        where SameType<coord_t, iterator::difference_type>;
38        where SameType<iterator::value_type,value_type>;
39    };
40
41    // Defines the type of a view similar to this type, except it invokes Deref upon dereferencing
42    template <PixelDereferenceAdaptorConcept Deref> struct add_deref {
43        typename type;        where RandomAccessNDImageViewConcept<type>;
44        static type make(const View& v, const Deref& deref);
45    };
46
47    static const size_t num_dimensions = point_t::num_dimensions;
48
49    // Create from a locator at the top-left corner and dimensions
50    View::View(const locator&, const point_type&);
51
52    size_type        View::size()       const; // total number of elements
53    reference        operator[](View, const difference_type&) const; // 1-dimensional reference
54    iterator         View::begin()      const;
55    iterator         View::end()        const;
56    reverse_iterator View::rbegin()     const;
57    reverse_iterator View::rend()       const;
58    iterator         View::at(const point_t&);
59    point_t          View::dimensions() const; // number of elements along each dimension
60    bool             View::is_1d_traversable() const;   // Does an iterator over the first dimension visit each value?
61
62    // iterator along a given dimension starting at a given point
63    template <size_t D> View::axis<D>::iterator View::axis_iterator(const point_t&) const;
64
65    reference operator()(View,const point_t&) const;
66  };
67
68  concept MutableRandomAccessNDImageViewConcept<RandomAccessNDImageViewConcept View>
69  {
70    where Mutable<reference>;
71  };
72
73Two-dimensional image views have the following extra requirements:
74
75.. code-block:: cpp
76
77  concept RandomAccess2DImageViewConcept<RandomAccessNDImageViewConcept View>
78  {
79    where num_dimensions==2;
80
81    typename x_iterator = axis<0>::iterator;
82    typename y_iterator = axis<1>::iterator;
83    typename x_coord_t  = axis<0>::coord_t;
84    typename y_coord_t  = axis<1>::coord_t;
85    typename xy_locator = locator;
86
87    x_coord_t View::width()  const;
88    y_coord_t View::height() const;
89
90    // X-navigation
91    x_iterator View::x_at(const point_t&) const;
92    x_iterator View::row_begin(y_coord_t) const;
93    x_iterator View::row_end  (y_coord_t) const;
94
95    // Y-navigation
96    y_iterator View::y_at(const point_t&) const;
97    y_iterator View::col_begin(x_coord_t) const;
98    y_iterator View::col_end  (x_coord_t) const;
99
100    // navigating in 2D
101    xy_locator View::xy_at(const point_t&) const;
102
103    // (x,y) versions of all methods taking point_t
104    View::View(x_coord_t,y_coord_t,const locator&);
105    iterator View::at(x_coord_t,y_coord_t) const;
106    reference operator()(View,x_coord_t,y_coord_t) const;
107    xy_locator View::xy_at(x_coord_t,y_coord_t) const;
108    x_iterator View::x_at(x_coord_t,y_coord_t) const;
109    y_iterator View::y_at(x_coord_t,y_coord_t) const;
110  };
111
112  concept MutableRandomAccess2DImageViewConcept<RandomAccess2DImageViewConcept View>
113    : MutableRandomAccessNDImageViewConcept<View> {};
114
115Image views that GIL typically uses operate on value types that model
116``PixelValueConcept`` and have some additional requirements:
117
118.. code-block:: cpp
119
120  concept ImageViewConcept<RandomAccess2DImageViewConcept View>
121  {
122    where PixelValueConcept<value_type>;
123    where PixelIteratorConcept<x_iterator>;
124    where PixelIteratorConcept<y_iterator>;
125    where x_coord_t == y_coord_t;
126
127    typename coord_t = x_coord_t;
128
129    std::size_t View::num_channels() const;
130  };
131
132
133  concept MutableImageViewConcept<ImageViewConcept View>
134    : MutableRandomAccess2DImageViewConcept<View>
135  {};
136
137Two image views are compatible if they have compatible pixels and the same
138number of dimensions:
139
140.. code-block:: cpp
141
142  concept ViewsCompatibleConcept<ImageViewConcept V1, ImageViewConcept V2>
143  {
144    where PixelsCompatibleConcept<V1::value_type, V2::value_type>;
145    where V1::num_dimensions == V2::num_dimensions;
146  };
147
148Compatible views must also have the same dimensions (i.e. the same width and
149height). Many algorithms taking multiple views require that they be pairwise
150compatible.
151
152.. seealso::
153
154   - `RandomAccessNDImageViewConcept<View> <reference/structboost_1_1gil_1_1_random_access_n_d_image_view_concept.html>`_
155   - `MutableRandomAccessNDImageViewConcept<View> <reference/structboost_1_1gil_1_1_mutable_random_access_n_d_image_view_concept.html>`_
156   - `RandomAccess2DImageViewConcept<View> <reference/structboost_1_1gil_1_1_random_access2_d_image_view_concept.html>`_
157   - `MutableRandomAccess2DImageViewConcept<View> <reference/structboost_1_1gil_1_1_mutable_random_access2_d_image_view_concept.html>`_
158   - `ImageViewConcept<View> <reference/structboost_1_1gil_1_1_image_view_concept.html>`_
159   - `MutableImageViewConcept<View> <reference/structboost_1_1gil_1_1_mutable_image_view_concept.html>`_
160   - `ViewsCompatibleConcept<View1,View2> <reference/structboost_1_1gil_1_1_views_compatible_concept.html>`_
161
162Models
163------
164
165GIL provides a model for ``ImageViewConcept`` called ``image_view``. It is
166templated over a model of ``PixelLocatorConcept``. (If instantiated with a
167model of ``MutablePixelLocatorConcept``, it models
168``MutableImageViewConcept``). Synopsis:
169
170.. code-block:: cpp
171
172  // Locator models PixelLocatorConcept, could be MutablePixelLocatorConcept
173  template <typename Locator>
174  class image_view
175  {
176  public:
177    typedef Locator xy_locator;
178    typedef iterator_from_2d<Locator> iterator;
179    ...
180  private:
181    xy_locator _pixels;     // 2D pixel locator at the top left corner of the image view range
182    point_t    _dimensions; // width and height
183  };
184
185Image views are lightweight objects. A regular interleaved view is typically
18616 bytes long - two integers for the width and height (inside dimensions) one
187for the number of bytes between adjacent rows (inside the locator) and one
188pointer to the beginning of the pixel block.
189
190Algorithms
191----------
192
193GIL provides algorithms constructing views from raw data or other views.
194
195Creating Views from Raw Pixels
196^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
197
198Standard image views can be constructed from raw data of any supported color
199space, bit depth, channel ordering or planar vs. interleaved structure.
200Interleaved views are constructed using ``interleaved_view``, supplying the
201image dimensions, number of bytes per row, and a pointer to the first pixel:
202
203.. code-block:: cpp
204
205  // Iterator models pixel iterator (e.g. rgb8_ptr_t or rgb8c_ptr_t)
206  template <typename Iterator>
207  image_view<...> interleaved_view(ptrdiff_t width, ptrdiff_t height, Iterator pixels, ptrdiff_t rowsize)
208
209Planar views are defined for every color space and take each plane separately.
210Here is the RGB one:
211
212.. code-block:: cpp
213
214  // Iterator models channel iterator (e.g. bits8* or bits8 const*)
215  template <typename Iterator>
216  image_view<...> planar_rgb_view(
217      ptrdiff_t width, ptrdiff_t height,
218      IC r, IC g, IC b, ptrdiff_t rowsize);
219
220Note that the supplied pixel/channel iterators could be constant (read-only),
221in which case the returned view is a constant-value (immutable) view.
222
223Creating Image Views from Other Image Views
224^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
225
226It is possible to construct one image view from another by changing some
227policy of how image data is interpreted. The result could be a view whose type
228is derived from the type of the source. GIL uses the following metafunctions
229to get the derived types:
230
231.. code-block:: cpp
232
233  // Some result view types
234  template <typename View>
235  struct dynamic_xy_step_type : public dynamic_y_step_type<typename dynamic_x_step_type<View>::type> {};
236
237  template <typename View>
238  struct dynamic_xy_step_transposed_type : public dynamic_xy_step_type<typename transposed_type<View>::type> {};
239
240  // color and bit depth converted view to match pixel type P
241  template <typename SrcView, // Models ImageViewConcept
242          typename DstP,    // Models PixelConcept
243          typename ColorConverter=gil::default_color_converter>
244  struct color_converted_view_type
245  {
246    typedef ... type;     // image view adaptor with value type DstP, over SrcView
247  };
248
249  // single-channel view of the N-th channel of a given view
250  template <typename SrcView>
251  struct nth_channel_view_type
252  {
253    typedef ... type;
254  };
255
256GIL Provides the following view transformations:
257
258.. code-block:: cpp
259
260  // flipped upside-down, left-to-right, transposed view
261  template <typename View> typename dynamic_y_step_type<View>::type             flipped_up_down_view(const View& src);
262  template <typename View> typename dynamic_x_step_type<View>::type             flipped_left_right_view(const View& src);
263  template <typename View> typename dynamic_xy_step_transposed_type<View>::type transposed_view(const View& src);
264
265  // rotations
266  template <typename View> typename dynamic_xy_step_type<View>::type            rotated180_view(const View& src);
267  template <typename View> typename dynamic_xy_step_transposed_type<View>::type rotated90cw_view(const View& src);
268  template <typename View> typename dynamic_xy_step_transposed_type<View>::type rotated90ccw_view(const View& src);
269
270  // view of an axis-aligned rectangular area within an image
271  template <typename View> View                                                 subimage_view(const View& src,
272             const View::point_t& top_left, const View::point_t& dimensions);
273
274  // subsampled view (skipping pixels in X and Y)
275  template <typename View> typename dynamic_xy_step_type<View>::type            subsampled_view(const View& src,
276             const View::point_t& step);
277
278  template <typename View, typename P>
279  color_converted_view_type<View,P>::type                                       color_converted_view(const View& src);
280  template <typename View, typename P, typename CCV> // with a custom color converter
281  color_converted_view_type<View,P,CCV>::type                                   color_converted_view(const View& src);
282
283  template <typename View>
284  nth_channel_view_type<View>::view_t                                           nth_channel_view(const View& view, int n);
285
286The implementations of most of these view factory methods are straightforward.
287Here is, for example, how the flip views are implemented. The flip upside-down
288view creates a view whose first pixel is the bottom left pixel of the original
289view and whose y-step is the negated step of the source.
290
291.. code-block:: cpp
292
293  template <typename View>
294  typename dynamic_y_step_type<View>::type flipped_up_down_view(const View& src)
295  {
296    gil_function_requires<ImageViewConcept<View> >();
297    typedef typename dynamic_y_step_type<View>::type RView;
298    return RView(src.dimensions(),typename RView::xy_locator(src.xy_at(0,src.height()-1),-1));
299  }
300
301The call to ``gil_function_requires`` ensures (at compile time) that the
302template parameter is a valid model of ``ImageViewConcept``. Using it
303generates easier to track compile errors, creates no extra code and has no
304run-time performance impact. We are using the ``boost::concept_check library``,
305but wrapping it in ``gil_function_requires``, which performs the check if the
306``BOOST_GIL_USE_CONCEPT_CHECK`` is set. It is unset by default, because there
307is a significant increase in compile time when using concept checks. We will
308skip ``gil_function_requires`` in the code examples in this guide for the sake
309of succinctness.
310
311Image views can be freely composed (see section :doc:`metafunctions` for
312explanation of the typedefs ``rgb16_image_t`` and ``gray16_step_view_t)``:
313
314.. code-block:: cpp
315
316  rgb16_image_t img(100,100);    // an RGB interleaved image
317
318  // grayscale view over the green (index 1) channel of img
319  gray16_step_view_t green=nth_channel_view(view(img),1);
320
321  // 50x50 view of the green channel of img, upside down and taking every other pixel in X and in Y
322  gray16_step_view_t ud_fud=flipped_up_down_view(subsampled_view(green,2,2));
323
324As previously stated, image views are fast, constant-time, shallow views over
325the pixel data. The above code does not copy any pixels; it operates on the
326pixel data allocated when ``img`` was created.
327
328STL-Style Algorithms on Image Views
329^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
330
331Image views provide 1D iteration of their pixels via ``begin()`` and ``end()``
332methods, which makes it possible to use STL algorithms with them. However,
333using nested loops over X and Y is in many cases more efficient.
334The algorithms in this section resemble STL algorithms, but they abstract away
335the nested loops and take views (as opposed to ranges) as input.
336
337.. code-block:: cpp
338
339  // Equivalents of std::copy and std::uninitialized_copy
340  // where ImageViewConcept<V1>, MutableImageViewConcept<V2>, ViewsCompatibleConcept<V1,V2>
341  template <typename V1, typename V2>
342  void copy_pixels(const V1& src, const V2& dst);
343  template <typename V1, typename V2>
344  void uninitialized_copy_pixels(const V1& src, const V2& dst);
345
346  // Equivalents of std::fill and std::uninitialized_fill
347  // where MutableImageViewConcept<V>, PixelConcept<Value>, PixelsCompatibleConcept<Value,V::value_type>
348  template <typename V, typename Value>
349  void fill_pixels(const V& dst, const Value& val);
350  template <typename V, typename Value>
351  void uninitialized_fill_pixels(const V& dst, const Value& val);
352
353  // Equivalent of std::for_each
354  // where ImageViewConcept<V>, boost::UnaryFunctionConcept<F>
355  // where PixelsCompatibleConcept<V::reference, F::argument_type>
356  template <typename V, typename F>
357  F for_each_pixel(const V& view, F fun);
358  template <typename V, typename F>
359  F for_each_pixel_position(const V& view, F fun);
360
361  // Equivalent of std::generate
362  // where MutableImageViewConcept<V>, boost::UnaryFunctionConcept<F>
363  // where PixelsCompatibleConcept<V::reference, F::argument_type>
364  template <typename V, typename F>
365  void generate_pixels(const V& dst, F fun);
366
367  // Equivalent of std::transform with one source
368  // where ImageViewConcept<V1>, MutableImageViewConcept<V2>
369  // where boost::UnaryFunctionConcept<F>
370  // where PixelsCompatibleConcept<V1::const_reference, F::argument_type>
371  // where PixelsCompatibleConcept<F::result_type, V2::reference>
372  template <typename V1, typename V2, typename F>
373  F transform_pixels(const V1& src, const V2& dst, F fun);
374  template <typename V1, typename V2, typename F>
375  F transform_pixel_positions(const V1& src, const V2& dst, F fun);
376
377  // Equivalent of std::transform with two sources
378  // where ImageViewConcept<V1>, ImageViewConcept<V2>, MutableImageViewConcept<V3>
379  // where boost::BinaryFunctionConcept<F>
380  // where PixelsCompatibleConcept<V1::const_reference, F::first_argument_type>
381  // where PixelsCompatibleConcept<V2::const_reference, F::second_argument_type>
382  // where PixelsCompatibleConcept<F::result_type, V3::reference>
383  template <typename V1, typename V2, typename V3, typename F>
384  F transform_pixels(const V1& src1, const V2& src2, const V3& dst, F fun);
385  template <typename V1, typename V2, typename V3, typename F>
386  F transform_pixel_positions(const V1& src1, const V2& src2, const V3& dst, F fun);
387
388  // Copies a view into another, color converting the pixels if needed, with the default or user-defined color converter
389  // where ImageViewConcept<V1>, MutableImageViewConcept<V2>
390  // V1::value_type must be convertible to V2::value_type.
391  template <typename V1, typename V2>
392  void copy_and_convert_pixels(const V1& src, const V2& dst);
393  template <typename V1, typename V2, typename ColorConverter>
394  void copy_and_convert_pixels(const V1& src, const V2& dst, ColorConverter ccv);
395
396  // Equivalent of std::equal
397  // where ImageViewConcept<V1>, ImageViewConcept<V2>, ViewsCompatibleConcept<V1,V2>
398  template <typename V1, typename V2>
399  bool equal_pixels(const V1& view1, const V2& view2);
400
401Algorithms that take multiple views require that they have the same
402dimensions. ``for_each_pixel_position`` and ``transform_pixel_positions`` pass
403pixel locators, as opposed to pixel references, to their function objects.
404This allows for writing algorithms that use pixel neighbours, as the tutorial
405demonstrates.
406
407Most of these algorithms check whether the image views are 1D-traversable.
408A 1D-traversable image view has no gaps at the end of the rows.
409In other words, if an x_iterator of that view is advanced past the last pixel
410in a row it will move to the first pixel of the next row. When image views are
4111D-traversable, the algorithms use a single loop and run more efficiently.
412If one or more of the input views are not 1D-traversable, the algorithms
413fall-back to an X-loop nested inside a Y-loop.
414
415The algorithms typically delegate the work to their corresponding STL
416algorithms. For example, ``copy_pixels`` calls ``std::copy`` either for each
417row, or, when the images are 1D-traversable, once for all pixels.
418
419In addition, overloads are sometimes provided for the STL algorithms.
420For example, ``std::copy`` for planar iterators is overloaded to perform
421``std::copy`` for each of the planes. ``std::copy`` over bitwise-copyable
422pixels results in ``std::copy`` over unsigned char, which STL
423implements via ``memmove``.
424
425As a result ``copy_pixels`` may result in a single call to ``memmove`` for
426interleaved 1D-traversable views, or one per each plane of planar
4271D-traversable views, or one per each row of interleaved non-1D-traversable
428images, etc.
429
430GIL also provides some beta-versions of image processing algorithms, such as
431resampling and convolution in a numerics extension available on
432http://stlab.adobe.com/gil/download.html. This code is in early stage of
433development and is not optimized for speed
434