Search code examples
c++boostboost-gil

How to set a pixel using GIL iterators


Trying to learn a bit of C++ and I am very new at it. My learning project is to generate images using various algorythms (like fractals). I am looking at the boost GIL library, since boost seems to be the most common and established C++ library around. So, I am trying to create an image in memory, iterate over pixels and set the RGB value based on some formula. My current code looks like this

int main() {
    rgb8_image_t img(IMAGE_W, IMAGE_H);
    auto b = view(img).begin();
    while (b != view(img).end()) {
        /* set the pixel value here */
        b++;
    }
    write_view("image.png", view(img), png_tag());
    return 0;
}

The iteration seems to work, but I cannot seem to figure out from the GIL docs, how to actually set the pixel in this loop. I could do a nested for loop and just set pixels using x and y coordinates, but I kinda want to use iterators since it seems neater and maybe I can later refactor this to use transform(). How do I proceed from here? How do I set a pixel to some RGB value?


Solution

  • Simply, create value of type of rgb8_pixel_t with desired channel values and assign it to the pixel pointed by iterator.

    For a simple example, this will fill your image with solid orange:

    #include <boost/gil.hpp>
    #include <boost/gil/extension/io/png.hpp>
    namespace gil = boost::gil;
    
    int main()
    {
        gil::rgb8_image_t img(100, 100);
        auto v = gil::view(img);
        auto b = v.begin();
        while (b != v.end())
        {
            *b = gil::rgb8_pixel_t{255, 128, 0};
            b++;
        }
        gil::write_view("image.png", gil::view(img), gil::png_tag());
    }
    

    For a more complex example, here is how to use a custom generator of pixel channel values:

    #include <boost/gil.hpp>
    #include <boost/gil/extension/io/png.hpp>
    #include <random>
    namespace gil = boost::gil;
    
    template <typename T>
    struct random_value
    {
        static_assert(std::is_integral<T>::value, "T must be integral type");
        static constexpr auto range_min = std::numeric_limits<T>::min();
        static constexpr auto range_max = std::numeric_limits<T>::max();
    
        random_value() : rng_(rd_()), uid_(range_min, range_max) {}
    
        T operator()()
        {
            auto value = uid_(rng_);
            return static_cast<T>(value);
        }
    
        std::random_device rd_;
        std::mt19937 rng_;
        std::uniform_int_distribution<typename gil::promote_integral<T>::type> uid_;
    };
    
    int main()
    {
        random_value<channel_type<gil::rgb8_pixel_t>::type> make_channel_value;
    
        gil::rgb8_image_t img(100, 100);
        auto v = gil::view(img);
        auto b = v.begin();
        while (b != v.end())
        {
            // generate random value for each channel of RGB image separately
            gil::static_generate(*b, [&make_channel_value]() { return make_channel_value(); });
            b++;
        }
        gil::write_view("image.png", gil::view(img), gil::png_tag());
    }
    

    UPDATE: The static_generate is one of GIL algorithms to operate on color bases (e.g. pixels). The struct random_value is a functor class because it encapsulates data elements of the random number generator. I simply copied it form GIL's test/core/image/test_fixture.hpp, but it does not have to be a class. It can be anything callable, usable as a functor. I have updated the code snippets with gil:: namespace qualification to make it clearer where things come from.