]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | //////////////////////////////////////////////////////////////////////////////////////// |
2 | /// \file | |
3 | /// \brief Doxygen documentation | |
4 | /// \author Lubomir Bourdev and Hailin Jin \n | |
5 | /// Adobe Systems Incorporated | |
6 | /// | |
7 | /// | |
8 | //////////////////////////////////////////////////////////////////////////////////////// | |
9 | ||
10 | /** | |
11 | \page GILDesignGuide Generic Image Library Design Guide | |
12 | ||
13 | \author Lubomir Bourdev (lbourdev@adobe.com) and Hailin Jin (hljin@adobe.com) \n | |
14 | Adobe Systems Incorporated | |
15 | \version 2.1 | |
16 | \date September 15, 2007 | |
17 | ||
18 | ||
19 | <p>This document describes the design of the Generic Image Library, a C++ image-processing library that abstracts image representation from algorithms on images. | |
20 | It covers more than you need to know for a causal use of GIL. You can find a quick, jump-start GIL tutorial on the main GIL page at http://stlab.adobe.com/gil | |
21 | ||
22 | - \ref OverviewSectionDG | |
23 | - \ref ConceptsSectionDG | |
24 | - \ref PointSectionDG | |
25 | - \ref ChannelSectionDG | |
26 | - \ref ColorSpaceSectionDG | |
27 | - \ref ColorBaseSectionDG | |
28 | - \ref PixelSectionDG | |
29 | - \ref PixelIteratorSectionDG | |
30 | - \ref FundamentalIteratorDG | |
31 | - \ref IteratorAdaptorDG | |
32 | - \ref PixelDereferenceAdaptorAG | |
33 | - \ref StepIteratorDG | |
34 | - \ref LocatorDG | |
35 | - \ref IteratorFrom2DDG | |
36 | - \ref ImageViewSectionDG | |
37 | - \ref ImageViewFrowRawDG | |
38 | - \ref ImageViewFrowImageViewDG | |
39 | - \ref ImageSectionDG | |
40 | - \ref VariantSecDG | |
41 | - \ref MetafunctionsDG | |
42 | - \ref IO_DG | |
43 | - \ref SampleImgCodeDG | |
44 | - \ref PixelLevelExampleDG | |
45 | - \ref SafeAreaExampleDG | |
46 | - \ref HistogramExampleDG | |
47 | - \ref ImageViewsExampleDG | |
48 | - \ref ExtendingGIL_DG | |
49 | - \ref NewColorSpacesDG | |
50 | - \ref NewColorConversionDG | |
51 | - \ref NewChannelsDG | |
52 | - \ref NewImagesDG | |
53 | - \ref TechnicalitiesDG | |
54 | - \ref ConclusionDG | |
55 | ||
56 | <br> | |
57 | <hr> | |
58 | \section OverviewSectionDG 1. Overview | |
59 | ||
60 | Images are essential in any image processing, vision and video project, and yet the variability in image representations makes it difficult | |
61 | to write imaging algorithms that are both generic and efficient. In this section we will describe some of the challenges that we would like to address. | |
62 | ||
63 | In the following discussion an <i>image</i> is a 2D array of pixels. A <i>pixel</i> is a set of color channels that represents the color at a given point in an image. Each | |
64 | <i>channel</i> represents the value of a color component. | |
65 | There are two common memory structures for an image. <i>Interleaved</i> images are represented by grouping the pixels together in memory and | |
66 | interleaving all channels together, whereas <i>planar</i> images keep the channels in separate color planes. Here is a 4x3 RGB image in | |
67 | which the second pixel of the first row is marked in red, in interleaved form: | |
68 | ||
69 | \image html interleaved.jpg | |
70 | and in planar form: | |
71 | ||
72 | \image html planar.jpg | |
73 | ||
74 | Note also that rows may optionally be aligned resulting in a potential padding at the end of rows. | |
75 | <p> | |
76 | The Generic Image Library (GIL) provides models for images that vary in: | |
77 | - Structure (planar vs. interleaved) | |
78 | - Color space and presence of alpha (RGB, RGBA, CMYK, etc.) | |
79 | - Channel depth (8-bit, 16-bit, etc.) | |
80 | - Order of channels (RGB vs. BGR, etc.) | |
81 | - Row alignment policy (no alignment, word-alignment, etc.) | |
82 | ||
83 | It also supports user-defined models of images, and images whose parameters are specified at run-time. | |
84 | GIL abstracts image representation from algorithms applied on images and allows us to write the algorithm once and have it work | |
85 | on any of the above image variations while generating code that is comparable in speed to that of hand-writing the algorithm for a specific image type. | |
86 | ||
87 | This document follows bottom-up design. Each section defines concepts that build on top of concepts defined in previous sections. | |
88 | It is recommended to read the sections in order. | |
89 | ||
90 | <hr> | |
91 | \section ConceptsSectionDG 2. About Concepts | |
92 | ||
93 | All constructs in GIL are models of GIL concepts. A \em concept is a set of requirements that a type (or a set of related types) must fulfill to | |
94 | be used correctly in generic algorithms. The requirements include syntactic and algorithming guarantees. | |
95 | For example, GIL's class \p pixel is a model of GIL's \p PixelConcept. The user may substitute the pixel class with one of their own, and, as long as | |
96 | it satisfies the requirements of \p PixelConcept, all other GIL classes and algorithms can be used with it. See more about concepts here: | |
97 | http://www.generic-programming.org/languages/conceptcpp/ | |
98 | ||
99 | In this document we will use a syntax for defining concepts that is described in a proposal for a Concepts extension to C++0x specified here: | |
100 | http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2081.pdf | |
101 | ||
102 | Here are some common concepts that will be used in GIL. Most of them are defined here: | |
103 | http://www.generic-programming.org/languages/conceptcpp/concept_web.php | |
104 | ||
105 | \code | |
106 | auto concept DefaultConstructible<typename T> { | |
107 | T::T(); | |
108 | }; | |
109 | ||
110 | auto concept CopyConstructible<typename T> { | |
111 | T::T(T); | |
112 | T::~T(); | |
113 | }; | |
114 | ||
115 | auto concept Assignable<typename T, typename U = T> { | |
116 | typename result_type; | |
117 | result_type operator=(T&, U); | |
118 | }; | |
119 | ||
120 | auto concept EqualityComparable<typename T, typename U = T> { | |
121 | bool operator==(T x, T y); | |
122 | bool operator!=(T x, T y) { return !(x==y); } | |
123 | }; | |
124 | ||
125 | concept SameType<typename T, typename U> { /* unspecified */ }; | |
126 | template<typename T> concept_map SameType<T, T> { /* unspecified */ }; | |
127 | ||
128 | auto concept Swappable<typename T> { | |
129 | void swap(T& t, T& u); | |
130 | }; | |
131 | \endcode | |
132 | ||
133 | Here are some additional basic concepts that GIL needs: | |
134 | ||
135 | \code | |
136 | ||
137 | auto concept Regular<typename T> : DefaultConstructible<T>, CopyConstructible<T>, EqualityComparable<T>, Assignable<T>, Swappable<T> {}; | |
138 | ||
139 | auto concept Metafunction<typename T> { | |
140 | typename type; | |
141 | }; | |
142 | ||
143 | \endcode | |
144 | ||
145 | ||
146 | ||
147 | ||
148 | ||
149 | ||
150 | ||
151 | ||
152 | ||
153 | ||
154 | ||
155 | ||
156 | ||
157 | ||
158 | ||
159 | ||
160 | ||
161 | \section PointSectionDG 3. Point | |
162 | ||
163 | A point defines the location of a pixel inside an image. It can also be used to describe the dimensions of an image. | |
164 | In most general terms, points are N-dimensional and model the following concept: | |
165 | ||
166 | \code | |
167 | concept PointNDConcept<typename T> : Regular<T> { | |
168 | // the type of a coordinate along each axis | |
169 | template <size_t K> struct axis; where Metafunction<axis>; | |
170 | ||
171 | const size_t num_dimensions; | |
172 | ||
173 | // accessor/modifier of the value of each axis. | |
174 | template <size_t K> const typename axis<K>::type& T::axis_value() const; | |
175 | template <size_t K> typename axis<K>::type& T::axis_value(); | |
176 | }; | |
177 | \endcode | |
178 | ||
179 | GIL uses a two-dimensional point, which is a refinement of \p PointNDConcept in which both dimensions are of the same type: | |
180 | ||
181 | \code | |
182 | concept Point2DConcept<typename T> : PointNDConcept<T> { | |
183 | where num_dimensions == 2; | |
184 | where SameType<axis<0>::type, axis<1>::type>; | |
185 | ||
186 | typename value_type = axis<0>::type; | |
187 | ||
188 | const value_type& operator[](const T&, size_t i); | |
189 | value_type& operator[]( T&, size_t i); | |
190 | ||
191 | value_type x,y; | |
192 | }; | |
193 | \endcode | |
194 | ||
195 | <b>Related Concepts:</b> | |
196 | ||
197 | - PointNDConcept\<T> | |
198 | - Point2DConcept\<T> | |
199 | ||
200 | <b>Models:</b> | |
201 | ||
202 | GIL provides a model of \p Point2DConcept, \p point2<T> where \p T is the coordinate type. | |
203 | ||
204 | ||
205 | ||
206 | ||
207 | ||
208 | ||
209 | ||
210 | ||
211 | ||
212 | ||
213 | ||
214 | ||
215 | ||
216 | ||
217 | ||
218 | ||
219 | ||
220 | ||
221 | ||
222 | ||
223 | <hr> | |
224 | \section ChannelSectionDG 4. Channel | |
225 | ||
226 | A channel indicates the intensity of a color component (for example, the red channel in an RGB pixel). | |
227 | Typical channel operations are getting, comparing and setting the channel values. Channels have associated | |
228 | minimum and maximum value. GIL channels model the following concept: | |
229 | ||
230 | \code | |
231 | ||
232 | concept ChannelConcept<typename T> : EqualityComparable<T> { | |
233 | typename value_type = T; // use channel_traits<T>::value_type to access it | |
234 | where ChannelValueConcept<value_type>; | |
235 | typename reference = T&; // use channel_traits<T>::reference to access it | |
236 | typename pointer = T*; // use channel_traits<T>::pointer to access it | |
237 | typename const_reference = const T&; // use channel_traits<T>::const_reference to access it | |
238 | typename const_pointer = const T*; // use channel_traits<T>::const_pointer to access it | |
239 | static const bool is_mutable; // use channel_traits<T>::is_mutable to access it | |
240 | ||
241 | static T min_value(); // use channel_traits<T>::min_value to access it | |
242 | static T max_value(); // use channel_traits<T>::min_value to access it | |
243 | }; | |
244 | ||
245 | concept MutableChannelConcept<ChannelConcept T> : Swappable<T>, Assignable<T> {}; | |
246 | ||
247 | concept ChannelValueConcept<ChannelConcept T> : Regular<T> {}; | |
248 | \endcode | |
249 | ||
250 | GIL allows built-in integral and floating point types to be channels. Therefore the associated types and range information | |
251 | are defined in \p channel_traits with the following default implementation: | |
252 | ||
253 | \code | |
254 | template <typename T> | |
255 | struct channel_traits { | |
256 | typedef T value_type; | |
257 | typedef T& reference; | |
258 | typedef T* pointer; | |
259 | typedef T& const const_reference; | |
260 | typedef T* const const_pointer; | |
261 | ||
262 | static value_type min_value() { return std::numeric_limits<T>::min(); } | |
263 | static value_type max_value() { return std::numeric_limits<T>::max(); } | |
264 | }; | |
265 | \endcode | |
266 | ||
267 | Two channel types are <i>compatible</i> if they have the same value type: | |
268 | ||
269 | \code | |
270 | concept ChannelsCompatibleConcept<ChannelConcept T1, ChannelConcept T2> { | |
271 | where SameType<T1::value_type, T2::value_type>; | |
272 | }; | |
273 | \endcode | |
274 | ||
275 | A channel may be <i>convertible</i> to another channel: | |
276 | ||
277 | \code | |
278 | template <ChannelConcept Src, ChannelValueConcept Dst> | |
279 | concept ChannelConvertibleConcept { | |
280 | Dst channel_convert(Src); | |
281 | }; | |
282 | \endcode | |
283 | ||
284 | Note that \p ChannelConcept and \p MutableChannelConcept do not require a default constructor. Channels that also | |
285 | support default construction (and thus are regular types) model \p ChannelValueConcept. To understand the motivation | |
286 | for this distinction, consider a 16-bit RGB pixel in a "565" bit pattern. Its channels correspond to bit ranges. To support | |
287 | such channels, we need to create a custom proxy class corresponding to a reference to a subbyte channel. | |
288 | Such a proxy reference class models only \p ChannelConcept, because, similar to native C++ references, it | |
289 | may not have a default constructor. | |
290 | ||
291 | Note also that algorithms may impose additional requirements on channels, such as support for arithmentic operations. | |
292 | ||
293 | <b>Related Concepts:</b> | |
294 | ||
295 | - ChannelConcept\<T> | |
296 | - ChannelValueConcept\<T> | |
297 | - MutableChannelConcept\<T> | |
298 | - ChannelsCompatibleConcept\<T1,T2> | |
299 | - ChannelConvertibleConcept\<SrcChannel,DstChannel> | |
300 | ||
301 | <b>Models:</b> | |
302 | ||
303 | All built-in integral and floating point types are valid channels. GIL provides standard typedefs for some integral channels: | |
304 | ||
305 | \code | |
306 | typedef boost::uint8_t bits8; | |
307 | typedef boost::uint16_t bits16; | |
308 | typedef boost::uint32_t bits32; | |
309 | typedef boost::int8_t bits8s; | |
310 | typedef boost::int16_t bits16s; | |
311 | typedef boost::int32_t bits32s; | |
312 | \endcode | |
313 | ||
314 | The minimum and maximum values of a channel modeled by a built-in type correspond to the minimum and maximum physical range of the built-in type, | |
315 | as specified by its \p std::numeric_limits. Sometimes the physical range is not appropriate. GIL provides \p scoped_channel_value, a model for a | |
316 | channel adapter that allows for specifying a custom range. We use it to define a [0..1] floating point channel type as follows: | |
317 | ||
318 | \code | |
319 | struct float_zero { static float apply() { return 0.0f; } }; | |
320 | struct float_one { static float apply() { return 1.0f; } }; | |
321 | typedef scoped_channel_value<float,float_zero,float_one> bits32f; | |
322 | \endcode | |
323 | ||
324 | GIL also provides models for channels corresponding to ranges of bits: | |
325 | ||
326 | \code | |
327 | // Value of a channel defined over NumBits bits. Models ChannelValueConcept | |
328 | template <int NumBits> class packed_channel_value; | |
329 | ||
330 | // Reference to a channel defined over NumBits bits. Models ChannelConcept | |
331 | template <int FirstBit, | |
332 | int NumBits, // Defines the sequence of bits in the data value that contain the channel | |
333 | bool Mutable> // true if the reference is mutable | |
334 | class packed_channel_reference; | |
335 | ||
336 | // Reference to a channel defined over NumBits bits. Its FirstBit is a run-time parameter. Models ChannelConcept | |
337 | template <int NumBits, // Defines the sequence of bits in the data value that contain the channel | |
338 | bool Mutable> // true if the reference is mutable | |
339 | class packed_dynamic_channel_reference; | |
340 | \endcode | |
341 | ||
342 | Note that there are two models of a reference proxy which differ based on whether the offset of the channel range is | |
343 | specified as a template or a run-time parameter. The first model is faster and more compact while the second model is more | |
344 | flexible. For example, the second model allows us to construct an iterator over bitrange channels. | |
345 | ||
346 | <b>Algorithms:</b> | |
347 | ||
348 | Here is how to construct the three channels of a 16-bit "565" pixel and set them to their maximum value: | |
349 | ||
350 | \code | |
351 | typedef packed_channel_reference<0,5,true> channel16_0_5_reference_t; | |
352 | typedef packed_channel_reference<5,6,true> channel16_5_6_reference_t; | |
353 | typedef packed_channel_reference<11,5,true> channel16_11_5_reference_t; | |
354 | ||
355 | boost::uint16_t data=0; | |
356 | channel16_0_5_reference_t channel1(&data); | |
357 | channel16_5_6_reference_t channel2(&data); | |
358 | channel16_11_5_reference_t channel3(&data); | |
359 | ||
360 | channel1=channel_traits<channel16_0_5_reference_t>::max_value(); | |
361 | channel2=channel_traits<channel16_5_6_reference_t>::max_value(); | |
362 | channel3=channel_traits<channel16_11_5_reference_t>::max_value(); | |
363 | assert(data==65535); | |
364 | \endcode | |
365 | ||
366 | Assignment, equality comparison and copy construction are defined only between compatible channels: | |
367 | ||
368 | \code | |
369 | packed_channel_value<5> channel_6bit = channel1; | |
370 | channel_6bit = channel3; | |
371 | ||
372 | //channel_6bit = channel2; // compile error: Assignment between incompatible channels. | |
373 | \endcode | |
374 | ||
375 | All channel models provided by GIL are pairwise convertible: | |
376 | ||
377 | \code | |
378 | channel1 = channel_traits<channel16_0_5_reference_t>::max_value(); | |
379 | assert(channel1 == 31); | |
380 | ||
381 | bits16 chan16 = channel_convert<bits16>(channel1); | |
382 | assert(chan16 == 65535); | |
383 | \endcode | |
384 | ||
385 | Channel conversion is a lossy operation. GIL's channel conversion is a linear transformation between the ranges of the source and destination channel. | |
386 | It maps precisely the minimum to the minimum and the maximum to the maximum. (For example, to convert from uint8_t to uint16_t GIL does not do a bit shift | |
387 | because it will not properly match the maximum values. Instead GIL multiplies the source by 257). | |
388 | ||
389 | All channel models that GIL provides are convertible from/to an integral or floating point type. Thus they support arithmetic operations. | |
390 | Here are the channel-level algorithms that GIL provides: | |
391 | ||
392 | \code | |
393 | // Converts a source channel value into a destrination channel. Linearly maps the value of the source | |
394 | // into the range of the destination | |
395 | template <typename DstChannel, typename SrcChannel> | |
396 | typename channel_traits<DstChannel>::value_type channel_convert(SrcChannel src); | |
397 | ||
398 | // returns max_value - x + min_value | |
399 | template <typename Channel> | |
400 | typename channel_traits<Channel>::value_type channel_invert(Channel x); | |
401 | ||
402 | // returns a * b / max_value | |
403 | template <typename Channel> | |
404 | typename channel_traits<Channel>::value_type channel_multiply(Channel a, Channel b); | |
405 | \endcode | |
406 | ||
407 | ||
408 | ||
409 | ||
410 | ||
411 | ||
412 | ||
413 | ||
414 | ||
415 | ||
416 | ||
417 | ||
418 | ||
419 | ||
420 | ||
421 | ||
422 | <hr> | |
423 | \section ColorSpaceSectionDG 5. Color Space and Layout | |
424 | ||
425 | A color space captures the set and interpretation of channels comprising a pixel. It is an MPL random access sequence containing the types | |
426 | of all elements in the color space. Two color spaces are considered <i>compatible</i> if they are equal (i.e. have the same set of colors in the same order). | |
427 | ||
428 | <b>Related Concepts:</b> | |
429 | ||
430 | - ColorSpaceConcept\<ColorSpace> | |
431 | - ColorSpacesCompatibleConcept\<ColorSpace1,ColorSpace2> | |
432 | - ChannelMappingConcept\<Mapping> | |
433 | ||
434 | <b>Models:</b> | |
435 | ||
436 | GIL currently provides the following color spaces: \p gray_t, \p rgb_t, \p rgba_t, and \p cmyk_t. It also provides unnamed | |
437 | N-channel color spaces of two to five channels, \p devicen_t<2>, | |
438 | \p devicen_t<3>, \p devicen_t<4>, \p devicen_t<5>. Besides the standard layouts, it provides \p bgr_layout_t, \p bgra_layout_t, \p abgr_layout_t | |
439 | and \p argb_layout_t. | |
440 | ||
441 | As an example, here is how GIL defines the RGBA color space: | |
442 | ||
443 | \code | |
444 | struct red_t{}; | |
445 | struct green_t{}; | |
446 | struct blue_t{}; | |
447 | struct alpha_t{}; | |
448 | typedef mpl::vector4<red_t,green_t,blue_t,alpha_t> rgba_t; | |
449 | \endcode | |
450 | ||
451 | The ordering of the channels in the color space definition specifies their semantic order. For example, \p red_t is the first semantic channel of \p rgba_t. | |
452 | While there is a unique semantic ordering of the channels in a color space, channels may vary in their physical ordering in memory. The mapping of channels is | |
453 | specified by \p ChannelMappingConcept, which is an MPL random access sequence of integral types. A color space and its associated mapping are often used together. | |
454 | Thus they are grouped in GIL's layout: | |
455 | ||
456 | \code | |
457 | template <typename ColorSpace, | |
458 | typename ChannelMapping = mpl::range_c<int,0,mpl::size<ColorSpace>::value> > | |
459 | struct layout { | |
460 | typedef ColorSpace color_space_t; | |
461 | typedef ChannelMapping channel_mapping_t; | |
462 | }; | |
463 | \endcode | |
464 | ||
465 | Here is how to create layouts for the RGBA color space: | |
466 | ||
467 | \code | |
468 | typedef layout<rgba_t> rgba_layout_t; // default ordering is 0,1,2,3... | |
469 | typedef layout<rgba_t, mpl::vector4_c<int,2,1,0,3> > bgra_layout_t; | |
470 | typedef layout<rgba_t, mpl::vector4_c<int,1,2,3,0> > argb_layout_t; | |
471 | typedef layout<rgba_t, mpl::vector4_c<int,3,2,1,0> > abgr_layout_t; | |
472 | \endcode | |
473 | ||
474 | ||
475 | ||
476 | ||
477 | ||
478 | ||
479 | ||
480 | ||
481 | ||
482 | ||
483 | ||
484 | ||
485 | ||
486 | ||
487 | ||
488 | ||
489 | ||
490 | ||
491 | ||
492 | ||
493 | ||
494 | ||
495 | ||
496 | ||
497 | ||
498 | ||
499 | ||
500 | <hr> | |
501 | \section ColorBaseSectionDG 6. Color Base | |
502 | ||
503 | A color base is a container of color elements. The most common use of color base is in the implementation of a pixel, in which case the color | |
504 | elements are channel values. The color base concept, however, can be used in other scenarios. For example, a planar pixel has channels that are not | |
505 | contiguous in memory. Its reference is a proxy class that uses a color base whose elements are channel references. Its iterator uses a color base | |
506 | whose elements are channel iterators. | |
507 | ||
508 | Color base models must satisfy the following concepts: | |
509 | ||
510 | \code | |
511 | concept ColorBaseConcept<typename T> : CopyConstructible<T>, EqualityComparable<T> { | |
512 | // a GIL layout (the color space and element permutation) | |
513 | typename layout_t; | |
514 | ||
515 | // The type of K-th element | |
516 | template <int K> struct kth_element_type; | |
517 | where Metafunction<kth_element_type>; | |
518 | ||
519 | // The result of at_c | |
520 | template <int K> struct kth_element_const_reference_type; | |
521 | where Metafunction<kth_element_const_reference_type>; | |
522 | ||
523 | template <int K> kth_element_const_reference_type<T,K>::type at_c(T); | |
524 | ||
525 | template <ColorBaseConcept T2> where { ColorBasesCompatibleConcept<T,T2> } | |
526 | T::T(T2); | |
527 | template <ColorBaseConcept T2> where { ColorBasesCompatibleConcept<T,T2> } | |
528 | bool operator==(const T&, const T2&); | |
529 | template <ColorBaseConcept T2> where { ColorBasesCompatibleConcept<T,T2> } | |
530 | bool operator!=(const T&, const T2&); | |
531 | ||
532 | }; | |
533 | ||
534 | concept MutableColorBaseConcept<ColorBaseConcept T> : Assignable<T>, Swappable<T> { | |
535 | template <int K> struct kth_element_reference_type; | |
536 | where Metafunction<kth_element_reference_type>; | |
537 | ||
538 | template <int K> kth_element_reference_type<T,K>::type at_c(T); | |
539 | ||
540 | template <ColorBaseConcept T2> where { ColorBasesCompatibleConcept<T,T2> } | |
541 | T& operator=(T&, const T2&); | |
542 | }; | |
543 | ||
544 | concept ColorBaseValueConcept<typename T> : MutableColorBaseConcept<T>, Regular<T> { | |
545 | }; | |
546 | ||
547 | concept HomogeneousColorBaseConcept<ColorBaseConcept CB> { | |
548 | // For all K in [0 ... size<C1>::value-1): | |
549 | // where SameType<kth_element_type<K>::type, kth_element_type<K+1>::type>; | |
550 | kth_element_const_reference_type<0>::type dynamic_at_c(const CB&, std::size_t n) const; | |
551 | }; | |
552 | ||
553 | concept MutableHomogeneousColorBaseConcept<MutableColorBaseConcept CB> : HomogeneousColorBaseConcept<CB> { | |
554 | kth_element_reference_type<0>::type dynamic_at_c(const CB&, std::size_t n); | |
555 | }; | |
556 | ||
557 | concept HomogeneousColorBaseValueConcept<typename T> : MutableHomogeneousColorBaseConcept<T>, Regular<T> { | |
558 | }; | |
559 | ||
560 | concept ColorBasesCompatibleConcept<ColorBaseConcept C1, ColorBaseConcept C2> { | |
561 | where SameType<C1::layout_t::color_space_t, C2::layout_t::color_space_t>; | |
562 | // also, for all K in [0 ... size<C1>::value): | |
563 | // where Convertible<kth_semantic_element_type<C1,K>::type, kth_semantic_element_type<C2,K>::type>; | |
564 | // where Convertible<kth_semantic_element_type<C2,K>::type, kth_semantic_element_type<C1,K>::type>; | |
565 | }; | |
566 | \endcode | |
567 | ||
568 | A color base must have an associated layout (which consists of a color space, as well as an ordering of the channels). | |
569 | There are two ways to index the elements of a color base: A physical index corresponds to the way they are ordered in memory, and | |
570 | a semantic index corresponds to the way the elements are ordered in their color space. | |
571 | For example, in the RGB color space the elements are ordered as {red_t, green_t, blue_t}. For a color base with a BGR layout, the first element | |
572 | in physical ordering is the blue element, whereas the first semantic element is the red one. | |
573 | Models of \p ColorBaseConcept are required to provide the \p at_c<K>(ColorBase) function, which allows for accessing the elements based on their | |
574 | physical order. GIL provides a \p semantic_at_c<K>(ColorBase) function (described later) which can operate on any model of ColorBaseConcept and returns | |
575 | the corresponding semantic element. | |
576 | ||
577 | Two color bases are <i>compatible</i> if they have the same color space and their elements (paired semantically) are convertible to each other. | |
578 | ||
579 | ||
580 | <b>Models:</b> | |
581 | ||
582 | GIL provides a model for a homogeneous color base (a color base whose elements all have the same type). | |
583 | ||
584 | \code | |
585 | namespace detail { | |
586 | template <typename Element, typename Layout, int K> struct homogeneous_color_base; | |
587 | } | |
588 | \endcode | |
589 | ||
590 | It is used in the implementation of GIL's pixel, planar pixel reference and planar pixel iterator. | |
591 | Another model of \p ColorBaseConcept is \p packed_pixel - it is a pixel whose channels are bit ranges. See the \ref PixelSectionDG | |
592 | section for more. | |
593 | ||
594 | <b>Algorithms:</b> | |
595 | ||
596 | GIL provides the following functions and metafunctions operating on color bases: | |
597 | ||
598 | \code | |
599 | // Metafunction returning an mpl::int_ equal to the number of elements in the color base | |
600 | template <class ColorBase> struct size; | |
601 | ||
602 | // Returns the type of the return value of semantic_at_c<K>(color_base) | |
603 | template <class ColorBase, int K> struct kth_semantic_element_reference_type; | |
604 | template <class ColorBase, int K> struct kth_semantic_element_const_reference_type; | |
605 | ||
606 | // Returns a reference to the element with K-th semantic index. | |
607 | template <class ColorBase, int K> | |
608 | typename kth_semantic_element_reference_type<ColorBase,K>::type semantic_at_c(ColorBase& p) | |
609 | template <class ColorBase, int K> | |
610 | typename kth_semantic_element_const_reference_type<ColorBase,K>::type semantic_at_c(const ColorBase& p) | |
611 | ||
612 | // Returns the type of the return value of get_color<Color>(color_base) | |
613 | template <typename Color, typename ColorBase> struct color_reference_t; | |
614 | template <typename Color, typename ColorBase> struct color_const_reference_t; | |
615 | ||
616 | // Returns a reference to the element corresponding to the given color | |
617 | template <typename ColorBase, typename Color> | |
618 | typename color_reference_t<Color,ColorBase>::type get_color(ColorBase& cb, Color=Color()); | |
619 | template <typename ColorBase, typename Color> | |
620 | typename color_const_reference_t<Color,ColorBase>::type get_color(const ColorBase& cb, Color=Color()); | |
621 | ||
622 | // Returns the element type of the color base. Defined for homogeneous color bases only | |
623 | template <typename ColorBase> struct element_type; | |
624 | template <typename ColorBase> struct element_reference_type; | |
625 | template <typename ColorBase> struct element_const_reference_type; | |
626 | \endcode | |
627 | ||
628 | GIL also provides the following algorithms which operate on color bases. Note that they all pair the elements semantically: | |
629 | ||
630 | \code | |
631 | // Equivalents to std::equal, std::copy, std::fill, std::generate | |
632 | template <typename CB1,typename CB2> bool static_equal(const CB1& p1, const CB2& p2); | |
633 | template <typename Src,typename Dst> void static_copy(const Src& src, Dst& dst); | |
634 | template <typename CB, typename Op> void static_generate(CB& dst,Op op); | |
635 | ||
636 | // Equivalents to std::transform | |
637 | template <typename CB , typename Dst,typename Op> Op static_transform( CB&,Dst&,Op); | |
638 | template <typename CB , typename Dst,typename Op> Op static_transform(const CB&,Dst&,Op); | |
639 | template <typename CB1,typename CB2,typename Dst,typename Op> Op static_transform( CB1&, CB2&,Dst&,Op); | |
640 | template <typename CB1,typename CB2,typename Dst,typename Op> Op static_transform(const CB1&, CB2&,Dst&,Op); | |
641 | template <typename CB1,typename CB2,typename Dst,typename Op> Op static_transform( CB1&,const CB2&,Dst&,Op); | |
642 | template <typename CB1,typename CB2,typename Dst,typename Op> Op static_transform(const CB1&,const CB2&,Dst&,Op); | |
643 | ||
644 | // Equivalents to std::for_each | |
645 | template <typename CB1, typename Op> Op static_for_each( CB1&,Op); | |
646 | template <typename CB1, typename Op> Op static_for_each(const CB1&,Op); | |
647 | template <typename CB1,typename CB2, typename Op> Op static_for_each( CB1&, CB2&,Op); | |
648 | template <typename CB1,typename CB2, typename Op> Op static_for_each( CB1&,const CB2&,Op); | |
649 | template <typename CB1,typename CB2, typename Op> Op static_for_each(const CB1&, CB2&,Op); | |
650 | template <typename CB1,typename CB2, typename Op> Op static_for_each(const CB1&,const CB2&,Op); | |
651 | template <typename CB1,typename CB2,typename CB3,typename Op> Op static_for_each( CB1&, CB2&, CB3&,Op); | |
652 | template <typename CB1,typename CB2,typename CB3,typename Op> Op static_for_each( CB1&, CB2&,const CB3&,Op); | |
653 | template <typename CB1,typename CB2,typename CB3,typename Op> Op static_for_each( CB1&,const CB2&, CB3&,Op); | |
654 | template <typename CB1,typename CB2,typename CB3,typename Op> Op static_for_each( CB1&,const CB2&,const CB3&,Op); | |
655 | template <typename CB1,typename CB2,typename CB3,typename Op> Op static_for_each(const CB1&, CB2&, CB3&,Op); | |
656 | template <typename CB1,typename CB2,typename CB3,typename Op> Op static_for_each(const CB1&, CB2&,const CB3&,Op); | |
657 | template <typename CB1,typename CB2,typename CB3,typename Op> Op static_for_each(const CB1&,const CB2&, CB3&,Op); | |
658 | template <typename CB1,typename CB2,typename CB3,typename Op> Op static_for_each(const CB1&,const CB2&,const CB3&,Op); | |
659 | ||
660 | // The following algorithms are only defined for homogeneous color bases: | |
661 | // Equivalent to std::fill | |
662 | template <typename HCB, typename Element> void static_fill(HCB& p, const Element& v); | |
663 | ||
664 | // Equivalents to std::min_element and std::max_element | |
665 | template <typename HCB> typename element_const_reference_type<HCB>::type static_min(const HCB&); | |
666 | template <typename HCB> typename element_reference_type<HCB>::type static_min( HCB&); | |
667 | template <typename HCB> typename element_const_reference_type<HCB>::type static_max(const HCB&); | |
668 | template <typename HCB> typename element_reference_type<HCB>::type static_max( HCB&); | |
669 | \endcode | |
670 | ||
671 | These algorithms are designed after the corresponding STL algorithms, except that instead of ranges they take color bases and operate on their elements. | |
672 | In addition, they are implemented with a compile-time recursion (thus the prefix "static_"). Finally, they pair the elements semantically instead of based | |
673 | on their physical order in memory. For example, here is the implementation of \p static_equal: | |
674 | ||
675 | \code | |
676 | namespace detail { | |
677 | template <int K> struct element_recursion { | |
678 | template <typename P1,typename P2> | |
679 | static bool static_equal(const P1& p1, const P2& p2) { | |
680 | return element_recursion<K-1>::static_equal(p1,p2) && | |
681 | semantic_at_c<K-1>(p1)==semantic_at_c<N-1>(p2); | |
682 | } | |
683 | }; | |
684 | template <> struct element_recursion<0> { | |
685 | template <typename P1,typename P2> | |
686 | static bool static_equal(const P1&, const P2&) { return true; } | |
687 | }; | |
688 | } | |
689 | ||
690 | template <typename P1,typename P2> | |
691 | bool static_equal(const P1& p1, const P2& p2) { | |
692 | gil_function_requires<ColorSpacesCompatibleConcept<P1::layout_t::color_space_t,P2::layout_t::color_space_t> >(); | |
693 | return detail::element_recursion<size<P1>::value>::static_equal(p1,p2); | |
694 | } | |
695 | \endcode | |
696 | ||
697 | This algorithm is used when invoking \p operator== on two pixels, for example. By using semantic accessors we are properly comparing an RGB pixel | |
698 | to a BGR pixel. Notice also that all of the above algorithms taking more than one color base require that they all have the same color space. | |
699 | ||
700 | ||
701 | ||
702 | ||
703 | ||
704 | ||
705 | ||
706 | ||
707 | ||
708 | ||
709 | ||
710 | ||
711 | ||
712 | ||
713 | ||
714 | ||
715 | ||
716 | ||
717 | ||
718 | ||
719 | ||
720 | ||
721 | ||
722 | ||
723 | ||
724 | ||
725 | ||
726 | ||
727 | ||
728 | ||
729 | ||
730 | ||
731 | ||
732 | ||
733 | ||
734 | ||
735 | ||
736 | ||
737 | ||
738 | ||
739 | <hr> | |
740 | \section PixelSectionDG 7. Pixel | |
741 | ||
742 | A pixel is a set of channels defining the color at a given point in an image. Conceptually, a pixel is little more than a color base whose elements | |
743 | model \p ChannelConcept. | |
744 | All properties of pixels inherit from color bases: pixels may be <i>homogeneous</i> if all of their channels have the same type; otherwise they are | |
745 | called <i>heterogeneous</i>. The channels of a pixel may be addressed using semantic or physical indexing, or by color; all color-base algorithms | |
746 | work on pixels as well. Two pixels are <i>compatible</i> if their color spaces are the same and their channels, paired semantically, are compatible. | |
747 | Note that constness, memory organization and reference/value are ignored. For example, an 8-bit RGB planar reference is compatible to a constant 8-bit | |
748 | BGR interleaved pixel value. Most pairwise pixel operations (copy construction, assignment, equality, etc.) are only defined for compatible pixels. | |
749 | ||
750 | Pixels (as well as other GIL constructs built on pixels, such as iterators, locators, views and images) must provide metafunctions to access | |
751 | their color space, channel mapping, number of channels, and (for homogeneous pixels) the channel type: | |
752 | ||
753 | \code | |
754 | concept PixelBasedConcept<typename T> { | |
755 | typename color_space_type<T>; | |
756 | where Metafunction<color_space_type<T> >; | |
757 | where ColorSpaceConcept<color_space_type<T>::type>; | |
758 | typename channel_mapping_type<T>; | |
759 | where Metafunction<channel_mapping_type<T> >; | |
760 | where ChannelMappingConcept<channel_mapping_type<T>::type>; | |
761 | typename is_planar<T>; | |
762 | where Metafunction<is_planar<T> >; | |
763 | where SameType<is_planar<T>::type, bool>; | |
764 | }; | |
765 | ||
766 | concept HomogeneousPixelBasedConcept<PixelBasedConcept T> { | |
767 | typename channel_type<T>; | |
768 | where Metafunction<channel_type<T> >; | |
769 | where ChannelConcept<channel_type<T>::type>; | |
770 | }; | |
771 | \endcode | |
772 | ||
773 | Pixels model the following concepts: | |
774 | ||
775 | \code | |
776 | concept PixelConcept<typename P> : ColorBaseConcept<P>, PixelBasedConcept<P> { | |
777 | where is_pixel<P>::type::value==true; | |
778 | // where for each K [0..size<P>::value-1]: | |
779 | // ChannelConcept<kth_element_type<K> >; | |
780 | ||
781 | typename value_type; where PixelValueConcept<value_type>; | |
782 | typename reference; where PixelConcept<reference>; | |
783 | typename const_reference; where PixelConcept<const_reference>; | |
784 | static const bool P::is_mutable; | |
785 | ||
786 | template <PixelConcept P2> where { PixelConcept<P,P2> } | |
787 | P::P(P2); | |
788 | template <PixelConcept P2> where { PixelConcept<P,P2> } | |
789 | bool operator==(const P&, const P2&); | |
790 | template <PixelConcept P2> where { PixelConcept<P,P2> } | |
791 | bool operator!=(const P&, const P2&); | |
792 | }; | |
793 | ||
794 | concept MutablePixelConcept<typename P> : PixelConcept<P>, MutableColorBaseConcept<P> { | |
795 | where is_mutable==true; | |
796 | }; | |
797 | ||
798 | concept HomogeneousPixelConcept<PixelConcept P> : HomogeneousColorBaseConcept<P>, HomogeneousPixelBasedConcept<P> { | |
799 | P::template element_const_reference_type<P>::type operator[](P p, std::size_t i) const { return dynamic_at_c(P,i); } | |
800 | }; | |
801 | ||
802 | concept MutableHomogeneousPixelConcept<MutablePixelConcept P> : MutableHomogeneousColorBaseConcept<P> { | |
803 | P::template element_reference_type<P>::type operator[](P p, std::size_t i) { return dynamic_at_c(p,i); } | |
804 | }; | |
805 | ||
806 | concept PixelValueConcept<typename P> : PixelConcept<P>, Regular<P> { | |
807 | where SameType<value_type,P>; | |
808 | }; | |
809 | ||
810 | concept PixelsCompatibleConcept<PixelConcept P1, PixelConcept P2> : ColorBasesCompatibleConcept<P1,P2> { | |
811 | // where for each K [0..size<P1>::value): | |
812 | // ChannelsCompatibleConcept<kth_semantic_element_type<P1,K>::type, kth_semantic_element_type<P2,K>::type>; | |
813 | }; | |
814 | \endcode | |
815 | ||
816 | A pixel is <i>convertible</i> to a second pixel if it is possible to approximate its color in the form of the second pixel. Conversion is an explicit, | |
817 | non-symmetric and often lossy operation (due to both channel and color space approximation). Convertability requires modeling the following concept: | |
818 | ||
819 | \code | |
820 | template <PixelConcept SrcPixel, MutablePixelConcept DstPixel> | |
821 | concept PixelConvertibleConcept { | |
822 | void color_convert(const SrcPixel&, DstPixel&); | |
823 | }; | |
824 | \endcode | |
825 | ||
826 | The distinction between \p PixelConcept and \p PixelValueConcept is analogous to that for channels and color bases - pixel reference proxies model both, | |
827 | but only pixel values model the latter. | |
828 | ||
829 | <b>Related Concepts:</b> | |
830 | ||
831 | - PixelBasedConcept\<P> | |
832 | - PixelConcept\<Pixel> | |
833 | - MutablePixelConcept\<Pixel> | |
834 | - PixelValueConcept\<Pixel> | |
835 | - HomogeneousPixelConcept\<Pixel> | |
836 | - MutableHomogeneousPixelConcept\<Pixel> | |
837 | - HomogeneousPixelValueConcept\<Pixel> | |
838 | - PixelsCompatibleConcept\<Pixel1,Pixel2> | |
839 | - PixelConvertibleConcept\<SrcPixel,DstPixel> | |
840 | ||
841 | <b>Models:</b> | |
842 | ||
843 | The most commonly used pixel is a homogeneous pixel whose values are together in memory. | |
844 | For this purpose GIL provides the struct \p pixel, templated over the channel value and layout: | |
845 | ||
846 | \code | |
847 | // models HomogeneousPixelValueConcept | |
848 | template <typename ChannelValue, typename Layout> struct pixel; | |
849 | ||
850 | // Those typedefs are already provided by GIL | |
851 | typedef pixel<bits8, rgb_layout_t> rgb8_pixel_t; | |
852 | typedef pixel<bits8, bgr_layout_t> bgr8_pixel_t; | |
853 | ||
854 | bgr8_pixel_t bgr8(255,0,0); // pixels can be initialized with the channels directly | |
855 | rgb8_pixel_t rgb8(bgr8); // compatible pixels can also be copy-constructed | |
856 | ||
857 | rgb8 = bgr8; // assignment and equality is defined between compatible pixels | |
858 | assert(rgb8 == bgr8); // assignment and equality operate on the semantic channels | |
859 | ||
860 | // The first physical channels of the two pixels are different | |
861 | assert(at_c<0>(rgb8) != at_c<0>(bgr8)); | |
862 | assert(dynamic_at_c(bgr8,0) != dynamic_at_c(rgb8,0)); | |
863 | assert(rgb8[0] != bgr8[0]); // same as above (but operator[] is defined for pixels only) | |
864 | \endcode | |
865 | ||
866 | Planar pixels have their channels distributed in memory. While they share the same value type (\p pixel) with interleaved pixels, their | |
867 | reference type is a proxy class containing references to each of the channels. This is implemented with the struct \p planar_pixel_reference: | |
868 | ||
869 | \code | |
870 | // models HomogeneousPixel | |
871 | template <typename ChannelReference, typename ColorSpace> struct planar_pixel_reference; | |
872 | ||
873 | // Define the type of a mutable and read-only reference. (These typedefs are already provided by GIL) | |
874 | typedef planar_pixel_reference< bits8&,rgb_t> rgb8_planar_ref_t; | |
875 | typedef planar_pixel_reference<const bits8&,rgb_t> rgb8c_planar_ref_t; | |
876 | \endcode | |
877 | ||
878 | Note that, unlike the \p pixel struct, planar pixel references are templated over the color space, not over the pixel layout. They always | |
879 | use a cannonical channel ordering. Ordering of their elements is unnecessary because their elements are references to the channels. | |
880 | ||
881 | Sometimes the channels of a pixel may not be byte-aligned. For example an RGB pixel in '5-5-6' format is a 16-bit pixel whose red, green and blue | |
882 | channels occupy bits [0..4],[5..9] and [10..15] respectively. GIL provides a model for such packed pixel formats: | |
883 | ||
884 | \code | |
885 | // define an rgb565 pixel | |
886 | typedef packed_pixel_type<uint16_t, mpl::vector3_c<unsigned,5,6,5>, rgb_layout_t>::type rgb565_pixel_t; | |
887 | ||
888 | function_requires<PixelValueConcept<rgb565_pixel_t> >(); | |
889 | BOOST_STATIC_ASSERT((sizeof(rgb565_pixel_t)==2)); | |
890 | ||
891 | // define a bgr556 pixel | |
892 | typedef packed_pixel_type<uint16_t, mpl::vector3_c<unsigned,5,6,5>, bgr_layout_t>::type bgr556_pixel_t; | |
893 | ||
894 | function_requires<PixelValueConcept<bgr556_pixel_t> >(); | |
895 | ||
896 | // rgb565 is compatible with bgr556. | |
897 | function_requires<PixelsCompatibleConcept<rgb565_pixel_t,bgr556_pixel_t> >(); | |
898 | \endcode | |
899 | ||
900 | In some cases, the pixel itself may not be byte aligned. For example, consider an RGB pixel in '2-3-2' format. Its size is 7 bits. | |
901 | GIL refers to such pixels, pixel iterators and images as "bit-aligned". Bit-aligned pixels (and images) are more complex than packed ones. | |
902 | Since packed pixels are byte-aligned, we can use a C++ reference as the reference type to a packed pixel, and a C pointer as an x_iterator | |
903 | over a row of packed pixels. For bit-aligned constructs we need a special reference proxy class (bit_aligned_pixel_reference) and iterator | |
904 | class (bit_aligned_pixel_iterator). The value type of bit-aligned pixels is a packed_pixel. Here is how to use bit_aligned pixels and pixel iterators: | |
905 | ||
906 | \code | |
907 | // Mutable reference to a BGR232 pixel | |
908 | typedef const bit_aligned_pixel_reference<unsigned char, mpl::vector3_c<unsigned,2,3,2>, bgr_layout_t, true> bgr232_ref_t; | |
909 | ||
910 | // A mutable iterator over BGR232 pixels | |
911 | typedef bit_aligned_pixel_iterator<bgr232_ref_t> bgr232_ptr_t; | |
912 | ||
913 | // BGR232 pixel value. It is a packed_pixel of size 1 byte. (The last bit is unused) | |
914 | typedef std::iterator_traits<bgr232_ptr_t>::value_type bgr232_pixel_t; | |
915 | BOOST_STATIC_ASSERT((sizeof(bgr232_pixel_t)==1)); | |
916 | ||
917 | bgr232_pixel_t red(0,0,3); // = 0RRGGGBB, = 01100000 = 0x60 | |
918 | ||
919 | // a buffer of 7 bytes fits exactly 8 BGR232 pixels. | |
920 | unsigned char pix_buffer[7]; | |
921 | std::fill(pix_buffer,pix_buffer+7,0); | |
922 | ||
923 | // Fill the 8 pixels with red | |
924 | bgr232_ptr_t pix_it(&pix_buffer[0],0); // start at bit 0 of the first pixel | |
925 | for (int i=0; i<8; ++i) { | |
926 | *pix_it++ = red; | |
927 | } | |
928 | // Result: 0x60 0x30 0x11 0x0C 0x06 0x83 0xC1 | |
929 | \endcode | |
930 | ||
931 | ||
932 | <b>Algorithms:</b> | |
933 | ||
934 | Since pixels model \p ColorBaseConcept and \p PixelBasedConcept all algorithms and metafunctions of color bases can work with them as well: | |
935 | ||
936 | \code | |
937 | // This is how to access the first semantic channel (red) | |
938 | assert(semantic_at_c<0>(rgb8) == semantic_at_c<0>(bgr8)); | |
939 | ||
940 | // This is how to access the red channel by name | |
941 | assert(get_color<red_t>(rgb8) == get_color<red_t>(bgr8)); | |
942 | ||
943 | // This is another way of doing it (some compilers don't like the first one) | |
944 | assert(get_color(rgb8,red_t()) == get_color(bgr8,red_t())); | |
945 | ||
946 | // This is how to use the PixelBasedConcept metafunctions | |
947 | BOOST_MPL_ASSERT(num_channels<rgb8_pixel_t>::value == 3); | |
948 | BOOST_MPL_ASSERT((is_same<channel_type<rgb8_pixel_t>::type, bits8>)); | |
949 | BOOST_MPL_ASSERT((is_same<color_space_type<bgr8_pixel_t>::type, rgb_t> )); | |
950 | BOOST_MPL_ASSERT((is_same<channel_mapping_type<bgr8_pixel_t>::type, mpl::vector3_c<int,2,1,0> > )); | |
951 | ||
952 | // Pixels contain just the three channels and nothing extra | |
953 | BOOST_MPL_ASSERT(sizeof(rgb8_pixel_t)==3); | |
954 | ||
955 | rgb8_planar_ref_t ref(bgr8); // copy construction is allowed from a compatible mutable pixel type | |
956 | ||
957 | get_color<red_t>(ref) = 10; // assignment is ok because the reference is mutable | |
958 | assert(get_color<red_t>(bgr8)==10); // references modify the value they are bound to | |
959 | ||
960 | // Create a zero packed pixel and a full regular unpacked pixel. | |
961 | rgb565_pixel_t r565; | |
962 | rgb8_pixel_t rgb_full(255,255,255); | |
963 | ||
964 | // Convert all channels of the unpacked pixel to the packed one & assert the packed one is full | |
965 | get_color(r565,red_t()) = channel_convert<rgb565_channel0_t>(get_color(rgb_full,red_t())); | |
966 | get_color(r565,green_t()) = channel_convert<rgb565_channel1_t>(get_color(rgb_full,green_t())); | |
967 | get_color(r565,blue_t()) = channel_convert<rgb565_channel2_t>(get_color(rgb_full,blue_t())); | |
968 | assert(r565 == rgb565_pixel_t((uint16_t)65535)); | |
969 | \endcode | |
970 | ||
971 | GIL also provides the \p color_convert algorithm to convert between pixels of different color spaces and channel types: | |
972 | ||
973 | \code | |
974 | rgb8_pixel_t red_in_rgb8(255,0,0); | |
975 | cmyk16_pixel_t red_in_cmyk16; | |
976 | color_convert(red_in_rgb8,red_in_cmyk16); | |
977 | \endcode | |
978 | ||
979 | ||
980 | ||
981 | ||
982 | ||
983 | ||
984 | ||
985 | ||
986 | ||
987 | ||
988 | ||
989 | ||
990 | ||
991 | ||
992 | ||
993 | ||
994 | ||
995 | ||
996 | ||
997 | ||
998 | ||
999 | ||
1000 | ||
1001 | ||
1002 | ||
1003 | ||
1004 | ||
1005 | ||
1006 | ||
1007 | ||
1008 | ||
1009 | ||
1010 | ||
1011 | ||
1012 | ||
1013 | ||
1014 | ||
1015 | ||
1016 | <hr> | |
1017 | \section PixelIteratorSectionDG 8. Pixel Iterator | |
1018 | ||
1019 | \section FundamentalIteratorDG Fundamental Iterator | |
1020 | ||
1021 | Pixel iterators are random traversal iterators whose \p value_type models \p PixelValueConcept. | |
1022 | Pixel iterators provide metafunctions to determine whether they are mutable (i.e. whether they allow for modifying the pixel they refer to), | |
1023 | to get the immutable (read-only) type of the iterator, and to determine whether they are plain iterators or adaptors over another pixel iterator: | |
1024 | ||
1025 | \code | |
1026 | concept PixelIteratorConcept<RandomAccessTraversalIteratorConcept Iterator> : PixelBasedConcept<Iterator> { | |
1027 | where PixelValueConcept<value_type>; | |
1028 | typename const_iterator_type<It>::type; | |
1029 | where PixelIteratorConcept<const_iterator_type<It>::type>; | |
1030 | static const bool iterator_is_mutable<It>::type::value; | |
1031 | static const bool is_iterator_adaptor<It>::type::value; // is it an iterator adaptor | |
1032 | }; | |
1033 | ||
1034 | template <typename Iterator> | |
1035 | concept MutablePixelIteratorConcept : PixelIteratorConcept<Iterator>, MutableRandomAccessIteratorConcept<Iterator> {}; | |
1036 | \endcode | |
1037 | ||
1038 | <b>Related Concepts:</b> | |
1039 | ||
1040 | - PixelIteratorConcept\<Iterator> | |
1041 | - MutablePixelIteratorConcept\<Iterator> | |
1042 | ||
1043 | <b>Models:</b> | |
1044 | ||
1045 | A built-in pointer to pixel, \p pixel<ChannelValue,Layout>*, is GIL's model for pixel iterator over interleaved homogeneous pixels. | |
1046 | Similarly, \p packed_pixel<PixelData,ChannelRefVec,Layout>* is GIL's model for an iterator over interleaved packed pixels. | |
1047 | ||
1048 | For planar homogeneous pixels, GIL provides the class \p planar_pixel_iterator, templated over a channel iterator and color space. Here is | |
1049 | how the standard mutable and read-only planar RGB iterators over unsigned char are defined: | |
1050 | ||
1051 | \code | |
1052 | template <typename ChannelPtr, typename ColorSpace> struct planar_pixel_iterator; | |
1053 | ||
1054 | // GIL provided typedefs | |
1055 | typedef planar_pixel_iterator<const bits8*, rgb_t> rgb8c_planar_ptr_t; | |
1056 | typedef planar_pixel_iterator< bits8*, rgb_t> rgb8_planar_ptr_t; | |
1057 | \endcode | |
1058 | ||
1059 | \p planar_pixel_iterator also models \p HomogeneousColorBaseConcept (it subclasses from \p homogeneous_color_base) and, as a result, all color base | |
1060 | algorithms apply to it. The element type of its color base is a channel iterator. For example, GIL implements \p operator++ of planar iterators approximately | |
1061 | like this: | |
1062 | ||
1063 | \code | |
1064 | template <typename T> | |
1065 | struct inc : public std::unary_function<T,T> { | |
1066 | T operator()(T x) const { return ++x; } | |
1067 | }; | |
1068 | ||
1069 | template <typename ChannelPtr, typename ColorSpace> | |
1070 | planar_pixel_iterator<ChannelPtr,ColorSpace>& | |
1071 | planar_pixel_iterator<ChannelPtr,ColorSpace>::operator++() { | |
1072 | static_transform(*this,*this,inc<ChannelPtr>()); | |
1073 | return *this; | |
1074 | } | |
1075 | \endcode | |
1076 | ||
1077 | Since \p static_transform uses compile-time recursion, incrementing an instance of \p rgb8_planar_ptr_t amounts to three pointer increments. | |
1078 | GIL also uses the class bit_aligned_pixel_iterator as a model for a pixel iterator over bit-aligned pixels. Internally it keeps track of the current byte and | |
1079 | the bit offset. | |
1080 | ||
1081 | \section IteratorAdaptorDG Iterator Adaptor | |
1082 | ||
1083 | Iterator adaptor is an iterator that wraps around another iterator. Its \p is_iterator_adaptor metafunction must evaluate to true, and it | |
1084 | needs to provide a member method to return the base iterator, a metafunction to get its type, and a metafunction to rebind to another base iterator: | |
1085 | ||
1086 | \code | |
1087 | concept IteratorAdaptorConcept<RandomAccessTraversalIteratorConcept Iterator> { | |
1088 | where SameType<is_iterator_adaptor<Iterator>::type, mpl::true_>; | |
1089 | ||
1090 | typename iterator_adaptor_get_base<Iterator>; | |
1091 | where Metafunction<iterator_adaptor_get_base<Iterator> >; | |
1092 | where boost_concepts::ForwardTraversalConcept<iterator_adaptor_get_base<Iterator>::type>; | |
1093 | ||
1094 | typename another_iterator; | |
1095 | typename iterator_adaptor_rebind<Iterator,another_iterator>::type; | |
1096 | where boost_concepts::ForwardTraversalConcept<another_iterator>; | |
1097 | where IteratorAdaptorConcept<iterator_adaptor_rebind<Iterator,another_iterator>::type>; | |
1098 | ||
1099 | const iterator_adaptor_get_base<Iterator>::type& Iterator::base() const; | |
1100 | }; | |
1101 | ||
1102 | template <boost_concepts::Mutable_ForwardIteratorConcept Iterator> | |
1103 | concept MutableIteratorAdaptorConcept : IteratorAdaptorConcept<Iterator> {}; | |
1104 | \endcode | |
1105 | ||
1106 | <b>Related Concepts:</b> | |
1107 | ||
1108 | - IteratorAdaptorConcept\<Iterator> | |
1109 | - MutableIteratorAdaptorConcept\<Iterator> | |
1110 | ||
1111 | <b>Models:</b> | |
1112 | ||
1113 | GIL provides several models of IteratorAdaptorConcept: | |
1114 | - \p memory_based_step_iterator\<Iterator>: An iterator adaptor that changes the fundamental step of the base iterator (see \ref StepIteratorDG) | |
1115 | - \p dereference_iterator_adaptor\<Iterator,Fn>: An iterator that applies a unary function \p Fn upon dereferencing. It is used, for example, | |
1116 | for on-the-fly color conversion. It can be used to construct a shallow image "view" that pretends to have a different color space or | |
1117 | channel depth. See \ref ImageViewFrowImageViewDG for more. The unary function \p Fn must model \p PixelDereferenceAdaptorConcept (see below). | |
1118 | ||
1119 | \section PixelDereferenceAdaptorAG Pixel Dereference Adaptor | |
1120 | ||
1121 | Pixel dereference adaptor is a unary function that can be applied upon dereferencing a pixel iterator. Its argument type could be anything | |
1122 | (usually a \p PixelConcept) and the result type must be convertible to \p PixelConcept | |
1123 | ||
1124 | \code | |
1125 | template <boost::UnaryFunctionConcept D> | |
1126 | concept PixelDereferenceAdaptorConcept : DefaultConstructibleConcept<D>, CopyConstructibleConcept<D>, AssignableConcept<D> { | |
1127 | typename const_t; where PixelDereferenceAdaptorConcept<const_t>; | |
1128 | typename value_type; where PixelValueConcept<value_type>; | |
1129 | typename reference; where PixelConcept<remove_reference<reference>::type>; // may be mutable | |
1130 | typename const_reference; // must not be mutable | |
1131 | static const bool D::is_mutable; | |
1132 | ||
1133 | where Convertible<value_type, result_type>; | |
1134 | }; | |
1135 | \endcode | |
1136 | ||
1137 | <b>Models:</b> | |
1138 | ||
1139 | GIL provides several models of \p PixelDereferenceAdaptorConcept | |
1140 | - \p color_convert_deref_fn: a function object that performs color conversion | |
1141 | - \p detail::nth_channel_deref_fn: a function object that returns a grayscale pixel corresponding to the n-th channel of a given pixel | |
1142 | - \p deref_compose: a function object that composes two models of \p PixelDereferenceAdaptorConcept. Similar to \p std::unary_compose, except | |
1143 | it needs to pull the additional typedefs required by \p PixelDereferenceAdaptorConcept | |
1144 | ||
1145 | GIL uses pixel dereference adaptors to implement image views that perform color conversion upon dereferencing, or that return the N-th channel of the | |
1146 | underlying pixel. They can be used to model virtual image views that perform an arbitrary function upon dereferencing, for example a view of | |
1147 | the Mandelbrot set. \p dereference_iterator_adaptor<Iterator,Fn> is an iterator wrapper over a pixel iterator \p Iterator that invokes the given dereference | |
1148 | iterator adaptor \p Fn upon dereferencing. | |
1149 | ||
1150 | \section StepIteratorDG Step Iterator | |
1151 | ||
1152 | Sometimes we want to traverse pixels with a unit step other than the one provided by the fundamental pixel iterators. | |
1153 | Examples where this would be useful: | |
1154 | - a single-channel view of the red channel of an RGB interleaved image | |
1155 | - left-to-right flipped image (step = -fundamental_step) | |
1156 | - subsampled view, taking every N-th pixel (step = N*fundamental_step) | |
1157 | - traversal in vertical direction (step = number of bytes per row) | |
1158 | - any combination of the above (steps are multiplied) | |
1159 | ||
1160 | Step iterators are forward traversal iterators that allow changing the step between adjacent values: | |
1161 | ||
1162 | \code | |
1163 | concept StepIteratorConcept<boost_concepts::ForwardTraversalConcept Iterator> { | |
1164 | template <Integral D> void Iterator::set_step(D step); | |
1165 | }; | |
1166 | ||
1167 | concept MutableStepIteratorConcept<boost_concepts::Mutable_ForwardIteratorConcept Iterator> : StepIteratorConcept<Iterator> {}; | |
1168 | \endcode | |
1169 | ||
1170 | GIL currently provides a step iterator whose \p value_type models \p PixelValueConcept. In addition, the step is specified in memory units (which are bytes or bits). | |
1171 | This is necessary, for example, when implementing an iterator navigating along a column of pixels - the size of a row of pixels | |
1172 | may sometimes not be divisible by the size of a pixel; for example rows may be word-aligned. | |
1173 | ||
1174 | To advance in bytes/bits, the base iterator must model MemoryBasedIteratorConcept. A memory-based iterator has an inherent memory unit, which is either a bit or a byte. | |
1175 | It must supply functions returning the number of bits per memory unit (1 or 8), the current step in memory units, | |
1176 | the memory-unit distance between two iterators, and a reference a given distance in memunits away. It must also supply a function that advances an iterator | |
1177 | a given distance in memory units. | |
1178 | \p memunit_advanced and \p memunit_advanced_ref have a default implementation but some iterators may supply a more efficient version: | |
1179 | ||
1180 | \code | |
1181 | concept MemoryBasedIteratorConcept<boost_concepts::RandomAccessTraversalConcept Iterator> { | |
1182 | typename byte_to_memunit<Iterator>; where metafunction<byte_to_memunit<Iterator> >; | |
1183 | std::ptrdiff_t memunit_step(const Iterator&); | |
1184 | std::ptrdiff_t memunit_distance(const Iterator& , const Iterator&); | |
1185 | void memunit_advance(Iterator&, std::ptrdiff_t diff); | |
1186 | Iterator memunit_advanced(const Iterator& p, std::ptrdiff_t diff) { Iterator tmp; memunit_advance(tmp,diff); return tmp; } | |
1187 | Iterator::reference memunit_advanced_ref(const Iterator& p, std::ptrdiff_t diff) { return *memunit_advanced(p,diff); } | |
1188 | }; | |
1189 | ||
1190 | \endcode | |
1191 | ||
1192 | It is useful to be able to construct a step iterator over another iterator. More generally, given a type, we want to be able to construct an equivalent | |
1193 | type that allows for dynamically specified horizontal step: | |
1194 | ||
1195 | \code | |
1196 | concept HasDynamicXStepTypeConcept<typename T> { | |
1197 | typename dynamic_x_step_type<T>; | |
1198 | where Metafunction<dynamic_x_step_type<T> >; | |
1199 | }; | |
1200 | \endcode | |
1201 | ||
1202 | All models of pixel iterators, locators and image views that GIL provides support \p HasDynamicXStepTypeConcept. | |
1203 | ||
1204 | <b>Related Concepts:</b> | |
1205 | ||
1206 | - StepIteratorConcept\<Iterator> | |
1207 | - MutableStepIteratorConcept\<Iterator> | |
1208 | - MemoryBasedIteratorConcept\<Iterator> | |
1209 | - HasDynamicXStepTypeConcept\<T> | |
1210 | ||
1211 | <b>Models:</b> | |
1212 | ||
1213 | All standard memory-based iterators GIL currently provides model \p MemoryBasedIteratorConcept. | |
1214 | GIL provides the class \p memory_based_step_iterator which models \p PixelIteratorConcept, \p StepIteratorConcept, and \p MemoryBasedIteratorConcept. | |
1215 | It takes the base iterator as a template parameter (which must model \p PixelIteratorConcept and \p MemoryBasedIteratorConcept) | |
1216 | and allows changing the step dynamically. GIL's implementation contains the base iterator and a \p ptrdiff_t denoting the number of memory units (bytes or bits) | |
1217 | to skip for a unit step. It may also be used with a negative number. GIL provides a function to create a step iterator from a base iterator and a step: | |
1218 | ||
1219 | \code | |
1220 | template <typename I> // Models MemoryBasedIteratorConcept, HasDynamicXStepTypeConcept | |
1221 | typename dynamic_x_step_type<I>::type make_step_iterator(const I& it, std::ptrdiff_t step); | |
1222 | \endcode | |
1223 | ||
1224 | GIL also provides a model of an iterator over a virtual array of pixels, \p position_iterator. It is a step iterator that keeps track of the pixel position | |
1225 | and invokes a function object to get the value of the pixel upon dereferencing. It models \p PixelIteratorConcept and \p StepIteratorConcept but | |
1226 | not \p MemoryBasedIteratorConcept. | |
1227 | ||
1228 | \section LocatorDG Pixel Locator | |
1229 | ||
1230 | A Locator allows for navigation in two or more dimensions. Locators are N-dimensional iterators in spirit, but we use a different | |
1231 | name because they don't satisfy all the requirements of iterators. For example, they don't supply increment and decrement operators because it is unclear | |
1232 | which dimension the operators should advance along. | |
1233 | N-dimensional locators model the following concept: | |
1234 | ||
1235 | \code | |
1236 | concept RandomAccessNDLocatorConcept<Regular Loc> { | |
1237 | typename value_type; // value over which the locator navigates | |
1238 | typename reference; // result of dereferencing | |
1239 | typename difference_type; where PointNDConcept<difference_type>; // return value of operator-. | |
1240 | typename const_t; // same as Loc, but operating over immutable values | |
1241 | typename cached_location_t; // type to store relative location (for efficient repeated access) | |
1242 | typename point_t = difference_type; | |
1243 | ||
1244 | static const size_t num_dimensions; // dimensionality of the locator | |
1245 | where num_dimensions = point_t::num_dimensions; | |
1246 | ||
1247 | // The difference_type and iterator type along each dimension. The iterators may only differ in | |
1248 | // difference_type. Their value_type must be the same as Loc::value_type | |
1249 | template <size_t D> struct axis { | |
1250 | typename coord_t = point_t::axis<D>::coord_t; | |
1251 | typename iterator; where RandomAccessTraversalConcept<iterator>; // iterator along D-th axis. | |
1252 | where iterator::value_type == value_type; | |
1253 | }; | |
1254 | ||
1255 | // Defines the type of a locator similar to this type, except it invokes Deref upon dereferencing | |
1256 | template <PixelDereferenceAdaptorConcept Deref> struct add_deref { | |
1257 | typename type; where RandomAccessNDLocatorConcept<type>; | |
1258 | static type make(const Loc& loc, const Deref& deref); | |
1259 | }; | |
1260 | ||
1261 | Loc& operator+=(Loc&, const difference_type&); | |
1262 | Loc& operator-=(Loc&, const difference_type&); | |
1263 | Loc operator+(const Loc&, const difference_type&); | |
1264 | Loc operator-(const Loc&, const difference_type&); | |
1265 | ||
1266 | reference operator*(const Loc&); | |
1267 | reference operator[](const Loc&, const difference_type&); | |
1268 | ||
1269 | // Storing relative location for faster repeated access and accessing it | |
1270 | cached_location_t Loc::cache_location(const difference_type&) const; | |
1271 | reference operator[](const Loc&,const cached_location_t&); | |
1272 | ||
1273 | // Accessing iterators along a given dimension at the current location or at a given offset | |
1274 | template <size_t D> axis<D>::iterator& Loc::axis_iterator(); | |
1275 | template <size_t D> axis<D>::iterator const& Loc::axis_iterator() const; | |
1276 | template <size_t D> axis<D>::iterator Loc::axis_iterator(const difference_type&) const; | |
1277 | }; | |
1278 | ||
1279 | template <typename Loc> | |
1280 | concept MutableRandomAccessNDLocatorConcept : RandomAccessNDLocatorConcept<Loc> { | |
1281 | where Mutable<reference>; | |
1282 | }; | |
1283 | \endcode | |
1284 | ||
1285 | Two-dimensional locators have additional requirements: | |
1286 | ||
1287 | \code | |
1288 | concept RandomAccess2DLocatorConcept<RandomAccessNDLocatorConcept Loc> { | |
1289 | where num_dimensions==2; | |
1290 | where Point2DConcept<point_t>; | |
1291 | ||
1292 | typename x_iterator = axis<0>::iterator; | |
1293 | typename y_iterator = axis<1>::iterator; | |
1294 | typename x_coord_t = axis<0>::coord_t; | |
1295 | typename y_coord_t = axis<1>::coord_t; | |
1296 | ||
1297 | // Only available to locators that have dynamic step in Y | |
1298 | //Loc::Loc(const Loc& loc, y_coord_t); | |
1299 | ||
1300 | // Only available to locators that have dynamic step in X and Y | |
1301 | //Loc::Loc(const Loc& loc, x_coord_t, y_coord_t, bool transposed=false); | |
1302 | ||
1303 | x_iterator& Loc::x(); | |
1304 | x_iterator const& Loc::x() const; | |
1305 | y_iterator& Loc::y(); | |
1306 | y_iterator const& Loc::y() const; | |
1307 | ||
1308 | x_iterator Loc::x_at(const difference_type&) const; | |
1309 | y_iterator Loc::y_at(const difference_type&) const; | |
1310 | Loc Loc::xy_at(const difference_type&) const; | |
1311 | ||
1312 | // x/y versions of all methods that can take difference type | |
1313 | x_iterator Loc::x_at(x_coord_t, y_coord_t) const; | |
1314 | y_iterator Loc::y_at(x_coord_t, y_coord_t) const; | |
1315 | Loc Loc::xy_at(x_coord_t, y_coord_t) const; | |
1316 | reference operator()(const Loc&, x_coord_t, y_coord_t); | |
1317 | cached_location_t Loc::cache_location(x_coord_t, y_coord_t) const; | |
1318 | ||
1319 | bool Loc::is_1d_traversable(x_coord_t width) const; | |
1320 | y_coord_t Loc::y_distance_to(const Loc& loc2, x_coord_t x_diff) const; | |
1321 | }; | |
1322 | ||
1323 | concept MutableRandomAccess2DLocatorConcept<RandomAccess2DLocatorConcept Loc> : MutableRandomAccessNDLocatorConcept<Loc> {}; | |
1324 | \endcode | |
1325 | ||
1326 | 2D locators can have a dynamic step not just horizontally, but also vertically. This gives rise to the Y equivalent of \p HasDynamicXStepTypeConcept: | |
1327 | ||
1328 | \code | |
1329 | concept HasDynamicYStepTypeConcept<typename T> { | |
1330 | typename dynamic_y_step_type<T>; | |
1331 | where Metafunction<dynamic_y_step_type<T> >; | |
1332 | }; | |
1333 | \endcode | |
1334 | ||
1335 | All locators and image views that GIL provides model \p HasDynamicYStepTypeConcept. | |
1336 | ||
1337 | Sometimes it is necessary to swap the meaning of X and Y for a given locator or image view type (for example, GIL provides a function to transpose an image view). | |
1338 | Such locators and views must be transposable: | |
1339 | ||
1340 | \code | |
1341 | concept HasTransposedTypeConcept<typename T> { | |
1342 | typename transposed_type<T>; | |
1343 | where Metafunction<transposed_type<T> >; | |
1344 | }; | |
1345 | \endcode | |
1346 | ||
1347 | All GIL provided locators and views model \p HasTransposedTypeConcept. | |
1348 | ||
1349 | The locators GIL uses operate over models of \p PixelConcept and their x and y dimension types are the same. They model the following concept: | |
1350 | ||
1351 | \code | |
1352 | concept PixelLocatorConcept<RandomAccess2DLocatorConcept Loc> { | |
1353 | where PixelValueConcept<value_type>; | |
1354 | where PixelIteratorConcept<x_iterator>; | |
1355 | where PixelIteratorConcept<y_iterator>; | |
1356 | where x_coord_t == y_coord_t; | |
1357 | ||
1358 | typename coord_t = x_coord_t; | |
1359 | }; | |
1360 | ||
1361 | concept MutablePixelLocatorConcept<PixelLocatorConcept Loc> : MutableRandomAccess2DLocatorConcept<Loc> {}; | |
1362 | \endcode | |
1363 | ||
1364 | <b>Related Concepts:</b> | |
1365 | ||
1366 | - HasDynamicYStepTypeConcept\<T> | |
1367 | - HasTransposedTypeConcept\<T> | |
1368 | - RandomAccessNDLocatorConcept\<Locator> | |
1369 | - MutableRandomAccessNDLocatorConcept\<Locator> | |
1370 | - RandomAccess2DLocatorConcept\<Locator> | |
1371 | - MutableRandomAccess2DLocatorConcept\<Locator> | |
1372 | - PixelLocatorConcept\<Locator> | |
1373 | - MutablePixelLocatorConcept\<Locator> | |
1374 | ||
1375 | <b>Models:</b> | |
1376 | ||
1377 | GIL provides two models of \p PixelLocatorConcept - a memory-based locator, \p memory_based_2d_locator and a virtual locator \p virtual_2d_locator. | |
1378 | ||
1379 | \p memory_based_2d_locator is a locator over planar or interleaved images that have their pixels in memory. | |
1380 | It takes a model of \p StepIteratorConcept over pixels as a template parameter. (When instantiated with a model of \p MutableStepIteratorConcept, | |
1381 | it models \p MutablePixelLocatorConcept). | |
1382 | ||
1383 | \code | |
1384 | template <typename StepIterator> // Models StepIteratorConcept, MemoryBasedIteratorConcept | |
1385 | class memory_based_2d_locator; | |
1386 | \endcode | |
1387 | ||
1388 | The step of \p StepIterator must be the number of memory units (bytes or bits) per row (thus it must be memunit advanceable). The class \p memory_based_2d_locator is a | |
1389 | wrapper around \p StepIterator and uses it to navigate vertically, while its base iterator is used to navigate horizontally. | |
1390 | ||
1391 | Combining fundamental and step iterators allows us to create locators that describe complex | |
1392 | pixel memory organizations. First, we have a choice of iterator to use for horizontal direction, i.e. for iterating over the pixels on the same row. | |
1393 | Using the fundamental and step iterators gives us four choices: | |
1394 | - \p pixel<T,C>* (for interleaved images) | |
1395 | - \p planar_pixel_iterator<T*,C> (for planar images) | |
1396 | - \p memory_based_step_iterator<pixel<T,C>*> (for interleaved images with non-standard step) | |
1397 | - <tt> memory_based_step_iterator<planar_pixel_iterator<T*,C> > </tt> (for planar images with non-standard step) | |
1398 | ||
1399 | Of course, one could provide their own custom x-iterator. One such example described later is an iterator adaptor that performs color | |
1400 | conversion when dereferenced. | |
1401 | ||
1402 | Given a horizontal iterator \p XIterator, we could choose the \e y-iterator, the iterator that moves along a column, as | |
1403 | \p memory_based_step_iterator<XIterator> with a step equal to the number of memory units (bytes or bits) per row. Again, one is free to provide their own y-iterator. | |
1404 | ||
1405 | Then we can instantiate \p memory_based_2d_locator<memory_based_step_iterator<XIterator> > to obtain a 2D pixel locator, as the diagram indicates: | |
1406 | \image html step_iterator.gif | |
1407 | ||
1408 | \p virtual_2d_locator is a locator that is instantiated with a function object invoked upon dereferencing a pixel. It returns the value of a pixel | |
1409 | given its X,Y coordiantes. Virtual locators can be used to implement virtual image views that can model any user-defined function. See the GIL | |
1410 | tutorial for an example of using virtual locators to create a view of the Mandelbrot set. | |
1411 | ||
1412 | Both the virtual and the memory-based locators subclass from \p pixel_2d_locator_base, a base class that provides most of the interface required | |
1413 | by \p PixelLocatorConcept. Users may find this base class useful if they need to provide other models of \p PixelLocatorConcept. | |
1414 | ||
1415 | Here is some sample code using locators: | |
1416 | ||
1417 | \code | |
1418 | loc=img.xy_at(10,10); // start at pixel (x=10,y=10) | |
1419 | above=loc.cache_location(0,-1); // remember relative locations of neighbors above and below | |
1420 | below=loc.cache_location(0, 1); | |
1421 | ++loc.x(); // move to (11,10) | |
1422 | loc.y()+=15; // move to (11,25) | |
1423 | loc-=point2<std::ptrdiff_t>(1,1);// move to (10,24) | |
1424 | *loc=(loc(0,-1)+loc(0,1))/2; // set pixel (10,24) to the average of (10,23) and (10,25) (grayscale pixels only) | |
1425 | *loc=(loc[above]+loc[below])/2; // the same, but faster using cached relative neighbor locations | |
1426 | \endcode | |
1427 | ||
1428 | The standard GIL locators are fast and lightweight objects. For example, the locator for a simple interleaved image consists of | |
1429 | one raw pointer to the pixel location plus one integer for the row size in bytes, for a total of 8 bytes. <tt> ++loc.x() </tt> amounts to | |
1430 | incrementing a raw pointer (or N pointers for planar images). Computing 2D offsets is slower as it requires multiplication and addition. | |
1431 | Filters, for example, need to access the same neighbors for every pixel in the image, in which case the relative positions can be cached | |
1432 | into a raw byte difference using \p cache_location. In the above example <tt> loc[above]</tt> for simple interleaved images amounts to a raw array | |
1433 | index operator. | |
1434 | ||
1435 | \section IteratorFrom2DDG Iterator over 2D image | |
1436 | ||
1437 | Sometimes we want to perform the same, location-independent operation over all pixels of an image. In such a case it is useful to represent the pixels | |
1438 | as a one-dimensional array. GIL's \p iterator_from_2d is a random access traversal iterator that visits all pixels in an image in the natural | |
1439 | memory-friendly order left-to-right inside top-to-bottom. It takes a locator, the width of the image and the current X position. This is sufficient | |
1440 | information for it to determine when to do a "carriage return". Synopsis: | |
1441 | ||
1442 | \code | |
1443 | template <typename Locator> // Models PixelLocatorConcept | |
1444 | class iterator_from_2d { | |
1445 | public: | |
1446 | iterator_from_2d(const Locator& loc, int x, int width); | |
1447 | ||
1448 | iterator_from_2d& operator++(); // if (++_x<_width) ++_p.x(); else _p+=point_t(-_width,1); | |
1449 | ||
1450 | ... | |
1451 | private: | |
1452 | int _x, _width; | |
1453 | Locator _p; | |
1454 | }; | |
1455 | \endcode | |
1456 | ||
1457 | Iterating through the pixels in an image using \p iterator_from_2d is slower than going through all rows and using the x-iterator at each row. | |
1458 | This is because two comparisons are done per iteration step - one for the end condition of the loop using the iterators, and one inside | |
1459 | \p iterator_from_2d::operator++ to determine whether we are at the end of a row. For fast operations, such as pixel copy, this second check | |
1460 | adds about 15% performance delay (measured for interleaved images on Intel platform). GIL overrides some STL algorithms, such as \p std::copy and | |
1461 | \p std::fill, when invoked with \p iterator_from_2d-s, to go through each row using their base x-iterators, and, if the image has no padding | |
1462 | (i.e. \p iterator_from_2d::is_1d_traversable() returns true) to simply iterate using the x-iterators directly. | |
1463 | ||
1464 | ||
1465 | ||
1466 | ||
1467 | ||
1468 | ||
1469 | ||
1470 | ||
1471 | ||
1472 | ||
1473 | ||
1474 | ||
1475 | ||
1476 | ||
1477 | ||
1478 | ||
1479 | ||
1480 | ||
1481 | ||
1482 | ||
1483 | ||
1484 | ||
1485 | ||
1486 | ||
1487 | ||
1488 | <hr> | |
1489 | \section ImageViewSectionDG 9. Image View | |
1490 | ||
1491 | An image view is a generalization of STL's range concept to multiple dimensions. Similar to ranges (and iterators), image views are shallow, don't | |
1492 | own the underlying data and don't propagate their constness over the data. For example, a constant image view cannot be resized, but may allow | |
1493 | modifying the pixels. For pixel-immutable operations, use constant-value image view (also called non-mutable image view). | |
1494 | Most general N-dimensional views satisfy the following concept: | |
1495 | ||
1496 | \code | |
1497 | concept RandomAccessNDImageViewConcept<Regular View> { | |
1498 | typename value_type; // for pixel-based views, the pixel type | |
1499 | typename reference; // result of dereferencing | |
1500 | typename difference_type; // result of operator-(iterator,iterator) (1-dimensional!) | |
1501 | typename const_t; where RandomAccessNDImageViewConcept<View>; // same as View, but over immutable values | |
1502 | typename point_t; where PointNDConcept<point_t>; // N-dimensional point | |
1503 | typename locator; where RandomAccessNDLocatorConcept<locator>; // N-dimensional locator. | |
1504 | typename iterator; where RandomAccessTraversalConcept<iterator>; // 1-dimensional iterator over all values | |
1505 | typename reverse_iterator; where RandomAccessTraversalConcept<reverse_iterator>; | |
1506 | typename size_type; // the return value of size() | |
1507 | ||
1508 | // Equivalent to RandomAccessNDLocatorConcept::axis | |
1509 | template <size_t D> struct axis { | |
1510 | typename coord_t = point_t::axis<D>::coord_t; | |
1511 | typename iterator; where RandomAccessTraversalConcept<iterator>; // iterator along D-th axis. | |
1512 | where SameType<coord_t, iterator::difference_type>; | |
1513 | where SameType<iterator::value_type,value_type>; | |
1514 | }; | |
1515 | ||
1516 | // Defines the type of a view similar to this type, except it invokes Deref upon dereferencing | |
1517 | template <PixelDereferenceAdaptorConcept Deref> struct add_deref { | |
1518 | typename type; where RandomAccessNDImageViewConcept<type>; | |
1519 | static type make(const View& v, const Deref& deref); | |
1520 | }; | |
1521 | ||
1522 | static const size_t num_dimensions = point_t::num_dimensions; | |
1523 | ||
1524 | // Create from a locator at the top-left corner and dimensions | |
1525 | View::View(const locator&, const point_type&); | |
1526 | ||
1527 | size_type View::size() const; // total number of elements | |
1528 | reference operator[](View, const difference_type&) const; // 1-dimensional reference | |
1529 | iterator View::begin() const; | |
1530 | iterator View::end() const; | |
1531 | reverse_iterator View::rbegin() const; | |
1532 | reverse_iterator View::rend() const; | |
1533 | iterator View::at(const point_t&); | |
1534 | point_t View::dimensions() const; // number of elements along each dimension | |
1535 | bool View::is_1d_traversable() const; // Does an iterator over the first dimension visit each value? | |
1536 | ||
1537 | // iterator along a given dimension starting at a given point | |
1538 | template <size_t D> View::axis<D>::iterator View::axis_iterator(const point_t&) const; | |
1539 | ||
1540 | reference operator()(View,const point_t&) const; | |
1541 | }; | |
1542 | ||
1543 | concept MutableRandomAccessNDImageViewConcept<RandomAccessNDImageViewConcept View> { | |
1544 | where Mutable<reference>; | |
1545 | }; | |
1546 | \endcode | |
1547 | ||
1548 | Two-dimensional image views have the following extra requirements: | |
1549 | ||
1550 | \code | |
1551 | concept RandomAccess2DImageViewConcept<RandomAccessNDImageViewConcept View> { | |
1552 | where num_dimensions==2; | |
1553 | ||
1554 | typename x_iterator = axis<0>::iterator; | |
1555 | typename y_iterator = axis<1>::iterator; | |
1556 | typename x_coord_t = axis<0>::coord_t; | |
1557 | typename y_coord_t = axis<1>::coord_t; | |
1558 | typename xy_locator = locator; | |
1559 | ||
1560 | x_coord_t View::width() const; | |
1561 | y_coord_t View::height() const; | |
1562 | ||
1563 | // X-navigation | |
1564 | x_iterator View::x_at(const point_t&) const; | |
1565 | x_iterator View::row_begin(y_coord_t) const; | |
1566 | x_iterator View::row_end (y_coord_t) const; | |
1567 | ||
1568 | // Y-navigation | |
1569 | y_iterator View::y_at(const point_t&) const; | |
1570 | y_iterator View::col_begin(x_coord_t) const; | |
1571 | y_iterator View::col_end (x_coord_t) const; | |
1572 | ||
1573 | // navigating in 2D | |
1574 | xy_locator View::xy_at(const point_t&) const; | |
1575 | ||
1576 | // (x,y) versions of all methods taking point_t | |
1577 | View::View(x_coord_t,y_coord_t,const locator&); | |
1578 | iterator View::at(x_coord_t,y_coord_t) const; | |
1579 | reference operator()(View,x_coord_t,y_coord_t) const; | |
1580 | xy_locator View::xy_at(x_coord_t,y_coord_t) const; | |
1581 | x_iterator View::x_at(x_coord_t,y_coord_t) const; | |
1582 | y_iterator View::y_at(x_coord_t,y_coord_t) const; | |
1583 | }; | |
1584 | ||
1585 | concept MutableRandomAccess2DImageViewConcept<RandomAccess2DImageViewConcept View> | |
1586 | : MutableRandomAccessNDImageViewConcept<View> {}; | |
1587 | \endcode | |
1588 | ||
1589 | Image views that GIL typically uses operate on value types that model \p PixelValueConcept and have some additional requirements: | |
1590 | ||
1591 | \code | |
1592 | concept ImageViewConcept<RandomAccess2DImageViewConcept View> { | |
1593 | where PixelValueConcept<value_type>; | |
1594 | where PixelIteratorConcept<x_iterator>; | |
1595 | where PixelIteratorConcept<y_iterator>; | |
1596 | where x_coord_t == y_coord_t; | |
1597 | ||
1598 | typename coord_t = x_coord_t; | |
1599 | ||
1600 | std::size_t View::num_channels() const; | |
1601 | }; | |
1602 | ||
1603 | ||
1604 | concept MutableImageViewConcept<ImageViewConcept View> : MutableRandomAccess2DImageViewConcept<View> {}; | |
1605 | \endcode | |
1606 | ||
1607 | Two image views are compatible if they have compatible pixels and the same number of dimensions: | |
1608 | \code | |
1609 | concept ViewsCompatibleConcept<ImageViewConcept V1, ImageViewConcept V2> { | |
1610 | where PixelsCompatibleConcept<V1::value_type, V2::value_type>; | |
1611 | where V1::num_dimensions == V2::num_dimensions; | |
1612 | }; | |
1613 | \endcode | |
1614 | ||
1615 | Compatible views must also have the same dimensions (i.e. the same width and height). Many algorithms taking multiple views require that they be pairwise compatible. | |
1616 | ||
1617 | <b>Related Concepts:</b> | |
1618 | ||
1619 | - RandomAccessNDImageViewConcept\<View> | |
1620 | - MutableRandomAccessNDImageViewConcept\<View> | |
1621 | - RandomAccess2DImageViewConcept\<View> | |
1622 | - MutableRandomAccess2DImageViewConcept\<View> | |
1623 | - ImageViewConcept\<View> | |
1624 | - MutableImageViewConcept\<View> | |
1625 | - ViewsCompatibleConcept\<View1,View2> | |
1626 | ||
1627 | <b>Models:</b> | |
1628 | ||
1629 | GIL provides a model for \p ImageViewConcept called \p image_view. It is templated over a model of \p PixelLocatorConcept. | |
1630 | (If instantiated with a model of \p MutablePixelLocatorConcept, it models \p MutableImageViewConcept). Synopsis: | |
1631 | ||
1632 | \code | |
1633 | template <typename Locator> // Models PixelLocatorConcept (could be MutablePixelLocatorConcept) | |
1634 | class image_view { | |
1635 | public: | |
1636 | typedef Locator xy_locator; | |
1637 | typedef iterator_from_2d<Locator> iterator; | |
1638 | ... | |
1639 | private: | |
1640 | xy_locator _pixels; // 2D pixel locator at the top left corner of the image view range | |
1641 | point_t _dimensions; // width and height | |
1642 | }; | |
1643 | \endcode | |
1644 | ||
1645 | Image views are lightweight objects. A regular interleaved view is typically 16 bytes long - two integers for the width and height (inside dimensions) | |
1646 | one for the number of bytes between adjacent rows (inside the locator) and one pointer to the beginning of the pixel block. | |
1647 | ||
1648 | <b>Algorithms:</b> | |
1649 | ||
1650 | \subsection ImageViewFrowRawDG Creating Views from Raw Pixels | |
1651 | ||
1652 | Standard image views can be constructed from raw data of any supported color space, bit depth, channel ordering or planar vs. interleaved structure. | |
1653 | Interleaved views are constructed using \p interleaved_view, supplying the image dimensions, number of bytes per row, and a | |
1654 | pointer to the first pixel: | |
1655 | ||
1656 | \code | |
1657 | template <typename Iterator> // Models pixel iterator (like rgb8_ptr_t or rgb8c_ptr_t) | |
1658 | image_view<...> interleaved_view(ptrdiff_t width, ptrdiff_t height, Iterator pixels, ptrdiff_t rowsize) | |
1659 | \endcode | |
1660 | ||
1661 | Planar views are defined for every color space and take each plane separately. Here is the RGB one: | |
1662 | ||
1663 | \code | |
1664 | template <typename IC> // Models channel iterator (like bits8* or const bits8*) | |
1665 | image_view<...> planar_rgb_view(ptrdiff_t width, ptrdiff_t height, | |
1666 | IC r, IC g, IC b, ptrdiff_t rowsize); | |
1667 | \endcode | |
1668 | ||
1669 | Note that the supplied pixel/channel iterators could be constant (read-only), in which case the returned view is a constant-value (immutable) view. | |
1670 | ||
1671 | \subsection ImageViewFrowImageViewDG Creating Image Views from Other Image Views | |
1672 | ||
1673 | It is possible to construct one image view from another by changing some policy of how image data is interpreted. The result could be a view whose type is | |
1674 | derived from the type of the source. GIL uses the following metafunctions to get the derived types: | |
1675 | ||
1676 | \code | |
1677 | ||
1678 | // Some result view types | |
1679 | template <typename View> | |
1680 | struct dynamic_xy_step_type : public dynamic_y_step_type<typename dynamic_x_step_type<View>::type> {}; | |
1681 | ||
1682 | template <typename View> | |
1683 | struct dynamic_xy_step_transposed_type : public dynamic_xy_step_type<typename transposed_type<View>::type> {}; | |
1684 | ||
1685 | // color and bit depth converted view to match pixel type P | |
1686 | template <typename SrcView, // Models ImageViewConcept | |
1687 | typename DstP, // Models PixelConcept | |
1688 | typename ColorConverter=gil::default_color_converter> | |
1689 | struct color_converted_view_type { | |
1690 | typedef ... type; // image view adaptor with value type DstP, over SrcView | |
1691 | }; | |
1692 | ||
1693 | // single-channel view of the N-th channel of a given view | |
1694 | template <typename SrcView> | |
1695 | struct nth_channel_view_type { | |
1696 | typedef ... type; | |
1697 | }; | |
1698 | \endcode | |
1699 | ||
1700 | GIL Provides the following view transformations: | |
1701 | ||
1702 | \code | |
1703 | // flipped upside-down, left-to-right, transposed view | |
1704 | template <typename View> typename dynamic_y_step_type<View>::type flipped_up_down_view(const View& src); | |
1705 | template <typename View> typename dynamic_x_step_type<View>::type flipped_left_right_view(const View& src); | |
1706 | template <typename View> typename dynamic_xy_step_transposed_type<View>::type transposed_view(const View& src); | |
1707 | ||
1708 | // rotations | |
1709 | template <typename View> typename dynamic_xy_step_type<View>::type rotated180_view(const View& src); | |
1710 | template <typename View> typename dynamic_xy_step_transposed_type<View>::type rotated90cw_view(const View& src); | |
1711 | template <typename View> typename dynamic_xy_step_transposed_type<View>::type rotated90ccw_view(const View& src); | |
1712 | ||
1713 | // view of an axis-aligned rectangular area within an image | |
1714 | template <typename View> View subimage_view(const View& src, | |
1715 | const View::point_t& top_left, const View::point_t& dimensions); | |
1716 | ||
1717 | // subsampled view (skipping pixels in X and Y) | |
1718 | template <typename View> typename dynamic_xy_step_type<View>::type subsampled_view(const View& src, | |
1719 | const View::point_t& step); | |
1720 | ||
1721 | template <typename View, typename P> | |
1722 | color_converted_view_type<View,P>::type color_converted_view(const View& src); | |
1723 | template <typename View, typename P, typename CCV> // with a custom color converter | |
1724 | color_converted_view_type<View,P,CCV>::type color_converted_view(const View& src); | |
1725 | ||
1726 | template <typename View> | |
1727 | nth_channel_view_type<View>::view_t nth_channel_view(const View& view, int n); | |
1728 | \endcode | |
1729 | ||
1730 | The implementations of most of these view factory methods are straightforward. Here is, for example, how the flip views are implemented. | |
1731 | The flip upside-down view creates a view whose first pixel is the bottom left pixel of the original view and whose y-step is the negated | |
1732 | step of the source. | |
1733 | ||
1734 | \code | |
1735 | template <typename View> | |
1736 | typename dynamic_y_step_type<View>::type flipped_up_down_view(const View& src) { | |
1737 | gil_function_requires<ImageViewConcept<View> >(); | |
1738 | typedef typename dynamic_y_step_type<View>::type RView; | |
1739 | return RView(src.dimensions(),typename RView::xy_locator(src.xy_at(0,src.height()-1),-1)); | |
1740 | } | |
1741 | \endcode | |
1742 | ||
1743 | The call to \p gil_function_requires ensures (at compile time) that the template parameter is a valid model of \p ImageViewConcept. Using it | |
1744 | generates easier to track compile errors, creates no extra code and has no run-time performance impact. | |
1745 | We are using the \p boost::concept_check library, but wrapping it in \p gil_function_requires, which performs the check if the \p BOOST_GIL_USE_CONCEPT_CHECK | |
1746 | is set. It is unset by default, because there is a significant increase in compile time when using concept checks. We will skip \p gil_function_requires | |
1747 | in the code examples in this guide for the sake of succinctness. | |
1748 | ||
1749 | Image views can be freely composed (see section \ref MetafunctionsDG for the typedefs \p rgb16_image_t and \p gray16_step_view_t): | |
1750 | ||
1751 | \code | |
1752 | rgb16_image_t img(100,100); // an RGB interleaved image | |
1753 | ||
1754 | // grayscale view over the green (index 1) channel of img | |
1755 | gray16_step_view_t green=nth_channel_view(view(img),1); | |
1756 | ||
1757 | // 50x50 view of the green channel of img, upside down and taking every other pixel in X and in Y | |
1758 | gray16_step_view_t ud_fud=flipped_up_down_view(subsampled_view(green,2,2)); | |
1759 | \endcode | |
1760 | ||
1761 | As previously stated, image views are fast, constant-time, shallow views over the pixel data. The above code does not copy any pixels; it operates | |
1762 | on the pixel data allocated when \p img was created. | |
1763 | ||
1764 | \subsection ImageViewAlgorithmsDG STL-Style Algorithms on Image Views | |
1765 | ||
1766 | <p>Image views provide 1D iteration of their pixels via begin() and end() methods, which makes it possible to use STL | |
1767 | algorithms with them. However, using nested loops over X and Y is in many cases more efficient. The algorithms in this | |
1768 | section resemble STL algorithms, but they abstract away the nested loops and take views (as opposed to ranges) as input. | |
1769 | ||
1770 | \code | |
1771 | // Equivalents of std::copy and std::uninitialized_copy | |
1772 | // where ImageViewConcept<V1>, MutableImageViewConcept<V2>, ViewsCompatibleConcept<V1,V2> | |
1773 | template <typename V1, typename V2> | |
1774 | void copy_pixels(const V1& src, const V2& dst); | |
1775 | template <typename V1, typename V2> | |
1776 | void uninitialized_copy_pixels(const V1& src, const V2& dst); | |
1777 | ||
1778 | // Equivalents of std::fill and std::uninitialized_fill | |
1779 | // where MutableImageViewConcept<V>, PixelConcept<Value>, PixelsCompatibleConcept<Value,V::value_type> | |
1780 | template <typename V, typename Value> | |
1781 | void fill_pixels(const V& dst, const Value& val); | |
1782 | template <typename V, typename Value> | |
1783 | void uninitialized_fill_pixels(const V& dst, const Value& val); | |
1784 | ||
1785 | // Equivalent of std::for_each | |
1786 | // where ImageViewConcept<V>, boost::UnaryFunctionConcept<F> | |
1787 | // where PixelsCompatibleConcept<V::reference, F::argument_type> | |
1788 | template <typename V, typename F> | |
1789 | F for_each_pixel(const V& view, F fun); | |
1790 | template <typename V, typename F> | |
1791 | F for_each_pixel_position(const V& view, F fun); | |
1792 | ||
1793 | // Equivalent of std::generate | |
1794 | // where MutableImageViewConcept<V>, boost::UnaryFunctionConcept<F> | |
1795 | // where PixelsCompatibleConcept<V::reference, F::argument_type> | |
1796 | template <typename V, typename F> | |
1797 | void generate_pixels(const V& dst, F fun); | |
1798 | ||
1799 | // Equivalent of std::transform with one source | |
1800 | // where ImageViewConcept<V1>, MutableImageViewConcept<V2> | |
1801 | // where boost::UnaryFunctionConcept<F> | |
1802 | // where PixelsCompatibleConcept<V1::const_reference, F::argument_type> | |
1803 | // where PixelsCompatibleConcept<F::result_type, V2::reference> | |
1804 | template <typename V1, typename V2, typename F> | |
1805 | F transform_pixels(const V1& src, const V2& dst, F fun); | |
1806 | template <typename V1, typename V2, typename F> | |
1807 | F transform_pixel_positions(const V1& src, const V2& dst, F fun); | |
1808 | ||
1809 | // Equivalent of std::transform with two sources | |
1810 | // where ImageViewConcept<V1>, ImageViewConcept<V2>, MutableImageViewConcept<V3> | |
1811 | // where boost::BinaryFunctionConcept<F> | |
1812 | // where PixelsCompatibleConcept<V1::const_reference, F::first_argument_type> | |
1813 | // where PixelsCompatibleConcept<V2::const_reference, F::second_argument_type> | |
1814 | // where PixelsCompatibleConcept<F::result_type, V3::reference> | |
1815 | template <typename V1, typename V2, typename V3, typename F> | |
1816 | F transform_pixels(const V1& src1, const V2& src2, const V3& dst, F fun); | |
1817 | template <typename V1, typename V2, typename V3, typename F> | |
1818 | F transform_pixel_positions(const V1& src1, const V2& src2, const V3& dst, F fun); | |
1819 | ||
1820 | // Copies a view into another, color converting the pixels if needed, with the default or user-defined color converter | |
1821 | // where ImageViewConcept<V1>, MutableImageViewConcept<V2> | |
1822 | // V1::value_type must be convertible to V2::value_type. | |
1823 | template <typename V1, typename V2> | |
1824 | void copy_and_convert_pixels(const V1& src, const V2& dst); | |
1825 | template <typename V1, typename V2, typename ColorConverter> | |
1826 | void copy_and_convert_pixels(const V1& src, const V2& dst, ColorConverter ccv); | |
1827 | ||
1828 | // Equivalent of std::equal | |
1829 | // where ImageViewConcept<V1>, ImageViewConcept<V2>, ViewsCompatibleConcept<V1,V2> | |
1830 | template <typename V1, typename V2> | |
1831 | bool equal_pixels(const V1& view1, const V2& view2); | |
1832 | \endcode | |
1833 | ||
1834 | Algorithms that take multiple views require that they have the same dimensions. | |
1835 | \p for_each_pixel_position and \p transform_pixel_positions pass pixel locators, as opposed to pixel references, to their function objects. | |
1836 | This allows for writing algorithms that use pixel neighbors, as the tutorial demonstrates. | |
1837 | ||
1838 | Most of these algorithms check whether the image views are 1D-traversable. A 1D-traversable image view has no gaps at the end of the rows. | |
1839 | In other words, if an x_iterator of that view is advanced past the last pixel in a row it will move to the first pixel of the next row. | |
1840 | When image views are 1D-traversable, the algorithms use a single loop and run more efficiently. If one or more of the input views are not | |
1841 | 1D-traversable, the algorithms fall-back to an X-loop nested inside a Y-loop. | |
1842 | ||
1843 | The algorithms typically delegate the work to their corresponding STL algorithms. For example, \p copy_pixels calls \p std::copy either for each | |
1844 | row, or, when the images are 1D-traversable, once for all pixels. | |
1845 | ||
1846 | In addition, overloads are sometimes provided for the STL algorithms. For example, \p std::copy for planar iterators is overloaded to perform | |
1847 | \p std::copy for each of the planes. \p std::copy over bitwise-copiable pixels results in \p std::copy over unsigned char, which STL typically | |
1848 | implements via \p memmove. | |
1849 | ||
1850 | As a result \p copy_pixels may result in a single call to \p memmove for interleaved 1D-traversable views, or one per each plane of planar | |
1851 | 1D-traversable views, or one per each row of interleaved non-1D-traversable images, etc. | |
1852 | ||
1853 | GIL also provides some beta-versions of image processing algorithms, such as resampling and convolution in a numerics extension available on | |
1854 | http://stlab.adobe.com/gil/download.html. This code is in early stage of development and is not optimized for speed | |
1855 | ||
1856 | ||
1857 | ||
1858 | ||
1859 | ||
1860 | ||
1861 | ||
1862 | ||
1863 | ||
1864 | ||
1865 | ||
1866 | ||
1867 | ||
1868 | ||
1869 | ||
1870 | ||
1871 | ||
1872 | ||
1873 | ||
1874 | ||
1875 | <hr> | |
1876 | \section ImageSectionDG 10. Image | |
1877 | ||
1878 | An image is a container that owns the pixels of a given image view. It allocates them in its constructor and deletes | |
1879 | them in the destructor. It has a deep assignment operator and copy constructor. Images are used rarely, just when | |
1880 | data ownership is important. Most STL algorithms operate on ranges, not containers. Similarly most GIL algorithms operate on image | |
1881 | views (which images provide). | |
1882 | ||
1883 | In the most general form images are N-dimensional and satisfy the following concept: | |
1884 | ||
1885 | \code | |
1886 | concept RandomAccessNDImageConcept<typename Img> : Regular<Img> { | |
1887 | typename view_t; where MutableRandomAccessNDImageViewConcept<view_t>; | |
1888 | typename const_view_t = view_t::const_t; | |
1889 | typename point_t = view_t::point_t; | |
1890 | typename value_type = view_t::value_type; | |
1891 | typename allocator_type; | |
1892 | ||
1893 | Img::Img(point_t dims, std::size_t alignment=0); | |
1894 | Img::Img(point_t dims, value_type fill_value, std::size_t alignment); | |
1895 | ||
1896 | void Img::recreate(point_t new_dims, std::size_t alignment=0); | |
1897 | void Img::recreate(point_t new_dims, value_type fill_value, std::size_t alignment); | |
1898 | ||
1899 | const point_t& Img::dimensions() const; | |
1900 | const const_view_t& const_view(const Img&); | |
1901 | const view_t& view(Img&); | |
1902 | }; | |
1903 | \endcode | |
1904 | ||
1905 | Two-dimensional images have additional requirements: | |
1906 | ||
1907 | \code | |
1908 | concept RandomAccess2DImageConcept<RandomAccessNDImageConcept Img> { | |
1909 | typename x_coord_t = const_view_t::x_coord_t; | |
1910 | typename y_coord_t = const_view_t::y_coord_t; | |
1911 | ||
1912 | Img::Img(x_coord_t width, y_coord_t height, std::size_t alignment=0); | |
1913 | Img::Img(x_coord_t width, y_coord_t height, value_type fill_value, std::size_t alignment); | |
1914 | ||
1915 | x_coord_t Img::width() const; | |
1916 | y_coord_t Img::height() const; | |
1917 | ||
1918 | void Img::recreate(x_coord_t width, y_coord_t height, std::size_t alignment=1); | |
1919 | void Img::recreate(x_coord_t width, y_coord_t height, value_type fill_value, std::size_t alignment); | |
1920 | }; | |
1921 | \endcode | |
1922 | ||
1923 | GIL's images have views that model \p ImageViewConcept and operate on pixels. | |
1924 | ||
1925 | \code | |
1926 | concept ImageConcept<RandomAccess2DImageConcept Img> { | |
1927 | where MutableImageViewConcept<view_t>; | |
1928 | typename coord_t = view_t::coord_t; | |
1929 | }; | |
1930 | \endcode | |
1931 | ||
1932 | Images, unlike locators and image views, don't have 'mutable' set of concepts because immutable images are not very useful. | |
1933 | ||
1934 | <b>Related Concepts:</b> | |
1935 | ||
1936 | - RandomAccessNDImageConcept\<Image> | |
1937 | - RandomAccess2DImageConcept\<Image> | |
1938 | - ImageConcept\<Image> | |
1939 | ||
1940 | <b>Models:</b> | |
1941 | ||
1942 | GIL provides a class, \p image, which is templated over the value type (the pixel) and models \p ImageConcept. | |
1943 | ||
1944 | \code | |
1945 | template <typename Pixel, \\ Models PixelValueConcept | |
1946 | bool IsPlanar, \\ planar or interleaved image | |
1947 | typename A=std::allocator<unsigned char> > | |
1948 | class image; | |
1949 | \endcode | |
1950 | ||
1951 | The image constructor takes an alignment parameter which allows for constructing images that are word-aligned or 8-byte aligned. The alignment is specified in | |
1952 | bytes. The default value for alignment is 0, which means there is no padding at the end of rows. Many operations are | |
1953 | faster using such 1D-traversable images, because \p image_view::x_iterator can be used to traverse the pixels, instead of the more complicated | |
1954 | \p image_view::iterator. Note that when alignment is 0, packed images are aligned to the bit - i.e. there are no padding bits at the end of rows of packed images. | |
1955 | <hr> | |
1956 | \section VariantSecDG 11. Run-time specified images and image views | |
1957 | ||
1958 | The color space, channel depth, channel ordering, and interleaved/planar structure of an image are defined by the type of its template argument, which | |
1959 | makes them compile-time bound. Often some of these parameters are available only at run time. | |
1960 | Consider, for example, writing a module that opens the image at a given file path, rotates it and saves it back in its original color space and channel | |
1961 | depth. How can we possibly write this using our generic image? What type is the image loading code supposed to return? | |
1962 | ||
1963 | <p>GIL's dynamic_image extension allows for images, image views or any GIL constructs to have their parameters defined at run time. Here is an example: | |
1964 | \code | |
1965 | #include <boost/gil/extension/dynamic_image/dynamic_image_all.hpp> | |
1966 | using namespace boost; | |
1967 | ||
1968 | #define ASSERT_SAME(A,B) BOOST_STATIC_ASSERT((is_same< A,B >::value)) | |
1969 | ||
1970 | // Define the set of allowed images | |
1971 | typedef mpl::vector<rgb8_image_t, cmyk16_planar_image_t> my_images_t; | |
1972 | ||
1973 | // Create any_image class (or any_image_view) class | |
1974 | typedef any_image<my_images_t> my_any_image_t; | |
1975 | ||
1976 | // Associated view types are available (equivalent to the ones in image_t) | |
1977 | typedef any_image_view<mpl::vector2<rgb8_view_t, cmyk16_planar_view_t > > AV; | |
1978 | ASSERT_SAME(my_any_image_t::view_t, AV); | |
1979 | ||
1980 | typedef any_image_view<mpl::vector2<rgb8c_view_t, cmyk16c_planar_view_t> > CAV; | |
1981 | ASSERT_SAME(my_any_image_t::const_view_t, CAV); | |
1982 | ASSERT_SAME(my_any_image_t::const_view_t, my_any_image_t::view_t::const_t); | |
1983 | ||
1984 | typedef any_image_view<mpl::vector2<rgb8_step_view_t, cmyk16_planar_step_view_t> > SAV; | |
1985 | ASSERT_SAME(typename dynamic_x_step_type<my_any_image_t::view_t>::type, SAV); | |
1986 | ||
1987 | // Assign it a concrete image at run time: | |
1988 | my_any_image_t myImg = my_any_image_t(rgb8_image_t(100,100)); | |
1989 | ||
1990 | // Change it to another at run time. The previous image gets destroyed | |
1991 | myImg = cmyk16_planar_image_t(200,100); | |
1992 | ||
1993 | // Assigning to an image not in the allowed set throws an exception | |
1994 | myImg = gray8_image_t(); // will throw std::bad_cast | |
1995 | \endcode | |
1996 | ||
1997 | \p any_image and \p any_image_view subclass from GIL's \p variant class, which breaks down the instantiated type | |
1998 | into a non-templated underlying base type and a unique instantiation type identifier. The underlying base instance is represented | |
1999 | as a block of bytes. The block is large enough to hold the largest of the specified types. | |
2000 | ||
2001 | GIL's variant is similar to \p boost::variant in spirit (hence we borrow the name from there) but it differs in several ways from | |
2002 | the current boost implementation. Perhaps the biggest difference is that GIL's variant always takes a single argument, which is a model | |
2003 | of MPL Random Access Sequence enumerating the allowed types. Having a single interface allows GIL's variant to be used easier in generic code. Synopsis: | |
2004 | ||
2005 | \code | |
2006 | template <typename Types> // models MPL Random Access Container | |
2007 | class variant { | |
2008 | ... _bits; | |
2009 | std::size_t _index; | |
2010 | public: | |
2011 | typedef Types types_t; | |
2012 | ||
2013 | variant(); | |
2014 | variant(const variant& v); | |
2015 | virtual ~variant(); | |
2016 | ||
2017 | variant& operator=(const variant& v); | |
2018 | template <typename TS> friend bool operator==(const variant<TS>& x, const variant<TS>& y); | |
2019 | template <typename TS> friend bool operator!=(const variant<TS>& x, const variant<TS>& y); | |
2020 | ||
2021 | // Construct/assign to type T. Throws std::bad_cast if T is not in Types | |
2022 | template <typename T> explicit variant(const T& obj); | |
2023 | template <typename T> variant& operator=(const T& obj); | |
2024 | ||
2025 | // Construct/assign by swapping T with its current instance. Only possible if they are swappable | |
2026 | template <typename T> explicit variant(T& obj, bool do_swap); | |
2027 | template <typename T> void move_in(T& obj); | |
2028 | ||
2029 | template <typename T> static bool has_type(); | |
2030 | ||
2031 | template <typename T> const T& _dynamic_cast() const; | |
2032 | template <typename T> T& _dynamic_cast(); | |
2033 | ||
2034 | template <typename T> bool current_type_is() const; | |
2035 | }; | |
2036 | ||
2037 | template <typename UOP, typename Types> | |
2038 | UOP::result_type apply_operation(variant<Types>& v, UOP op); | |
2039 | template <typename UOP, typename Types> | |
2040 | UOP::result_type apply_operation(const variant<Types>& v, UOP op); | |
2041 | ||
2042 | template <typename BOP, typename Types1, typename Types2> | |
2043 | BOP::result_type apply_operation( variant<Types1>& v1, variant<Types2>& v2, UOP op); | |
2044 | ||
2045 | template <typename BOP, typename Types1, typename Types2> | |
2046 | BOP::result_type apply_operation(const variant<Types1>& v1, variant<Types2>& v2, UOP op); | |
2047 | ||
2048 | template <typename BOP, typename Types1, typename Types2> | |
2049 | BOP::result_type apply_operation(const variant<Types1>& v1, const variant<Types2>& v2, UOP op); | |
2050 | \endcode | |
2051 | ||
2052 | GIL's \p any_image_view and \p any_image are subclasses of \p variant: | |
2053 | ||
2054 | \code | |
2055 | template <typename ImageViewTypes> | |
2056 | class any_image_view : public variant<ImageViewTypes> { | |
2057 | public: | |
2058 | typedef ... const_t; // immutable equivalent of this | |
2059 | typedef std::ptrdiff_t x_coord_t; | |
2060 | typedef std::ptrdiff_t y_coord_t; | |
2061 | typedef point2<std::ptrdiff_t> point_t; | |
2062 | ||
2063 | any_image_view(); | |
2064 | template <typename T> explicit any_image_view(const T& obj); | |
2065 | any_image_view(const any_image_view& v); | |
2066 | ||
2067 | template <typename T> any_image_view& operator=(const T& obj); | |
2068 | any_image_view& operator=(const any_image_view& v); | |
2069 | ||
2070 | // parameters of the currently instantiated view | |
2071 | std::size_t num_channels() const; | |
2072 | point_t dimensions() const; | |
2073 | x_coord_t width() const; | |
2074 | y_coord_t height() const; | |
2075 | }; | |
2076 | ||
2077 | template <typename ImageTypes> | |
2078 | class any_image : public variant<ImageTypes> { | |
2079 | typedef variant<ImageTypes> parent_t; | |
2080 | public: | |
2081 | typedef ... const_view_t; | |
2082 | typedef ... view_t; | |
2083 | typedef std::ptrdiff_t x_coord_t; | |
2084 | typedef std::ptrdiff_t y_coord_t; | |
2085 | typedef point2<std::ptrdiff_t> point_t; | |
2086 | ||
2087 | any_image(); | |
2088 | template <typename T> explicit any_image(const T& obj); | |
2089 | template <typename T> explicit any_image(T& obj, bool do_swap); | |
2090 | any_image(const any_image& v); | |
2091 | ||
2092 | template <typename T> any_image& operator=(const T& obj); | |
2093 | any_image& operator=(const any_image& v); | |
2094 | ||
2095 | void recreate(const point_t& dims, unsigned alignment=1); | |
2096 | void recreate(x_coord_t width, y_coord_t height, unsigned alignment=1); | |
2097 | ||
2098 | std::size_t num_channels() const; | |
2099 | point_t dimensions() const; | |
2100 | x_coord_t width() const; | |
2101 | y_coord_t height() const; | |
2102 | }; | |
2103 | \endcode | |
2104 | ||
2105 | Operations are invoked on variants via \p apply_operation passing a function object to perform the operation. The code for every allowed type in the | |
2106 | variant is instantiated and the appropriate instantiation is selected via a switch statement. Since image view algorithms typically have time complexity | |
2107 | at least linear on the number of pixels, the single switch statement of image view variant adds practically no measurable performance overhead compared | |
2108 | to templated image views. | |
2109 | ||
2110 | Variants behave like the underlying type. Their copy constructor will invoke the copy constructor of the underlying instance. Equality operator will | |
2111 | check if the two instances are of the same type and then invoke their operator==, etc. The default constructor of a variant will default-construct the | |
2112 | first type. That means that \p any_image_view has shallow default-constructor, copy-constructor, assigment and equaty comparison, whereas \p any_image | |
2113 | has deep ones. | |
2114 | ||
2115 | It is important to note that even though \p any_image_view and \p any_image resemble the static \p image_view and \p image, they do not model the full | |
2116 | requirements of \p ImageViewConcept and \p ImageConcept. In particular they don't provide access to the pixels. There is no "any_pixel" or | |
2117 | "any_pixel_iterator" in GIL. Such constructs could be provided via the \p variant mechanism, but doing so would result in inefficient algorithms, since | |
2118 | the type resolution would have to be performed per pixel. Image-level algorithms should be implemented via \p apply_operation. That said, | |
2119 | many common operations are shared between the static and dynamic types. In addition, all of the image view transformations and many STL-like image view | |
2120 | algorithms have overloads operating on \p any_image_view, as illustrated with \p copy_pixels: | |
2121 | ||
2122 | \code | |
2123 | rgb8_view_t v1(...); // concrete image view | |
2124 | bgr8_view_t v2(...); // concrete image view compatible with v1 and of the same size | |
2125 | any_image_view<Types> av(...); // run-time specified image view | |
2126 | ||
2127 | // Copies the pixels from v1 into v2. | |
2128 | // If the pixels are incompatible triggers compile error | |
2129 | copy_pixels(v1,v2); | |
2130 | ||
2131 | // The source or destination (or both) may be run-time instantiated. | |
2132 | // If they happen to be incompatible, throws std::bad_cast | |
2133 | copy_pixels(v1, av); | |
2134 | copy_pixels(av, v2); | |
2135 | copy_pixels(av, av); | |
2136 | \endcode | |
2137 | ||
2138 | By having algorithm overloads supporting dynamic constructs, we create a base upon which it is possible to write algorithms that can work with | |
2139 | either compile-time or runtime images or views. The following code, for example, uses the GIL I/O extension to turn an image on disk upside down: | |
2140 | ||
2141 | \code | |
2142 | #include <boost\gil\extension\io\jpeg_dynamic_io.hpp> | |
2143 | ||
2144 | template <typename Image> // Could be rgb8_image_t or any_image<...> | |
2145 | void save_180rot(const std::string& file_name) { | |
2146 | Image img; | |
2147 | jpeg_read_image(file_name, img); | |
2148 | jpeg_write_view(file_name, rotated180_view(view(img))); | |
2149 | } | |
2150 | \endcode | |
2151 | ||
2152 | It can be instantiated with either a compile-time or a runtime image because all functions it uses have overloads taking runtime constructs. | |
2153 | For example, here is how \p rotated180_view is implemented: | |
2154 | ||
2155 | \code | |
2156 | // implementation using templated view | |
2157 | template <typename View> | |
2158 | typename dynamic_xy_step_type<View>::type rotated180_view(const View& src) { ... } | |
2159 | ||
2160 | namespace detail { | |
2161 | // the function, wrapped inside a function object | |
2162 | template <typename Result> struct rotated180_view_fn { | |
2163 | typedef Result result_type; | |
2164 | template <typename View> result_type operator()(const View& src) const { | |
2165 | return result_type(rotated180_view(src)); | |
2166 | } | |
2167 | }; | |
2168 | } | |
2169 | ||
2170 | // overloading of the function using variant. Takes and returns run-time bound view. | |
2171 | // The returned view has a dynamic step | |
2172 | template <typename ViewTypes> inline // Models MPL Random Access Container of models of ImageViewConcept | |
2173 | typename dynamic_xy_step_type<any_image_view<ViewTypes> >::type rotated180_view(const any_image_view<ViewTypes>& src) { | |
2174 | return apply_operation(src,detail::rotated180_view_fn<typename dynamic_xy_step_type<any_image_view<ViewTypes> >::type>()); | |
2175 | } | |
2176 | \endcode | |
2177 | ||
2178 | Variants should be used with caution (especially algorithms that take more than one variant) because they instantiate the algorithm | |
2179 | for every possible model that the variant can take. This can take a toll on compile time and executable size. | |
2180 | Despite these limitations, \p variant is a powerful technique that allows us to combine the speed of compile-time resolution with | |
2181 | the flexibility of run-time resolution. It allows us to treat images of different parameters uniformly as a collection and store | |
2182 | them in the same container. | |
2183 | ||
2184 | ||
2185 | <hr> | |
2186 | \section MetafunctionsDG 12. Useful Metafunctions and Typedefs | |
2187 | ||
2188 | Flexibility comes at a price. GIL types can be very long and hard to read. | |
2189 | To address this problem, GIL provides typedefs to refer to any standard image, pixel iterator, pixel locator, pixel reference or pixel value. | |
2190 | They follow this pattern: | |
2191 | <p> | |
2192 | \e ColorSpace + \e BitDepth + ["s|f"] + ["c"] + ["_planar"] + ["_step"] + \e ClassType + "_t" | |
2193 | <p> | |
2194 | Where \e ColorSpace also indicates the ordering of components. Examples are \p rgb, \p bgr, \p cmyk, \p rgba. \e BitDepth can be, for example, | |
2195 | \p 8,\p 16,\p 32. By default the bits are unsigned integral type. Append \p s to the bit depth to indicate signed integral, or \p f to indicate | |
2196 | floating point. \p c indicates object whose associated pixel reference is immutable. \p _planar indicates planar organization (as opposed to interleaved). | |
2197 | \p _step indicates the type has a dynamic step and \e ClassType is \p _image (image, using a standard allocator), \p _view (image view), \p _loc | |
2198 | (pixel locator), \p _ptr (pixel iterator), \p _ref (pixel reference), \p _pixel (pixel value). Here are examples: | |
2199 | ||
2200 | \code | |
2201 | bgr8_image_t i; // 8-bit unsigned (unsigned char) interleaved BGR image | |
2202 | cmyk16_pixel_t; x; // 16-bit unsigned (unsigned short) CMYK pixel value; | |
2203 | cmyk16sc_planar_ref_t p(x); // const reference to a 16-bit signed integral (signed short) planar CMYK pixel x. | |
2204 | rgb32f_planar_step_ptr_t ii; // step iterator to a floating point 32-bit (float) planar RGB pixel. | |
2205 | \endcode | |
2206 | ||
2207 | GIL provides the metafunctions that return the types of standard homogeneous memory-based GIL constructs given a channel type, a layout, and whether | |
2208 | the construct is planar, has a step along the X direction, and is mutable: | |
2209 | ||
2210 | \code | |
2211 | template <typename ChannelValue, typename Layout, bool IsPlanar=false, bool IsMutable=true> | |
2212 | struct pixel_reference_type { typedef ... type; }; | |
2213 | ||
2214 | template <typename Channel, typename Layout> | |
2215 | struct pixel_value_type { typedef ... type; }; | |
2216 | ||
2217 | template <typename ChannelValue, typename Layout, bool IsPlanar=false, bool IsStep=false, bool IsMutable=true> | |
2218 | struct iterator_type { typedef ... type; }; | |
2219 | ||
2220 | template <typename ChannelValue, typename Layout, bool IsPlanar=false, bool IsXStep=false, bool IsMutable=true> | |
2221 | struct locator_type { typedef ... type; }; | |
2222 | ||
2223 | template <typename ChannelValue, typename Layout, bool IsPlanar=false, bool IsXStep=false, bool IsMutable=true> | |
2224 | struct view_type { typedef ... type; }; | |
2225 | ||
2226 | template <typename ChannelValue, typename Layout, bool IsPlanar=false, typename Alloc=std::allocator<unsigned char> > | |
2227 | struct image_type { typedef ... type; }; | |
2228 | ||
2229 | template <typename BitField, typename ChannelBitSizeVector, typename Layout, typename Alloc=std::allocator<unsigned char> > | |
2230 | struct packed_image_type { typedef ... type; }; | |
2231 | ||
2232 | template <typename ChannelBitSizeVector, typename Layout, typename Alloc=std::allocator<unsigned char> > | |
2233 | struct bit_aligned_image_type { typedef ... type; }; | |
2234 | \endcode | |
2235 | ||
2236 | There are also helper metafunctions to construct packed and bit-aligned images with up to five channels: | |
2237 | ||
2238 | \code | |
2239 | template <typename BitField, unsigned Size1, | |
2240 | typename Layout, typename Alloc=std::allocator<unsigned char> > | |
2241 | struct packed_image1_type { typedef ... type; }; | |
2242 | ||
2243 | template <typename BitField, unsigned Size1, unsigned Size2, | |
2244 | typename Layout, typename Alloc=std::allocator<unsigned char> > | |
2245 | struct packed_image2_type { typedef ... type; }; | |
2246 | ||
2247 | template <typename BitField, unsigned Size1, unsigned Size2, unsigned Size3, | |
2248 | typename Layout, typename Alloc=std::allocator<unsigned char> > | |
2249 | struct packed_image3_type { typedef ... type; }; | |
2250 | ||
2251 | template <typename BitField, unsigned Size1, unsigned Size2, unsigned Size3, unsigned Size4, | |
2252 | typename Layout, typename Alloc=std::allocator<unsigned char> > | |
2253 | struct packed_image4_type { typedef ... type; }; | |
2254 | ||
2255 | template <typename BitField, unsigned Size1, unsigned Size2, unsigned Size3, unsigned Size4, unsigned Size5, | |
2256 | typename Layout, typename Alloc=std::allocator<unsigned char> > | |
2257 | struct packed_image5_type { typedef ... type; }; | |
2258 | ||
2259 | template <unsigned Size1, | |
2260 | typename Layout, typename Alloc=std::allocator<unsigned char> > | |
2261 | struct bit_aligned_image1_type { typedef ... type; }; | |
2262 | ||
2263 | template <unsigned Size1, unsigned Size2, | |
2264 | typename Layout, typename Alloc=std::allocator<unsigned char> > | |
2265 | struct bit_aligned_image2_type { typedef ... type; }; | |
2266 | ||
2267 | template <unsigned Size1, unsigned Size2, unsigned Size3, | |
2268 | typename Layout, typename Alloc=std::allocator<unsigned char> > | |
2269 | struct bit_aligned_image3_type { typedef ... type; }; | |
2270 | ||
2271 | template <unsigned Size1, unsigned Size2, unsigned Size3, unsigned Size4, | |
2272 | typename Layout, typename Alloc=std::allocator<unsigned char> > | |
2273 | struct bit_aligned_image4_type { typedef ... type; }; | |
2274 | ||
2275 | template <unsigned Size1, unsigned Size2, unsigned Size3, unsigned Size4, unsigned Size5, | |
2276 | typename Layout, typename Alloc=std::allocator<unsigned char> > | |
2277 | struct bit_aligned_image5_type { typedef ... type; }; | |
2278 | ||
2279 | \endcode | |
2280 | ||
2281 | Here \p ChannelValue models \p ChannelValueConcept. We don't need \p IsYStep because GIL's memory-based locator and | |
2282 | view already allow the vertical step to be specified dynamically. Iterators and views can be constructed from a pixel type: | |
2283 | ||
2284 | \code | |
2285 | template <typename Pixel, bool IsPlanar=false, bool IsStep=false, bool IsMutable=true> | |
2286 | struct iterator_type_from_pixel { typedef ... type; }; | |
2287 | ||
2288 | template <typename Pixel, bool IsPlanar=false, bool IsStepX=false, bool IsMutable=true> | |
2289 | struct view_type_from_pixel { typedef ... type; }; | |
2290 | \endcode | |
2291 | ||
2292 | Using a heterogeneous pixel type will result in heterogeneous iterators and views. Types can also be constructed from horizontal iterator: | |
2293 | ||
2294 | \code | |
2295 | template <typename XIterator> | |
2296 | struct type_from_x_iterator { | |
2297 | typedef ... step_iterator_t; | |
2298 | typedef ... xy_locator_t; | |
2299 | typedef ... view_t; | |
2300 | }; | |
2301 | \endcode | |
2302 | ||
2303 | There are metafunctions to construct the type of a construct from an existing type by changing one or more of its properties: | |
2304 | ||
2305 | \code | |
2306 | template <typename PixelReference, | |
2307 | typename ChannelValue, typename Layout, typename IsPlanar, typename IsMutable> | |
2308 | struct derived_pixel_reference_type { | |
2309 | typedef ... type; // Models PixelConcept | |
2310 | }; | |
2311 | ||
2312 | template <typename Iterator, | |
2313 | typename ChannelValue, typename Layout, typename IsPlanar, typename IsStep, typename IsMutable> | |
2314 | struct derived_iterator_type { | |
2315 | typedef ... type; // Models PixelIteratorConcept | |
2316 | }; | |
2317 | ||
2318 | template <typename View, | |
2319 | typename ChannelValue, typename Layout, typename IsPlanar, typename IsXStep, typename IsMutable> | |
2320 | struct derived_view_type { | |
2321 | typedef ... type; // Models ImageViewConcept | |
2322 | }; | |
2323 | ||
2324 | template <typename Image, | |
2325 | typename ChannelValue, typename Layout, typename IsPlanar> | |
2326 | struct derived_image_type { | |
2327 | typedef ... type; // Models ImageConcept | |
2328 | }; | |
2329 | \endcode | |
2330 | ||
2331 | You can replace one or more of its properties and use \p boost::use_default for the rest. In this case \p IsPlanar, \p IsStep and \p IsMutable | |
2332 | are MPL boolean constants. For example, here is how to create the type of a view just like \p View, but being grayscale and planar: | |
2333 | ||
2334 | \code | |
2335 | typedef typename derived_view_type<View, boost::use_default, gray_t, mpl::true_>::type VT; | |
2336 | \endcode | |
2337 | ||
2338 | You can get pixel-related types of any pixel-based GIL constructs (pixels, iterators, locators and views) using the following | |
2339 | metafunctions provided by PixelBasedConcept, HomogeneousPixelBasedConcept and metafunctions built on top of them: | |
2340 | ||
2341 | \code | |
2342 | template <typename T> struct color_space_type { typedef ... type; }; | |
2343 | template <typename T> struct channel_mapping_type { typedef ... type; }; | |
2344 | template <typename T> struct is_planar { typedef ... type; }; | |
2345 | ||
2346 | // Defined by homogeneous constructs | |
2347 | template <typename T> struct channel_type { typedef ... type; }; | |
2348 | template <typename T> struct num_channels { typedef ... type; }; | |
2349 | \endcode | |
2350 | ||
2351 | These are metafunctions, some of which return integral types which can be evaluated like this: | |
2352 | ||
2353 | \code | |
2354 | BOOST_STATIC_ASSERT(is_planar<rgb8_planar_view_t>::value == true); | |
2355 | \endcode | |
2356 | ||
2357 | \endcode | |
2358 | ||
2359 | GIL also supports type analysis metafunctions of the form: | |
2360 | [pixel_reference/iterator/locator/view/image] + \p "_is_" + [basic/mutable/step]. For example: | |
2361 | ||
2362 | \code | |
2363 | if (view_is_mutable<View>::value) { | |
2364 | ... | |
2365 | } | |
2366 | \endcode | |
2367 | ||
2368 | A <i>basic</i> GIL construct is a memory-based construct that uses the built-in GIL classes and does not have any function object to invoke upon dereferencing. | |
2369 | For example, a simple planar or interleaved, step or non-step RGB image view is basic, but a color converted view or a virtual view is not. | |
2370 | ||
2371 | <hr> | |
2372 | \section IO_DG 13. I/O Extension | |
2373 | ||
2374 | GIL's I/O extension provides low level image i/o utilities. It supports loading and saving several image formats, each of which requires linking | |
2375 | against the corresponding library: | |
2376 | ||
2377 | - <b>JPEG</b>: To use JPEG files, include the file <tt>gil/extension/io/jpeg_io.hpp</tt>. If you are using run-time images, | |
2378 | you need to include <tt>gil/extension/io/jpeg_dynamic_io.hpp</tt> instead. You need to compile and link against libjpeg.lib | |
2379 | (available at http://www.ijg.org). You need to have <tt>jpeglib.h</tt> in your include path. | |
2380 | ||
2381 | - <b>TIFF</b>: To use TIFF files, include the file <tt>gil/extension/io/tiff_io.hpp</tt>. If you are using run-time images, | |
2382 | you need to include <tt>gil/extension/io/tiff_dynamic_io.hpp</tt> instead. You need to compile and link against libtiff.lib | |
2383 | (available at http://www.libtiff.org). You need to have <tt>tiffio.h</tt> in your include path. | |
2384 | ||
2385 | - <b>PNG</b>: To use PNG files, include the file <tt>gil/extension/io/png_io.hpp</tt>. If you are using run-time images, | |
2386 | you need to include <tt>gil/extension/io/png_dynamic_io.hpp</tt> instead. You need to compile and link against libpng.lib | |
2387 | (available at http://wwwlibpng.org). You need to have <tt>png.h</tt> in your include path. | |
2388 | ||
2389 | You don't need to install all these libraries; just the ones you will use. | |
2390 | Here are the I/O APIs for JPEG files (replace \p "jpeg" with \p "tiff" or \p "png" for the APIs of the other libraries): | |
2391 | ||
2392 | \code | |
2393 | // Returns the width and height of the JPEG file at the specified location. | |
2394 | // Throws std::ios_base::failure if the location does not correspond to a valid JPEG file | |
2395 | point2<std::ptrdiff_t> jpeg_read_dimensions(const char*); | |
2396 | ||
2397 | // Allocates a new image whose dimensions are determined by the given jpeg image file, and loads the pixels into it. | |
2398 | // Triggers a compile assert if the image color space or channel depth are not supported by the JPEG library or by the I/O extension. | |
2399 | // Throws std::ios_base::failure if the file is not a valid JPEG file, or if its color space or channel depth are not | |
2400 | // compatible with the ones specified by Image | |
2401 | template <typename Img> void jpeg_read_image(const char*, Img&); | |
2402 | ||
2403 | // Allocates a new image whose dimensions are determined by the given jpeg image file, and loads the pixels into it, | |
2404 | // color-converting and channel-converting if necessary. | |
2405 | // Triggers a compile assert if the image color space or channel depth are not supported by the JPEG library or by the I/O extension. | |
2406 | // Throws std::ios_base::failure if the file is not a valid JPEG file or if it fails to read it. | |
2407 | template <typename Img> void jpeg_read_and_convert_image(const char*, Img&); | |
2408 | template <typename Img, typename CCV> void jpeg_read_and_convert_image(const char*, Img&, CCV color_converter); | |
2409 | ||
2410 | // Loads the image specified by the given jpeg image file name into the given view. | |
2411 | // Triggers a compile assert if the view color space and channel depth are not supported by the JPEG library or by the I/O extension. | |
2412 | // Throws std::ios_base::failure if the file is not a valid JPEG file, or if its color space or channel depth are not | |
2413 | // compatible with the ones specified by View, or if its dimensions don't match the ones of the view. | |
2414 | template <typename View> void jpeg_read_view(const char*, const View&); | |
2415 | ||
2416 | // Loads the image specified by the given jpeg image file name into the given view and color-converts (and channel-converts) it if necessary. | |
2417 | // Triggers a compile assert if the view color space and channel depth are not supported by the JPEG library or by the I/O extension. | |
2418 | // Throws std::ios_base::failure if the file is not a valid JPEG file, or if its dimensions don't match the ones of the view. | |
2419 | template <typename View> void jpeg_read_and_convert_view(const char*, const View&); | |
2420 | template <typename View, typename CCV> void jpeg_read_and_convert_view(const char*, const View&, CCV color_converter); | |
2421 | ||
2422 | // Saves the view to a jpeg file specified by the given jpeg image file name. | |
2423 | // Triggers a compile assert if the view color space and channel depth are not supported by the JPEG library or by the I/O extension. | |
2424 | // Throws std::ios_base::failure if it fails to create the file. | |
2425 | template <typename View> void jpeg_write_view(const char*, const View&); | |
2426 | ||
2427 | // Determines whether the given view type is supported for reading | |
2428 | template <typename View> struct jpeg_read_support { | |
2429 | static const bool value = ...; | |
2430 | }; | |
2431 | ||
2432 | // Determines whether the given view type is supported for writing | |
2433 | template <typename View> struct jpeg_write_support { | |
2434 | static const bool value = ...; | |
2435 | }; | |
2436 | \endcode | |
2437 | ||
2438 | If you use the dynamic image extension, make sure to include \p "jpeg_dynamic_io.hpp" instead of \p "jpeg_io.hpp". | |
2439 | In addition to the above methods, you have the following overloads dealing with dynamic images: | |
2440 | ||
2441 | \code | |
2442 | // Opens the given JPEG file name, selects the first type in Images whose color space and channel are compatible to those of the image file | |
2443 | // and creates a new image of that type with the dimensions specified by the image file. | |
2444 | // Throws std::ios_base::failure if none of the types in Images are compatible with the type on disk. | |
2445 | template <typename Images> void jpeg_read_image(const char*, any_image<Images>&); | |
2446 | ||
2447 | // Saves the currently instantiated view to a jpeg file specified by the given jpeg image file name. | |
2448 | // Throws std::ios_base::failure if the currently instantiated view type is not supported for writing by the I/O extension | |
2449 | // or if it fails to create the file. | |
2450 | template <typename Views> void jpeg_write_view(const char*, any_image_view<Views>&); | |
2451 | \endcode | |
2452 | ||
2453 | All of the above methods have overloads taking \p std::string instead of <tt>const char*</tt> | |
2454 | ||
2455 | <hr> | |
2456 | \section SampleImgCodeDG 14. Sample Code | |
2457 | ||
2458 | \subsection PixelLevelExampleDG Pixel-level Sample Code | |
2459 | ||
2460 | Here are some operations you can do with pixel values, pointers and references: | |
2461 | ||
2462 | \code | |
2463 | rgb8_pixel_t p1(255,0,0); // make a red RGB pixel | |
2464 | bgr8_pixel_t p2 = p1; // RGB and BGR are compatible and the channels will be properly mapped. | |
2465 | assert(p1==p2); // p2 will also be red. | |
2466 | assert(p2[0]!=p1[0]); // operator[] gives physical channel order (as laid down in memory) | |
2467 | assert(semantic_at_c<0>(p1)==semantic_at_c<0>(p2)); // this is how to compare the two red channels | |
2468 | get_color(p1,green_t()) = get_color(p2,blue_t()); // channels can also be accessed by name | |
2469 | ||
2470 | const unsigned char* r; | |
2471 | const unsigned char* g; | |
2472 | const unsigned char* b; | |
2473 | rgb8c_planar_ptr_t ptr(r,g,b); // constructing const planar pointer from const pointers to each plane | |
2474 | ||
2475 | rgb8c_planar_ref_t ref=*ptr; // just like built-in reference, dereferencing a planar pointer returns a planar reference | |
2476 | ||
2477 | p2=ref; p2=p1; p2=ptr[7]; p2=rgb8_pixel_t(1,2,3); // planar/interleaved references and values to RGB/BGR can be freely mixed | |
2478 | ||
2479 | //rgb8_planar_ref_t ref2; // compile error: References have no default constructors | |
2480 | //ref2=*ptr; // compile error: Cannot construct non-const reference by dereferencing const pointer | |
2481 | //ptr[3]=p1; // compile error: Cannot set the fourth pixel through a const pointer | |
2482 | //p1 = pixel<float, rgb_layout_t>();// compile error: Incompatible channel depth | |
2483 | //p1 = pixel<bits8, rgb_layout_t>();// compile error: Incompatible color space (even though it has the same number of channels) | |
2484 | //p1 = pixel<bits8,rgba_layout_t>();// compile error: Incompatible color space (even though it contains red, green and blue channels) | |
2485 | \endcode | |
2486 | ||
2487 | Here is how to use pixels in generic code: | |
2488 | ||
2489 | \code | |
2490 | template <typename GrayPixel, typename RGBPixel> | |
2491 | void gray_to_rgb(const GrayPixel& src, RGBPixel& dst) { | |
2492 | gil_function_requires<PixelConcept<GrayPixel> >(); | |
2493 | gil_function_requires<MutableHomogeneousPixelConcept<RGBPixel> >(); | |
2494 | ||
2495 | typedef typename color_space_type<GrayPixel>::type gray_cs_t; | |
2496 | BOOST_STATIC_ASSERT((boost::is_same<gray_cs_t,gray_t>::value)); | |
2497 | ||
2498 | typedef typename color_space_type<RGBPixel>::type rgb_cs_t; | |
2499 | BOOST_STATIC_ASSERT((boost::is_same<rgb_cs_t,rgb_t>::value)); | |
2500 | ||
2501 | typedef typename channel_type<GrayPixel>::type gray_channel_t; | |
2502 | typedef typename channel_type<RGBPixel>::type rgb_channel_t; | |
2503 | ||
2504 | gray_channel_t gray = get_color(src,gray_color_t()); | |
2505 | static_fill(dst, channel_convert<rgb_channel_t>(gray)); | |
2506 | } | |
2507 | ||
2508 | // example use patterns: | |
2509 | ||
2510 | // converting gray l-value to RGB and storing at (5,5) in a 16-bit BGR interleaved image: | |
2511 | bgr16_view_t b16(...); | |
2512 | gray_to_rgb(gray8_pixel_t(33), b16(5,5)); | |
2513 | ||
2514 | // storing the first pixel of an 8-bit grayscale image as the 5-th pixel of 32-bit planar RGB image: | |
2515 | rgb32f_planar_view_t rpv32; | |
2516 | gray8_view_t gv8(...); | |
2517 | gray_to_rgb(*gv8.begin(), rpv32[5]); | |
2518 | \endcode | |
2519 | ||
2520 | As the example shows, both the source and the destination can be references or values, planar or interleaved, as long as they model \p PixelConcept | |
2521 | and \p MutablePixelConcept respectively. | |
2522 | ||
2523 | ||
2524 | \subsection SafeAreaExampleDG Creating a Copy of an Image with a Safe Buffer | |
2525 | ||
2526 | Suppose we want to convolve an image with multiple kernels, the largest of which is 2K+1 x 2K+1 pixels. It may be worth | |
2527 | creating a margin of K pixels around the image borders. Here is how to do it: | |
2528 | ||
2529 | \code | |
2530 | template <typename SrcView, // Models ImageViewConcept (the source view) | |
2531 | typename DstImage> // Models ImageConcept (the returned image) | |
2532 | void create_with_margin(const SrcView& src, int k, DstImage& result) { | |
2533 | gil_function_requires<ImageViewConcept<SrcView> >(); | |
2534 | gil_function_requires<ImageConcept<DstImage> >(); | |
2535 | gil_function_requires<ViewsCompatibleConcept<SrcView, typename DstImage::view_t> >(); | |
2536 | ||
2537 | result=DstImage(src.width()+2*k, src.height()+2*k); | |
2538 | typename DstImage::view_t centerImg=subimage_view(view(result), k,k,src.width(),src.height()); | |
2539 | std::copy(src.begin(), src.end(), centerImg.begin()); | |
2540 | } | |
2541 | \endcode | |
2542 | ||
2543 | We allocated a larger image, then we used \p subimage_view to create a shallow image of | |
2544 | its center area of top left corner at (k,k) and of identical size as \p src, and finally we copied \p src into that center image. If the margin | |
2545 | needs initialization, we could have done it with \p fill_pixels. Here is how to simplify this code using | |
2546 | the \p copy_pixels algorithm: | |
2547 | ||
2548 | \code | |
2549 | template <typename SrcView, typename DstImage> | |
2550 | void create_with_margin(const SrcView& src, int k, DstImage& result) { | |
2551 | result.recreate(src.width()+2*k, src.height()+2*k); | |
2552 | copy_pixels(src, subimage_view(view(result), k,k,src.width(),src.height())); | |
2553 | } | |
2554 | \endcode | |
2555 | ||
2556 | (Note also that \p image::recreate is more efficient than \p operator=, as the latter will do an unnecessary copy construction). | |
2557 | Not only does the above example work for planar and interleaved images of any color space and pixel depth; it is also optimized. | |
2558 | GIL overrides \p std::copy - when called on two identical interleaved images with no padding at the end of rows, it | |
2559 | simply does a \p memmove. For planar images it does \p memmove for each channel. If one of the images has padding, (as in | |
2560 | our case) it will try to do \p memmove for each row. When an image has no padding, it will use its lightweight | |
2561 | horizontal iterator (as opposed to the more complex 1D image iterator that has to check for the end of rows). | |
2562 | It choses the fastest method, taking into account both static and run-time parameters. | |
2563 | ||
2564 | \subsection HistogramExampleDG Histogram | |
2565 | ||
2566 | The histogram can be computed by counting the number of pixel values that fall in each bin. | |
2567 | The following method takes a grayscale (one-dimensional) image view, since only grayscale pixels | |
2568 | are convertible to integers: | |
2569 | \code | |
2570 | template <typename GrayView, typename R> | |
2571 | void grayimage_histogram(const GrayView& img, R& hist) { | |
2572 | for (typename GrayView::iterator it=img.begin(); it!=img.end(); ++it) | |
2573 | ++hist[*it]; | |
2574 | } | |
2575 | \endcode | |
2576 | ||
2577 | Using \p boost::lambda and GIL's \p for_each_pixel algorithm, we can write this more compactly: | |
2578 | ||
2579 | \code | |
2580 | template <typename GrayView, typename R> | |
2581 | void grayimage_histogram(const GrayView& v, R& hist) { | |
2582 | for_each_pixel(v, ++var(hist)[_1]); | |
2583 | } | |
2584 | \endcode | |
2585 | ||
2586 | Where \p for_each_pixel invokes \p std::for_each and \p var and \p _1 are \p boost::lambda constructs. | |
2587 | To compute the luminosity histogram, we call the above method using the grayscale view of an image: | |
2588 | ||
2589 | \code | |
2590 | template <typename View, typename R> | |
2591 | void luminosity_histogram(const View& v, R& hist) { | |
2592 | grayimage_histogram(color_converted_view<gray8_pixel_t>(v),hist); | |
2593 | } | |
2594 | \endcode | |
2595 | ||
2596 | This is how to invoke it: | |
2597 | ||
2598 | \code | |
2599 | unsigned char hist[256]; | |
2600 | std::fill(hist,hist+256,0); | |
2601 | luminosity_histogram(my_view,hist); | |
2602 | \endcode | |
2603 | ||
2604 | If we want to view the histogram of the second channel of the image in the top left 100x100 area, we call: | |
2605 | ||
2606 | \code | |
2607 | grayimage_histogram(nth_channel_view(subimage_view(img,0,0,100,100),1),hist); | |
2608 | \endcode | |
2609 | ||
2610 | No pixels are copied and no extra memory is allocated - the code operates directly on the source pixels, which could | |
2611 | be in any supported color space and channel depth. They could be either planar or interleaved. | |
2612 | ||
2613 | \subsection ImageViewsExampleDG Using Image Views | |
2614 | ||
2615 | The following code illustrates the power of using image views: | |
2616 | ||
2617 | \code | |
2618 | jpeg_read_image("monkey.jpg", img); | |
2619 | step1=view(img); | |
2620 | step2=subimage_view(step1, 200,300, 150,150); | |
2621 | step3=color_converted_view<rgb8_view_t,gray8_pixel_t>(step2); | |
2622 | step4=rotated180_view(step3); | |
2623 | step5=subsampled_view(step4, 2,1); | |
2624 | jpeg_write_view("monkey_transform.jpg", step5); | |
2625 | \endcode | |
2626 | ||
2627 | The intermediate images are shown here: | |
2628 | \image html monkey_steps.jpg | |
2629 | ||
2630 | Notice that no pixels are ever copied. All the work is done inside \p jpeg_write_view. | |
2631 | If we call our \p luminosity_histogram with \p step5 it will do the right thing. | |
2632 | ||
2633 | ||
2634 | <hr> | |
2635 | \section ExtendingGIL_DG 15. Extending the Generic Image Library | |
2636 | ||
2637 | You can define your own pixel iterators, locators, image views, images, channel types, color spaces and algorithms. | |
2638 | You can make virtual images that live on the disk, inside a jpeg file, somewhere on the internet, or even fully-synthetic images | |
2639 | such as the Mandelbrot set. | |
2640 | As long as they properly model the corresponding concepts, they will work with any existing GIL code. | |
2641 | Most such extensions require no changes to the library and can thus be | |
2642 | supplied in another module. | |
2643 | ||
2644 | \subsection NewColorSpacesDG Defining New Color Spaces | |
2645 | ||
2646 | Each color space is in a separate file. To add a new color space, just copy one of the existing ones (like rgb.hpp) and change it | |
2647 | accordingly. If you want color conversion support, you will have to provide methods to convert between it and the existing color spaces | |
2648 | (see color_convert.h). For convenience you may want to provide useful typedefs for pixels, pointers, references and images with the new | |
2649 | color space (see typedefs.h). | |
2650 | ||
2651 | \subsection NewChannelsDG Defining New Channel Types | |
2652 | ||
2653 | Most of the time you don't need to do anything special to use a new channel type. You can just use it: | |
2654 | ||
2655 | \code | |
2656 | typedef pixel<double,rgb_layout_t> rgb64_pixel_t; // 64 bit RGB pixel | |
2657 | typedef rgb64_pixel* rgb64_pixel_ptr_t;// pointer to 64-bit interleaved data | |
2658 | typedef image_type<double,rgb_layout_t>::type rgb64_image_t; // 64-bit interleaved image | |
2659 | \endcode | |
2660 | ||
2661 | If you want to use your own channel class, you will need to provide a specialization of \p channel_traits for it (see channel.hpp). | |
2662 | If you want to do conversion between your and existing channel types, you will need to provide an overload of \p channel_convert. | |
2663 | ||
2664 | \subsection NewColorConversionDG Overloading Color Conversion | |
2665 | ||
2666 | Suppose you want to provide your own color conversion. For example, you may want to implement higher quality color conversion using color profiles. | |
2667 | Typically you may want to redefine color conversion only in some instances and default to GIL's color conversion in all other cases. Here is, for | |
2668 | example, how to overload color conversion so that color conversion to gray inverts the result but everything else remains the same: | |
2669 | ||
2670 | \code | |
2671 | // make the default use GIL's default | |
2672 | template <typename SrcColorSpace, typename DstColorSpace> | |
2673 | struct my_color_converter_impl | |
2674 | : public default_color_converter_impl<SrcColorSpace,DstColorSpace> {}; | |
2675 | ||
2676 | // provide specializations only for cases you care about | |
2677 | // (in this case, if the destination is grayscale, invert it) | |
2678 | template <typename SrcColorSpace> | |
2679 | struct my_color_converter_impl<SrcColorSpace,gray_t> { | |
2680 | template <typename SrcP, typename DstP> // Model PixelConcept | |
2681 | void operator()(const SrcP& src, DstP& dst) const { | |
2682 | default_color_converter_impl<SrcColorSpace,gray_t>()(src,dst); | |
2683 | get_color(dst,gray_color_t())=channel_invert(get_color(dst,gray_color_t())); | |
2684 | } | |
2685 | }; | |
2686 | ||
2687 | // create a color converter object that dispatches to your own implementation | |
2688 | struct my_color_converter { | |
2689 | template <typename SrcP, typename DstP> // Model PixelConcept | |
2690 | void operator()(const SrcP& src,DstP& dst) const { | |
2691 | typedef typename color_space_type<SrcP>::type SrcColorSpace; | |
2692 | typedef typename color_space_type<DstP>::type DstColorSpace; | |
2693 | my_color_converter_impl<SrcColorSpace,DstColorSpace>()(src,dst); | |
2694 | } | |
2695 | }; | |
2696 | \endcode | |
2697 | ||
2698 | GIL's color conversion functions take the color converter as an optional parameter. You can pass your own color converter: | |
2699 | ||
2700 | \code | |
2701 | color_converted_view<gray8_pixel_t>(img_view,my_color_converter()); | |
2702 | \endcode | |
2703 | ||
2704 | \subsection NewImagesDG Defining New Image Views | |
2705 | ||
2706 | <p> You can provide your own pixel iterators, locators and views, overriding either the mechanism for getting from one pixel to the next or doing an arbitrary | |
2707 | pixel transformation on dereference. For example, let's look at the implementation of \p color_converted_view (an image factory method that, | |
2708 | given any image view, returns a new, otherwise identical view, except that color conversion is performed on pixel access). | |
2709 | First we need to define a model of \p PixelDereferenceAdaptorConcept; a function object that will be called when we dereference a pixel iterator. | |
2710 | It will call \p color_convert to convert to the destination pixel type: | |
2711 | ||
2712 | \code | |
2713 | template <typename SrcConstRefP, // const reference to the source pixel | |
2714 | typename DstP> // Destination pixel value (models PixelValueConcept) | |
2715 | class color_convert_deref_fn { | |
2716 | public: | |
2717 | typedef color_convert_deref_fn const_t; | |
2718 | typedef DstP value_type; | |
2719 | typedef value_type reference; // read-only dereferencing | |
2720 | typedef const value_type& const_reference; | |
2721 | typedef SrcConstRefP argument_type; | |
2722 | typedef reference result_type; | |
2723 | BOOST_STATIC_CONSTANT(bool, is_mutable=false); | |
2724 | ||
2725 | result_type operator()(argument_type srcP) const { | |
2726 | result_type dstP; | |
2727 | color_convert(srcP,dstP); | |
2728 | return dstP; | |
2729 | } | |
2730 | }; | |
2731 | ||
2732 | \endcode | |
2733 | ||
2734 | We then use the \p add_deref member struct of image views to construct the type of a view that invokes a given function object (\p deref_t) upon | |
2735 | dereferencing. In our case, it performs color conversion: | |
2736 | ||
2737 | \code | |
2738 | template <typename SrcView, typename DstP> | |
2739 | struct color_converted_view_type { | |
2740 | private: | |
2741 | typedef typename SrcView::const_t::reference src_pix_ref; // const reference to pixel in SrcView | |
2742 | typedef color_convert_deref_fn<src_pix_ref, DstP> deref_t; // the dereference adaptor that performs color conversion | |
2743 | typedef typename SrcView::template add_deref<deref_t> add_ref_t; | |
2744 | public: | |
2745 | typedef typename add_ref_t::type type; // the color converted view type | |
2746 | static type make(const SrcView& sv) { return add_ref_t::make(sv, deref_t()); } | |
2747 | }; | |
2748 | \endcode | |
2749 | ||
2750 | Finally our \p color_converted_view code simply creates color-converted view from the source view: | |
2751 | ||
2752 | \code | |
2753 | template <typename DstP, typename View> inline | |
2754 | typename color_converted_view_type<View,DstP>::type color_convert_view(const View& src) { | |
2755 | return color_converted_view_type<View,DstP>::make(src); | |
2756 | } | |
2757 | \endcode | |
2758 | ||
2759 | (The actual color convert view transformation is slightly more complicated, as it takes an optional color conversion object, which | |
2760 | allows users to specify their own color conversion methods). | |
2761 | See the GIL tutorial for an example of creating a virtual image view that defines the Mandelbrot set. | |
2762 | ||
2763 | <hr> | |
2764 | \section TechnicalitiesDG 16. Technicalities | |
2765 | ||
2766 | \subsection CreatingReferenceProxyDG Creating a reference proxy | |
2767 | ||
2768 | Sometimes it is necessary to create a proxy class that represents a reference to a given object. Examples of these are GIL's reference | |
2769 | to a planar pixel (\p planar_pixel_reference) and GIL's subbyte channel references. Writing a reference proxy class can be tricky. One | |
2770 | problem is that the proxy reference is constructed as a temporary object and returned by value upon dereferencing the iterator: | |
2771 | ||
2772 | \code | |
2773 | struct rgb_planar_pixel_iterator { | |
2774 | typedef my_reference_proxy<T> reference; | |
2775 | reference operator*() const { return reference(red,green,blue); } | |
2776 | }; | |
2777 | \endcode | |
2778 | ||
2779 | The problem arises when an iterator is dereferenced directly into a function that takes a mutable pixel: | |
2780 | ||
2781 | \code | |
2782 | template <typename Pixel> // Models MutablePixelConcept | |
2783 | void invert_pixel(Pixel& p); | |
2784 | ||
2785 | rgb_planar_pixel_iterator myIt; | |
2786 | invert_pixel(*myIt); // compile error! | |
2787 | \endcode | |
2788 | ||
2789 | C++ does not allow for matching a temporary object against a non-constant reference. The solution is to: | |
2790 | - Use const qualifier on all members of the reference proxy object: | |
2791 | ||
2792 | \code | |
2793 | template <typename T> | |
2794 | struct my_reference_proxy { | |
2795 | const my_reference_proxy& operator=(const my_reference_proxy& p) const; | |
2796 | const my_reference_proxy* operator->() const { return this; } | |
2797 | ... | |
2798 | }; | |
2799 | \endcode | |
2800 | ||
2801 | - Use different classes to denote mutable and constant reference (maybe based on the constness of the template parameter) | |
2802 | ||
2803 | - Define the reference type of your iterator with const qualifier: | |
2804 | ||
2805 | \code | |
2806 | struct iterator_traits<rgb_planar_pixel_iterator> { | |
2807 | typedef const my_reference_proxy<T> reference; | |
2808 | }; | |
2809 | \endcode | |
2810 | ||
2811 | A second important issue is providing an overload for \p swap for your reference class. The default \p std::swap will not | |
2812 | work correctly. You must use a real value type as the temporary. | |
2813 | A further complication is that in some implementations of the STL the \p swap function is incorreclty called qualified, as \p std::swap. | |
2814 | The only way for these STL algorithms to use your overload is if you define it in the \p std namespace: | |
2815 | \code | |
2816 | namespace std { | |
2817 | template <typename T> | |
2818 | void swap(my_reference_proxy<T>& x, my_reference_proxy<T>& y) { | |
2819 | my_value<T> tmp=x; | |
2820 | x=y; | |
2821 | y=tmp; | |
2822 | } | |
2823 | } | |
2824 | \endcode | |
2825 | ||
2826 | Lastly, remember that constructors and copy-constructors of proxy references are always shallow and assignment operators are deep. | |
2827 | ||
2828 | We are grateful to Dave Abrahams, Sean Parent and Alex Stepanov for suggesting the above solution. | |
2829 | ||
2830 | <hr> | |
2831 | \section ConclusionDG 17. Conclusion | |
2832 | ||
2833 | <p>The Generic Image Library is designed with the following five goals in mind: | |
2834 | ||
2835 | \li <b> Generality.</b> Abstracts image representations from algorithms on images. It allows for writing code once and have it work for any image type. | |
2836 | \li <b> Performance.</b> Speed has been instrumental to the design of the library. The generic algorithms provided in the library are in many cases comparable in | |
2837 | speed to hand-coding the algorithm for a specific image type. | |
2838 | \li <b> Flexibility.</b> Compile-type parameter resolution results in faster code, but severely limits code flexibility. The library allows for any | |
2839 | image parameter to be specified at run time, at a minor performance cost. | |
2840 | \li <b> Extensibility.</b> Virtually every construct in GIL can be extended - new channel types, color spaces, layouts, iterators, locators, image views and images | |
2841 | can be provided by modeling the corresponding GIL concepts. | |
2842 | \li <b> Compatibility.</b> The library is designed as an STL complement. Generic STL algorithms can be used for pixel manipulation, and they | |
2843 | are specifically targeted for optimization. The library works with existing raw pixel data from another image library. | |
2844 | ||
2845 | <div id="footerrow"><!--give footer 25px of white above--></div><div id="footer" title="footer: links to copyright and other legal information"><p><a href="licenses.html" class="el">Copyright © 2005 Adobe Systems Incorporated</a></p><ul id="list1"><!-- due to a rendering error in IE, these links should all be on one line without returns --><li id="terms"><a title="Terms of Use" href="http://www.adobe.com/misc/copyright.html">Terms of Use</a></li><li><a title="Privacy Policy" href="http://www.adobe.com/misc/privacy.html">Privacy Policy</a></li><li><a href="http://access.adobe.com">Accessibility</a></li><li><a title="Avoid software piracy" href="http://www.adobe.com/aboutadobe/antipiracy/main.html">Avoid software piracy</a></li><li id="tms"><a title="Permissions and trademarks" href="http://www.adobe.com/misc/agreement.html">Permissions and trademarks</a></li><li><a title="Product License Agreements" href="http://www.adobe.com/products/eulas/main.html">Product License Agreements</a></li></ul></div> | |
2846 | ||
2847 | */ | |
2848 |