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,一般情况下不需要修改此项配置)。

  6. 开启jpeg图片存储格式 enable_jpeg_storage_mode:工程默认的图片存储格式为无损压缩的png格式,这样可以保证不影响算法效果; 若想要添加大量样本的同时保持工程体积不至于过大,可开启此选项使用jpeg格式存储图片,但由于其为有损压缩, 可能对算法效果有影响,开启后建议进行效果验证。 详情请移步 visionflow::InitOptions (默认为关闭)。

以下是 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;
}
import visionflow as vflow

if __name__ == "__main__":
    opts = vflow.InitOptions()

    # 指定log文件
    opts.logger.file_sink = "visionflow.log"
    opts.logger.stdout_sink = True
    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)
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;
desc = vflow.ProjectDescriptor(workspace_token = "./workspace", project_name = "test")
out_desc  = vflow.Project.Create(desc)

print("My first VisionFlow project created at: ", out_desc.created_time)
print("The VisionFlow version which the project created by: ", out_desc.sdk_version)

# And then you can open the project with the desc or the output_desc.
project = vflow.Project.Open(desc)

print("My first VisionFlow project name is: ", project.descriptor().project_name)
var desc = new visionflow.ProjectDescriptor();

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

var out_desc = visionflow.Project.Create(desc);

Console.WriteLine("My first VisionFlow project created at: " + out_desc.created_time);
Console.WriteLine("The VisionFlow version which the project created by: " + out_desc.sdk_version);

var project = visionflow.Project.Open(desc);

Console.WriteLine("My first VisionFlow project name is: " + project.descriptor().project_name);

添加工具#

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;
}
input_id = project.add_tool("Input")
segmentation_id = project.add_tool("Segmentation")

connected = project.auto_connect(input_id, segmentation_id)
if not connected:
    print("auto_connect failed, Please use Project::connect instead.")
var input_id = project.add_tool("Input");
var segmentation_id = project.add_tool("Segmentation");

var connected = project.auto_connect(input_id, segmentation_id);
if (!connected)
{
    Console.WriteLine("auto_connect failed, Please use Project::connect instead.");
}

导入图片#

在 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});
sample_set = project.main_sample_set()
input_image_id = vflow.ToolNodeId(input_id, vflow.Input.image)
input_image_set = sample_set.property_set(input_image_id)
var sample_set = project.main_sample_set();
var input_image_set = sample_set.property_set(new visionflow.ToolNodeId(input_id, visionflow.Input.image));

// 注意:我们要求在 Project 析构前,它所属的所有 SampleSet 或 PropertySet 都被析构,否则将导致不可恢复的异常。
// 由于 C# 的析构是受垃圾回收控制,将这个析构交给 C# 运行时可能导致析构顺序不正确。
// 因此,在使用完毕后,请手动调用 SampleSet.Dispose() 和 PropertySet.Dispose()。

导入图像到项目中有几种不同的方法。如果您想要将一系列图像文件导入到项目中,可以使用辅助类型 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);
old_sample_num = sample_set.size()
input_helper = vflow.helper.InputHelper(project)

for i in range(1, 11):
    input_helper.add_image(kStorageDir + "/data/{}.png".format(i))

receipts = input_helper.commit()

success_count = 0
for receipt in receipts:
    if receipt.is_success:
        success_count += 1
    else :
        print("Failed to import image ", receipt.image_files[0], " as: ", receipt.error_message)

assert sample_set.size() - old_sample_num == success_count

# 注意工程需要存在InputImageParam参数才能导入图片
var old_sample_num = sample_set.size();

var input_helper = new visionflow.helper.InputHelper(project);

for (int i = 0; i < 100; ++i)
{
    input_helper.add_image($"D:/path/to/image/{i}.png");
}
var receipts = input_helper.commit();

int success_count = 0;
foreach (var receipt in receipts)
{
    if (receipt.is_success)
    {
        success_count++;
    }
    else
    {
        Console.WriteLine($"Failed to import image {receipt.image_files[0]} as: {receipt.error_message}");
    }
}

if (sample_set.size() - old_sample_num != success_count)
{
    Console.WriteLine("Sample set size not match!");
}

在某些情况下,您的图像可能不存在于文件系统中,而是已经加载到内存中。在这种情况下,您可以使用函数 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);
image = vflow.img.Image.FromFile(kStorageDir + "/data/1.png")
sample  = sample_set.create_empty_sample()
vflow.helper.add_image_to_sample(sample, image, input_id, True, 512)
sample_set.add(sample)

image_info_id = vflow.ToolNodeId(input_id, vflow.Input.image_info)
image_info_set = sample_set.property_set(image_info_id)
for id, image_info in image_info_set:
    print(id, image_info.import_time())
// We have a image loaded into memory:
var image = visionflow.img.Image.FromFile("D:/path/to/image/0.png");

// Create a sample template from the sample set:
var sample = sample_set.create_empty_sample();
// Add the the image into sample:
visionflow_helpers_global.add_image_to_sample(sample, image, input_id, true, 512);
// Add the sample to sample set:
sample_set.add(sample);

Note

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

Note

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

添加标注#

在介绍如何向数据集添加标注之前,让我们简要解释一下如何通过 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);
    }
}
sample  = sample_set.create_empty_sample()
sample_set.add(sample)

image_set = sample_set.property_set(vflow.ToolNodeId(input_id, vflow.Input.image))
for sample_id, image_prop in image_set:
    if image_prop:
        image_prop.image().show(10)
    else:
        modify_image  = vflow.img.Image.FromFile(kStorageDir + "/data/1.png")
        new_prop = vflow.props.Image(modify_image)
        image_set.update(sample_id, new_prop)
        assert image_set.at(sample_id)
        print("update image successfully")
// 获取属性集.
var image_set = sample_set.property_set(new visionflow.ToolNodeId(input_id, visionflow.Input.image));
// 遍历属性集中的数据.
foreach (var (sample_id, image_prop) in image_set)
{
    if (image_prop == null) // 如果某个样本在此数据节点上的数据不存在,则会返回空指针.
    {
        var modify_image = visionflow.img.Image.FromFile("D:/path/to/image/0.png");
        var new_prop = new visionflow.props.Image(modify_image);
        // 更新属性集中的数据.
        image_set.update(sample_id, new_prop);
    }
}

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

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);
    }
}
views_set = sample_set.property_set(vflow.ToolNodeId(input_id, vflow.Input.views))
for sample_id, views_prop in views_set:
    if views_prop:
        for view_id, view in views_prop:
            view.set_split_tag(vflow.kTrain)

        views_set.update(sample_id, views_prop)
var views_set = sample_set.property_set(new visionflow.ToolNodeId(input_id, visionflow.Input.views));

foreach (var (sample_id, views_prop) in views_set)
{
    if (views_prop != null)
    {
        var view_list = new visionflow.props.ViewList();
        view_list.load(views_prop.dump());
        foreach (var view_id in view_list.keys())
        {
            var view = view_list.at(view_id) as visionflow.TaggedView;
            view.set_split_tag(visionflow.SplitTag.kTrain);
            view_list.update(view_id, view);
        }
        views_set.update(sample_id, view_list);
    }
}

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

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);
    }
}
label_set = sample_set.property_set(vflow.ToolNodeId(segmentation_id, vflow.Segmentation.truth))
for sample_id, label in label_set:
    if label:
        for region_id, region in label:
            region.set_name("MyClass")

        label_set.update(sample_id, label)
var label_set = sample_set.property_set(new visionflow.ToolNodeId(segmentation_id, visionflow.Segmentation.truth));

foreach (var (sample_id, old_label) in label_set)
{
    // 你可以使用你自定义的函数替换下面的流程
    if (old_label != null)
    {
        var new_label = new visionflow.props.PolygonRegionList();
        new_label.load(old_label.dump());
        foreach (var region_id in new_label.keys())
        {
            var region = new_label.at(region_id) as visionflow.PolygonRegion;
            region.set_name("MyClass");
            new_label.update(region_id, region);
        }
        label_set.update(sample_id, new_label);
    }
}

设置训练参数#

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

// 设置标注的类别清单.
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);
classes = vflow.param.LabelClasses()
classes.add("MyClass")
project.set_param(vflow.ToolNodeId(segmentation_id, vflow.Segmentation.classes), classes)

color = vflow.param.BaseColor()
color.set_color(vflow.param.kGray)
project.set_param(vflow.ToolNodeId(segmentation_id, vflow.Segmentation.base_color), color)

train_param = vflow.param.SegmentationTrainingParameters()
train_param.set_epoch(10).get_augmentations().get_geometry_augmentation().set_flip_horizontal(True).set_flip_vertical(True)
project.set_param(vflow.ToolNodeId(segmentation_id, vflow.Segmentation.trainer_args), train_param)
// 设置标注的类别清单.
var classes = new visionflow.param.LabelClasses();
classes.add("MyClass");
project.set_param(new visionflow.ToolNodeId(segmentation_id, visionflow.Segmentation.classes), classes);

// 设置用于训练的图像颜色.
var color = new visionflow.param.BaseColor();
color.set_color(visionflow.param.ColorType.kGray);
project.set_param(new visionflow.ToolNodeId(segmentation_id, visionflow.Segmentation.base_color), color);

// 设置训练参数.
var train_param = new visionflow.param.SegmentationTrainingParameters();
// 设置训练参数.
var train_param = new visionflow.param.SegmentationTrainingParameters();
train_param.set_epoch(10);
train_param.get_augmentations()
    .get_geometry_augmentation()
    .set_flip_horizontal(true)
    .set_flip_vertical(true);
project.set_param(new visionflow.ToolNodeId(segmentation_id, visionflow.Segmentation.trainer_args), train_param);

训练和配置参数#

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

// 创建训练器的策略,你可以通过阅读有关类型的详细接口文档.
// 详细了解这些参数的作用.
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);
strategy = vflow.runtime.StrategyOptions()
strategy.allow_auto_update = True
strategy.allow_auto_update_rely_on_prop = True
strategy.call_back = None

trainer = project.create_config_runtime(vflow.ToolNodeId(segmentation_id, vflow.Segmentation.trainer), strategy)
data_server = vflow.adapt(project, trainer)

trainer.initialize(data_server)
trainer.execute(data_server)
// 创建训练器的策略,你可以通过阅读有关类型的详细接口文档
// 详细了解这些参数的作用.
var strategy = new visionflow.runtime.StrategyOptions();
strategy.allow_auto_update = true;
strategy.allow_auto_update_rely_on_prop = true;

// 创建训练执行器.
var trainer = project.create_config_runtime(new visionflow.ToolNodeId(segmentation_id, visionflow.Segmentation.trainer), strategy);
// 为训练执行器创建数据服务.
var data_server = new visionflow.ConfigureRuntimeProjectAdapter(project, trainer);

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

导出模型#

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

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

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

加载导出的模型#

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

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

vflow::Model model("D:/path/to/my_first_project.vfmodel");
model = vflow.Model("D:/path/to/my_first_project.vfmodel")
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);
filter_param_id = vflow.ToolNodeId(segmentation_id, vflow.Segmentation.filter_args)
filter_param = model.get_param(filter_param_id)

class_polygon_filter_param = vflow.param.SingleClassPolygonsFilterParameters()
class_polygon_filter_param.set_enable(True).set_area_range([100, 50000])
filter_param.set_class_thresholds("MyClass", class_polygon_filter_param)

model.set_param(filter_param_id, filter_param)
// 根据你的工程中的工具的名称读取相应工具中参数.
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");
model.resave_to("D:/other/path/model_2.vfmodel")
model.resave_to("D:/other/path/model_2.vfmodel");

执行模型#

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

vflow::runtime::AllTools strategy;
auto runtime = model.create_runtime(strategy);
strategy = vflow.runtime.AllTools()
runtime = model.create_runtime(strategy)
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);
input_id = "Input" # The id of the Input tool.

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

runtime.execute(sample)
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_helper_global.add_image_to_sample(sample1, image1, input_id);

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

Note

上面的示例中,我们直接指定了输入工具的ID。但工具的ID可能是变化的,一些场景中,我们无法在编写代码时就确定工程中的 输入工具的名称,为了解决这类问题,从0.9.2版本开始我们提供一个更简便的接口,可以通过工具类型来查询工具的ID列表:

// query the tool id list with the tool type like "Input", "Segmentation", etc.
std::vector<std::string> input_id_list = model.tool_list("Input");

if (input_id_list.size() != 1) {
    std::cout << "There are " << input_id_list.size()
            << " input tools, please specify the tool id." << std::endl;
    throw std::runtime_error("Invalid input tool number.");
}

string input_id = input_id_list[0]; // The id of the Input tool.
# query the tool id list with the tool type like "Input", "Segmentation", etc.
input_id_list = model.tool_list("Input")

if len(input_id_list) != 1:
    print(f"There are {len(input_id_list)} input tools, please specify the tool id.")
    raise Exception("Invalid input tool number.")

input_id = input_id_list[0]
// query the tool id list with the tool type like "Input", "Segmentation", etc.
var input_id_list = model.tool_list("Input");

if (input_id_list.Count != 1)
{
    Console.WriteLine(
        $"There are {input_id_list.Count} input tools, please specify the tool id.");
    throw new Exception("Invalid input tool number.");
}

string input_id = input_id_list[0];

在执行之后,我们可以从样本中检索每个工具的检测结果(及中间结果),并根据我们的要求处理这些结果 (你可以通过查阅每个 工具的详细流程图 获得每个中间或最终结果的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:
segment_pred.draw_on(image);
image.show();
segment_pred  = sample.get(vflow.ToolNodeId(segmentation_id, vflow.Segmentation.pred))
print("Result: ", segment_pred.to_json().to_string())

for id, region in segment_pred:
    print(
        f"Found a defect: {id}, name: {region.name()}, area: {region.area()}"
    )
segment_pred.draw_on(image)
image.show()
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());
}

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