VisionFlow 入门教程#

我们已经在 在开发环境中引入VisionFlow 文档中解释了如何将 VisionFlow 添加为 您项目的依赖项。现在,我们将通过一个简单的示例快速演示 VisionFlow 中的主要功能及接口的使用。

这个示例将包括从创建工程、添加工具、添加数据和标注,训练模型,到部署模型的完整工作流程。如果您对如 何通过 VisionFlow 接口创建工程和训练模型不感兴趣,只想知道如何部署已导出为模型文件的训练好的模型, 那么您可以从 加载导出的模型 部分开始阅读。

初始化VisionFlow#

VisionFlow 库有一些在运行时需要指定并且必须在调用任何其他接口之前设置的全局配置,包括:

  1. 日志输出设置:通过设置日志输出选项,您可以自定义 VisionFlow 的日志输出方式,例如指定文件路径、 回调函数、标准输出/终端或 MSVC 调试器。

  2. 语言偏好:VisionFlow 支持多种语言。默认情况下,在初始化过程中,语言偏好会自动设置为与您的操作系统 语言匹配。如果您希望使用不同的语言,可以通过初始化参数来指定。

  3. 授权设置:VisionFlow 中的部分功能需要商业授权(加密狗),一般情况下,VisionFlow会自动从你的执行 环境中自动查找授权。但部分情况下,你可能希望自行指定使用的授权设备的ID,授权设备ID也可以在初始化时 指定。

  4. 产品标识设置:详情请移步 产品标识

  5. 自动设置训练集测试集的高低阈值大小:用于根据旧视图的数据集情况自动给新生成的视图添加到训练集、测试集中。 详情请移步 visionflow::InitOptions (默认为0.1和0.85,一般情况下不需要修改此项配置)。

以下是 VisionFlow 初始化的示例:

#include <iostream>
#include "visionflow/visionflow.hpp"

namespace vflow = visionflow;

void my_logger_output(int log_level, const char *content, size_t len) {
    if (log_level > 2) {
        std::cout << std::string(content, len) << std::endl;
    }
}

int main(int /*argc*/, char ** /*argv*/) try {

    vflow::InitOptions opts;

    // Set the log output file.
    opts.logger.file_sink = "visionflow.log";
    // Set whether to output the logs to the standard output terminal.
    opts.logger.stdout_sink = true;
    // You can customize the handling of VisionFlow's log output by setting a log output callback function.
    // opts.logger.func_sink = my_logger_output;

    // Set the language to Chinese.
    opts.language = "zh_CN";

    // opts.license.license_id = "your license device id";

    // opts.product_mark = "YourProductName";
    // opts.compatible_product_marks = {"AIDI", "OtherProduct"};

    vflow::initialize(opts);

    return 0;
} catch (const std::exception &ex) {
    std::cerr << "Unexpected Escaped Exception: " << ex.what();
    return -1;
}
To be completed
using System;
using System.Runtime.InteropServices;

public class Program
{
    public static void Main()
    {
        try
        {
            visionflow.InitOptions opts = new visionflow.InitOptions();

            // Set the log output file.
            opts.logger.file_sink = "visionflow.log";
            // Set whether to output the logs to the standard output terminal.
            opts.logger.stdout_sink = true;

            // Set the language to Chinese.
            opts.language = "zh_CN";

            // opts.license.license_id = "your license device id";

            // opts.product_mark = "YourProductName";
            // opts.compatible_product_marks = new std.VectorString{"AIDI", "OtherProduct"};

            visionflow_global.initialize(opts);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
    }
}

创建工程#

Project 是 VisionFlow 中管理数据和处理工作流的基本单位。在继续进行任何后续步骤之前, 我们必须首先创建一个项目:

vflow::ProjectDescriptor desc;

desc.workspace_token = "D:/the/path/to/workspace";
desc.project_name = "my_first_project";

auto out_desc = vflow::Project::Create(desc);

std::cout << "My first VisionFlow project created at: "
          << out_desc.created_time << std::endl;
std::cout << "The VisionFlow version which the project created by: "
          << out_desc.sdk_version << std::endl;

// And then you can open the project with the desc or the output_desc.
auto project = vflow::Project::Open(desc);

std::cout << "My first VisionFlow project name is: "
          << project->descriptor().project_name << std::endl;
To be completed
To be completed

添加工具#

VisionFlow 提供了不同的算法工具,您可以自由地组合和连接这些工具以完成不同的处理流程。 您可以在 工具及详细流程图 中找到 VisionFlow 提供的所有 工具的功能说明及每一个工具的详细内部图结构。

您也可以在 节点名常量 中查看工具中所有节点的名称。 节点名称作为字符串常量存放在:visionflow命名空间下,以工具类型为结构体标识符的结构体中。 如果图结构中的节点名包含’.’,则常量名会将’.’替换为’_’,否则二者完全相同。

Note

例如:分割工具的预测结果节点名为 visionflow::Segmentation::pred;

分割工具的训练参数节点名为 visionflow::Segmentation::trainer_args;

在这个示例中,我们将添加一个 Input 工具和一个 Segmentation 工具,并将 Segmentation 工具连接到 Input 工具的输出:

std::string input_id = project->add_tool("Input");
std::string segmentation_id = project->add_tool("Segmentation");

bool connected = project->auto_connect(input_id, segmentation_id);
if (!connected) {
    std::cerr << "auto_connect failed, Please use Project::connect instead."
              << std::endl;
}
To be completed
To be completed

导入图片#

在 VisionFlow 中,我们使用 SampleSet 来管理项目内的数据。所有对 Sample 数据的管理都通过 SampleSetPropertySet 进行。一个项目可以有多个 SampleSet ,通常包括一个用于训练和验证模型的主数据集。不过,您也可以根据需要创建额外的 SampleSet 。将图像数据添加到项目中意味着将图像数据添加到项目内的一个数据集。以下是一个 从工程中获取 SampleSetPropertySet 的示例:

#include "visionflow/node_names.hpp" // 引入节点名常量头文件

auto sample_set = project->main_sample_set();
auto input_image_set = sample_set.property_set({input_id, vflow::Input::image});

// Or you can get the read only sample set and read only property set:
// auto ro_sample_set = project->readonly_main_sample_set();
// auto ro_input_image_set = sample_set.readonly_property_set({input_id, vflow::Input::image});
To be completed
To be completed

导入图像到项目中有几种不同的方法。如果您想要将一系列图像文件导入到项目中,可以使用辅助类型 visionflow::helper::InputHelper 来完成这个任务。以下是一个示例:

const auto old_sample_num = sample_set.size();

vflow::helper::InputHelper input_helper(project.get());

for (int i = 0; i < 100; ++i) {
    input_helper.add_image("D:/path/to/image/" + std::to_string(i) + ".png");
}
// Import and commit the images.
auto receipts = input_helper.commit();

// Some image may be improted failed, you can get the reason from the receipts:
size_t success_count = 0;
for (const auto &receipt : receipts) {
    if (receipt.is_success) {
        success_count ++;
    }else {
        std::cout << "Failed to import image " << receipt.image_files[0]
                  << " as: " << receipt.error_message << std::endl;
    }
}

// And then you can get the sample from the sample set:
assert(sample_set.size() - old_sample_num == success_count);
To be completed
To be completed

在某些情况下,您的图像可能不存在于文件系统中,而是已经加载到内存中。在这种情况下,您可以使用函数 visionflow::helper::add_image_to_sample() 快速将图像添加到一个样本集中:

// We have a image loaded into memory:
auto image = vflow::Image::FromFile("D:/path/to/image/0.png");

// Create a sample template from the sample set:
auto sample = sample_set.create_empty_sample();
// Add the the image into sample:
vflow::helper::add_image_to_sample(sample, image, input_id, true, 512);
// Add the sample to sample set:
sample_set.add(sample);
To be completed
To be completed

Note

函数 visionflow::helper::add_image_to_sample() 的接口文档提供了关于该函数 详细的功能说明,这可以帮助您深入理解其更多详细使用方法和运行原理。如果您想更深入地了解 VisionFlow 的数据管理机制,强烈建议仔细阅读该文档。

Note

通过 visionflow::helper::add_image_to_sample() 初始化的sample,若需将其添 加到样本集,则thumbnail_long_side参数不能为0。

添加标注#

在介绍如何向数据集添加注释之前,让我们简要解释一下如何通过 :term:PropertySet` 中读取和写入数据。 PropertySet 是一个可以被迭代访问的数据容器。每个属性集对应处理流程中的一个数据节点, 并包含数据集中所有样本在该节点上的数据。以我们之前添加的图像属性集为例,我们可以按如下方式访问其 中的数据(您可以参考 visionflow::data::PropertySet 的文档,了解与 PropertySet 相关的更多接口信息):

// 获取属性集.
auto image_set = sample_set.property_set({input_id, vflow::Input::image});
// 遍历属性集中的数据.
for (const auto &[sample_id, image_prop] : image_set) {
    if (image_prop) { // 如果某个样本在此数据节点上的数据不存在,则会返回空指针.
        image_prop->as<vflow::props::Image>().image().show(10);
    } else {
        auto modify_image = vflow::Image::FromFile("D:/path/to/image/0.png");
        vflow::props::Image new_prop(modify_image);
        // 更新属性集中的数据.
        image_set.update(sample_id, new_prop);
    }
}
To be completed
To be completed

通过这种方式,我们可以将在图像导入过程中生成的所有视图添加到训练集中(这对于后续的训练是必要的, 因为只有添加到训练集的视图才会参与训练过程):

auto views_set = sample_set.property_set({input_id, vflow::Input::views});

for (auto [sample_id, views_prop] : views_set) {
    if (views_prop) {
        for (auto &[view_id, view] : views_prop->as<vflow::props::ViewList>()) {
            view.set_split_tag(vflow::kTrain);
        }
        views_set.update(sample_id, *views_prop);
    }
}
To be completed
To be completed

在理解了属性集的功能和用法之后,添加注释本质上是将我们的注释数据更新到与注释数据节点相对应的属性集中:

auto label_set = sample_set.property_set({segmentation_id, vflow::Segmentation::truth});

for (const auto &[sample_id, old_label] : label_set) {
    // 你可以使用你自定义的函数替换下面的流程,例如:
    // auto new_label = show_and_modify_label(image_prop, old_label);
    if (old_label) {
        auto new_label = old_label->as<vflow::props::PolygonRegionList>();
        for (auto &[region_id, region] : new_label) {
            region.set_name("MyClass");
        }
        label_set.update(sample_id, new_label);
    }
}
To be completed
To be completed

设置训练参数#

不同的工具在不同的阶段需要不同的参数集。对于我们示例中的分割工具,在训练之前,我们需要设置以下参数组:

// 设置标注的类别清单.
vflow::param::LabelClasses classes;
classes.add("MyClass");
project->set_param({segmentation_id, vflow::Segmentation::classes}, classes);

// 设置用于训练的图像颜色.
vflow::param::BaseColor color;
color.set_color(vflow::param::kGray);
project->set_param({segmentation_id, vflow::Segmentation::base_color}, color);

// 设置训练参数.
vflow::param::SegmentationTrainingParameters train_param;
train_param.set_epoch(10)
    .get_augmentations()
    .get_geometry_augmentation()
    .set_flip_horizontal(true)
    .set_flip_vertical(true);
project->set_param({segmentation_id, vflow::Segmentation::trainer_args}, train_param);
To be completed
To be completed

训练和配置参数#

一旦所有数据准备好,训练模型非常简单:

// 创建训练器的策略,你可以通过阅读有关类型的详细接口文档.
// 详细了解这些参数的作用.
vflow::runtime::StrategyOptions strategy;
strategy.allow_auto_update = true;
strategy.allow_auto_update_rely_on_prop = true;
// 你可以通过设置自定义的回调函数来接收训练进度信息.
strategy.call_back = nullptr;

// 创建训练执行器.
auto trainer = project->create_config_runtime({segmentation_id, vflow::Segmentation::trainer}, strategy);

// 为训练执行器创建数据服务.
auto data_server = vflow::adapt(project.get(), trainer);

// 初始化和执行训练,训练成功后模型会自动保存到工程中.
trainer.initialize(data_server);
trainer.execute(data_server);
To be completed
To be completed

导出模型#

在配置好参数并训练好模型之后,您可能希望将模型部署到其他主机。一种方法是通过直接将工程复制到另一台 主机,并使用复制的工程来部署您的检测检测流程。但是,由于工程包含了用于训练和验证模型的所有数据, 其体积可能相当大,导致直接复制非常不方便。为了便于部署,您可以将所有模型和配置的参数导出到一个独立 的文件中,如下所示:

project->export_model("D:/path/to/save/my_first_project.vfmodel");
To be completed
To be completed

如果一切顺利,现在您应该能够在 D:/path/to/save/ 目录中找到名为 my_first_project.vfmodel 的文件。您可以将此文件复制到需要部署检测流程的机器上,并继续按照以下的部署步骤来完成模型的部署。

加载导出的模型#

在加载导出的模型之前,请确保 VisionFlow 依赖库已经初始化。有关初始化依赖库的详细过程,请参阅 初始化VisionFlow 部分。

之后,您可以按照下面的代码示例打开导出的模型:

vflow::Model model("D:/path/to/my_first_project.vfmodel");
To be completed
visionflow.Model model = new visionflow.Model("D:/path/to/my_first_project.vfmodel");

设置推理参数#

在部署阶段,我们提供了一些便捷的接口,允许您读取和修改模型中的某些参数。虽然在实际情况中,部署阶段 可以修改的参数是有限的,但在接口上,我们仍然允许通过接口修改模型中的任何参数。打开模型后,您可以通 过从模型中读取参数、进行必要的更改,然后将其保存回模型中来修改参数,如下所示:

// 根据你的工程中的工具的名称读取相应工具中参数.
std::string segmentation_id = "Segmentation";
auto filter_param = model.get_param({segmentation_id, vflow::Segmentation::filter_args});
if (!filter_param) {
    std::cerr << "Filter parameter for Segmentation not exist." << std::endl;
    exit(-1);
}

// 在这里,我们修改了类别名为"MyClass"的缺陷的过滤参数.
filter_param->as<vflow::param::PolygonsFilterParameters>()
    .get_class_thresholds("MyClass")
    .set_enable(true)
    .set_area_range({100, 50000});

// 然后, 你可以将修改后的参数重新保存到模型中.
model.set_param({segmentation_id, vflow::Segmentation::filter_args}, *filter_param);
To be completed
// 根据你的工程中的工具的名称读取相应工具中参数.
string segmentation_id = "Segmentation";
// 这里读取了推理结果之后的过滤参数.
var tool_node_id = new visionflow.ToolNodeId(segmentation_id, visionflow.Segmentation.filter_args);
var param = model.get_param(tool_node_id);

// 可以序列化为json文件并输出.
// 注意这里是 `to_string()` 而不是 `ToString()`.
Console.WriteLine(param.to_json().to_string());

// 注意这里的类型的必须和节点的类型一致.
// 否则之后反序列化会出现问题.
var filter_args = new visionflow.param.PolygonsFilterParameters();

// 也可以反序列化输入内容.
filter_args.load(param.dump());

// 在这里,我们修改了类别名为"1"的缺陷的过滤参数.
var area_range = new std.VectorInt {100, 5000};
filter_args.get_class_thresholds("1").set_enable(true).set_area_range(area_range);

// 然后, 你可以将修改后的参数重新保存到模型中.
model.set_param(tool_node_id, filter_args);

// 确认是否保存成功.
param = model.get_param(tool_node_id);
Console.WriteLine(param.to_json().to_string());

Warning

使用 model.set_param(***) 接口设置的参数或其他任何对于导出模型的修改,都仅在当前打开的模型 中生效。在关闭此模型并重新打开时,这些修改将丢失。如果您想要永久保存这些修改,您需要创建一个打 开模型的备份,如下面的代码所示:

model.resave_to("D:/other/path/model_2.vfmodel");
To be completed
model.resave_to("D:/other/path/model_2.vfmodel");

执行模型#

在开始执行模型之前,我们需要创建一个可以基于模型数据执行的运行时。创建运行时有各种策略, 但在这里,为了简单起见,我们将使用 所有工具 策略来运行模型中的所有工具:

vflow::runtime::AllTools strategy;
auto runtime = model.create_runtime(strategy);
To be completed
var strategy = new visionflow.runtime.AllTools();
var runtime = model.create_runtime(strategy);

创建了运行时之后,我们可以使用它来检测我们想要检测的图像。运行时需要一个 Sample 来存储输入图像、中间结果以及整个检测过程的最终输出。因此,我们需要创建一个 Sample, 并将我们需要检测的数据添加到其中,如下所示,然后我们可以在这个样本上执行我们的处理流程:

std::string input_id = "Input"; // The id of the Input tool.

auto sample = runtime.create_sample();
auto image = vflow::Image::FromFile("D:/path/to/image.png");
vflow::helper::add_image_to_sample(sample, image, input_id);

// Then we can execute the processing flow on the sample.
runtime.execute(sample);
To be completed
string input_id = "Input"; // The id of the Input tool.

var sample1 = runtime.create_sample();
var image1 = visionflow.img.Image.FromFile("D:/path/to/save/1.bmp");
visionflow_helpers_global.add_image_to_sample(sample1, image1, input_id);

// Then we can execute the processing flow on the sample.
runtime.execute(sample1);

在执行之后,我们可以从样本中检索每个工具的检测结果(及中间结果),并根据我们的要求处理这些结果 (你可以通过查阅每个 工具的详细流程图 获得每个中间或最终结果的ID):

auto result = sample.get({segmentation_id, vflow::Segmentation::pred});
std::cout << "Result: " << result->to_json().to_string() << std::endl;

const auto &segment_pred = result->as<vflow::props::PolygonRegionList>();
for (const auto &[id, region] : segment_pred) {
    std::cout << "Found a defect: " << id
              << ", name: " << region.name()
              << ", area: " << region.area() << std::endl;
}

// Draw the defects on the image and show the image:
vflow::img::draw(image, segment_pred.to_multi_polygons(), {10, 10, 240}, 2);
image.show();
To be completed
var pred_node_id = new visionflow.ToolNodeId(segmentation_id, visionflow.Segmentation.pred);
var result = sample1.get(pred_node_id);
var segment_pred = new visionflow.props.PolygonRegionList();
segment_pred.load(result.dump());

// 输出推理结果的json内容.
// 注意这里是 `to_string()` 而不是 `ToString()`.
Console.WriteLine(segment_pred.to_json().to_string());

// 遍历推理结果.
var ids = segment_pred.keys();
foreach (var id in ids) {
    // 对照C++接口确定对应类型.
    var region = segment_pred.at(id) as visionflow.PolygonRegion;
    Console.WriteLine("id = {0}, region_name = {1}, region_area = {2}", id, region.name(), region.area());
}

// 设置颜色.
var color = new std.VectorInt {10, 10, 240};

// 在原图上画上推理结果并延时展示一会儿.
visionflow_img_global.draw(image1, segment_pred.to_multi_polygons(), color, 2);
image1.show(2000);

完整的示例#

我们维护了一个从创建工程、添加图像、标注、设置参数到训练和导出模型,并用导出的模型进行推理的 完整的C++示例工程,详情请查看这个仓库:vflow-example

下面还提供了一个部署导出的模型的完整示例代码:

Online Inference Usage#
 1#include <exception>
 2#include <iostream>
 3#include <string>
 4
 5#include "visionflow/visionflow.hpp"
 6
 7namespace vflow = visionflow;
 8
 9void my_logger_output(int log_level, const char *content, size_t len) {
10  if (log_level > 2) {
11    std::cout << std::string(content, len) << std::endl;
12  }
13}
14
15int main(int /*argc*/, char ** /*argv*/) try {
16
17  vflow::InitOptions opts;
18
19  // 设置日志输出文件
20  opts.logger.file_sink = "visionflow.log";
21  // 设置是否将日志输出到标准输出终端
22  opts.logger.stdout_sink = true;
23  // 你可以通过设置日志输出的回调函数自定义处理VisionFlow输出的日志
24  // opts.logger.func_sink = my_logger_output;
25
26  // 设置语言为中文
27  opts.language = "zh_CN";
28
29  vflow::initialize(opts);
30
31  vflow::Model model("D:/path/to/save/my_first_project.vfmodel");
32
33  // 根据你的工程中的工具的名称读取相应工具中参数
34  std::string segmentation_id = "Segmentation";
35  auto filter_param = model.get_param({segmentation_id, "filter.args"});
36  if (!filter_param) {
37    std::cerr << "Filter parameter for Segmentation not exist." << std::endl;
38    exit(-1);
39  }
40
41  // 在这里,我们修改了类别名为"MyClass"的缺陷的过滤参数.
42  filter_param->as<vflow::param::PolygonsFilterParameters>()
43      .get_class_thresholds("MyClass")
44      .set_enable(true)
45      .set_area_range({0, 500000});
46
47  // 然后, 你可以将修改后的参数重新保存到模型中
48  model.set_param({segmentation_id, "filter.args"}, *filter_param);
49
50  model.resave_to("D:/other/path/model_2.vfmodel");
51
52  // 在开始推理之前,我们需要根据模型数据创建出一个可以执行的运行时,创建运行时有很多不同的策略,
53  // 在这里,为了简单起见,我们直接使用默认参数运行模型中所有的工具:
54  vflow::runtime::AllTools strategy;
55  auto runtime = model.create_runtime(strategy);
56
57  // 在创建出运行时之后,我们就可以使用运行时来检测我们需要检测的图像了。
58  // 运行时需要一个样本来保存整个检测流程的输入图像,中间结果和最终的
59  // 输出,因此,我们需要像下面这样,先创建出一个样本,并将我们的图像添加到样本中:
60  std::string input_id = "Input";
61  while (true) {
62    auto sample = runtime.create_sample();
63    auto image = vflow::Image::FromFile("D:/path/to/image.png");
64    vflow::helper::add_image_to_sample(sample, image, input_id);
65    runtime.execute(sample);
66
67    auto result = sample.get({segmentation_id, "pred"});
68
69    std::cout << "Result: " << result->to_json().to_string() << std::endl;
70
71    const auto &segment_pred = result->as<vflow::props::PolygonRegionList>();
72    for (const auto &[id, region] : segment_pred) {
73      std::cout << "Found a defect: " << id << ", name: " << region.name()
74                << ", area: " << region.area() << std::endl;
75    }
76
77    // Draw the defects on the image and show the image:
78    vflow::img::draw(image, segment_pred.to_multi_polygons(), {10, 10, 240}, 2);
79    image.show();
80  }
81
82  return 0;
83
84} catch (const std::exception &ex) {
85  std::cerr << "Unexpected Escaped Exception: " << ex.what();
86  return -1;
87}
To be completed
Online Inference Usage#
  1using System;
  2using System.Runtime.InteropServices;
  3
  4// 自定义日志接受对象.
  5// public class MyLogSink : visionflow.ILogSink {
  6//     public override void log(int level, string message) {
  7//         Console.WriteLine(message);
  8//     }
  9// }
 10
 11public class Program
 12{
 13    public static void Main()
 14    {
 15        try
 16        {
 17            visionflow.InitOptions opts = new visionflow.InitOptions();
 18
 19            // 设置日志输出文件.
 20            opts.logger.file_sink = "visionflow.log";
 21            // 设置是否输出到终端.
 22            opts.logger.stdout_sink = true;
 23            // 设置自定义日志接受对象.
 24            // opts.logger.custom_sink = new MyLogSink();
 25
 26            // 设置语言为中文.
 27            opts.language = "zh_CN";
 28
 29            // 初始化.
 30            visionflow_global.initialize(opts);
 31
 32            // 加载模型.
 33            visionflow.Model model = new visionflow.Model("D:/path/to/save/segmentation.vfmodel");
 34
 35            // 获取模型中的工具信息.
 36            var tool_list = model.tool_list();
 37            foreach (var tool in tool_list)
 38            {
 39                // 工具名称.
 40                Console.WriteLine(tool);
 41                // 工具信息.
 42                var tool_info = model.tool_info(tool);
 43                // 这里以工具类型为例.
 44                Console.WriteLine(tool_info.type());
 45            }
 46
 47            // 根据你的工程中的工具的名称读取相应工具中参数.
 48            string segmentation_id = "Segmentation";
 49            // 这里读取了推理结果之后的过滤参数.
 50            var tool_node_id = new visionflow.ToolNodeId(segmentation_id, "filter.args");
 51            var param = model.get_param(tool_node_id);
 52
 53            // 可以序列化为json文件并输出.
 54            // 注意这里是 `to_string()` 而不是 `ToString()`.
 55            Console.WriteLine(param.to_json().to_string());
 56
 57            // 注意这里的类型的必须和节点的类型一致.
 58            // 否则之后反序列化会出现问题.
 59            var filter_args = new visionflow.param.PolygonsFilterParameters();
 60
 61            // 也可以反序列化输入内容.
 62            filter_args.load(param.dump());
 63
 64            // 在这里,我们修改了类别名为"1"的缺陷的过滤参数.
 65            var area_range = new std.VectorInt { 100, 10000 };
 66            filter_args.get_class_thresholds("1").set_enable(true).set_area_range(area_range);
 67
 68            // 然后, 你可以将修改后的参数重新保存到模型中.
 69            model.set_param(tool_node_id, filter_args);
 70
 71            // 确认是否保存成功.
 72            param = model.get_param(tool_node_id);
 73            Console.WriteLine(param.to_json().to_string());
 74
 75            // 在开始推理之前,我们需要根据模型数据创建出一个可以执行的运行时,创建运行时有很多不同的策略,
 76            // 在这里,为了简单起见,我们直接使用默认参数运行模型中所有的工具:
 77            var strategy = new visionflow.runtime.AllTools();
 78            // 显示指定GPU设备ID.
 79            var gpu_ids = new std.VectorInt { 0 };
 80            strategy.options.specified_gpu_ids = gpu_ids;
 81            // 默认允许最大GPU数量为极大值.
 82            // 因此同时建议配置最大GPU数量,以免自动分配了其他未指定的GPU.
 83            strategy.options.allow_max_gpu_num = 1;
 84
 85            var runtime = model.create_runtime(strategy);
 86
 87            // 自动获取输入节点,方便输入需要检测的图像.
 88            var input_list = new std.VectorString();
 89            foreach (var tool in tool_list)
 90            {
 91                if (model.tool_info(tool).type() == "Input") input_list.Add(tool);
 92            }
 93            // 输入节点不唯一,则需要用户选择输入节点.
 94            if (input_list.Count != 1)
 95            {
 96                Console.WriteLine("Input nodes are not unique!");
 97            }
 98            string input_id = input_list[0];
 99
100            // 在创建出运行时之后,我们就可以使用运行时来检测我们需要检测的图像了.
101            // 运行时需要一个样本来保存整个检测流程的输入图像,中间结果和最终的
102            // 输出,因此,我们需要像下面这样,先创建出一个样本,并将我们的图像添加到样本中:
103            while (true)
104            {
105                // 对于多张图片,自然也要创建多个样本:
106                var sample1 = runtime.create_sample();
107                var sample2 = runtime.create_sample();
108                var image1 = visionflow.img.Image.FromFile("D:/path/to/save/1.bmp");
109
110                // 假设已有内存数据byte[],通过其首地址和配置图片属性即可输入图片:
111                // byte[] array = new byte[length];
112                // 在C#中这是一个unsafe的行为,因此需要在工程“属性->生成”中勾选“允许不安全代码”。
113                // IntPtr image_ptr = System.Runtime.InteropServices.Marshal.UnsafeAddrOfPinnedArrayElement(array, 0);
114                //
115                // var image2 = new visionflow.img.Image(image_ptr, h, w, 1, visionflow.img.Image.Depth.kDepthU8, 0);
116                var image2 = visionflow.img.Image.FromFile("D:/path/to/save/2.bmp");
117
118                visionflow_helpers_global.add_image_to_sample(sample1, image1, input_id);
119                visionflow_helpers_global.add_image_to_sample(sample2, image2, input_id);
120                runtime.execute(sample1);
121
122                // 获取推理后的预测结果.
123                var pred_node_id = new visionflow.ToolNodeId(segmentation_id, "pred");
124                var result = sample1.get(pred_node_id);
125                var segment_pred = new visionflow.props.PolygonRegionList();
126                segment_pred.load(result.dump());
127
128                // 输出推理结果的json内容.
129                // 注意这里是 `to_string()` 而不是 `ToString()`.
130                Console.WriteLine(segment_pred.to_json().to_string());
131
132                // 遍历推理结果.
133                var ids = segment_pred.keys();
134                foreach (var id in ids)
135                {
136                    // 对照C++接口确定对应类型.
137                    var region = segment_pred.at(id) as visionflow.PolygonRegion;
138                    Console.WriteLine("id = {0}, region_name = {1}, region_area = {2}", id, region.name(), region.area());
139                }
140
141                // 设置颜色.
142                var color = new std.VectorInt { 10, 10, 240 };
143
144                // 在原图上画上推理结果并延时展示一会儿.
145                visionflow_img_global.draw(image1, segment_pred.to_multi_polygons(), color, 2);
146                image1.show(2000);
147
148                // 执行另一张图.
149                runtime.execute(sample2);
150                result = sample2.get(pred_node_id);
151                segment_pred.load(result.dump());
152                visionflow_img_global.draw(image2, segment_pred.to_multi_polygons(), color, 2);
153                image2.show(2000);
154            }
155        }
156        catch (Exception ex)
157        {
158            Console.WriteLine(ex);
159        }
160    }
161}
Online Inference Usage#
  1#include <stdio.h>
  2
  3#include "cabi/visionflow.h"
  4
  5int main() {
  6  while (true) {
  7    {
  8
  9      const C_VisionflowInitOptions init_options =
 10          new_visionflow_init_options();
 11      const C_VisionflowLoggerOptions logger_options =
 12          new_visionflow_logger_options();
 13
 14      // 设置输出日志。
 15      set_visionflow_logger_options_file_sink(logger_options, "visionflow.log");
 16
 17      // 设置是否将日志输出到标准输出终端。
 18      set_visionflow_logger_options_stdout_sink(logger_options, true);
 19
 20      // 日志配置写回,否则配置不会生效。
 21      set_visionflow_init_options_logger(init_options, logger_options);
 22
 23      // 设置语言为中文。
 24      set_visionflow_init_options_language(init_options, "zh_CN");
 25
 26      // 如果需要使用变量的引用,可以拷贝一份变量并将 mem_owner 字段设为 false。
 27      // 这样创建的引用无需释放。
 28      // 在本例子的下文可以看到,创建引用变量在大多数时候是不需要的。
 29      C_VisionflowInitOptions init_options_ref = init_options;
 30      init_options_ref.mem_owner = false;
 31
 32      // 初始化。
 33      c_visionflow_initialize(init_options_ref);
 34
 35      // 对于非引用变量,使用完都需要注意手动释放。
 36      delete_visionflow_init_options(init_options);
 37      delete_visionflow_logger_options(logger_options);
 38      // 手动释放引用变量也并不会引发问题。
 39      delete_visionflow_init_options(init_options_ref);
 40
 41      const String password = new_String_1("");
 42      const String model_path =
 43          new_String_1("D:/path/to/save/my_first_project.vfmodel");
 44
 45      // 导入模型。
 46      const C_VisionflowModel model =
 47          new_visionflow_model(model_path, password);
 48
 49      delete_String(password);
 50      delete_String(model_path);
 51
 52      // 根据你的工程中的工具的名称读取相应工具中参数。
 53      const String tool_id = new_String_1("Segmentation");
 54      const String node_id = new_String_1("filter.args");
 55      const C_VisionflowToolNodeId tool_node_id =
 56          new_visionflow_tool_node_id(tool_id, node_id, -1);
 57
 58      delete_String(tool_id);
 59      delete_String(node_id);
 60
 61      const SharedPtrC_VisionflowParamIParameter shared_ptr_iparam =
 62          visionflow_model_get_param(model, tool_node_id);
 63
 64      // 注意shared_ptr特殊的地方,这里不需要手动释放。
 65      const C_VisionflowParamIParameter iparam =
 66          SharedPtrC_VisionflowParamIParameter_get(shared_ptr_iparam);
 67      // 经过类型转换获得的是引用,可通过验证变量的 mem_owner 字段为 false
 68      // 来确认。
 69      const C_VisionflowParamSchemableParameter schemable_param =
 70          c_visionflow_param_i_parameter_to_c_visionflow_param_schemable_parameter(
 71              iparam);
 72      const C_VisionflowParamPolygonsFilterParameters filter_param =
 73          c_visionflow_param_schemable_parameter_to_c_visionflow_param_polygons_filter_parameters(
 74              schemable_param);
 75
 76      // 在这里,我们修改了类别名为"1"的缺陷的过滤参数。
 77      const String my_class = new_String_1("1");
 78      const C_VisionflowParamSingleClassPolygonsFilterParameters single_class =
 79          visionflow_param_polygons_filter_parameters_get_class_thresholds_1(
 80              filter_param, my_class);
 81
 82      visionflow_param_single_class_polygons_filter_parameters_set_enable(
 83          single_class, true);
 84
 85      // 写回修改。
 86      visionflow_param_polygons_filter_parameters_set_class_thresholds_1(
 87          filter_param, my_class, single_class);
 88
 89      delete_String(my_class);
 90
 91      // 将修改后的参数写回模型中。
 92      visionflow_model_set_param(model, tool_node_id, iparam);
 93
 94      delete_visionflow_tool_node_id(tool_node_id);
 95
 96      // 注意一定要正确释放子类。
 97      delete_SharedPtrC_VisionflowParamIParameter(shared_ptr_iparam);
 98
 99      // 在开始推理之前,我们需要根据模型数据创建出一个可以执行的运行时,创建运行时有很多不同的策略,
100      // 在这里,为了简单起见,我们直接使用默认参数运行模型中所有的工具:
101      const C_VisionflowRuntimeAllTools all_tools =
102          new_visionflow_runtime_all_tools();
103      const C_VisionflowRuntimeIStrategy istrategy =
104          c_visionflow_runtime_all_tools_to_c_visionflow_runtime_i_strategy(
105              all_tools);
106      const C_VisionflowRuntimeStrategyOptions strategy_option =
107          get_visionflow_runtime_i_strategy_options(istrategy);
108
109      // 配置gpu允许最大数量和id。
110      const VectorInt gpu_ids = new_VectorInt();
111      VectorInt_push_back(gpu_ids, 0);
112
113      set_visionflow_runtime_strategy_options_specified_gpu_ids(strategy_option,
114                                                                gpu_ids);
115      set_visionflow_runtime_strategy_options_allow_max_gpu_num(strategy_option,
116                                                                1);
117
118      delete_VectorInt(gpu_ids);
119
120      // 注意写回配置。
121      set_visionflow_runtime_i_strategy_options(istrategy, strategy_option);
122
123      delete_visionflow_runtime_strategy_options(strategy_option);
124
125      const C_VisionflowRuntime runtime =
126          visionflow_model_create_runtime(model, istrategy);
127
128      delete_visionflow_model(model);
129      delete_visionflow_runtime_all_tools(all_tools);
130
131      // 在创建出运行时之后,我们就可以使用运行时来检测我们需要检测的图像了.
132      // 运行时需要一个样本来保存整个检测流程的输入图像,中间结果和最终的
133      // 输出,因此,我们需要像下面这样,先创建出一个样本,并将我们的图像添加到样本中:
134      const String input_id = new_String_1("Input");
135
136      // 创建出一个样本。
137      const C_VisionflowSample sample =
138          visionflow_runtime_create_sample(runtime);
139      const C_VisionflowISample isample =
140          c_visionflow_sample_to_c_visionflow_i_sample(sample);
141
142      // 导入图片。
143      const String image_path = new_String_1("D:/path/to/image.png");
144      const C_VisionflowImgImage image =
145          visionflow_img_Image_FromFile(image_path, -1);
146
147      delete_String(image_path);
148
149      // 添加图片到样本中。
150      c_visionflow_helper_add_image_to_sample(
151          isample, image, input_id, false, 0);
152
153      delete_String(input_id);
154
155      visionflow_runtime_execute(runtime, isample, false);
156
157      delete_visionflow_runtime(runtime);
158
159      // 获取推理后的预测结果。
160      const String pred_tool_id = new_String_1("Segmentation");
161      const String pred_node_id = new_String_1("pred");
162      const C_VisionflowToolNodeId pred_tool_node_id =
163          new_visionflow_tool_node_id(pred_tool_id, pred_node_id, -1);
164
165      delete_String(pred_tool_id);
166      delete_String(pred_node_id);
167
168      const SharedPtrC_VisionflowPropsIProperty pred =
169          visionflow_sample_get(sample, pred_tool_node_id);
170
171      delete_visionflow_tool_node_id(pred_tool_node_id);
172
173      const C_VisionflowPropsIProperty iproperty =
174          SharedPtrC_VisionflowPropsIProperty_get(pred);
175      const C_VisionflowPropsPolygonRegionList segment_pred =
176          c_visionflow_props_i_property_to_c_visionflow_props_polygon_region_list(
177              iproperty);
178
179      // 遍历推理结果信息。
180      const VectorString keys =
181          visionflow_props_polygon_region_list_keys(segment_pred);
182      const unsigned long long size = VectorString_size(keys);
183      for (int i = 0; i < size; i++) {
184        const String key = VectorString_get(keys, i);
185        const C_VisionflowPolygonRegion region =
186            visionflow_props_polygon_region_list_at(segment_pred, key);
187        printf("%f\n", visionflow_polygon_region_area(region));
188
189        delete_String(key);
190      }
191
192      delete_VectorString(keys);
193
194      const C_VisionflowGeometryMultiPolygon2f polygons =
195          visionflow_props_polygon_region_list_to_multi_polygons_1(
196              segment_pred);
197
198      delete_visionflow_sample(sample);
199
200      const VectorInt color = new_VectorInt();
201      VectorInt_push_back(color, 10);
202      VectorInt_push_back(color, 10);
203      VectorInt_push_back(color, 240);
204
205      c_visionflow_img_draw(
206          image, polygons, color, 2, C_VisionflowImgLineType_kLine8);
207
208      delete_visionflow_geometry_multi_polygon2f(polygons);
209      delete_VectorInt(color);
210
211      const String win_name = new_String_1("image");
212      visionflow_img_image_show(image, 2000, win_name, 0);
213
214      delete_String(win_name);
215      delete_visionflow_img_image(image);
216    }
217    printf("%s\n", "==========END==========");
218  }
219}
Online Inference Usage#
  1// #include "stdafx.h"
  2
  3#include <iostream>
  4
  5#include "cppabi/visionflow.hpp"
  6
  7namespace vflow = cpp2x::visionflow;
  8
  9int main() {
 10  while (true) {
 11    {
 12      vflow::InitOptions init_options;
 13      vflow::LoggerOptions logger_options;
 14
 15      // 设置输出日志。
 16      logger_options.set_file_sink("visionflow.log");
 17
 18      // 设置是否将日志输出到标准输出终端。
 19      logger_options.set_stdout_sink(true);
 20
 21      // 日志配置写回,否则配置不会生效。
 22      init_options.set_logger(logger_options);
 23
 24      // 设置语言为中文。
 25      init_options.set_language("zh_CN");
 26
 27      // 初始化。
 28      vflow::initialize(init_options);
 29
 30      // 导入模型。
 31      vflow::Model model({"D:/path/to/save/my_first_project.vfmodel"},
 32                         cpp2x::String{""});
 33
 34      // 根据你的工程中的工具的名称读取相应工具中参数。
 35      const cpp2x::String segmentation_id = "Segmentation";
 36      const auto tool_node_id =
 37          vflow::ToolNodeId(segmentation_id, {"filter.args"}, -1);
 38      auto filter_param = model.get_param(tool_node_id);
 39
 40      // 在这里,我们修改了类别名为"1"的缺陷的过滤参数.
 41      auto area_range = cpp2x::VectorInt();
 42      area_range.push_back(0);
 43      area_range.push_back(500000);
 44
 45      filter_param.get()
 46          .as<vflow::param::PolygonsFilterParameters>()
 47          .get_class_thresholds({"1"})
 48          .set_enable(true)
 49          .set_area_range(area_range);
 50
 51      // 将修改后的参数写回模型中。
 52      model.set_param(tool_node_id, filter_param.get());
 53
 54      // 在开始推理之前,我们需要根据模型数据创建出一个可以执行的运行时,创建运行时有很多不同的策略。
 55      vflow::runtime::StrategyOptions strategy_option;
 56
 57      // 配置gpu允许最大数量和id。
 58      strategy_option.set_allow_max_gpu_num(1);
 59      auto gpu_ids = cpp2x::VectorInt();
 60      gpu_ids.push_back(0);
 61      strategy_option.set_specified_gpu_ids(gpu_ids);
 62
 63      // 在这里,为了简单起见,我们直接使用默认参数运行模型中所有的工具:
 64      vflow::runtime::AllTools all_tools(strategy_option);
 65
 66      const auto runtime = model.create_runtime(all_tools);
 67
 68      // 在创建出运行时之后,我们就可以使用运行时来检测我们需要检测的图像了.
 69      // 运行时需要一个样本来保存整个检测流程的输入图像,中间结果和最终的
 70      // 输出,因此,我们需要像下面这样,先创建出一个样本,并将我们的图像添加到样本中:
 71      const auto input_id = cpp2x::String("Input");
 72      auto sample = runtime.create_sample();
 73      auto image = vflow::img::Image::FromFile({"D:/path/to/image.png"}, -1);
 74
 75      // 添加图片到样本中。
 76      vflow::helper::add_image_to_sample(sample, image, input_id, false, 0);
 77
 78      runtime.execute(sample, false);
 79
 80      // 获取推理后的预测结果。
 81      const auto pred_tool_node_id =
 82          vflow::ToolNodeId(segmentation_id, {"pred"}, -1);
 83      const auto pred = sample.get(pred_tool_node_id);
 84
 85      std::cout << "Result: " << pred.get().to_json().to_string().c_str()
 86                << '\n';
 87
 88      const auto segment_pred =
 89          pred.get().as<vflow::props::PolygonRegionList>();
 90
 91      // 遍历推理结果信息。
 92      const auto keys = segment_pred.keys();
 93      const auto size = keys.size();
 94      for (int i = 0; i < size; i++) {
 95        const auto key = keys.get(i);
 96        const auto region = segment_pred.at(key);
 97        std::cout << region.area() << "\n";
 98      }
 99
100      auto color = cpp2x::VectorInt();
101      color.push_back(10);
102      color.push_back(10);
103      color.push_back(240);
104
105      vflow::img::draw(image,
106                       segment_pred.to_multi_polygons(),
107                       color,
108                       2,
109                       vflow::img::kLine8);
110      image.show(2000, {"image"}, 0);
111    }
112    std::cout << "==========END==========" << '\n';
113  }
114}