好的,请阅读 PNG:
I've 裁剪空白边框 http://stackoverflow-sehe.s3.amazonaws.com/53705f8e-f64c-4719-b799-5f0e15576f9e/graph.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
to 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);
}
Note考虑将体重标准化为例如[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));
}
The 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);
}
这导致a grid.dot file像这样 http://stackoverflow-sehe.s3.amazonaws.com/910b323a-b05a-4ccd-aeab-8061d5bc2a49/grid.dot.
接下来我们来布局using neato https://linux.die.net/man/1/neato:
neato -T png grid.dot -o grid.png
And the result is:
完整代码清单
#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);
}