Search code examples
c++boostjpeglibjpegboost-gil

Writing a jpeg file with boost::gil


I am trying to read a jpeg file, do some simple transformation and write it out using boost::gil library. Right now I am stuck as even reading out a file, extracting non-white points (it contains only red and white points) and writing them into another file gives completely unexpected results. I want the pictures to be the same, but this is what happens:

This is the input picture. Original Picture

And This is the output picture: enter image description here

This is how I read the non-white coordinates:

vector<Point2D> points;
//load points from file
rgb8_image_t img;
jpeg_read_image(in_file_path, img );

assert( 2 == img._view.num_dimensions );
const auto dimensions = img._view.dimensions();
assert( 2 == dimensions.num_dimensions );

row_width = dimensions.x;
col_height = dimensions.y;

size_t white_cnt = 0;
size_t non_white_cnt = 0;
for ( uint32_t x = 0; x < dimensions.x; ++x ){
    for ( uint32_t y = 0; y < dimensions.y; ++y ){
        const rgb8_pixel_t& pix = const_view(img)( rgb8_image_t::point_t(x,y) );
        if ( !is_white(pix) ){
            points.push_back( Point2D {x,y} );
        }
    }
}

And this is how write the same points out

rgb8_image_t img(row_width,col_heigth);

rgb8_pixel_t white(255,255,255);
rgb8_pixel_t red(255,0,0);
rgb8_pixel_t blue(0,0,255);

fill_pixels( view(img), white );

cout << "printing " << points.size() << "points" << std::endl;
for ( Point2D p : points ){
        const auto x = p[0];
        const auto y = p[1];
        cout << x <<"," << y << endl;
        view(img)( rgb8_image_t::point_t( x,y ) ) = red;
}

jpeg_write_view( out_file_path, const_view(img));

For completeness, this is the is_white check:

bool is_white( const boost::gil::rgb8_pixel_t& pix ){
    const auto r = boost::gil::at_c<0>(pix);
    const auto g = boost::gil::at_c<1>(pix);
    const auto b = boost::gil::at_c<2>(pix);
    constexpr auto MAX_VAL = std::numeric_limits<decltype(r)>::max();
    return ( MAX_VAL == r ) && ( MAX_VAL == g ) && ( MAX_VAL == b );
}

Any Idea what am I doing wrong?

Thank you


Solution

  • Due to JPEG artifacts in your source data, you have many non-white pixels that aren't actually red:

    enter image description here

    You need to compare colors with a threshold. I decided that for your current data it would be more effective to detect non-whiteness in a grayscale view, so the whole program effectively becomes:

    gil::transform_pixels(
            const_view(input),
            view(output),
            [](auto& pix) { return pix<200? blue : white; });
    

    Here's a modified take:

    #include <boost/gil.hpp>
    #include <boost/gil/color_convert.hpp>
    #include <boost/gil/extension/io/jpeg.hpp>
    #include <boost/gil/io/io.hpp>
    #include <iostream>
    namespace gil = boost::gil;
    
    int main() {
        static const gil::rgb8_pixel_t
            white(255, 255, 255),
            red(255, 0, 0),
            blue(0, 0, 255);
    
        // load points from file
        gil::gray8_image_t input;
        read_and_convert_image("input.jpg", input,
                gil::image_read_settings<gil::jpeg_tag>{});
    
        auto dimensions = input._view.dimensions();
        gil::rgb8_image_t output(dimensions.x, dimensions.y, white);
    
        gil::transform_pixels(
                const_view(input),
                view(output),
                [](auto& pix) { return pix<200? blue : white; });
    
        write_view("output.jpg", const_view(output), gil::jpeg_tag());
    }
    

    Note

    • there are several simplifications
    • I'm using the new IO interface
    • I picked a random threshold value in the gray channel (200) to detect whiteness, which was my first threshold and it seems to work out well enough, but you might want to tune it for your inputs
    • I changed the output pixel color to blue to "prove" that I'm not just showing the input image :)

    Here's input and output alternating:

    enter image description here