Pipeline fundamentals
The main building blocks
Pipeline
A Pipeline is the graph that executes your complete inference workflow.
You either:
- load it from a
.denkflowfile, or - build it manually from
.denkmodelfiles and utility nodes
A pipeline can be modified only before initialization.
- Python
- C#
- C / C++
# Load from export
pipeline = Pipeline.from_denkflow("model.denkflow", pat="YOUR-PAT")
# Or build manually
pipeline = Pipeline()
pipeline.add_image_resize_node(...)
// Load from export. C# uses an explicit LicenseSource; FromDenkflow has no
// PAT shorthand overload.
using var license = HubLicenseSource.FromPat("YOUR-PAT");
using var pipeline = Pipeline.FromDenkflow("model.denkflow", license);
// Or build manually.
using var customPipeline = new Pipeline();
customPipeline.AddImageResizeNode(/* ... */);
// Load from export
DenkflowPipeline* pipeline = NULL;
denkflow_pipeline_from_denkflow(&pipeline, "model.denkflow", (void*)license_source);
Node
A Node is one processing step inside the pipeline, such as:
- image resize
- object detection
- classification
- OCR
- segmentation
- anomaly detection
Topic
A Topic is the connection name used to move tensors between producers and consumers.
Topic strings follow this format:
[{node_name}]/{port_name}[?timeout={ms}]
- Node outputs and internal connections use
{node_name}/{port_name}— for exampleimage-resize/resized_imageorbb-filter/filtered_bounding_boxes. - Pipeline inputs and constants use an empty node name:
/port_name— for example/imagefor the external image input, or/target_sizefor a fixed resize dimension. - Optional timeout — append
?timeout=500to set a per-topic receive timeout in milliseconds.
Constant topic values
Instead of dedicated constant nodes, you set fixed tensor values on topics before initialize(). Constant topics must use the /port_name form (empty node name).
- Python
- C#
- C / C++
pipeline.set_constant_value("/target_size", np.array([1088, 1280], dtype=np.int64))
defaults = pipeline.get_constant_values() # before initialize()
pipeline.remove_constant_value("/target_size")
pipeline.SetConstantValue("/target_size", new long[] { 1088, 1280 });
var constants = pipeline.GetConstantValues(); // before Initialize()
var targetSize = (long[])constants.Single(
value => value.TopicName == "/target_size").Data;
pipeline.RemoveConstantValue("/target_size");
GetConstantValues() returns ConstantValueInfo records containing
TopicName, TensorType, DataType, Shape, and Data. Data is a
caller-owned, flat managed array whose element type matches DataType; use
Shape to interpret its row-major tensor dimensions.
For other element types or multidimensional shapes, build a BaseTensor with
BaseTensor.FromBuffer(...) and pass it to SetConstantValue. The C# method
clones the tensor, so the caller retains and disposes the original wrapper.
DenkflowConstantValueArray* constants = NULL;
DenkflowBaseTensor* tensor = NULL;
int64_t values[2] = {1088, 1280};
size_t shape[1] = {2};
denkflow_base_tensor_from_buffer(&tensor, values, DenkflowArrayDataType_Int64, shape, 1);
denkflow_pipeline_set_constant_value(pipeline, "/target_size", (void **)&tensor);
denkflow_pipeline_get_constant_values(&constants, pipeline);
denkflow_pipeline_remove_constant_value(pipeline, "/target_size");
denkflow_constant_value_array_free(&constants);
Build numeric tensors with denkflow_base_tensor_from_buffer before calling denkflow_pipeline_set_constant_value. Both functions consume the tensor pointer on success.
Each DenkflowConstantValue contains topic_name, tensor_type, shape/shape_length, data, and data_type (DenkflowArrayDataType enum).
Runtime parameters on exported pipelines
When you load a .denkflow file, graph constants from the export are applied automatically (for example /iou_threshold or /image_size). To supply different values on each run:
- Discover constant topics and their stored payloads with
get_constant_values()beforeinitialize() - Call
remove_constant_value(topic)for each parameter you intend to override - Publish your own values before every
run()
See Runtime parameters on exported pipelines for a full walkthrough.
Inspecting topics with TopicDescription
After initialize(), call get_topics() to list every topic in the pipeline. Each entry is a TopicDescription with:
topic_name— the topic stringtopic_type—ExternalInput,ExternalOutput, orInternalConnectiontensor_type— e.g.ImageTensor,BoundingBoxTensoris_constant—truewhen the topic has a fixed initializer value
- Python
- C#
- C / C++
pipeline.initialize()
for topic in pipeline.get_topics():
if topic.topic_type.name == "ExternalInput" and not topic.is_constant:
print("Publish to:", topic.topic_name)
if topic.topic_type.name == "ExternalOutput":
print("Subscribe to:", topic.topic_name)
pipeline.Initialize();
foreach (var topic in pipeline.GetTopics())
{
if (topic.TopicType == TopicType.ExternalInput && !topic.IsConstant)
Console.WriteLine($"Publish to: {topic.Name}");
if (topic.TopicType == TopicType.ExternalOutput)
Console.WriteLine($"Subscribe to: {topic.Name}");
}
DenkflowTopicDescriptionArray* topics = NULL;
denkflow_initialized_pipeline_get_topics(&topics, initialized_pipeline);
for (size_t i = 0; i < topics->topic_information_length; ++i) {
DenkflowTopicDescription* t = &topics->topic_information[i];
// t->topic_name, t->topic_type, t->tensor_type, t->is_constant
}
Tensor
A Tensor is a payload flowing through the graph.
Common tensor types include:
ImageTensorBoundingBoxTensorScalarTensorOcrTensorSegmentationMaskTensorInstanceSegmentationMaskTensorBarcodeTensorImageTensorU8
Receiver
A Receiver subscribes to a topic and lets you wait for output data.
This is how you collect inference results after calling run().
- Python
- C#
- C / C++
receiver = pipeline.subscribe("filter/output")
# Then use the typed receive method that matches the topic's tensor type:
# receiver.receive_bounding_box_tensor()
using var receiver = pipeline.Subscribe("filter/output");
// Then use the typed receive method that matches the topic's tensor type:
// using var boxes = receiver.ReceiveBoundingBoxTensor();
DenkflowReceiverTensor* receiver = NULL;
denkflow_initialized_pipeline_subscribe(&receiver, initialized_pipeline, "filter/output");
Typical evaluation workflow
Most inference scripts follow this shape:
- Create or load a pipeline
- Initialize the pipeline
- Subscribe to one or more output topics
- Publish input tensors
- Call
run() - Receive and decode the result
- Python
- C#
- C / C++
pipeline = Pipeline.from_denkflow("model.denkflow", pat="PAT")
pipeline.initialize()
# Discover topics (see Topic section above)
input_topics = [
t.topic_name for t in pipeline.get_topics()
if t.topic_type.name == "ExternalInput" and not t.is_constant
]
output_topics = [
t.topic_name for t in pipeline.get_topics()
if t.topic_type.name == "ExternalOutput"
]
receiver = pipeline.subscribe(output_topics[0])
pipeline.publish_image_tensor("/image", ImageTensor.from_file("image.jpg"))
pipeline.run()
objects = receiver.receive_bounding_box_tensor().to_objects(0.5)
using var license = HubLicenseSource.FromPat("PAT");
using var pipeline = Pipeline.FromDenkflow("model.denkflow", license);
pipeline.Initialize();
var topics = pipeline.GetTopics();
var inputTopic = topics.First(t =>
t.TopicType == TopicType.ExternalInput && !t.IsConstant).Name;
var outputTopic = topics.First(t =>
t.TopicType == TopicType.ExternalOutput).Name;
using var receiver = pipeline.Subscribe(outputTopic);
using var image = ImageTensor.FromFile("image.jpg");
pipeline.Publish(inputTopic, image);
pipeline.Run();
using var tensor = receiver.ReceiveBoundingBoxTensor();
var objects = tensor.ToObjects(0.5f);
Publish clones the native tensor. The image wrapper remains valid after
the call and is still owned by the caller.
denkflow_pipeline_from_denkflow(&pipeline, "model.denkflow", (void*)license_source);
denkflow_initialize_pipeline(&initialized_pipeline, &pipeline);
denkflow_initialized_pipeline_subscribe(&receiver, initialized_pipeline, "output/topic");
denkflow_image_tensor_from_file(&image_tensor, "image.jpg");
denkflow_initialized_pipeline_publish_tensor(initialized_pipeline, "/image", (void **)&image_tensor);
denkflow_initialized_pipeline_run(initialized_pipeline, 8000);
denkflow_receiver_receive_bounding_box_tensor(&tensor, receiver);
denkflow_bounding_box_tensor_to_objects(&results, tensor, 0.5f);
.denkflow versus .denkmodel
.denkflow
Use .denkflow when:
- you want the simplest deployment path
- you want export-target-specific runtime configuration
- you want quantized deployment artifacts
.denkmodel
Use .denkmodel when:
- you want to assemble the graph yourself
- you want to choose or force runtime strings like
cuda,tensorrt,directml, oropenvinofrom Python, C#, or C/C++ - you want to combine multiple models in one hand-built pipeline
Execution providers and device IDs
ONNX-backed nodes run on an execution provider (cpu, cuda, tensorrt,
directml, or openvino). Custom pipelines select it on node methods that
expose execution_provider= (Python), executionProvider (C#), or the C-API
equivalent. ONNX-backed nodes in exported .denkflow files carry the runtime
intent chosen at export time and can be redirected before initialization;
compiled Ambarella nodes cannot be redirected.
For the full provider list, device-id semantics (e.g. OpenVINO -1/-2/>=0), and override rules, see Runtime and Device Selection.
Recommended production strategy
- Export a
.denkflowfile from the Vision AI Hub - Install the SDK for the target runtime and language
- Keep the SDK data directory persistent (default paths are in Configuration; in Docker, mount a host volume at that default path or at the path
DENKFLOW_DATA_DIRECTORYpoints to) - Deploy and benchmark on the actual target hardware