Skip to main content

Configure Model Options

Configure Model Options — animated walkthrough overview

FieldValue
DifficultyBeginner
Estimated Read Time5 minutes
Labelsmodel-options, configuration, contracts

Chapter 001 loaded a model with sensible defaults. Real models — especially detection models like YOLOv8 — need you to declare their contract: what pixel format and size the input arrives in, how it should be normalized, and how raw network outputs become filtered boxes. ModelOptions groups all of that into a single struct you fill in before construction.

This chapter configures a YOLOv8 model end to end, then inspects the contract the runtime resolved from those options. By the end you will have set input, preprocessing, and postprocessing knobs, read back the resolved input_specs()/output_specs()/metadata, and run one deterministic frame through the configured model.

Walkthrough

Declare input and preprocessing

The first block describes what a frame looks like and how to prepare it for the network. format (BGR here) and the input_max_width/height/depth bounds set the input contract the runtime validates against and sizes buffers for. The normalization fields supply the per-channel mean and standard deviation the model was trained with, so raw pixels are scaled into the range the network expects.

Fields live under opt.preprocess.*: kind = InputKind::Image, color_convert.input_format = PreprocessColorFormat::BGR, and normalize.enable = AutoFlag::On with mean/stddev as std::array<float, 3>.

tutorials/005_configure_model_options/configure_model_options.cpp
opt.preprocess.kind = simaai::neat::InputKind::Image;
opt.preprocess.color_convert.input_format = simaai::neat::PreprocessColorFormat::BGR;
opt.preprocess.input_max_width = 640;
opt.preprocess.input_max_height = 640;
opt.preprocess.input_max_depth = 3;
opt.preprocess.normalize.enable = simaai::neat::AutoFlag::On;
opt.preprocess.normalize.mean = std::array<float, 3>{0.485f, 0.456f, 0.406f};
opt.preprocess.normalize.stddev = std::array<float, 3>{0.229f, 0.224f, 0.225f};

Declare postprocessing

The second block shapes the detector's output. decode_type selects the YOLOv8 box-decode path, and score_threshold, nms_iou_threshold, and top_k filter the raw detections — dropping low-confidence boxes, merging overlapping ones, and capping how many survive. boxdecode_original_width/boxdecode_original_height give the decoder the source-frame geometry it needs to map normalized coordinates back to pixels, and name_suffix stabilizes the generated stage names so the pipeline graph stays readable when composed with others.

decode_type = BoxDecodeType::YoloV8; the geometry fields are boxdecode_original_width/boxdecode_original_height.

tutorials/005_configure_model_options/configure_model_options.cpp
opt.decode_type = simaai::neat::BoxDecodeType::YoloV8;
opt.score_threshold = 0.55f;
opt.nms_iou_threshold = 0.45f;
opt.top_k = 100;
opt.boxdecode_original_width = 640;
opt.boxdecode_original_height = 640;
opt.name_suffix = "_chapter";

Load and inspect the resolved contract

Constructing the Model with these options resolves the contract against the archive. We then read it back: input_specs() and output_specs() report the negotiated tensor constraints, and metadata() exposes the key/value contract baked into the archive. Inspecting these after load confirms the runtime accepted your options and tells you the concrete shapes you will be working with.

The specs are TensorConstraint values; we print the concrete shape.

tutorials/005_configure_model_options/configure_model_options.cpp
simaai::neat::Model model(model_path, opt);
print_spec("input_specs[0]", model.input_specs().front());
print_spec("output_specs[0]", model.output_specs().front());
std::cout << "metadata_keys=" << model.metadata().size() << "\n";

Run one frame

Finally we synthesize one 640×640 BGR frame and run it through the configured model, confirming the whole contract executes end to end and printing how many outputs came back.

The frame is a cv::Mat; run() returns a TensorList whose size() we print as outputs=.

tutorials/005_configure_model_options/configure_model_options.cpp
cv::Mat bgr(640, 640, CV_8UC3, cv::Scalar(10, 20, 30));
if (!bgr.isContinuous())
bgr = bgr.clone();
auto out = model.run(std::vector<cv::Mat>{bgr}, /*timeout_ms=*/2000);
std::cout << "outputs=" << out.size() << "\n";

Run

Run it and you should see the resolved spec shapes, metadata key count, and the output tally. Run the Python and C++ (prebuilt) commands from the Neat install root (the directory that contains share/ and lib/); run the build from source commands from the repo root.

C++ (prebuilt):

./lib/sima-neat/tutorials/tutorial_005_configure_model_options \
--model /tmp/yolo_v8s.tar.gz

C++ (build from source):

./build.sh --target tutorial_005_configure_model_options
./build/tutorials-standalone/tutorial_005_configure_model_options \
--model /tmp/yolo_v8s.tar.gz

Expected output (shape and key counts depend on the model archive; the C++ build prints the detailed spec lines and outputs=, the Python build prints shapes and output_count=):

input_specs[0]: shape=[640,640,3]
output_specs[0]: shape=[]
metadata_keys=8
outputs=1
[OK] 005_configure_model_options

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.

In Practice

Verbosity presets

Framework build/run messaging is controlled with VerboseOptions on GraphOptions, Model::Options, and Model::RouteOptions.

Current development default: VerboseOptions::debug_all(). Call production() or quiet() explicitly when you want less output.

PresetIntended use
VerboseOptions::quiet()Suppress framework progress and detail output.
VerboseOptions::production()Show clean phase progress only.
VerboseOptions::debug_plugins()Keep production UX, but also surface plugin and GStreamer topics.
VerboseOptions::debug_all()Force the full verbose/detail sweep across all topics.

For runtime queue/throughput tuning, see Tune Throughput and Queue Depth.

Full source

Show the complete C++ and Python programs
tutorials/005_configure_model_options/configure_model_options.cpp
// Model::Options chapter: configure input/preproc/boxdecode via Options, inspect specs.
//
// Usage:
// tutorial_005_configure_model_options --model /path/to/yolo_v8s.tar.gz

#include "neat.h"

#include <opencv2/core.hpp>

#include <array>
#include <iostream>
#include <stdexcept>
#include <string>

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;
}

void print_spec(const char* label, const simaai::neat::TensorConstraint& spec) {
std::cout << label << ": shape=[";
for (std::size_t i = 0; i < spec.shape.size(); ++i) {
std::cout << (i ? "," : "") << spec.shape[i];
}
std::cout << "]\n";
}

} // namespace

int main(int argc, char** argv) {
try {
std::string model_path;
if (!get_arg(argc, argv, "--model", model_path)) {
std::cerr << "Usage: tutorial_005_configure_model_options --model <path>\n";
return 1;
}

// Model::Options groups input caps, preproc, and box-decode into one struct.
simaai::neat::Model::Options opt;
opt.preprocess.kind = simaai::neat::InputKind::Image;
opt.preprocess.color_convert.input_format = simaai::neat::PreprocessColorFormat::BGR;
opt.preprocess.input_max_width = 640;
opt.preprocess.input_max_height = 640;
opt.preprocess.input_max_depth = 3;
opt.preprocess.normalize.enable = simaai::neat::AutoFlag::On;
opt.preprocess.normalize.mean = std::array<float, 3>{0.485f, 0.456f, 0.406f};
opt.preprocess.normalize.stddev = std::array<float, 3>{0.229f, 0.224f, 0.225f};
opt.decode_type = simaai::neat::BoxDecodeType::YoloV8;
opt.score_threshold = 0.55f;
opt.nms_iou_threshold = 0.45f;
opt.top_k = 100;
opt.boxdecode_original_width = 640;
opt.boxdecode_original_height = 640;
opt.name_suffix = "_chapter";

// CORE LOGIC
simaai::neat::Model model(model_path, opt);
print_spec("input_specs[0]", model.input_specs().front());
print_spec("output_specs[0]", model.output_specs().front());
std::cout << "metadata_keys=" << model.metadata().size() << "\n";

cv::Mat bgr(640, 640, CV_8UC3, cv::Scalar(10, 20, 30));
if (!bgr.isContinuous())
bgr = bgr.clone();
auto out = model.run(std::vector<cv::Mat>{bgr}, /*timeout_ms=*/2000);
std::cout << "outputs=" << out.size() << "\n";
std::cout << "[OK] 005_configure_model_options\n";
return 0;
} catch (const std::exception& e) {
std::cerr << "[FAIL] " << e.what() << "\n";
return 1;
}
}

Source