mxprepost provides a unified, high-performance interface for YOLO pre-processing and post-processing, designed for seamless integration with MemryX MXA applications across YOLOv8–YOLOv11 tasks such as detection, segmentation, and pose estimation.
It handles tasks like:
- Pre-processing: Frame preparation (e.g., resizing, normalization).
- Post-processing: Model output decoding, Non-Maximum Suppression (NMS), class filtering, and result annotation.
It is intended to be used inside the MXA input/output callbacks (via mxapi.MxAccl). Under the hood, optimized C++ bindings accelerate compute-intensive operations such as NMS and output decoding.
-
Create
mxapi.MxAccl -
Connect input/output callbacks
-
Initialize
mxprepost.MxPrepostwith the accelerator -
Call:
preprocess(frame)in input callbackpostprocess(mxa_output, ori_height, ori_width)orpostprocess(mxa_output, ori_frame)in output callbackdraw(frame, result)for visualization
import cv2
import mxprepost
from memryx import mxapi
class App:
def __init__(self, dfp_path, video_path):
# Open video source
self.cap = cv2.VideoCapture(video_path)
# Store original frame dimensions
self.ori_width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))
self.ori_height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
# Create MXA accelerator
self.accl = mxapi.MxAccl(dfp_path, [0], [False, False], False)
#---------------------------------------------------------------
#-------------------------- MxPrepost --------------------------
#---------------------------------------------------------------
# Initialize MxPrepost
self.pp = mxprepost.MxPrepost(
accl=self.accl,
task="yolov8-det" # (task examples: "yolov8-det", "yolov8-seg", "yolov11-pose")
)
# Connect callbacks
self.accl.connect_stream(self.in_callback, self.out_callback, stream_id=0)
def in_callback(self, stream_id):
ret, frame = self.cap.read()
if not ret:
return None
#---------------------------------------------------------------
#-------------------------- MxPrepost --------------------------
#---------------------------------------------------------------
return self.pp.preprocess(frame)
def out_callback(self, mxa_output, stream_id):
#---------------------------------------------------------------
#-------------------------- MxPrepost --------------------------
#---------------------------------------------------------------
# Postprocess using original dimensions
result = self.pp.postprocess(
mxa_output,
self.ori_height,
self.ori_width
)
# Access outputs (depending on task)
# ---------------------------
# Detection (Bounding Boxes)
# ---------------------------
if result.boxes is not None:
for box in result.boxes:
print("xyxy:", box.xyxy) # [x1, y1, x2, y2]
print("xywh:", box.xywh) # [x_center, y_center, w, h]
print("conf:", box.conf) # confidence score
print("cls_id:", box.cls_id) # class index
print("cls_name:", box.cls_name)# class label string
print("----")
# ---------------------------
# Segmentation (Masks)
# ---------------------------
if result.masks is not None:
for mask in result.masks:
# mask is a polygon (list of Point2f), not a binary HxW array
pts = [(p.x, p.y) for p in mask.xys]
print("mask cls_id:", int(mask.cls_id))
print("num polygon points:", len(pts))
if pts:
print("first 5 points:", pts[:5])
print("----")
# ---------------------------
# Pose Estimation (Keypoints)
# ---------------------------
if result.keypoints is not None:
for det_id, kps in enumerate(result.keypoints):
print(f"detection {det_id} keypoints:", len(kps))
# each kp has kp.xy (Point2f) and kp.conf
for kp_id, kp in enumerate(kps):
print(f" kp[{kp_id}] = (x={kp.xy.x:.2f}, y={kp.xy.y:.2f}, conf={kp.conf:.3f})")
print("----")
# Optional: draw detections
# annotated = self.pp.draw(frame, result)prepost = mxprepost.MxPrepost(
accl=accl, # [required] mxapi.MxAccl instance
task="yolov8-det", # [required] Expected format: yolov<n>-[det|seg|pose]
conf=0.3, # [optional] Default 0.3
iou=0.4, # [optional] Default 0.4
classmap_path="/path/to/classmap.txt" # [optional] Path to a .txt file containing custom class names (one per line). Defaults to COCO dataset if not given.
custom_class_labels=List[str], # [optional] List of custom class labels, instead of using a text file input.
valid_classes=[0], # [optional] List of class IDs to return. All other detections will be ignored (e.g., [0] for person only in COCO dataset).
class_agnostic=False, # [optional] If False, use class-specific NMS. If True, use class-agnostic NMS. Default False.
fast_sigmoid=False, # [optional] If True, use an approximated sigmoid function for slightly faster processing. Default False.
model_id=0, # [optional] model_id in the .dfp file. Default 0
override_layer_mapping={} # [optional] Advanced feature to override the per-stride layer name->dfp port mapping.
)-
taskexamples:yolov8-detyolov11-segyolov11-pose
-
model_idis required only if multiple models are compiled into the same DFP.
git clone --recurse-submodules git@github.com:memryx/MxPrepost.git
cd MxPrepost# activate your virtualenv with MemryX SDK 2.2
source ~/.mx/bin/activate
sh build.shcd MxPrepost/samples/python
# link mxprepost module in same directory as you targeted python code.
ln -sfv ../../pymodule/build/mxprepost.cpython-*.so .Copy the .so file to the same path as your python code if working in another directory.
python run.py \
-d models/yolov8.dfp \
-t yolov8-det \
--video_paths /dev/video0python run.py \
-d models/yolov8.dfp \
-t yolov8-det \
--video_paths videos/sample.mp4python run.py \
-d models/yolov8.dfp \
-t yolov8-det \
--video_paths /dev/video0 videos/sample.mp4python run.py \
-d models/yolov8.dfp \
-t yolov8-det \
--video_paths /dev/video0 \
--no-show| Argument | Description | Default |
|---|---|---|
-d, --dfp |
Path to compiled .dfp file |
Required |
-t, --task |
YOLO task (yolov8-det, yolov11-seg, yolov11-pose, etc.) |
Required |
--video_paths |
One or more input sources (camera or video files) | /dev/video0 |
--no-show |
Disable display window | Display enabled by default |
The C++ API follows the same pattern as Python:
-
Create
MX::Runtime::MxAccl -
Connect input/output callbacks
-
Create
MxPrepostwith the accelerator + task -
Call:
preprocess(frame)in the input callbackpostprocess(outputs, ori_w, ori_h)(orpostprocess(outputs, result, original_image)) in the output callbackdraw(frame, result)for visualization
#include <memx/accl/MxAccl.h>
#include <memx/prepost/MxPrepost.h>
#include <opencv2/opencv.hpp>
using namespace MX::Runtime;
// Create MxAccl (dfp path, device ids, use_model_shape flags, local)
MxAccl accl{dfp_path, {0}, {false, false}, /*local=*/false};
//---------------------------------------------------------------
//-------------------------- MxPrepost --------------------------
//---------------------------------------------------------------
// Create MxPrepost (task examples: "yolov8-det", "yolov8-seg", "yolov11-pose")
YoloUserConfig cfg; // inits with the defaults for COCO
cfg.conf = 0.3f;
cfg.iou = 0.4f;
std::unique_ptr<MxPrepost> pp{MxPrepost::create(&accl, task, cfg)};
// Get original dimensions once (from your cv::VideoCapture)
int ori_w = (int)cap.get(cv::CAP_PROP_FRAME_WIDTH);
int ori_h = (int)cap.get(cv::CAP_PROP_FRAME_HEIGHT);
// --- Input callback: capture + preprocess ---
auto in_cb = [&](std::vector<const MX::Types::FeatureMap*> dst, int stream_id) -> bool {
cv::Mat frame;
if (!cap.read(frame)) return false;
//---------------------------------------------------------------
//-------------------------- MxPrepost --------------------------
//---------------------------------------------------------------
std::unique_ptr<MxPrepost> pp;
// ===========================
// Method 1: Throwing create()
// ===========================
try {
pp.reset(MxPrepost::create(&accl, task, cfg)); // may throw (e.g., unsupported task)
} catch (const std::exception& e) {
std::cerr << "Failed to create MxPrepost: " << e.what() << "\n";
return 1; // or handle error appropriately
}
// ==================================
// Method 2: No-throw create_safe()
// ==================================
// std::string err;
// if (!MxPrepost::create_safe(&accl, task, cfg, pp, err)) {
// std::cerr << "Failed to create MxPrepost: " << err << "\n";
// return 1; // or handle error appropriately
// }
dst[0]->set_data(reinterpret_cast<float*>(input.data));
return true;
};
// --- Output callback: postprocess
auto out_cb = [&](std::vector<const MX::Types::FeatureMap*> outs, int stream_id) -> bool {
// Copy accelerator outputs into your own float buffers, then build float* vector:
//---------------------------------------------------------------
//-------------------------- MxPrepost --------------------------
//---------------------------------------------------------------
Result result;
pp->postprocess(ofmap_ptrs, result, ori_h, ori_w); // or: pp->postprocess(ofmap_ptrs, result, original_frame)
// Access outputs (depending on task)
// ---------------------------
// Detection (Bounding Boxes)
// ---------------------------
for (const auto& box : result.boxes) {
// box.xyxy = [x1,y1,x2,y2], box.xywh = [xc,yc,w,h]
// box.conf, box.cls_id, box.cls_name
}
// ---------------------------
// Segmentation (polygons)
// ---------------------------
for (const auto& mask : result.masks) {
// mask.xys is a polygon: vector<Point2f>
// mask.cls_id
}
// ---------------------------
// Pose (keypoints)
// ---------------------------
for (size_t det_id = 0; det_id < result.keypoints.size(); ++det_id) {
const auto& kps = result.keypoints[det_id]; // vector<Keypoint>
// each kp has kp.xy (Point2f) and kp.conf
}
// Optional visualization
// pp->draw(frame, result);
return true;
};
accl.connect_stream(in_cb, out_cb, /*stream_id=*/0);
accl.start();
accl.wait();- Prefer
postprocess(ofmaps, result, ori_h, ori_w)when you already know original frame size. - Use
postprocess(ofmaps, result, original_frame)if you have the originalcv::Matavailable in the output callback.
SyncAcclAsyncAcclMultistreamAsyncAccl
Only mxapi.MxAccl is supported.
- ✅ YOLOv7 (det only)
- ✅ YOLOv8 (det / seg / pose)
- ✅ YOLOv9 (det only)
- ✅ YOLOv10 (det only)
- ✅ YOLOv11 (det / seg / pose)
- ✅ Custom detection datasets for all of the above
- ✅ Custom model resolutions (not just 640x640)