Skip to content
Pranam Lashkari edited this page Sep 18, 2019 · 19 revisions

Gil strength is its expressiveness by using concepts and plenty of meta-programming. This makes gil hard to learn and worse hard to extend without creating a mess.

This wiki will show some of the power of gil.

// Always assume
#include <vector>
#include <boost/gil.hpp>

using namespace std;
using namespace boost;
using namespace boost::gil;

Simple

rgba8_image_t src(1,1);

// get the first pixel of an image
auto first_pixel = view(src)[0];

// get first channel of first pixel of an image (should be uint8_t in this case)
uint8_t first_channel_of_first_pixel = view(src)[0][0];

How to get the color space type from an image/view?

typedef rgb8_image_t image_t;
typedef typename color_space_type<image_t::view_t::value_type>::type colour_space_t;

There are rgb8_pixel_t, bgr8_pixel_t, and any other number of channel combinations. So, how do you get the red channel value?

auto get_red(pixel_t p)
{
    return get_color(p, red_t());
}

Each pixel type is defined with a bunch of channel types. Look here for the correct way of defining a new pixel type.

Damn it, my image is interleaved but I only want the red values!

argb8_image_t img( 640, 480 );
fill_pixels(view(img), argb8_pixel_t(0, 255, 0, 0 ));
    
// get a view to the first channel
auto v = nth_channel_view( view(img), 1 );

// convert the first channel view to gray8
auto c = color_converted_view<gray8_pixel_t>( v );

Raw Memory

You got memory and need to run through in an orderly fashion? gil got you covered!

Create a bit_aligned iterator from raw memory

std::vector<uint8_t> buf( 100 );
typedef bit_aligned_image1_type<1, gray_layout_t>::type::view_t view1_t;
typedef color_converted_view_type< view1_t, view1_t::reference >::type cc_view_t;
typedef cc_view_t::x_iterator cc_it_t;

auto cc_it = cc_it_t( view1_t::x_iterator(buf.data()) );
int Width = 640;
int Height = 480;

// create a rgb float buffer
float* src_buffer = new float[ 3 * Width * Height ];

// create a gil view of the src buffer
rgb32f_view_t v = interleaved_view(
    Width
    , Height
    , (rgb32f_pixel_t*) src_buffer
    , 3 * 4 * Width // row length in bytes
    );
    
// set pixel values
fill_pixels(v, rgb32f_pixel_t( 1.f ));

// let's create a rgb8 view
typedef color_converted_view_type<rgb32f_view_t, rgb8_pixel_t>::type ccv_t;
ccv_t dst = color_converted_view<rgb8_pixel_t>(v);

// all channels should be 255 which is gil's default behavior
rgb8_pixel_t p = *dst.xy_at(0,0);

The design guide has a nice example of how to use a bit aligned pixel type:

// Mutable reference to a BGR232 pixel
typedef const bit_aligned_pixel_reference<unsigned char, mpl::vector3_c<unsigned,2,3,2>, bgr_layout_t, true>  bgr232_ref_t;

// A mutable iterator over BGR232 pixels
typedef bit_aligned_pixel_iterator<bgr232_ref_t> bgr232_ptr_t;

// BGR232 pixel value. It is a packed_pixel of size 1 byte. (The last bit is unused)
typedef std::iterator_traits<bgr232_ptr_t>::value_type bgr232_pixel_t;
BOOST_STATIC_ASSERT((sizeof(bgr232_pixel_t)==1));

bgr232_pixel_t red(0,0,3); // = 0RRGGGBB, = 01100000 = 0x60

// a buffer of 7 bytes fits exactly 8 BGR232 pixels.
unsigned char pix_buffer[7];
std::fill(pix_buffer,pix_buffer+7,0);

// Fill the 8 pixels with red
bgr232_ptr_t pix_it(&pix_buffer[0],0);  // start at bit 0 of the first pixel
for (int i=0; i<8; ++i)
{
  *pix_it++ = red;
}
// Result: 0x60 0x30 0x11 0x0C 0x06 0x83 0xC1

You have a planar image and want to get access to each plane:

rgb8_planar_image_t planar_img(100, 100);

void* red_plane_data   = &planar_img[0]->channel<0>();
void* green_plane_data = &planar_img[0]->channel<1>();

Color Conversion

Can be tricky.

// bits32f is a scoped value channel
typedef pixel<bits32f, gray_layout_t> pixel_t;
gray8_pixel_t src(23);
pixel_t dst(0);

// 23 / 255 <-- uint8 max value
color_convert(src, dst);

// dst[0] is ~0.09..

A common surprise is that the default color converter from rgba to rgb will multiply the alpha value. When alpha is zero the the result rgb pixel will be all 0. That's not always wanted. In this case just create your own converter!

class convert_rgba_to_rgb
{
public:
    void operator() (const rgba8_pixel_t& src
        , rgb8_pixel_t& dst
        ) const
    {
        get_color(dst, red_t()) = get_color(src, red_t());
        get_color(dst, green_t()) = get_color(src, green_t());
        get_color(dst, blue_t()) = get_color(src, blue_t());
    }
};


rgba8_image_t src(1,1);
fill_pixels(view(src), rgba8_pixel_t(19, 79, 99, 0));

rgb8_image_t dst(1,1);

copy_and_convert_pixels(view(src), view(dst), convert_rgba_to_rgb());

The above example only works for rbga8 to rgb8 pixel types. That's not good when dealing with bgr8 pixels or similar. A better way is to take for the whole rgba color space instead of hardcoding the pixel type.

// make the default use GIL's default
template <typename SrcColorSpace, typename DstColorSpace>
struct my_color_converter_impl
    : public default_color_converter_impl<SrcColorSpace, DstColorSpace> {};

// provide specializations only for cases you care about
// (in this case, if the destination is grayscale, invert it)
template<>
struct my_color_converter_impl<rgba_t, rgb_t>
{
    template <typename SrcP, typename DstP>  // Model PixelConcept
    void operator()(const SrcP& src, DstP& dst) const
    {
        get_color(dst, red_t()) = get_color(src, red_t());
        get_color(dst, green_t()) = get_color(src, green_t());
        get_color(dst, blue_t()) = get_color(src, blue_t());
    }
};

// create a color converter object that dispatches to your own implementation
struct my_color_converter
{
    template <typename SrcP, typename DstP>  // Model PixelConcept
    void operator()(const SrcP& src, DstP& dst) const
    {
        typedef typename color_space_type<SrcP>::type SrcColorSpace;
        typedef typename color_space_type<DstP>::type DstColorSpace;
        my_color_converter_impl<SrcColorSpace, DstColorSpace>()(src, dst);
    }
};


bgra8_image_t src(1,1);
fill_pixels(view(src), bgra8_pixel_t(99, 79, 19, 0));

rgb8_image_t dst(1,1);
copy_and_convert_pixels(view(src), view(dst), my_color_converter());

auto fp = view(dst)[0];
assert(get_color(view(dst)[0], red_t()) == 19);


auto vv = color_converted_view<rgb8_pixel_t>(view(src), my_color_converter());
assert(get_color(view(dst)[0], red_t()) == 19);

Misc

Generic way for hashing pixels. We are using static_for_each() to loop through all channels of a pixel.

#include <unordered_map>

template< typename Pixel >
struct pixel_hasher
{
    std::size_t operator()( const Pixel& p ) const
    {
        typedef channel_type<Pixel>::type channel_t;

        std::size_t hash = 0;
        static_for_each( p, [&] ( const channel_t& c )
        {
            boost::hash_combine(hash, c);
        });

        return hash;
    }
};

void test()
{
    typedef rgb8_pixel_t pixel_t;
    pixel_t p( 1,2,3);
    unordered_map< pixel_t, int, pixel_hasher< pixel_t > > n;
    n[p] = 99;
}

Heterogeneous Pixel

Such a pixel has a bunch of channels which might be a different data type. For instance, for a color space like rgb we want red to be uint8 but need more space for green and blue, maybe uint32_t?

bit aligned image example

#include <boost\gil\extension\toolbox\gil_extensions.hpp>

typedef bit_aligned_image4_type<4, 2, 2, 4, bgra_layout_t>::type image_t;
typedef image_t::view_t view_t;

image_t img( 640, 480 );
auto x_it = view(img).row_begin(0);
auto data = (uint8_t*) &at_c<0>(*it);

Create your own bit aligned image

typedef bit_aligned_pixel_reference< uint64_t // needs to be at least 40bits hence uint64_t
                                    , mpl::vector4_c<uint16_t, 10, 10, 10, 10>
                                    , rgba_layout_t
                                    , true
                                    >  rgba10_ref_t;

// A mutable iterator over RGBA10 pixels
typedef bit_aligned_pixel_iterator< rgba10_ref_t > rgba10_ptr_t;

typedef std::iterator_traits< rgba10_ptr_t >::value_type rgba10_pixel_t;

rgba10_pixel_t src_10( 20, 30, 40, 50 );
assert( get_color( src_10, red_t()   ) == 20 );
assert( get_color( src_10, green_t() ) == 30 );
assert( get_color( src_10, blue_t()  ) == 40 );
assert( get_color( src_10, alpha_t() ) == 50 );

Create a pixel type from a bunch of channels with each different ranges (aka different type).

struct double_0 { static double apply() { return 0.0; } }; 
struct double_1 { static double apply() { return 1.0; } }; 
struct double_100 { static double apply() { return 100.0; } }; 
struct double_255 { static double apply() { return 255.0; } };

typedef scoped_channel_value<double, double_0, double_1> channel_0_1_t; 
typedef scoped_channel_value<double, double_0, double_255> channel_0_255_t; 
typedef scoped_channel_value<double, double_0, double_100> channel_0_100_t; 

//color space 
typedef mpl::vector3< channel_0_1_t, channel_0_255_t, channel_0_100_t> my_cs_t; 

//layout 
typedef layout<my_cs_t> my_layout_t; 

//pixel 
typedef pixel< double, my_layout_t > my_pixel_t; my_pixel_t op;

Be sure to define the correct integral data type when defining a bit aligned image. It must be able to contain all channels

typedef mpl::vector5_c<unsigned, 16, 16, 16, 8, 8> CBSV;
int sum = mpl::accumulate< CBSV, mpl::int_<0>, mpl::plus<mpl::_1, mpl::_2> >::type::value;

static_assert(mpl::accumulate< CBSV, mpl::int_<0>, mpl::plus<mpl::_1, mpl::_2> >::type::value < sizeof(uint64_t));

Packed Pixels and Bit Aligned Pixel

A packed pixel type is byte aligned. That means that all channels fit exacatly into integer type, like uint8_t or uint32_t. The advantage is that a user can use a C pointer to iterate over such packed pixels. An example:

// 5 + 6 + 5 = 16bits -> uint16_t
typedef packed_pixel_type<uint16_t, mpl::vector3_c<unsigned,5,6,5>, rgb_layout_t>::type rgb565_pixel_t;

A bit aligned pixel on the other hand might not aligned with common integer sizes. For such pixels the iteration over a bunch of them is more complex.

typedef const bit_aligned_pixel_reference<uint32_t, mpl::vector3_c<unsigned,11,14,5>, bgr_layout_t, true>  bgr232_ref_t;

Note that the pixel type is proxy class named bit_aligned_pixel_reference. Also note, the the sum of all pixels

Meta Functions

channel_view

rgb8_image_t a;
typedef channel_view_type< red_t, rgb8_view_t>::type view_t;
view_t red_channel_view = channel_view< red_t >(view(a));

Using gil with other toolkits

SFML

#include <SFML/Graphics.hpp>

#include <cassert>
#include <complex>
#include <iostream>

#include <boost/gil.hpp>
#include <boost/gil/extension/toolbox/color_spaces/hsv.hpp>

#ifdef _DEBUG
const int width  = 640; //1024;
const int height = 480; //768;
#else
const int width  = 1024;
const int height = 768;
#endif

template< typename T = double >
struct coloring_info
{
    coloring_info()
    : _inside( true )
    {}

    coloring_info( bool inside, const std::complex< T >& value, int iterations, int max_iterations )
    : _inside( inside )
    , _value( value )
    , _iterations( iterations )
    , _max_iterations( max_iterations )
    {}

    sf::Color get_color() const
    {
        sf:: Color color;
        color.a = 255;

        using namespace boost;
        using namespace gil;
       
        hsv32f_pixel_t src( ( _iterations % 256 ) / 255.f                 // hue
                          , 1.f                                           // (full) saturation
                          , ( _iterations < _max_iterations ) ? 1.f : 0.f // value
                          );

        rgb8_pixel_t dst;
        color_convert(src, dst);
       
        color.r = gil::get_color(dst, red_t());
        color.g = gil::get_color(dst, green_t());
        color.b = gil::get_color(dst, blue_t());

        if( _inside )
        {
            color = sf::Color::Black;
        }
        else
        {
            if( _iterations < _max_iterations )
            {
                // point does not belong to Mandelbrot set
        
                int r = static_cast<int>(( 255.0 / _max_iterations ) * _iterations );
        
                if( _iterations < ( _max_iterations / 2 ))
                {
                    color.r = r;
                    color.g = 0;
                    color.b = 0;
                }
                else
                {
                    color.r = r;
                    color.g = 255;
                    color.b = 255;
                }
        
                color.r;
                color.g;
                color.b;
            }
        }

        return color;
    }

    bool _inside;

    std::complex< T > _value;

    int _iterations;
    int _max_iterations;
};

template< typename T = double >
struct fractal_parameters
{
    // set all parameters by hand
    fractal_parameters( T r_min, T r_max, T i_min, T i_max, int max_iterations, int width , int height )
    : _real_min(r_min)
    , _real_max(r_max)
    , _imag_min(i_min)
    , _imag_max(i_max)

    , _max_iterations( max_iterations )

    , _real( 0 )
    , _imag( 0 )
    {
        // real_max =  1.0
        // real_min = -2.0

        // imag_max =  2.0
        // imag_min = -1.2

        // width  = 640
        // height = 480
       
        // x_step = ( 1 +   2 ) / 639 = 0.005
        // y_step = ( 2 + 1.2 ) / 479 = 0.007
       
        _x_step = ( _real_max - _real_min ) / ( width  - 1 );
        _y_step = ( _imag_max - _imag_min ) / ( height - 1 );
    }

    // scales to include aspect ration
    fractal_parameters( T r_min, T r_max, T i_min, int max_iterations, int width , int height )
    : _real_min(r_min)
    , _real_max(r_max)
    , _imag_min(i_min)

    , _max_iterations( max_iterations )

    , _real( 0 )
    , _imag( 0 )
    {
        _imag_max = _imag_min + ( _real_max - _real_min ) * height / width;
       
        _x_step = ( _real_max - _real_min ) / ( width  - 1 );
        _y_step = ( _imag_max - _imag_min ) / ( height - 1 );
    }


    // real_min represents x = 0 or the left boundary of the image
    void set_x( int x ) { _real = _real_min + x * _x_step; }
   
    // imag_max represents y = 0 or the upper boundary of the image
    void set_y( int y ) { _imag = _imag_max - y * _y_step; }

    coloring_info<> calc() const
    {
        coloring_info<> ci;

        std:: complex< T> c( _real, _imag );
        std:: complex< T> Z( c );

        int n;
        for( n = 0; n < _max_iterations; ++n )
        {
            if( std::abs( Z ) > 2.0 )
            {
                ci._inside = false;
                break;
            }

            Z = Z * Z + c;
        }
       
        ci._iterations = n;
        ci._max_iterations = _max_iterations;
       
        return ci;
    }


    T _real_min;
    T _real_max;

    T _imag_min;
    T _imag_max;

    T _x_step;
    T _y_step;

    int _max_iterations;

    T _real;
    T _imag;
};


// upper left is 0,0
void set_pixel( sf::Image & image , int x , int y , unsigned char r = 255, unsigned char g = 255, unsigned char b = 255 )
{
    image.setPixel( x, y, sf:: Color( r, g, b, 255));
}

void set_pixel( sf::Image & image , int x , int y , sf::Color c )
{
    image.setPixel( x, y, c );
}

void paint( sf::Image & image , sf::Color c )
{
    for( int y = 0; y < height; ++y )
    {
        for( int x = 0; x < width; ++x )
        {
            set_pixel( image, x, y, c.r, c.g, c.b );
        }
    }
}

void redraw( sf::Image & image , fractal_parameters <> mandel_brot )
{
    //paint(image, sf::Color::Black);


    sf::Color color;

    for( int y = 0; y < height; ++y )
    {
        mandel_brot.set_y( y );
       
        for( int x = 0; x < width; ++x )
        {
            mandel_brot.set_x( x );
           
            auto ci = mandel_brot.calc();

            set_pixel( image, x, y, ci.get_color() );
        }
    }
}


int main()
{
    sf::RenderWindow window(sf:: VideoMode(width, height), "Title");
    window.setFramerateLimit( 30 );

    sf::Texture texture;
    if( !texture.create(width, height))
    {
        exit(1);
    }

    sf::Image image;
    image.create(width, height);

   
    fractal_parameters<> mandel_brot_1( -2.0, 1.0, -1.2, 50, width, height );
    fractal_parameters<> mandel_brot_2( -2.0, 1.0, -1.2, 300, width, height );
   
    //max_iterations++;
    redraw( image, mandel_brot_1 );

    while (window.isOpen())
    {
        sf:: Event event;
        while (window.pollEvent(event))
        {
           
            switch(event.type)
            {
                case sf:: Event:: Closed:
                {
                    window.close();

                    break;
                }

                case sf:: Event:: KeyPressed:
                {
                    if( sf:: Keyboard::isKeyPressed( sf:: Keyboard:: Right ))
                    {
                        std::cout << "right" << std::endl;
                        std::cout << "begin" << std::endl;
                        redraw( image, mandel_brot_2 );
                        std::cout << "end" << std::endl;
                    }
                    else if( sf:: Keyboard::isKeyPressed( sf:: Keyboard:: Left ))
                    {
                        std::cout << "left" << std::endl;
                        std::cout << "begin" << std::endl;
                        redraw( image, mandel_brot_1 );
                        std::cout << "end" << std::endl;
                    }
              
                    break;
                }
            }
        }

        texture.update(image);
        sf:: Sprite sprite(texture);

        window.clear();
        window.draw(sprite);
        window.display();
    }



    return 0;
}

SDL

#include <boost/test/unit_test.hpp>
#include <boost/gil.hpp>


using namespace std;

unsigned int width  = 640;
unsigned int height = 480;

// format is ARGB8888 -> 4 bytes

// scanline size in bytes
unsigned int scanline = width  * sizeof( Uint32 );
unsigned int frame_size = scanline * height;

SDL_Renderer* ren = NULL;
SDL_Texture* tex = NULL;
Uint32* pixels = NULL;

Uint32 draw( Uint32 interval, void* param )
{
    using namespace boost;
    using namespace gil;

    argb8_view_t v = interleaved_view( width, height, (argb8_pixel_t*) pixels, scanline );
    fill_pixels( v, argb8_pixel_t( 255, 255, 0 , 0 ));

    //memset( pixels, 0, frame_size );

    SDL_UpdateTexture( tex, NULL, pixels, scanline );

    SDL_RenderClear( ren );
   
    SDL_RenderCopy( ren, tex, NULL, NULL );   

    SDL_RenderPresent( ren );

    return interval;
}

BOOST_AUTO_TEST_CASE( sdl_test )
{
    if( SDL_Init( SDL_INIT_EVERYTHING ) == -1 )
    {
        cout << SDL_GetError() << endl;
    }

    // Create Window

    SDL_Window* win = SDL_CreateWindow( "First"
                                      , 100
                                      , 100
                                      , width
                                      , height
                                      , SDL_WINDOW_SHOWN
                                      );

    if( win == NULL )
    {
        cout << SDL_GetError() << endl;
    }

    // Create Renderer

    ren = SDL_CreateRenderer( win
                            , -1 // SDL selects video driver
                            , SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC
                            );

    if( ren == NULL )
    {
        cout << SDL_GetError() << endl;
    }

    tex = SDL_CreateTexture( ren
                           , SDL_PIXELFORMAT_ARGB8888
                           , SDL_TEXTUREACCESS_STREAMING
                           , width
                           , height
                           );

    if( tex == NULL )
    {
        cout << SDL_GetError() << endl;
    }


    pixels = (Uint32*) malloc( frame_size );

    // Add Timer
    SDL_AddTimer( 100, draw, NULL );

    // Wait for user to quit
    bool quit = false;
    SDL_Event e;

    while( quit == false )
    {
        while( SDL_PollEvent( &e ))
        {
            if( e.type == SDL_WINDOWEVENT )
            {
                auto id = e.window.windowID;

                break;
            }

            if( e.type == SDL_QUIT )
            {
                quit = true;
                break;
            }

            if( e.type == SDL_KEYDOWN )
            {
                quit = true;
                break;
            }
        }
    }


    // Clean up
    SDL_DestroyTexture( tex );
    SDL_DestroyRenderer( ren );
    SDL_DestroyWindow( win );

    SDL_Quit();
}