Package specification¶
A MuseBox package holds your model and the instructions the AI server needs to run it. You normally
create it with mbx-compile init (chapter 6), which writes everything for you.
This page describes what is inside, for when you want to check or change it by hand.
What a package contains¶
my_model/
├── manifest.json how to run the model (required)
├── model.hef the model: .hef (Hailo NPU), .tflite or .onnx (CPU)
├── labels.txt class names, one per line (optional)
├── preprocess.cpp your own image preparation (optional)
├── postprocess.cpp your own decoding of the output (optional)
└── musebox_plugin.hpp added by mbx-compile package when there are .cpp files
mbx-compile package turns the folder into a single .mbx file, and mbx-compile deploy copies it into
/root/musebox-ai-servers/ai_models/ on the board: use them. If you copy the folder yourself
(scp -r my_model root@<board-ip>:/root/musebox-ai-servers/ai_models/) and it has .cpp files, copy
musebox-compiler/include/musebox_plugin.hpp into it too, because only package adds it. In either case the AI server unpacks .mbx files and loads every folder
that has a manifest.json when it starts.
The .cpp files are compiled on the board the first time the model is loaded. See
Custom pre- and post-processing.
manifest.json¶
An example, for a ResNet-50 classifier on the Hailo NPU:
{
"format_version": "1.0",
"model_name": "resnet_v1_50",
"task_type": "classification",
"backend": "hailo",
"hef_file": "resnet_v1_50.hef",
"input": {
"shape": [224, 224, 3],
"format": "RGB"
},
"postprocessing": {
"top_k": 5,
"apply_softmax": false,
"labels_file": "labels.txt"
}
}
The model¶
| Field | Description |
|---|---|
format_version |
Always "1.0". |
model_name |
The model's name: the editor, the clients and /models show it. Letters, digits and _. |
task_type |
What the model does: see Tasks. |
backend |
Where it runs: "hailo" (NPU), "tflite" or "onnx" (CPU). |
hef_file / tflite_file / onnx_file |
The model file, for the chosen backend. |
version, author, description |
Free text, for you. |
The input image¶
| Field | Description |
|---|---|
input.shape |
The size of the model's input, for example [224, 224, 3] or [1, 3, 224, 224]. Both channel orders work. |
input.format |
The colour order the model expects: "RGB" (default), "BGR" or "GRAY". |
input.data_type |
CPU models only: "FLOAT32" (default), or "UINT8" / "INT8" for quantized models. |
input.normalization |
CPU models with float input only: {"mean": [...], "std": [...]}, one value per channel. Each pixel becomes (pixel - mean) / std. If all the values are at most 1, the pixel is first divided by 255. |
preprocessing.type |
How the frame is resized to the input: "standard" (default) stretches it, "letterbox" keeps the proportions and fills the rest with grey. |
Models on the Hailo NPU receive the 8-bit pixels directly: the normalization is part of the .hef.
The result¶
| Field | Description |
|---|---|
postprocessing.labels_file |
The class names file, default labels.txt. |
postprocessing.confidence_threshold |
Detection: results below this score are dropped. A client can change it per request. |
postprocessing.max_detections |
Detection: at most this many results per frame. |
postprocessing.top_k |
Classification: how many classes to return. |
postprocessing.apply_softmax |
Classification: true if the model outputs raw scores rather than probabilities. |
preprocessing.custom_params, postprocessing.custom_params |
Your own settings, read by your .cpp files. |
CPU models¶
| Field | Description |
|---|---|
runtime.num_threads |
How many CPU cores to use. 0 (default) chooses automatically. |
mbx-compile also writes other fields (outputs, tensor_name, nms_threshold, the rest of
runtime): they describe the model, and the AI server does not need them.
Tasks¶
Without a postprocess.cpp, the AI server decodes the output with its built-in post-processing, chosen by
task_type. It works only when the model's output already has the format below:
task_type |
The model's output must be | What the AI server returns |
|---|---|---|
object_detection |
One tensor with the final boxes, one row per object: [ymin, xmin, ymax, xmax, score] or [ymin, xmin, ymax, xmax, score, class], coordinates from 0 to 1 or in pixels. Hailo models compiled with NMS on the chip produce exactly this. |
A box, a label and a score for each object (confidence_threshold, max_detections). |
classification |
One vector with a score per class, in the order of labels.txt (probabilities, or raw scores with apply_softmax: true). |
The top_k classes, with label and probability. |
segmentation |
One tensor with a score per class for each pixel (the highest wins), or with the class number of each pixel: height × width × classes on Hailo and TFLite, classes × height × width on ONNX. | The class of each pixel (class_mask_rle, width, height) and the classes found (classes_detected). The editor and the clients draw it over the image. |
feature_extraction |
One vector (embedding). | The vector (embedding, dim), and every output tensor as for custom. |
custom |
Anything: no decoding. | The values of every output tensor (tensors: shape, dtype, values). A tensor above 65 536 values comes without values (values_omitted). |
NMS (non-maximum suppression) removes the overlapping boxes of a detector. If mbx-compile inspect shows a
single output that already lists the boxes (5 or 6 values each), NMS runs on the chip and the built-in
object_detection works; otherwise you need a postprocess.cpp.
object_detection, classification and segmentation read only the first output tensor. Any other output (for
example YOLO before NMS, SSD anchors, several heads, depth maps) needs a postprocess.cpp that decodes it; with
custom you get the raw values, but nothing in the editor or the clients knows how to show them.
The segmentation mask is sent run-length encoded ("encoding": "rle"): every run of pixels of the same class is
5 bytes, the class (uint8) and the length (uint32, little-endian), row by row, in base64. A request with
"mask_encoding": "raw" gets class_mask instead: one byte per pixel. Class numbers above 255 are sent as 255.
With a postprocess.cpp, the AI server returns whatever your code produces, for any task_type.