管理工程中的工具#

打开一个工程 后,可以管理其中的工具。

auto project = visionflow::Project::Open(...);

添加工具#

使用 visionflow::Project::add_tool() 函数向工程中添加工具。

该函数接收2个参数。

第1个为字符串,指定工具类型。可用的工具包括VisionFlow内置的输入(Input)、 分割(Segmentation)等,和用户通过 visionflow::graph::ToolFactory::Register() 自行注册的工具。可以使用 visionflow::graph::ToolFactory::GetAllToolNames() 获取。

Note

如果指定的工具类型有误,将抛出 visionflow::excepts::InvalidToolType 异常。

第2个参数可选,为工具的唯一标识符,默认为空字符串。 当参数留空时,VisionFlow会根据工具类型生成一个不冲突的标识符。

Note

如果指定了标识符,且工程中已经存在的同名工具,将抛出 visionflow::excepts::ToolAlreadyExist 异常。

函数返回新添加工具的标识符。如果通过参数指定了标识符,则返回的标识符一定与指定的相同。

// 自动生成一个标识符 返回的标识符可能形如 Input_1
std::string tool_id_input = project->add_tool("Input");
// 指定标识符 只要成功添加,保证返回的标识符一定与指定的相同
std::string tool_id_seg = project->add_tool("Segmentation", "Seg_1");
assert(tool_id_seg == "Seg_1");
// 指定标识符,但是工程中已经存在同名工具,抛出异常
// std::string exception = project->add_tool("Segmentation", "Seg_1");

查看现有工具列表#

使用 visionflow::Project::tool_list() 函数获取工程中所有工具的标识符列表。

在工具之间建立连接#

手动连接#

使用 visionflow::Project::connect() 函数手动建立连接。

该函数接收2个参数,为 visionflow::ToolNodeId 类型,分别指定源节点和目标节点。 每个 ToolNodeId 中包括工具标识符、节点标识符和可省略的下标。

// 将输入工具的图像输出连接到分割工具的图像输入
project->connect({ "Input_1", "image" }, { "Seg_1", "image" });
// 另一种可能的ToolNodeId形式
project->connect({ "Input_1/views" }, { "Seg_1/views" });
// 含有下标的节点标识符
project->connect({ "Input_1", "image" }, { "Integration", "properties", 0 });
project->connect({ "Seg_1", "match_result" }, { "Integration/properties[1]" });

如果2个节点之间已经存在连接,将不进行任何操作。 如果指定的目标节点含有合法下标,将源节点连接到目标节点的指定下标;覆盖此下标已经存在的连接。 如果指定的目标节点不含下标,且目标节点的输入数量仍有剩余,将自动连接到目标节点的下一个输入下标。

Note

必须保证源和目标标识符正确、不会成环、输入输出方向正确、类型匹配。如果存在错误,将抛出以下异常: visionflow::excepts::ToolNotFound visionflow::excepts::NodeNotFound visionflow::excepts::LoopConnection visionflow::excepts::InvalidConnection visionflow::excepts::DataTypeMismatch visionflow::excepts::InActiveOutputNode

自动连接(不推荐)#

使用 visionflow::Project::auto_connect() 函数尝试自动建立连接。 该函数接收2个参数,分别为源工具标识符和目标工具标识符。

该函数会尽可能为目标工具的所有输入节点建立连接。如果目标工具存在多输入节点, 或存在函数无法自动建立连接的输入节点,均认为自动连接失败, 函数返回 false ,不会做出任何更改。如果连接成功,函数返回 true

Note

该函数建立的连接可能并不符合预期。因此,不推荐 在复杂的工程中使用该函数。

即使成功自动连接,也应仔细检查建立的连接是否是用户所期望的。

尝试建立连接的逻辑如下:

可以使用的输出节点范围是:源工具的所有输出节点,以及源工具的所有前序依赖的输出节点,按照拓扑顺序

4次遍历仍未建立连接的输入节点,优先选择尚未连接且数据特征完全匹配的输出节点, 然后选择已经连接或数据特征不完全匹配的输出节点。具体逻辑是:

  1. 尚未连接、与输入节点的特征要求完全匹配的输出节点

  2. 尚未建立连接的输出节点中,能满足输入要求特征的其他输出节点(不是完全匹配,即输出特征多于输入特征)

  3. 所有输出节点中,特征完全匹配的输出节点

  4. 所有输出节点中,能满足输入要求的任何输出节点

bool ok = project->auto_connect("Input_1", "Seg_1");
// 如果 ok==false,保证不对工程做出任何更改

修改连接关系#

修改现有连接#

对于已经建立的连接,可以通过 visionflow::Project::connect() 函数修改连接关系。

通过参数传入新的源工具的标识符和节点,和已经连接的目标工具、节点、下标,即断开目标工具原有连接, 在传入参数节点之间建立新连接。

// 现有连接
project->connect({ "Input_1/image" }, { "Integration/properties[0]" });
// 修改连接:Input_1/image 到 Integration/properties[0] 的连接断开
project->connect({ "Input_2/image" }, { "Integration/properties[0]" });

断开连接#

使用 visionflow::Project::disconnect() 函数断开连接。

指定上下游节点的重载#

函数接收2个 visionflow::ToolNodeId 类型的参数,分别指定要断开连接的源节点和目标节点。

// 断开连接
project->disconnect({ "Input_1/image" }, { "Integration/properties[0]" });

Note

需保证指定的源节点和目标节点存在、输入输出方向正确、类型正确、连接存在,否则将抛出以下异常: visionflow::excepts::ToolNotFound visionflow::excepts::NodeNotFound visionflow::excepts::InvalidConnection visionflow::excepts::InvalidNodeId

如果指定了下标,且下标超过实际连接数, 不会抛出异常 ,也不做任何更改。

只指定下游节点的重载#

函数接收1个 visionflow::ToolNodeId 类型的参数,指定要断开连接的下游节点。

如果参数指定了下标,只断开该下标的连接;否则,断开该节点上的所有连接。

project->disconnect({ "Integration/properties[0]" });

删除工具#

使用 visionflow::Project::remove_tool() 函数删除工具。

函数接收2个参数。第1个为工具的标识符。

第2个参数 remove_all_depends_on_this 为可选的删除策略,默认为 false ,即只删除本工具; 如果传入 true ,将同时删除所有直接或间接依赖于本工具的工具。

函数会删除工具、断开所有与被删除工具的连接、删除工具中的所有数据和自定义内容。

// 删除工具
project->remove_tool("Seg_1");

// 删除工具和下游所有依赖该工具的工具
project->remove_tool("Input_1", true);

Note

如果传入工具标识符不存在, 不会 抛出异常,不进行任何修改。

复制工具#

复制单个工具#

使用 visionflow::Project::copy_tool() 函数复制单个工具。

函数接收3个参数。第1个为被复制的工具标识符。第2个为可选参数,指定新工具的标识符;默认为空字符串, 表示自动生成新标识符。第3个为 visionflow::graph::CopyToolOptions 类型可选参数,指定复制策略。

复制策略包含2个字段。

auto_connect 指定是否为新复制的工具建立连接,默认为 false ,即新复制的工具上不带有任何连接。

tool_mapsstd::map<std::string, std::string> ,仅在 auto_connecttrue 时有效, 对于源工具的每个上游连接,如果 tool_maps 中存在上游工具标识符的映射,新工具将连接到映射的工具上; 否则将与源工具连接到相同的上游工具。

举例说明,假设工程中现有如图所示的工具

../_images/tutorial-tool_manage-copy_tool.png

指定将源工具复制一份, CopyToolOptions{ true, {{"B1", "B2"}} } 。 复制的结果如下图,可见新工具到上游A的连接仍然存在,到上游B1的连接被重新映射到B2。

../_images/tutorial-tool_manage-copy_tool-1.png

函数返回新工具的标识符。如果通过传入参数指定了标识符,保证返回的标识符与传入参数相同。 源工具内部的数据都将被原样复制。

// 新的工具有新生成的标识符、源工具的数据,没有连接
std::string new_seg_1 = project->copy_tool("Seg_1");
// 新工具标识符固定为 Seg_2
std::string new_seg_2 = project->copy_tool("Seg_1", "Seg_2");
// 新工具的有新生成的标识符,与源工具连接到相同的上游工具
std::string new_seg_3 = project->copy_tool("Seg_1", "", { true });
// 源工具原来向上连接到 Input_1,新工具连接到 Input_2
std::string new_seg_4 = project->copy_tool("Seg_1", "", { true, { { "Input_1", "Input_2" } } });

Note

如果源工具标识符为空或不存在,分别抛 visionflow::excepts::InvalidToolIdvisionflow::excepts::ToolNotFound 异常。

如果指定了新工具标识符,但此标识符已经存在,抛 visionflow::excepts::ToolAlreadyExist 异常。

如果指定了 tool_maps ,但其中指定的新上游工具标识符不存在,抛 visionflow::excepts::ToolNotFound 异常。

复制工具组#

使用 visionflow::Project::copy_tool_group() 函数复制工具组。

函数接收2个参数。第1个为 std::set<std::string> ,指定将被复制的工具标识符。 第2个为可选的 visionflow::graph::CopyToolOptions 类型参数以指定复制策略。

复制工具组时不支持指定新工具的标识符,都由VisionFlow自动生成。

当传入的 CopyToolOptionsauto_connectfalse 时, 新复制的工具将不包含任何连接,包括源工具组内部的连接也被删除;为 true 时, 工具组内部的连接将原样被复制,组内工具的上游连接如复制单个工具一样被复制。

函数返回一个 std::map<std::string, std::string> ,为源工具标识符到复制工具标识符的映射。

举例说明,工程中现有如图所示的工具,将工具A、B、C作为一组复制一份。

../_images/tutorial-tool_manage-copy_tool_group-1.png

传入 CopyToolOptions{ true } ,即保留内部和上游的连接关系,结果如图所示

../_images/tutorial-tool_manage-copy_tool_group-2.png

复制工具组也支持将上游连接映射到新的工具,请参考复制单个工具的例子。

// 这样复制出的两个工具不具有任何连接
auto copied_tools_1 = project->copy_tool_group({ "Seg_1", "Seg_2" });
// Seg_1 与 Seg—_2 之间如果有连接,新工具间也复制相同的连接
// Seg_1 Seg_2 的上游连接也被复制
auto copied_tools_2 = project->copy_tool_group({ "Seg_1", "Seg_2" }, { true, {} });

Note

抛出异常的情况与复制单个工具相同。

重命名工具#

使用 visionflow::Project::rename_tool() 函数重命名工具。

函数接收2个字符串参数,为工具当前的标识符和新的标识符。

函数将用新的标识符新建一个相同的工具,然后复制数据和连接,最后删除原工具。

project->rename_tool("Seg_1", "Seg_1_renamed");

Note

如果任何一个标识符为空,抛 visionflow::excepts::InvalidToolId 异常。

如果旧工具不存在,抛 visionflow::excepts::ToolNotFound 异常。

如果新工具标识符已经存在,抛 visionflow::excepts::ToolAlreadyExist 异常。

在复制数据时还可能抛出 visionflow::excepts::DatasetNotExists visionflow::excepts::DatasetAlreadyExists visionflow::excepts::RocksDBError 异常。

导出/导入工程模板#

工程模板是一个JSON文件,工程中的工具和其连接关系可以被导出为工程模板,从工程模板可以导入这些工具和之间的连接。

使用 visionflow::Project::export_proj_template() 函数导出工程模板, 使用 visionflow::Project::import_proj_template() 函数导入工程模板。

2个函数都只接收1个参数,指定工程模板文件的路径。

工程模板的导入等价于向工程中添加模板中的所有工具,然后在这些工具之间建立连接。 因此,导入时工程内可以含有其他工具和连接,只要其标识符不与模板内冲突即可。

project->export_proj_template("template.json");
project->import_proj_template("template.json");

导出的工程模板内容如以下所示, tools 是工具标识符到其类型的映射, connections 是连接关系的数组。

{
    "tools": {
        "Input_1": "Input",
        "Segmentation_1": "Segmentation",
    },
    "connections": [
        {
            "from_node_id": "image",
            "from_tool_id": "Input_1",
            "to_node_id": "image",
            "to_tool_id": "Segmentation_1"
        },
        {
            "from_node_id": "views",
            "from_tool_id": "Input_1",
            "to_node_id": "views",
            "to_tool_id": "Segmentation_1"
        }
    ]
}

Note

导出模板时,如果指定的输出文件已经存在或无法正常创建,分别抛出 visionflow::excepts::FileExistedvisionflow::excepts::CanNotOpenFile 异常。

Note

导入模板时,如果指定输入文件无法正常打开,抛 visionflow::excepts::CanNotOpenFile 异常;

如果文件内容损坏导致无法导入,抛 visionflow::excepts::DeSerializeFailed 异常;

如果导入时添加工具出错,或在添加的工具之间建立连接出错,均抛出异常, 可参考对应函数 visionflow::Project::add_tool()visionflow::Project::connect()

查看工程检测流程#

获取Graphviz结构图#

使用 visionflow::Project::to_graphviz() 函数将工程转换成Graphviz的dot语言。

该函数接收1个 visionflow::graph::DotInfoOptions 参数,用于指定是否导出特定元素。

DotInfoOptions 有3个字段,依次为: with_node_type 控制是否显示节点类型, with_node_docs 控制是否显示节点描述, with_update_time 控制是否显示节点更新时间。

以上3个字段均默认为 true ,即导出的图示含有最多信息。

可以使用一些工具,如 Graphviz Online ,查看导出的图示。

std::string dotlang = project->to_graphviz();

获取工具内部信息#

使用 visionflow::Project::tool_info() 函数获取工具的信息。

该函数接收1个参数,为工具标识符。返回一个 visionflow::ToolInfo 对象。 通过该对象,可以查询工具类型、输入输出、内部计算节点等。

ToolInfo 对象的详细用法#

工具一般信息#

visionflow::ToolInfo::id()visionflow::ToolInfo::type() 函数返回工具的标识符和类型。

数据节点(数据边/属性或参数)#

visionflow::ToolInfo::input_edges() visionflow::ToolInfo::output_edges()visionflow::ToolInfo::data_edges() 函数返回一个 std::vector<visionflow::DataEdge> , 分别为工具的输入、输出数据节点和所有数据节点(包括输入输出)。

对于每个 visionflow::DataEdge 对象,可以获取的信息有:

  1. 节点标识符 visionflow::DataEdge::id()

  2. 允许的最大连接数 visionflow::DataEdge::allow_max_connect()

  3. 概念类型(属性或参数) visionflow::DataEdge::cpt_tag()

  4. 是否是虚拟输入节点 visionflow::DataEdge::is_virtual_port()

  5. 是否是工具的输出节点 visionflow::DataEdge::is_tool_output()

  6. 使用该数据的计算节点 visionflow::DataEdge::used_by()

如果是虚拟输入节点,还可以获取:

  1. 其要求的数据特征 visionflow::DataEdge::required_features()

  2. 连接到的上游工具 visionflow::DataEdge::redirects()

如果不是虚拟输入节点,还可以获取:

  1. 节点是否使能 visionflow::DataEdge::is_active()

  2. 节点的数据类型 visionflow::DataEdge::type()

  3. 产生该数据的计算节点 visionflow::DataEdge::generated_by()

  4. 更新时间 visionflow::DataEdge::last_update_time()

计算节点(算子和配置器)#

visionflow::ToolInfo::compute_nodes() 函数返回一个 std::vector<visionflow::ComputeNode> , 为工具的所有计算节点。

对于每个 visionflow::ComputeNode 对象,可以获取的信息有:

  1. 节点标识符 visionflow::ComputeNode::id()

  2. 节点类型 visionflow::ComputeNode::type()

  3. 概念类型(算子或配置器) visionflow::ComputeNode::cpt_tag()

  4. 最后更新时间 visionflow::ComputeNode::last_update_time()

  5. 使用的参数 visionflow::ComputeNode::param_nodes()

  6. 所有输入节点 visionflow::ComputeNode::input_nodes()

  7. 所有输出节点 visionflow::ComputeNode::output_nodes()