Docs / Concepts / Pre- & post-processing
Pre- & post-processingv0.2.0
Real inference rarely starts with a model-ready tensor. Sensor data has to be resized, normalized and quantized on the way in, and raw model output has to be turned into class labels or bounding boxes on the way out. SynapticOS treats these transformations as first-class pipeline stages with a stable function signature, a set of built-in processors declared in syn_infer.h, and a DSP HAL underneath for hardware acceleration.
Processing stages in the pipeline
A pipeline is an ordered chain: zero or more pre-processing stages, one model, zero or more post-processing stages. Every stage — built-in or your own — implements the same signature: it reads one tensor, writes another, and takes an optional per-stage configuration pointer.
/* syn_infer.h — pre/post-processing function signatures */
typedef int (*syn_preprocess_fn_t)(const syn_tensor_t *in,
syn_tensor_t *out, const void *config);
typedef int (*syn_postprocess_fn_t)(const syn_tensor_t *in,
syn_tensor_t *out, const void *config);Stages are attached with syn_pipeline_add_preprocess() and syn_pipeline_add_postprocess() during pipeline construction. A typical image-classification pipeline chains resize, INT8 quantization, the model, then softmax:
syn_pipeline_add_preprocess(pipe, syn_preprocess_image_resize,
&(syn_resize_config_t){.w=96, .h=96});
syn_pipeline_add_preprocess(pipe, syn_preprocess_quantize_int8, NULL);
syn_pipeline_add_model(pipe, model);
syn_pipeline_add_postprocess(pipe, syn_postprocess_softmax, NULL);See Inference pipelines & scheduler for how the built pipeline is submitted and executed.
Built-in pre-processors
Four built-in pre-processors are declared in syn_infer.h and implemented (as of Phase 2) across src/preprocess/:
| Symbol | Transformation | Source file |
|---|---|---|
syn_preprocess_image_resize | Edge-aligned bilinear resize of [1, H, W, C] byte images to the target dimensions in syn_resize_config_t (w, h) | syn_preprocess_image.c |
syn_preprocess_image_normalize | Per-channel (x − mean) / std to float32, configured with syn_normalize_config_t | syn_preprocess_image.c |
syn_preprocess_quantize_int8 | Float32 → INT8: q = round(x / scale) + zero_point, configured with syn_quantize_config_t | syn_preprocess_quant.c |
syn_preprocess_audio_mfcc | Raw float32 audio → MFCC features: Hamming window → FFT → mel filterbank → log → DCT-II, configured with syn_mfcc_config_t. Documented simplifications: non-overlapping frames, no pre-emphasis | syn_preprocess_audio.c |
The resize configuration struct lives in syn_infer.h; every other config type is defined in the Phase 2 header syn_process.h:
typedef struct { uint16_t w; uint16_t h; } syn_resize_config_t;Built-in post-processors
Five built-in post-processors, all implemented in Phase 2, turn raw model output into application-level results:
| Symbol | Transformation | Source file |
|---|---|---|
syn_postprocess_softmax | Logits → normalized classification scores. Accepts int8 or float32 input; an optional syn_dequantize_config_t dequantizes int8 logits first | syn_postprocess_classify.c |
syn_postprocess_argmax | Top-1 class — emits one syn_classification_t (uint32_t index, float score) | syn_postprocess_classify.c |
syn_postprocess_top_k | Top-K classes — emits k syn_classification_t entries (syn_topk_config_t) | syn_postprocess_classify.c |
syn_postprocess_nms | Greedy per-class non-max suppression over syn_bbox_t records (syn_nms_config_t: IoU threshold, score threshold, max boxes) | syn_postprocess_detect.c |
syn_postprocess_dequantize | INT8 → Float32: x = (q − zero_point) × scale (syn_dequantize_config_t) | syn_postprocess_classify.c |
The result-record layouts (syn_classification_t, syn_bbox_t) and all the config structures are documented field-by-field in the syn_process.h reference.
DSP acceleration via the DSP HAL
Pre- and post-processing is exactly the workload the MCXN947's PowerQuad co-processor exists for. The built-in stages are designed to call through syn_hal_dsp.h rather than doing the math on the CPU:
/* syn_hal_dsp.h — DSP HAL (PowerQuad) */
int syn_hal_dsp_normalize_int8(const uint8_t *in, int8_t *out,
size_t len, float scale, int32_t zero_point);
int syn_hal_dsp_softmax_f32(const float *in, float *out, size_t len);
int syn_hal_dsp_argmax(const int8_t *data, size_t len, uint32_t *index);
int syn_hal_dsp_fft_f32(const float *in, float *out, size_t len);
int syn_hal_dsp_mat_mult_q15(const int16_t *a, const int16_t *b,
int16_t *out, uint16_t rows, uint16_t cols);The mapping is direct: softmax uses syn_hal_dsp_softmax_f32(), argmax uses syn_hal_dsp_argmax(), and the MFCC front-end builds on syn_hal_dsp_fft_f32(). Because everything goes through the HAL, the same pipeline code runs on QEMU (software fallback) and on the FRDM board.
As of v0.2.0 every DSP HAL function is implemented and no longer returns -ENOTSUP — and on the MCXN947 the FFT and Q15 matrix multiply run on the PowerQuad hardware, with a boot-time self-calibration and per-operation software fallback (out-of-range sizes and failed calibration checks drop to the shared kernels in src/hal/common/syn_dsp_soft.c). Measured on the board: 5.51× vs software for a 256-point FFT and 1.66× for a 16×16 Q15 matrix × vector multiply, wrapper conversion cost included — short of the phase plan's ≥10× target, reported as-is. See NPU, DSP & DMA abstraction.
Implementation status (Phase 2)
Honest status: all five processing source files, placeholders in v0.1.0, are fully implemented as of v0.2.0:
| File | Content | Status in v0.2.0 |
|---|---|---|
src/preprocess/syn_preprocess_image.c | Edge-aligned bilinear resize, per-channel normalize | Implemented |
src/preprocess/syn_preprocess_audio.c | MFCC feature extraction (Hamming → FFT → mel → log → DCT-II) | Implemented |
src/preprocess/syn_preprocess_quant.c | Float32 → INT8 quantization | Implemented |
src/postprocess/syn_postprocess_classify.c | Softmax, argmax, top-K, dequantize | Implemented |
src/postprocess/syn_postprocess_detect.c | Greedy per-class non-max suppression | Implemented |
The nine built-in processors are exercised by the QEMU test suite (syn_process_suite, 15 tests, part of the 99-test / 13-suite run) and verified live on the FRDM-MCXN947 (2026-07-12): the face_detection sample runs resize → normalize → quantize → model → decode → NMS continuously on the board at 215.8 FPS average over 30 frames — with the deterministic stub NPU backend, as always labeled. v0.2.0 is tagged and released.