Skip to main content

Model in 5 Minutes

FieldValue
DifficultyBeginner
Estimated Read Time<5 minutes
Labelsmodel, mpk, inference, foundations

Concept

This tutorial teaches the quickest practical path to run inference with a compiled model in NEAT.

A compiled model is a deployable model package (.tar.gz, often called an MPK) that NEAT can load and execute on the target device. It contains the model artifacts and runtime metadata needed for inference. You provide input data, run inference, and consume model outputs.

After this chapter, you should understand the minimum end-to-end loop:

  • Load a compiled model package.
  • Prepare input data that matches model expectations.
  • Run synchronous inference.
  • Read and validate output behavior.

References

Learning Process

  1. Set up runtime inputs: parse CLI args, locate the compiled ResNet50 MPK, and prepare sample input data.
  2. Build the minimal model execution path for one model and one input stream.
  3. Run synchronous inference to keep behavior deterministic and easy to debug.
  4. Inspect top-1 predictions and validation output (CHECK, SIGNATURE, [OK]).

Run

NEAT_EXTRAS_ROOT=<sima-neat-*-Linux-extras>
cd $NEAT_EXTRAS_ROOT/lib/sima-neat/tutorials
./tutorial_v2_001_model_in_5_minutes --mpk /path/to/model.tar.gz

Code

tutorials/001_model_in_5_minutes/model_in_5_minutes.cpp
// Run a ResNet-50 model on an image in three lines of NEAT.
//
// Usage:
// tutorial_v2_001_model_in_5_minutes --mpk /path/to/resnet_50.tar.gz [--image /path/to.jpg]

#include "neat.h"

#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>

#include <cstring>
#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;
}

cv::Mat load_rgb(const fs::path& image_path, int size) {
cv::Mat bgr = cv::imread(image_path.string(), cv::IMREAD_COLOR);
if (bgr.empty())
throw std::runtime_error("failed to read image: " + image_path.string());
if (bgr.cols != size || bgr.rows != size) {
cv::resize(bgr, bgr, cv::Size(size, size), 0, 0, cv::INTER_AREA);
}
cv::Mat rgb;
cv::cvtColor(bgr, rgb, cv::COLOR_BGR2RGB);
if (!rgb.isContinuous())
rgb = rgb.clone();
return rgb;
}

simaai::neat::Model::Options build_options(int size) {
simaai::neat::Model::Options opt;
opt.format = "RGB";
opt.input_max_width = size;
opt.input_max_height = size;
opt.input_max_depth = 3;
opt.preproc.channel_mean = {0.485f, 0.456f, 0.406f};
opt.preproc.channel_stddev = {0.229f, 0.224f, 0.225f};
return opt;
}

int top1_from_output(const simaai::neat::Sample& out) {
if (!out.tensor.has_value())
throw std::runtime_error("no tensor output");
const simaai::neat::Mapping m = out.tensor->map_read();
const size_t n = m.size_bytes / sizeof(float);
const float* p = reinterpret_cast<const float*>(m.data);
int best = 0;
for (size_t i = 1; i < n && i < 1000; ++i) {
if (p[i] > p[best])
best = static_cast<int>(i);
}
return best;
}

} // namespace

int main(int argc, char** argv) {
try {
std::string mpk, image;
if (!get_arg(argc, argv, "--mpk", mpk)) {
std::cerr << "Usage: tutorial_v2_001_model_in_5_minutes --mpk <path> [--image <path>]\n";
return 1;
}
get_arg(argc, argv, "--image", image);

const int size = 224;

// CORE LOGIC
// The three-line NEAT story:
simaai::neat::Model model(mpk, build_options(size));
cv::Mat input = image.empty() ? cv::Mat(size, size, CV_8UC3, cv::Scalar(99, 99, 99))
: load_rgb(image, size);
simaai::neat::Sample sample = model.run(input, /*timeout_ms=*/2000);

std::cout << "top1=" << top1_from_output(sample) << "\n";
std::cout << "[OK] 001_model_in_5_minutes\n";
return 0;
} catch (const std::exception& e) {
std::cerr << "[FAIL] " << e.what() << "\n";
return 1;
}
}

Source