Hey-Edge / edge-impulse-sdk /classifier /ei_data_normalization.h
eoinedge's picture
Publish Edge Impulse model via ModelForge
25ade36 verified
Raw
History Blame Contribute Delete
7.37 kB
/* The Clear BSD License
*
* Copyright (c) 2025 EdgeImpulse Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted (subject to the limitations in the disclaimer
* below) provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
* THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __EI_DATA_NORMALIZATION_H__
#define __EI_DATA_NORMALIZATION_H__
#include "model-parameters/model_metadata.h"
#include "edge-impulse-sdk/classifier/ei_model_types.h"
#include "edge-impulse-sdk/classifier/ei_classifier_types.h"
#include "edge-impulse-sdk/porting/ei_classifier_porting.h"
#if EI_CLASSIFIER_HAS_DATA_NORMALIZATION
extern "C" EI_IMPULSE_ERROR init_data_normalization(ei_impulse_handle_t *handle) {
if (!handle) {
return EI_IMPULSE_OUT_OF_MEMORY;
}
auto impulse = handle->impulse;
for (size_t i = 0; i < impulse->dsp_blocks_size; i++) {
if(impulse->dsp_blocks[i].data_normalization_config) {
auto dn_config = impulse->dsp_blocks[i].data_normalization_config;
if (dn_config->init_fn) {
EI_IMPULSE_ERROR res = dn_config->init_fn(handle);
if (res != EI_IMPULSE_OK) {
return res;
}
}
}
}
return EI_IMPULSE_OK;
}
extern "C" EI_IMPULSE_ERROR deinit_data_normalization(ei_impulse_handle_t *handle) {
if (!handle) {
return EI_IMPULSE_OUT_OF_MEMORY;
}
auto impulse = handle->impulse;
for (size_t i = 0; i < impulse->dsp_blocks_size; i++) {
if(impulse->dsp_blocks[i].data_normalization_config) {
auto dn_config = impulse->dsp_blocks[i].data_normalization_config;
if (dn_config->deinit_fn) {
EI_IMPULSE_ERROR res = dn_config->deinit_fn(handle);
if (res != EI_IMPULSE_OK) {
return res;
}
}
}
}
return EI_IMPULSE_OK;
}
extern "C" EI_IMPULSE_ERROR run_data_normalization(ei_impulse_handle_t *handle,
ei_feature_t *features) {
if (!handle) {
return EI_IMPULSE_OUT_OF_MEMORY;
}
auto impulse = handle->impulse;
for (size_t i = 0; i < impulse->dsp_blocks_size; i++) {
auto dsp_block = impulse->dsp_blocks[i];
if(dsp_block.data_normalization_config
&& dsp_block.data_normalization_config->config) {
auto dn_config = impulse->dsp_blocks[i].data_normalization_config;
if (dn_config->exec_fn) {
EI_IMPULSE_ERROR res = dn_config->exec_fn((void*)&handle->impulse->dsp_blocks[i], features[i].matrix);
if (res != EI_IMPULSE_OK) {
return res;
}
}
}
}
return EI_IMPULSE_OK;
}
EI_IMPULSE_ERROR data_normalization_standard_scaler(
void *dsp_block,
matrix_t *input_matrix)
{
// Standard scaler implements:
// z = (x - u) * s'
//
// where:
// x is the feature
// u is the mean
// s' is 1/s
// s is the standard deviation
if (dsp_block == NULL) {
return EI_IMPULSE_DATA_NORMALIZATION_ERROR;
}
EI_IMPULSE_ERROR ret = EI_IMPULSE_DATA_NORMALIZATION_ERROR;
ei_model_dsp_t *block = (ei_model_dsp_t *)dsp_block;
ei_data_normalization_t *dn_config = (ei_data_normalization_t *)block->data_normalization_config;
// We only use mean and scale here.
if (dn_config->config) {
ei_data_normalization_standard_scaler_config_t *sc_config = (ei_data_normalization_standard_scaler_config_t *) dn_config->config;
if(sc_config->mean_data && sc_config->scale_data && sc_config->var_data
&& (sc_config->mean_data_len > 0)
&& (sc_config->scale_data_len > 0) && (sc_config->var_data_len > 0)) {
if (input_matrix->rows != 1) {
ei_printf("ERR: data normalization: input matrix invalid num of rows, expected: (1), got (%d)\n", input_matrix->rows);
return EI_IMPULSE_INVALID_SIZE;
}
const uint32_t numb_els_input = input_matrix->rows * input_matrix->cols;
if (block->n_output_features != numb_els_input) {
ei_printf("ERR: data normalization: input matrix size, expected (%ld), got (%d)\n", block->n_output_features, numb_els_input);
return EI_IMPULSE_INVALID_SIZE;
}
uint32_t numb_els = 0;
numb_els = sc_config->mean_data_len;
if (numb_els != numb_els_input) {
ei_printf("ERR: data normalization: mean size mismatch, expected (%d), got (%d)\n", numb_els_input, numb_els);
return EI_IMPULSE_INVALID_SIZE;
}
numb_els = sc_config->scale_data_len;
if (numb_els != numb_els_input) {
ei_printf("ERR: data normalization: scale size mismatch, expected (%d), got (%d)\n", numb_els_input, numb_els);
return EI_IMPULSE_INVALID_SIZE;
}
// note: (N, 1)
matrix_t mean_matrix(input_matrix->cols, 1, sc_config->mean_data);
matrix_t scale_matrix(input_matrix->cols, 1, sc_config->scale_data);
// transpose the input matrix from (1, N) -> (N, 1)
matrix_t temp_input_matrix(input_matrix->cols, 1, input_matrix->buffer);
int err = numpy::subtract(&temp_input_matrix, &mean_matrix);
if (err != EIDSP_OK) {
return EI_IMPULSE_DATA_NORMALIZATION_ERROR;
}
err = numpy::scale(&temp_input_matrix, &scale_matrix);
if (err != EIDSP_OK) {
return EI_IMPULSE_DATA_NORMALIZATION_ERROR;
}
ret = EI_IMPULSE_OK;
}
}
return ret;
}
#endif // EI_CLASSIFIER_HAS_DATA_NORMALIZATION
#endif // __EI_DATA_NORMALIZATION_H__