Как загрузить изображение TIFF как граф в C ++ BOOST

Я хочу загрузить изображение TIFF (GEOTIFF с пикселями со значениями с плавающей запятой), как график в boost C ++ (я новичок в C ++). Моя цель — использовать двунаправленную Dijkstra из источника A в цель B, чтобы повысить производительность.

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

Но как конвертировать в граф Boost? Я читаю документацию и ищу примеры, но пока не смог ее реализовать.

Подобные вопросы и примеры, которые я нашел:

Алгоритм графа кратчайшего пути поможет Boost;

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

В настоящее время я использую библиотеку scikit-image и использую функцию skimage.graph.route_through_array для загрузки графа с массивом в python. Я использую GDAL, чтобы получить массив по загрузке изображения, как предложено @ustroetz в этом примере Вот:

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

Пример TIFF (был преобразован в PNG после загрузки):
Это пример изображения

3

Решение

Итак, прочитайте PNG:

Я обрезать пустую границу так как это не было последовательным в любом случае

Чтение и выборка изображения

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);

Далее, убедитесь, что мы знаем размеры и как обращаться к центральным пикселям для каждой ячейки:

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);

Построение Графа

Теперь давайте сделаем график. Для этой задачи грид-граф выглядит в порядке. Так должно быть w×h а не оборачиваться по краям (если надо, поменяй false в true):

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

Мы хотим прикрепить веса на каждом краю. Мы можем использовать старомодную карту внешних свойств, размер которой заранее:

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));

В качестве альтернативы мы можем использовать динамически размещаемую и растущую карту свойств:

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

В качестве бонуса, вот эквивалентный подход с использованием ассоциативной карты свойств:

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

Каждый из них совместим с подключением, и выбор за вами.

Заполнение графика

Мы просто перебираем все ребра, устанавливая стоимость из разницы цветов:

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);
}

Заметка Рассмотрите возможность нормализации веса, например [0.0, 1.0) используя фактическую битовую глубину исходного изображения

Давайте создадим проверочный TIF, чтобы мы могли увидеть, где были взяты образцы на изображении:

{
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 заканчивается как (обратите внимание на центральный пиксель для каждой ячейки):

введите описание изображения здесь

Бонус: визуализируйте график сетки

Давайте напишем это в файл Graphviz:

{
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);
}

Это вычисляет цвет по тому же образцу пикселя и использует некоторую магию, специфичную для Graphviz, для записи в файл:

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);
}

Что приводит к grid.dot файл как это.

Далее давайте вернемся с помощью neato:

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

И результат:введите описание изображения здесь

#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);
}
6

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]