123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- Examples
- ========
- .. contents::
- :local:
- :depth: 2
- Pixel-level Operations
- ----------------------
- Here are some operations you can do with pixel values, pixel pointers and
- pixel references:
- .. code-block:: cpp
- rgb8_pixel_t p1(255,0,0); // make a red RGB pixel
- bgr8_pixel_t p2 = p1; // RGB and BGR are compatible and the channels will be properly mapped.
- assert(p1==p2); // p2 will also be red.
- assert(p2[0]!=p1[0]); // operator[] gives physical channel order (as laid down in memory)
- assert(semantic_at_c<0>(p1)==semantic_at_c<0>(p2)); // this is how to compare the two red channels
- get_color(p1,green_t()) = get_color(p2,blue_t()); // channels can also be accessed by name
- const unsigned char* r;
- const unsigned char* g;
- const unsigned char* b;
- rgb8c_planar_ptr_t ptr(r,g,b); // constructing const planar pointer from const pointers to each plane
- rgb8c_planar_ref_t ref=*ptr; // just like built-in reference, dereferencing a planar pointer returns a planar reference
- 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
- //rgb8_planar_ref_t ref2; // compile error: References have no default constructors
- //ref2=*ptr; // compile error: Cannot construct non-const reference by dereferencing const pointer
- //ptr[3]=p1; // compile error: Cannot set the fourth pixel through a const pointer
- //p1 = pixel<float, rgb_layout_t>();// compile error: Incompatible channel depth
- //p1 = pixel<bits8, rgb_layout_t>();// compile error: Incompatible color space (even though it has the same number of channels)
- //p1 = pixel<bits8,rgba_layout_t>();// compile error: Incompatible color space (even though it contains red, green and blue channels)
- Here is how to use pixels in generic code:
- .. code-block:: cpp
- template <typename GrayPixel, typename RGBPixel>
- void gray_to_rgb(const GrayPixel& src, RGBPixel& dst)
- {
- gil_function_requires<PixelConcept<GrayPixel> >();
- gil_function_requires<MutableHomogeneousPixelConcept<RGBPixel> >();
- typedef typename color_space_type<GrayPixel>::type gray_cs_t;
- static_assert(boost::is_same<gray_cs_t,gray_t>::value, "");
- typedef typename color_space_type<RGBPixel>::type rgb_cs_t;
- static_assert(boost::is_same<rgb_cs_t,rgb_t>::value, "");
- typedef typename channel_type<GrayPixel>::type gray_channel_t;
- typedef typename channel_type<RGBPixel>::type rgb_channel_t;
- gray_channel_t gray = get_color(src,gray_color_t());
- static_fill(dst, channel_convert<rgb_channel_t>(gray));
- }
- // example use patterns:
- // converting gray l-value to RGB and storing at (5,5) in a 16-bit BGR interleaved image:
- bgr16_view_t b16(...);
- gray_to_rgb(gray8_pixel_t(33), b16(5,5));
- // storing the first pixel of an 8-bit grayscale image as the 5-th pixel of 32-bit planar RGB image:
- rgb32f_planar_view_t rpv32;
- gray8_view_t gv8(...);
- gray_to_rgb(*gv8.begin(), rpv32[5]);
- As the example shows, both the source and the destination can be references or
- values, planar or interleaved, as long as they model ``PixelConcept`` and
- ``MutablePixelConcept`` respectively.
- Resizing image canvas
- ---------------------
- Resizing an image canvas means adding a buffer of pixels around existing
- pixels. Size of canvas of an image can never be smaller than the image itself.
- 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 creating a margin of K pixels
- around the image borders. Here is how to do it:
- .. code-block:: cpp
- template <typename SrcView, // Models ImageViewConcept (the source view)
- typename DstImage> // Models ImageConcept (the returned image)
- void create_with_margin(const SrcView& src, int k, DstImage& result)
- {
- gil_function_requires<ImageViewConcept<SrcView> >();
- gil_function_requires<ImageConcept<DstImage> >();
- gil_function_requires<ViewsCompatibleConcept<SrcView, typename DstImage::view_t> >();
- result=DstImage(src.width()+2*k, src.height()+2*k);
- typename DstImage::view_t centerImg=subimage_view(view(result), k,k,src.width(),src.height());
- std::copy(src.begin(), src.end(), centerImg.begin());
- }
- We allocated a larger image, then we used ``subimage_view`` to create a
- shallow image of its center area of top left corner at (k,k) and of identical
- size as ``src``, and finally we copied ``src`` into that center image. If the
- margin needs initialization, we could have done it with ``fill_pixels``. Here
- is how to simplify this code using the ``copy_pixels`` algorithm:
- .. code-block:: cpp
- template <typename SrcView, typename DstImage>
- void create_with_margin(const SrcView& src, int k, DstImage& result)
- {
- result.recreate(src.width()+2*k, src.height()+2*k);
- copy_pixels(src, subimage_view(view(result), k,k,src.width(),src.height()));
- }
- (Note also that ``image::recreate`` is more efficient than ``operator=``, as
- the latter will do an unnecessary copy construction). Not only does the above
- example work for planar and interleaved images of any color space and pixel
- depth; it is also optimized. GIL overrides ``std::copy`` - when called on two
- identical interleaved images with no padding at the end of rows, it simply
- does a ``memmove``. For planar images it does ``memmove`` for each channel.
- If one of the images has padding, (as in our case) it will try to do
- ``memmove`` for each row. When an image has no padding, it will use its
- lightweight horizontal iterator (as opposed to the more complex 1D image
- iterator that has to check for the end of rows). It choses the fastest method,
- taking into account both static and run-time parameters.
- Histogram
- ---------
- The histogram can be computed by counting the number of pixel values that fall
- in each bin. The following method takes a grayscale (one-dimensional) image
- view, since only grayscale pixels are convertible to integers:
- .. code-block:: cpp
- template <typename GrayView, typename R>
- void grayimage_histogram(const GrayView& img, R& hist)
- {
- for (typename GrayView::iterator it=img.begin(); it!=img.end(); ++it)
- ++hist[*it];
- }
- Using ``boost::lambda`` and GIL's ``for_each_pixel`` algorithm, we can write
- this more compactly:
- .. code-block:: cpp
- template <typename GrayView, typename R>
- void grayimage_histogram(const GrayView& v, R& hist)
- {
- for_each_pixel(v, ++var(hist)[_1]);
- }
- Where ``for_each_pixel`` invokes ``std::for_each`` and ``var`` and ``_1`` are
- ``boost::lambda`` constructs. To compute the luminosity histogram, we call the
- above method using the grayscale view of an image:
- .. code-block:: cpp
- template <typename View, typename R>
- void luminosity_histogram(const View& v, R& hist)
- {
- grayimage_histogram(color_converted_view<gray8_pixel_t>(v),hist);
- }
- This is how to invoke it:
- .. code-block:: cpp
- unsigned char hist[256];
- std::fill(hist,hist+256,0);
- luminosity_histogram(my_view,hist);
- If we want to view the histogram of the second channel of the image in the top
- left 100x100 area, we call:
- .. code-block:: cpp
- grayimage_histogram(nth_channel_view(subimage_view(img,0,0,100,100),1),hist);
- No pixels are copied and no extra memory is allocated - the code operates
- directly on the source pixels, which could be in any supported color space and
- channel depth. They could be either planar or interleaved.
- Using image views
- -----------------
- The following code illustrates the power of using image views:
- .. code-block:: cpp
- jpeg_read_image("monkey.jpg", img);
- step1=view(img);
- step2=subimage_view(step1, 200,300, 150,150);
- step3=color_converted_view<rgb8_view_t,gray8_pixel_t>(step2);
- step4=rotated180_view(step3);
- step5=subsampled_view(step4, 2,1);
- jpeg_write_view("monkey_transform.jpg", step5);
- The intermediate images are shown here:
- .. image:: ../images/monkey_steps.jpg
- Notice that no pixels are ever copied. All the work is done inside
- ``jpeg_write_view``. If we call our ``luminosity_histogram`` with
- ``step5`` it will do the right thing.
|