Search code examples
c++imageboostboost-gil

Using Boost.GIL to convert an image into "raw" bytes


Goal

I'm trying to move towards Boost GIL to replace some similar functionality I've implemented that is reaching the end of its maintainable life.

I have existing code that works with 24 BPP, 8bit RGB images using uint8_t*. I can't change that as the same interface is used to expose images from different places (e.g. OpenGL buffers) and there's already quite a lot of code.

Therefore I'm trying to use GIL in small steps, starting by reading a file and copying the pixels byte by byte into a std::vector<uint8_t> which I can use to manage the storage, but still get a uint8_t* by using &vector[0].

This can be dropped in transparently behind the existing interfaces until it's at a point where refactoring makes sense.

What I've tried

I thought this should be a simple case of using copy_pixels() with two appropriately constructed views.

I've put together a minimal, complete example that illustrates the sum of what I've manage to achieve by looking over the documents and trying things out:

#include <boost/gil/rgb.hpp>
#include <boost/gil/extension/io/png_dynamic_io.hpp>
#include <stdint.h>
#include <vector>

int main() {
  std::vector<uint8_t> storage;
  {
    using namespace boost::gil;
    rgb8_image_t img;
    png_read_image("test.png", img);

    // what should replace 3 here to be more "generic"?
    storage.resize(img.width()*img.height()*3);

    // doesn't work, the type of the images aren't compatible.
    copy_pixels(const_view(img), 
                interleaved_view(img.width(), img.height(), &storage[0], 3*img.width()));
  }
}

Where I'm stuck

This doesn't compile:

error: cannot convert ‘const boost::gil::pixel<unsigned char, boost::gil::layout<boost::mpl::vector3<boost::gil::red_t, boost::gil::green_t, boost::gil::blue_t> > >’ to ‘unsigned char’ in assignment

Which is fairly self-explanatory - an RGB pixel can't be converted to a single unsigned char automatically. I thought I'd try using copy_and_convert_pixels() to fix this, but I can't see a way around the 3:1 (i.e. I have 3 unsigned chars in the output for every pixel in the source image) problem with these conversions. Conversion seems to be more aimed at colourspace conversions (e.g. RGB->HSV) or packing changes.


Solution

  • I would just push_back each color of the rgb8_pixel_t individually:

    struct PixelInserter{
            std::vector<uint8_t>* storage;
            PixelInserter(std::vector<uint8_t>* s) : storage(s) {}
            void operator()(boost::gil::rgb8_pixel_t p) const {
                    storage->push_back(boost::gil::at_c<0>(p));
                    storage->push_back(boost::gil::at_c<1>(p));
                    storage->push_back(boost::gil::at_c<2>(p));
            }
    };
    
    int main() {
      std::vector<uint8_t> storage;
      {
        using namespace boost::gil;
        rgb8_image_t img;
        png_read_image("test.png", img);
        storage.reserve(img.width() * img.height() * num_channels<rgb8_image_t>());
        for_each_pixel(const_view(img), PixelInserter(&storage));
      }
    ...
    }
    

    ...but I'm not an expert on GIL either.