How to load TIFF image as graph in C ++ BOOST

I want to load a tiff image (GEOTIFF with pixels with float values), for example in boost in C ++ (I'm new to C ++). My goal is to use bi-directional Dijkstra from source A to target B to improve performance.

Boost:GIL load tiif images:
std::string filename( "raster_clip.tif" );
rgb8_image_t img;
read_image( filename, img, tiff_tag() ); 

      

But how do you convert to a Boost graph? I am reading the documentation and looking for examples, but I haven't been able to implement it yet.

Similar questions and examples I found:

The shortest path algorithm diagram helps to improve efficiency ;

http://www.geeksforgeeks.org/shortest-path-for-directed-acyclic-graphs/

I am currently using the scikit image library and using the skimage.graph.route_through_array function to load a graph with an array in python. I am using GDAL to get an array from boot image as suggested by @ustroetz in this example here :

    raster = gdal.Open("raster.tiff")
    band = raster.GetRasterBand(1)
    array = band.ReadAsArray()

      

TIFF example (converted to PNG after upload): This is an example image

+3


source to share


1 answer


So read the PNG:

I trimmed the border of the space since it was not consistent anyway

Read and fetch Image

using Img = boost::gil::rgb8_image_t; // gray8_image_t;
using Px  = Img::value_type;

Img img;
//boost::gil::png_read_image("graph.png", img);
boost::gil::png_read_and_convert_image("graph.png", img);
auto vw = view(img);

      

Next, make sure we know the dimensions and how to address the center pixels for each cell:

double constexpr cell_w = 30.409;
double constexpr cell_h = 30.375;

auto pixel_sample = [=](boost::array<size_t, 2> xy) -> auto& {
    return vw((xy[0]+.5)*cell_w, (xy[1]+.5)*cell_h);
};

auto const w= static_cast<size_t>(img.dimensions()[0] / cell_w);
auto const h= static_cast<size_t>(img.dimensions()[1] / cell_h);

      

Plotting

Now let's make a graph. For this task, the graph-graph looks in order. It should be w×h

and not wrap around the edges (change false

to if needed true

):

using Graph = boost::grid_graph<2>;
Graph graph({{w,h}}, false);

      

We want to attach weights to each edge. We can either use the old outer property map, which is sized in front:

std::vector<double> weight_v(num_edges(graph));
auto weights = boost::make_safe_iterator_property_map(weight_v.begin(), weight_v.size(), get(boost::edge_index, graph));

      

Alternatively, we can use a dynamically allocating and growing property map:

auto weights = boost::make_vector_property_map<float>(get(boost::edge_index, graph));

      

As a bonus, here's an equivalent approach using an associative map-property:

std::map<Graph::edge_descriptor, double> weight_m;
auto weights = boost::make_assoc_property_map(weight_m);

      

Each one is recording compatible, and the choice is yours.

Filling in the schedule

We simply iterate over all the edges, setting the color difference value:



BGL_FORALL_EDGES(e, graph, Graph) {
    auto& from = pixel_sample(e.first);
    auto& to   = pixel_sample(e.second);

    // compare RED channels only
    auto cost = std::abs(from[0] - to[0]);
    put(weights, e, cost);
}

      

Note . Consider weight normalization, for example. [0.0, 1.0)

using the actual bit depth of the original image

Create a control TIF so we can see where the samples were taken in the image:

{
    BGL_FORALL_VERTICES(v, graph, Graph) {
        pixel_sample(v) = Px(255, 0, 123); // mark the center pixels so we can verify the sampling
    }

    boost::gil::tiff_write_view("/tmp/verification.tif", const_view(img));
}

      

verification.tif

ends (note the center pixel for each cell):

enter image description here

Bonus: Rendering the Grid Graph

Let's write it to a Graphviz file:

{
    auto calc_color = [&](size_t v) {
        std::ostringstream oss;
        oss << std::hex << std::noshowbase << std::setfill('0');

        auto const& from = pixel_sample(vertex(v, graph));
        oss << "#" << std::setw(2) << static_cast<int>(from[0])
            << std::setw(2) << static_cast<int>(from[1])
            << std::setw(2) << static_cast<int>(from[2]);

        return oss.str();
    };

    write_dot_file(graph, weights, calc_color);
}

      

This computes the color from the same pixel sample and uses some Graphviz-specific magic to write to a file:

template <typename Graph, typename Weights, typename ColorFunction>
void write_dot_file(Graph const& graph, Weights const& weights, ColorFunction calc_color) {
    boost::dynamic_properties dp;
    dp.property("node_id",   get(boost::vertex_index, graph));
    dp.property("fillcolor", boost::make_transform_value_property_map(calc_color, get(boost::vertex_index, graph)));
    dp.property("style", boost::make_static_property_map<typename Graph::vertex_descriptor>(std::string("filled")));
    std::ofstream ofs("grid.dot");

    auto vpw = boost::dynamic_vertex_properties_writer { dp, "node_id" };
    auto epw = boost::make_label_writer(weights);
    auto gpw = boost::make_graph_attributes_writer(
            std::map<std::string, std::string> { },
            std::map<std::string, std::string> { {"shape", "rect"} },
            std::map<std::string, std::string> { }
        );

    boost::write_graphviz(ofs, graph, vpw, epw, gpw);
}

      

The result is a file like this grid.dot

.

Next, give the layout withneato

:

neato -T png grid.dot -o grid.png

      

And the result: enter image description here

FULL CODE LISTING

#include <boost/gil/extension/io/png_dynamic_io.hpp>
#include <boost/gil/extension/io/tiff_dynamic_io.hpp>
#include <boost/graph/grid_graph.hpp>
#include <boost/graph/iteration_macros.hpp>
#include <boost/graph/graphviz.hpp>
#include <iostream>

template <typename Graph, typename Weights, typename ColorFunction>
void write_dot_file(Graph const& graph, Weights const& weights, ColorFunction);

int main() try {
    using Img = boost::gil::rgb8_image_t; // gray8_image_t;
    using Px  = Img::value_type;

    Img img;
    //boost::gil::png_read_image("/home/sehe/graph.png", img);
    boost::gil::png_read_and_convert_image("/home/sehe/graph.png", img);
    auto vw = view(img);

    double constexpr cell_w = 30.409;
    double constexpr cell_h = 30.375;

    auto pixel_sample = [=](boost::array<size_t, 2> xy) -> auto& {
        return vw((xy[0]+.5)*cell_w, (xy[1]+.5)*cell_h);
    };

    auto const w= static_cast<size_t>(img.dimensions()[0] / cell_w);
    auto const h= static_cast<size_t>(img.dimensions()[1] / cell_h);

    using Graph = boost::grid_graph<2>;
    Graph graph({{w,h}}, false);

#if 0 // dynamic weight map
    auto weights = boost::make_vector_property_map<float>(get(boost::edge_index, graph));
    std::cout << "Edges: " << (weights.storage_end() - weights.storage_begin()) << "\n";

#elif 1 // fixed vector weight map
    std::vector<double> weight_v(num_edges(graph));
    auto weights = boost::make_safe_iterator_property_map(weight_v.begin(), weight_v.size(), get(boost::edge_index, graph));

#else // associative weight map
    std::map<Graph::edge_descriptor, double> weight_m;
    auto weights = boost::make_assoc_property_map(weight_m);
#endif

    auto debug_vertex = [] (auto& v) -> auto& { return std::cout << "{" << v[0] << "," << v[1] << "}"; };
    auto debug_edge   = [&](auto& e) -> auto& { debug_vertex(e.first) << " -> "; return debug_vertex(e.second); };

    BGL_FORALL_EDGES(e, graph, Graph) {
        //debug_edge(e) << "\n";
        auto& from = pixel_sample(e.first);
        auto& to   = pixel_sample(e.second);

        // compare RED channels only
        auto cost = std::abs(from[0] - to[0]);
        put(weights, e, cost);
    }

    {
        auto calc_color = [&](size_t v) {
            std::ostringstream oss;
            oss << std::hex << std::noshowbase << std::setfill('0');

            auto const& from = pixel_sample(vertex(v, graph));
            oss << "#" << std::setw(2) << static_cast<int>(from[0])
                << std::setw(2) << static_cast<int>(from[1])
                << std::setw(2) << static_cast<int>(from[2]);

            return oss.str();
        };

        write_dot_file(graph, weights, calc_color);
    }

    {
        BGL_FORALL_VERTICES(v, graph, Graph) {
            pixel_sample(v) = Px(255, 0, 123); // mark the center pixels so we can verify the sampling
        }

        boost::gil::tiff_write_view("/tmp/verification.tif", const_view(img));
    }

} catch(std::exception const& e) {
    std::cout << "Exception occured: " << e.what() << "\n";
}

template <typename Graph, typename Weights, typename ColorFunction>
void write_dot_file(Graph const& graph, Weights const& weights, ColorFunction calc_color) {
    boost::dynamic_properties dp;
    dp.property("node_id",   get(boost::vertex_index, graph));
    dp.property("fillcolor", boost::make_transform_value_property_map(calc_color, get(boost::vertex_index, graph)));
    dp.property("style", boost::make_static_property_map<typename Graph::vertex_descriptor>(std::string("filled")));
    std::ofstream ofs("grid.dot");

    auto vpw = boost::dynamic_vertex_properties_writer { dp, "node_id" };
    auto epw = boost::make_label_writer(weights);
    auto gpw = boost::make_graph_attributes_writer(
            std::map<std::string, std::string> { },
            std::map<std::string, std::string> { {"shape", "rect"} },
            std::map<std::string, std::string> { }
        );

    boost::write_graphviz(ofs, graph, vpw, epw, gpw);
}

      

+5


source







All Articles