如何在 C++ BOOST 中像图形一样加载 TIFF 图像

2024-05-18

我想要加载一个 tiff 图像(带有带有浮点值的像素的 GEOTIFF),例如 boost C++ 中的图形(我是 C++ 的新手)。我的目标是使用从源 A 到目标 B 的双向 Dijkstra 来获得更高的性能。

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

但如何转换为Boost图呢?我正在阅读文档并寻找示例,但我尚未能够实现它。

我发现的类似问题和例子:

最短路径图算法助力Boost https://stackoverflow.com/questions/21144105/shortest-path-graph-algorithm-help-boost;

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

我目前正在使用 scikit-image 库,并使用 skimage.graph.route_through_array 函数在 python 中使用数组加载图形。我使用 GDAL 通过加载图像来获取数组,如本示例中 @ustroetz 所建议的Here https://gis.stackexchange.com/questions/28583/gdal-perform-simple-least-cost-path-analysis:

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

Example of TIFF (was converted to PNG after upload) is: This is a example of image


好的,请阅读 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:enter image description here

完整代码清单

#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);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 C++ BOOST 中像图形一样加载 TIFF 图像 的相关文章

  • ROWNUM 的 OracleType 是什么

    我试图参数化所有现有的 sql 但以下代码给了我一个问题 command CommandText String Format SELECT FROM 0 WHERE ROWNUM lt maxRecords command CommandT
  • C++:无法使用scoped_allocator_adaptor传播polymorphic_allocator

    我有一个vector
  • 嵌入式系统中的malloc [重复]

    这个问题在这里已经有答案了 我正在使用嵌入式系统 该应用程序在 AT91SAMxxxx 和 cortex m3 lpc17xxx 上运行 我正在研究动态内存分配 因为它会极大地改变应用程序的外观 并给我更多的力量 我认为我唯一真正的路线是为
  • SSH 主机密钥指纹与模式 C# WinSCP 不匹配

    我尝试通过 WinSCP 使用 C 连接到 FTPS 服务器 但收到此错误 SSH 主机密钥指纹 与模式不匹配 经过大量研究 我相信这与密钥的长度有关 当使用 服务器和协议信息 下的界面进行连接时 我从 WinSCP 获得的密钥是xx xx
  • Cygwin 下使用 CMake 编译库

    我一直在尝试使用 CMake 来编译 TinyXML 作为一种迷你项目 尝试学习 CMake 作为补充 我试图将其编译成动态库并自行安装 以便它可以工作 到目前为止 我已经设法编译和安装它 但它编译成 dll 和 dll a 让它工作的唯一
  • 使用 Microsoft Graph API 订阅 Outlook 推送通知时出现 400 错误请求错误

    我正在尝试使用 Microsoft Graph API 创建订阅以通过推送通知获取 Outlook 电子邮件 mentions 我在用本文档 https learn microsoft com en us graph api subscri
  • c 中的错误:声明隐藏了全局范围内的变量

    当我尝试编译以下代码时 我收到此错误消息 错误 声明隐藏了全局范围内的变量 无效迭代器 节点 根 我不明白我到底在哪里隐藏或隐藏了之前声明的全局变量 我怎样才能解决这个问题 typedef node typedef struct node
  • C# 用数组封送结构体

    假设我有一个类似于 public struct MyStruct public float a 我想用一些自定义数组大小实例化一个这样的结构 在本例中假设为 2 然后我将其封送到字节数组中 MyStruct s new MyStruct s
  • c# Asp.NET MVC 使用FileStreamResult下载excel文件

    我需要构建一个方法 它将接收模型 从中构建excel 构建和接收部分完成没有问题 然后使用内存流导出 让用户下载它 不将其保存在服务器上 我是 ASP NET 和 MVC 的新手 所以我找到了指南并将其构建为教程项目 public File
  • 按字典顺序对整数数组进行排序 C++

    我想按字典顺序对一个大整数数组 例如 100 万个元素 进行排序 Example input 100 21 22 99 1 927 sorted 1 100 21 22 927 99 我用最简单的方法做到了 将所有数字转换为字符串 非常昂贵
  • .Net Core / 控制台应用程序 / 配置 / XML

    我第一次尝试使用新的 ConfigurationBuilder 和选项模式进入 Net Core 库 这里有很多很好的例子 https docs asp net en latest fundamentals configuration ht
  • Windows 窗体不会在调试模式下显示

    我最近升级到 VS 2012 我有一组在 VS 2010 中编码的 UI 测试 我试图在 VS 2012 中启动它们 我有一个 Windows 窗体 在开始时显示使用 AssemblyInitialize 属性运行测试 我使用此表单允许用户
  • 使用 LINQ 查找列表中特定类型的第一个元素

    使用 LINQ 和 C 在元素列表中查找特定类型的第一个项目的最短表示法是什么 var first yourCollection OfType
  • 网络参考共享类

    我用 Java 编写了一些 SOAP Web 服务 在 JBoss 5 1 上运行 其中两个共享一个类 AddressTO Web 服务在我的 ApplycationServer 上正确部署 一切都很顺利 直到我尝试在我的 C 客户端中使用
  • 什么是 C 语言的高效工作流程? - Makefile + bash脚本

    我正在开发我的第一个项目 该项目将跨越多个 C 文件 对于我的前几个练习程序 我只是在中编写了我的代码main c并使用编译gcc main c o main 当我学习时 这对我有用 现在 我正在独自开展一个更大的项目 我想继续自己进行编译
  • ListDictionary 类是否有通用替代方案?

    我正在查看一些示例代码 其中他们使用了ListDictionary对象来存储少量数据 大约 5 10 个对象左右 但这个数字可能会随着时间的推移而改变 我使用此类的唯一问题是 与我所做的其他所有事情不同 它不是通用的 这意味着 如果我在这里
  • 在 ASP.NET 中将事件冒泡为父级

    我已经说过 ASP NET 中的层次结构 page user control 1 user control 2 control 3 我想要做的是 当控件 3 它可以是任何类型的控件 我一般都想这样做 让用户用它做一些触发回发的事情时 它会向
  • 将变量分配给另一个变量,并将一个变量的更改反映到另一个变量中

    是否可以将一个变量分配给另一个变量 并且当您更改第二个变量时 更改会瀑布式下降到第一个变量 像这样 int a 0 int b a b 1 现在 b 和 a 都 1 我问这个问题的原因是因为我有 4 个要跟踪的对象 并且我使用名为 curr
  • 如何将字符串“07:35”(HH:MM) 转换为 TimeSpan

    我想知道是否有办法将 24 小时时间格式的字符串转换为 TimeSpan 现在我有一种 旧时尚风格 string stringTime 07 35 string values stringTime Split TimeSpan ts new
  • 为什么 strtok 会导致分段错误?

    为什么下面的代码给出了Seg 最后一行有问题吗 char m ReadName printf nRead String s n m Writes OK char token token strtok m 如前所述 读取字符串打印没有问题 但

随机推荐