Plug a Model Into Your Pipeline
| Field | Value |
|---|---|
| Difficulty | Intermediate |
| Estimated Read Time | 15 minutes |
| Labels | graph, composition, patterns |
Concept
Three ways to drop a model into a Graph — direct node composition, model.graph(), and model.graph(options) — so you know which pattern fits which context. All three produce a runnable Graph; they differ in explicitness and control.
The three composition patterns shown:
- Direct Graph: add
Input+Outputnodes yourself withgraph.add(...). Most explicit; useful when you need full control over the wiring. - Model-default Graph:
model.graph()injects the model's default pipeline group with sensible defaults. Shortest path for most cases. - Model-attached Graph:
model.graph(ModelRouteOptions)controls appsrc/appsink inclusion and stage naming when attaching model groups into larger pipelines.
Why this matters:
- Teams often start with direct Graphs for clarity, then move to model-backed composition as systems scale.
ModelRouteOptionskeeps graph wiring explicit in multi-camera or multi-model deployments.- Consistent naming (
upstream_name,name_suffix,buffer_name) improves diagnostics and backend graph readability.
APIs introduced
model.graph()— inject the default model pipeline.pyneat.ModelRouteOptions()+model.graph(opts)— attach with explicit options.graph.add(group_or_node)— what all three patterns reduce to underneath.
Prerequisites Chapter 001 (Model). Chapter 002 or 003 (Graph basics).
References
Learning Process
- Build a minimal direct Graph and validate it can run end-to-end.
- Construct model-backed Graph variants (
model.graph()andmodel.graph(options)). - Compare generated backend graphs with
--print-gstto understand composition differences.
Run
Python:
python3 share/sima-neat/tutorials/007_plug_model_into_pipeline/plug_model_into_pipeline.py \
--model /tmp/yolo_v8s.tar.gz
C++ (prebuilt):
./lib/sima-neat/tutorials/tutorial_007_plug_model_into_pipeline \
--model /tmp/yolo_v8s.tar.gz
C++ (build from source):
./build.sh --target tutorial_007_plug_model_into_pipeline
./build/tutorials-standalone/tutorial_007_plug_model_into_pipeline \
--model /tmp/yolo_v8s.tar.gz
To integrate this chapter's C++ source into your own project with a custom CMakeLists.txt (no extras folder required), see How to Run Tutorials on the landing page.
Code
tutorials/007_plug_model_into_pipeline/plug_model_into_pipeline.cpp
// Three Graph composition patterns: direct nodes, model.graph(), attached Graph.
//
// Usage:
// tutorial_007_plug_model_into_pipeline [--model /path/to/model.tar.gz]
#include "neat.h"
#include <opencv2/core.hpp>
#include <filesystem>
#include <iostream>
#include <stdexcept>
#include <string>
namespace fs = std::filesystem;
namespace {
bool get_arg(int argc, char** argv, const std::string& key, std::string& out) {
for (int i = 1; i + 1 < argc; ++i) {
if (key == argv[i]) {
out = argv[i + 1];
return true;
}
}
return false;
}
} // namespace
int main(int argc, char** argv) {
try {
const int width = 224;
const int height = 224;
cv::Mat rgb(height, width, CV_8UC3, cv::Scalar(80, 40, 160));
if (!rgb.isContinuous())
rgb = rgb.clone();
// Pattern 1: build a Graph by adding Input/Output nodes directly.
simaai::neat::InputOptions in;
in.format = "RGB";
in.width = width;
in.height = height;
in.depth = 3;
in.do_timestamp = true;
// CORE LOGIC
simaai::neat::Graph direct;
direct.add(simaai::neat::nodes::Input(in));
direct.add(simaai::neat::nodes::Output());
std::string model_path;
if (get_arg(argc, argv, "--model", model_path) && fs::exists(model_path)) {
simaai::neat::Model model(model_path);
// Pattern 2: ask model.graph() to include explicit public Input/Output boundaries.
simaai::neat::Model::RouteOptions runnable_opt;
runnable_opt.include_input = true;
runnable_opt.include_output = true;
// CORE LOGIC
simaai::neat::Graph from_model;
from_model.add(model.graph(runnable_opt));
std::cout << "model_graph_backend=\n" << from_model.describe_backend() << "\n";
// Pattern 3: attach the model under an upstream name with custom options.
simaai::neat::Model::RouteOptions sopt;
sopt.include_input = false;
sopt.include_output = true;
sopt.upstream_name = "camera0";
sopt.name_suffix = "_camera0";
sopt.buffer_name = "camera0";
// CORE LOGIC
simaai::neat::Graph attached;
attached.add(model.graph(sopt));
std::cout << "attached_graph_backend=\n" << attached.describe_backend() << "\n";
}
auto run = direct.build(std::vector<cv::Mat>{rgb}, simaai::neat::RunMode::Sync);
simaai::neat::TensorList out = run.run(std::vector<cv::Mat>{rgb}, /*timeout_ms=*/1000);
if (out.empty())
throw std::runtime_error("direct Graph output missing tensor");
std::cout << "direct_rank=" << out.front().shape.size() << "\n";
std::cout << "[OK] 007_plug_model_into_pipeline\n";
return 0;
} catch (const std::exception& e) {
std::cerr << "[FAIL] " << e.what() << "\n";
return 1;
}
}