pixel_locator.rst 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. Pixel Locator
  2. =============
  3. .. contents::
  4. :local:
  5. :depth: 2
  6. Overview
  7. --------
  8. A Locator allows for navigation in two or more dimensions. Locators are
  9. N-dimensional iterators in spirit, but we use a different name because they
  10. don't satisfy all the requirements of iterators. For example, they don't
  11. supply increment and decrement operators because it is unclear which dimension
  12. the operators should advance along.
  13. N-dimensional locators model the following concept:
  14. .. code-block:: cpp
  15. concept RandomAccessNDLocatorConcept<Regular Loc>
  16. {
  17. typename value_type; // value over which the locator navigates
  18. typename reference; // result of dereferencing
  19. typename difference_type; where PointNDConcept<difference_type>; // return value of operator-.
  20. typename const_t; // same as Loc, but operating over immutable values
  21. typename cached_location_t; // type to store relative location (for efficient repeated access)
  22. typename point_t = difference_type;
  23. static const size_t num_dimensions; // dimensionality of the locator
  24. where num_dimensions = point_t::num_dimensions;
  25. // The difference_type and iterator type along each dimension. The iterators may only differ in
  26. // difference_type. Their value_type must be the same as Loc::value_type
  27. template <size_t D> struct axis {
  28. typename coord_t = point_t::axis<D>::coord_t;
  29. typename iterator; where RandomAccessTraversalConcept<iterator>; // iterator along D-th axis.
  30. where iterator::value_type == value_type;
  31. };
  32. // Defines the type of a locator similar to this type, except it invokes Deref upon dereferencing
  33. template <PixelDereferenceAdaptorConcept Deref> struct add_deref {
  34. typename type; where RandomAccessNDLocatorConcept<type>;
  35. static type make(const Loc& loc, const Deref& deref);
  36. };
  37. Loc& operator+=(Loc&, const difference_type&);
  38. Loc& operator-=(Loc&, const difference_type&);
  39. Loc operator+(const Loc&, const difference_type&);
  40. Loc operator-(const Loc&, const difference_type&);
  41. reference operator*(const Loc&);
  42. reference operator[](const Loc&, const difference_type&);
  43. // Storing relative location for faster repeated access and accessing it
  44. cached_location_t Loc::cache_location(const difference_type&) const;
  45. reference operator[](const Loc&,const cached_location_t&);
  46. // Accessing iterators along a given dimension at the current location or at a given offset
  47. template <size_t D> axis<D>::iterator& Loc::axis_iterator();
  48. template <size_t D> axis<D>::iterator const& Loc::axis_iterator() const;
  49. template <size_t D> axis<D>::iterator Loc::axis_iterator(const difference_type&) const;
  50. };
  51. template <typename Loc>
  52. concept MutableRandomAccessNDLocatorConcept
  53. : RandomAccessNDLocatorConcept<Loc>
  54. {
  55. where Mutable<reference>;
  56. };
  57. Two-dimensional locators have additional requirements:
  58. .. code-block:: cpp
  59. concept RandomAccess2DLocatorConcept<RandomAccessNDLocatorConcept Loc>
  60. {
  61. where num_dimensions==2;
  62. where Point2DConcept<point_t>;
  63. typename x_iterator = axis<0>::iterator;
  64. typename y_iterator = axis<1>::iterator;
  65. typename x_coord_t = axis<0>::coord_t;
  66. typename y_coord_t = axis<1>::coord_t;
  67. // Only available to locators that have dynamic step in Y
  68. //Loc::Loc(const Loc& loc, y_coord_t);
  69. // Only available to locators that have dynamic step in X and Y
  70. //Loc::Loc(const Loc& loc, x_coord_t, y_coord_t, bool transposed=false);
  71. x_iterator& Loc::x();
  72. x_iterator const& Loc::x() const;
  73. y_iterator& Loc::y();
  74. y_iterator const& Loc::y() const;
  75. x_iterator Loc::x_at(const difference_type&) const;
  76. y_iterator Loc::y_at(const difference_type&) const;
  77. Loc Loc::xy_at(const difference_type&) const;
  78. // x/y versions of all methods that can take difference type
  79. x_iterator Loc::x_at(x_coord_t, y_coord_t) const;
  80. y_iterator Loc::y_at(x_coord_t, y_coord_t) const;
  81. Loc Loc::xy_at(x_coord_t, y_coord_t) const;
  82. reference operator()(const Loc&, x_coord_t, y_coord_t);
  83. cached_location_t Loc::cache_location(x_coord_t, y_coord_t) const;
  84. bool Loc::is_1d_traversable(x_coord_t width) const;
  85. y_coord_t Loc::y_distance_to(const Loc& loc2, x_coord_t x_diff) const;
  86. };
  87. concept MutableRandomAccess2DLocatorConcept<RandomAccess2DLocatorConcept Loc>
  88. : MutableRandomAccessNDLocatorConcept<Loc> {};
  89. 2D locators can have a dynamic step not just horizontally, but
  90. vertically. This gives rise to the Y equivalent of
  91. ``HasDynamicXStepTypeConcept``:
  92. .. code-block:: cpp
  93. concept HasDynamicYStepTypeConcept<typename T>
  94. {
  95. typename dynamic_y_step_type<T>;
  96. where Metafunction<dynamic_y_step_type<T> >;
  97. };
  98. All locators and image views that GIL provides model
  99. ``HasDynamicYStepTypeConcept``.
  100. Sometimes it is necessary to swap the meaning of X and Y for a given locator
  101. or image view type (for example, GIL provides a function to transpose an image
  102. view). Such locators and views must be transposable:
  103. .. code-block:: cpp
  104. concept HasTransposedTypeConcept<typename T>
  105. {
  106. typename transposed_type<T>;
  107. where Metafunction<transposed_type<T> >;
  108. };
  109. All GIL provided locators and views model ``HasTransposedTypeConcept``.
  110. The locators GIL uses operate over models of ``PixelConcept`` and their x and
  111. y dimension types are the same. They model the following concept:
  112. .. code-block:: cpp
  113. concept PixelLocatorConcept<RandomAccess2DLocatorConcept Loc>
  114. {
  115. where PixelValueConcept<value_type>;
  116. where PixelIteratorConcept<x_iterator>;
  117. where PixelIteratorConcept<y_iterator>;
  118. where x_coord_t == y_coord_t;
  119. typename coord_t = x_coord_t;
  120. };
  121. concept MutablePixelLocatorConcept<PixelLocatorConcept Loc> : MutableRandomAccess2DLocatorConcept<Loc> {};
  122. .. seealso::
  123. - `HasDynamicYStepTypeConcept<T> <reference/structboost_1_1gil_1_1_has_dynamic_y_step_type_concept.html>`_
  124. - `HasTransposedTypeConcept<T> <reference/structboost_1_1gil_1_1_has_transposed_type_concept.html>`_
  125. - `RandomAccessNDLocatorConcept<Locator> <reference/structboost_1_1gil_1_1_random_access_n_d_locator_concept.html>`_
  126. - `MutableRandomAccessNDLocatorConcept<Locator> <reference/structboost_1_1gil_1_1_mutable_random_access_n_d_locator_concept.html>`_
  127. - `RandomAccess2DLocatorConcept<Locator> <reference/structboost_1_1gil_1_1_random_access2_d_locator_concept.html>`_
  128. - `MutableRandomAccess2DLocatorConcept<Locator> <reference/structboost_1_1gil_1_1_mutable_random_access2_d_locator_concept.html>`_
  129. - `PixelLocatorConcept<Locator> <reference/structboost_1_1gil_1_1_pixel_locator_concept.html>`_
  130. - `MutablePixelLocatorConcept<Locator> <reference/structboost_1_1gil_1_1_mutable_pixel_locator_concept.html>`_
  131. Models
  132. ------
  133. GIL provides two models of ``PixelLocatorConcept`` - a memory-based locator,
  134. ``memory_based_2d_locator`` and a virtual locator ``virtual_2d_locator``.
  135. The ``memory_based_2d_locator`` is a locator over planar or interleaved images
  136. that have their pixels in memory. It takes a model of ``StepIteratorConcept``
  137. over pixels as a template parameter. (When instantiated with a model of
  138. ``MutableStepIteratorConcept``, it models ``MutablePixelLocatorConcept``).
  139. .. code-block:: cpp
  140. // StepIterator models StepIteratorConcept, MemoryBasedIteratorConcept
  141. template <typename StepIterator>
  142. class memory_based_2d_locator;
  143. The step of ``StepIterator`` must be the number of memory units (bytes or
  144. bits) per row (thus it must be memunit advanceable). The class
  145. ``memory_based_2d_locator`` is a wrapper around ``StepIterator`` and uses it
  146. to navigate vertically, while its base iterator is used to navigate
  147. horizontally.
  148. Combining fundamental iterator and step iterator allows us to create locators
  149. that describe complex pixel memory organizations. First, we have a choice of
  150. iterator to use for horizontal direction, i.e. for iterating over the pixels
  151. on the same row. Using the fundamental and step iterators gives us four
  152. choices:
  153. - ``pixel<T,C>*`` - for interleaved images
  154. - ``planar_pixel_iterator<T*,C>`` - for planar images
  155. - ``memory_based_step_iterator<pixel<T,C>*>`` - for interleaved images with
  156. non-standard step)
  157. - ``memory_based_step_iterator<planar_pixel_iterator<T*,C> >`` - for planar
  158. images with non-standard step
  159. Of course, one could provide their own custom x-iterator. One such example
  160. described later is an iterator adaptor that performs color conversion when
  161. dereferenced.
  162. Given a horizontal iterator ``XIterator``, we could choose the ``y-iterator``,
  163. the iterator that moves along a column, as
  164. ``memory_based_step_iterator<XIterator>`` with a step equal to the number of
  165. memory units (bytes or bits) per row. Again, one is free to provide their own
  166. y-iterator.
  167. Then we can instantiate
  168. ``memory_based_2d_locator<memory_based_step_iterator<XIterator> >`` to obtain
  169. a 2D pixel locator, as the diagram indicates:
  170. .. image:: ../images/pixel_locator.gif
  171. The ``memory_based_2d_locator`` also offers `cached_location_t` as mechanism
  172. to store relative locations for optimized repeated access of neighborhood
  173. pixels. The 2D coordinates of relative locations are cached as 1-dimensional
  174. raw byte offsets. This provides efficient access if a neighboring locations
  175. relative to a given locator are read or written frequently (e.g. in filters).
  176. The ``virtual_2d_locator`` is a locator that is instantiated with a function
  177. object invoked upon dereferencing a pixel. It returns the value of a pixel
  178. given its X,Y coordinates. Virtual locators can be used to implement virtual
  179. image views that can model any user-defined function. See the GIL tutorial for
  180. an example of using virtual locators to create a view of the Mandelbrot set.
  181. Both the virtual and the memory-based locators subclass from
  182. ``pixel_2d_locator_base``, a base class that provides most of the interface
  183. required by ``PixelLocatorConcept``. Users may find this base class useful if
  184. they need to provide other models of ``PixelLocatorConcept``.
  185. Here is some sample code using locators:
  186. .. code-block:: cpp
  187. loc=img.xy_at(10,10); // start at pixel (x=10,y=10)
  188. above=loc.cache_location(0,-1); // remember relative locations of neighbors above and below
  189. below=loc.cache_location(0, 1);
  190. ++loc.x(); // move to (11,10)
  191. loc.y()+=15; // move to (11,25)
  192. loc-=point<std::ptrdiff_t>(1,1);// move to (10,24)
  193. *loc=(loc(0,-1)+loc(0,1))/2; // set pixel (10,24) to the average of (10,23) and (10,25) (grayscale pixels only)
  194. *loc=(loc[above]+loc[below])/2; // the same, but faster using cached relative neighbor locations
  195. The standard GIL locators are fast and lightweight objects. For example, the
  196. locator for a simple interleaved image consists of one raw pointer to the
  197. pixel location plus one integer for the row size in bytes, for a total of
  198. 8 bytes. ``++loc.x()`` amounts to incrementing a raw pointer (or N pointers
  199. for planar images). Computing 2D offsets is slower as it requires
  200. multiplication and addition. Filters, for example, need to access the same
  201. neighbors for every pixel in the image, in which case the relative positions
  202. can be cached into a raw byte difference using ``cache_location``.
  203. In the above example ``loc[above]`` for simple interleaved images amounts to a
  204. raw array index operator.
  205. Iterator over 2D image
  206. ----------------------
  207. Sometimes we want to perform the same, location-independent operation
  208. over all pixels of an image. In such a case it is useful to represent
  209. the pixels as a one-dimensional array. GIL's ``iterator_from_2d`` is a
  210. random access traversal iterator that visits all pixels in an image in
  211. the natural memory-friendly order left-to-right inside
  212. top-to-bottom. It takes a locator, the width of the image and the
  213. current X position. This is sufficient information for it to determine
  214. when to do a "carriage return". Synopsis:
  215. .. code-block:: cpp
  216. template <typename Locator> // Models PixelLocatorConcept
  217. class iterator_from_2d
  218. {
  219. public:
  220. iterator_from_2d(const Locator& loc, int x, int width);
  221. iterator_from_2d& operator++(); // if (++_x<_width) ++_p.x(); else _p+=point_t(-_width,1);
  222. ...
  223. private:
  224. int _x, _width;
  225. Locator _p;
  226. };
  227. Iterating through the pixels in an image using ``iterator_from_2d`` is slower
  228. than going through all rows and using the x-iterator at each row. This is
  229. because two comparisons are done per iteration step - one for the end
  230. condition of the loop using the iterators, and one inside
  231. ``iterator_from_2d::operator++`` to determine whether we are at the end of a
  232. row. For fast operations, such as pixel copy, this second check adds about
  233. 15% performance delay (measured for interleaved images on Intel platform).
  234. GIL overrides some STL algorithms, such as ``std::copy`` and ``std::fill``,
  235. when invoked with ``iterator_from_2d``-s, to go through each row using their
  236. base x-iterators, and, if the image has no padding (i.e.
  237. ``iterator_from_2d::is_1d_traversable()`` returns true) to simply iterate
  238. using the x-iterators directly.